export xlsx

pull/5/head
Christophe SEUX 2023-04-22 15:42:38 +02:00
parent 02785f9ec0
commit d0230672f4
7 changed files with 184 additions and 46 deletions

View File

@ -1,10 +1,12 @@
import importlib import importlib
import re import re
import subprocess import subprocess
import platform
import sys import sys
import unicodedata import unicodedata
from pathlib import Path from pathlib import Path
def install_module(module_name, package_name=None): def install_module(module_name, package_name=None):
'''Install a python module with pip or return it if already installed''' '''Install a python module with pip or return it if already installed'''
try: try:
@ -111,3 +113,22 @@ def read_file(path):
data = txt data = txt
return data return data
def open_file(filepath, env=None, select=False):
if platform.system() == 'Darwin': # macOS
cmd = ['open']
if select:
cmd += ['-R']
elif platform.system() == 'Windows': # Windows
cmd = ['explorer']
if select:
cmd += ['/select,']
else: # linux variants
cmd = ['xdg-open']
if select:
cmd = ['nemo']
cmd += [str(filepath)]
subprocess.Popen(cmd, env=env)

View File

@ -55,7 +55,7 @@ from vse_toolbox.sequencer_utils import (
) )
from vse_toolbox.bl_utils import get_addon_prefs, get_scene_settings, get_strip_settings from vse_toolbox.bl_utils import get_addon_prefs, get_scene_settings, get_strip_settings
from vse_toolbox.file_utils import install_module, norm_name, norm_str from vse_toolbox.file_utils import install_module, norm_name, norm_str, open_file
class VSETB_OT_tracker_connect(Operator): class VSETB_OT_tracker_connect(Operator):
@ -83,16 +83,27 @@ class VSETB_OT_tracker_connect(Operator):
return {"CANCELLED"} return {"CANCELLED"}
def get_custom_name_items(self, context):
settings = get_scene_settings()
project = settings.active_project
return [(m.field_name, m.name, '') for m in project.metadata_types if m.entity_type=='ASSET']
class VSETB_OT_export_spreadsheet(Operator): class VSETB_OT_export_spreadsheet(Operator):
bl_idname = "vse_toolbox.export_spreadsheet" bl_idname = "vse_toolbox.export_spreadsheet"
bl_label = "Export Spreadsheet" bl_label = "Export Spreadsheet"
bl_description = "Export Shot data in a table as a csv or an xlsl" bl_description = "Export Shot data in a table as a csv or an xlsl"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
format : EnumProperty(items=[(i, i, '') for i in ('CSV', 'XLSL')]) format : EnumProperty(items=[(i, i, '') for i in ('CSV', 'XLSX')])
separator : StringProperty(default='\\n') separator : StringProperty(default='\\n')
delimiter : StringProperty(default=';') delimiter : StringProperty(default=';')
export_path : StringProperty(default='//export') export_path : StringProperty(default='//export')
use_custom_name : BoolProperty(default=False)
custom_name : EnumProperty(items=get_custom_name_items,
description='Use a custom name for asset using a metadata value')
open_folder : BoolProperty(default=False)
show_settings : BoolProperty(default=False)
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
@ -122,13 +133,26 @@ class VSETB_OT_export_spreadsheet(Operator):
col = layout.column() col = layout.column()
col.use_property_split = True col.use_property_split = True
col.prop(self, "separator", expand=True, text='Separator') row = col.row(align=True, heading='Custom Name')
row = col.row(align=True) #row.use_property_split = True
row.prop(self, "format", expand=True, text='Format') row.prop(self, 'use_custom_name', text='')
if self.format == 'CSV': sub = row.row(align=True)
col.prop(self, "delimiter", expand=True, text='Delimiter') sub.enabled = self.use_custom_name
sub.prop(self, 'custom_name', text='')
col.prop(self, 'export_path') col.separator()
row = col.row(align=False)
row.prop(self, "format", expand=True, text='Format')
row.prop(self, 'show_settings', text='', icon='PREFERENCES')
if self.show_settings:
col.prop(self, "separator", expand=True, text='Separator')
if self.format == 'CSV':
col.prop(self, "delimiter", expand=True, text='Delimiter')
col.separator()
col.prop(self, 'open_folder', text='Open Folder')
col.prop(self, 'export_path', text='Export Path')
def execute(self, context): def execute(self, context):
#self.report({'ERROR'}, f'Export not implemented yet.') #self.report({'ERROR'}, f'Export not implemented yet.')
@ -143,11 +167,30 @@ class VSETB_OT_export_spreadsheet(Operator):
# Header # Header
rows.append([cell.export_name for cell in cells]) rows.append([cell.export_name for cell in cells])
separator = self.separator.replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r')
delimiter = self.delimiter.replace('\\n', '\n').replace('\\t', '\t').replace('\\r', '\r')
for strip in get_strips('Shots'): for strip in get_strips('Shots'):
row = [] row = []
for cell in cells: for cell in cells:
if cell.is_metadata: if cell.type == "METADATA":
row += [getattr(strip.vsetb_strip_settings.metadata, cell.field_name)] row += [getattr(strip.vsetb_strip_settings.metadata, cell.field_name)]
elif cell.type == "ASSET_TYPE":
asset_castings = []
for asset_casting in strip.vsetb_strip_settings.casting:
asset = asset_casting.asset
if not asset.asset_type == cell.name:
continue
if self.use_custom_name:
if asset.get('metadata', {}).get(self.custom_name):
asset_castings.append(asset['metadata'][self.custom_name])
else:
self.report({'ERROR'}, f'The asset {asset.tracker_name} has no data {self.custom_name}')
else:
asset_castings.append(asset.tracker_name)
row += [separator.join(asset_castings)]
elif cell.field_name == 'EPISODE': elif cell.field_name == 'EPISODE':
row += [settings.active_episode.name] row += [settings.active_episode.name]
elif cell.field_name == 'SEQUENCE': elif cell.field_name == 'SEQUENCE':
@ -188,14 +231,36 @@ class VSETB_OT_export_spreadsheet(Operator):
#2023_04_11_kitsu_boris_ep01_shots #2023_04_11_kitsu_boris_ep01_shots
export_path.parent.mkdir(parents=True, exist_ok=True) export_path.parent.mkdir(parents=True, exist_ok=True)
print('Writing .csv file to', export_path) if self.format == 'CSV':
with open(str(export_path), 'w', newline='\n', encoding='utf-8') as f: print('Writing .csv file to', export_path)
writer = csv.writer(f) with open(str(export_path), 'w', newline='\n', encoding='utf-8') as f:
for row in rows: writer = csv.writer(f, delimiter=self.delimiter)
writer.writerow(row) for row in rows:
writer.writerow(row)
#if show_in_explorer: elif self.format == 'XLSX':
# open_file(filepath, select=True) try:
import openpyxl
except ModuleNotFoundError():
self.report({'INFO'}, 'Installing openpyxl')
openpyxl = install_module('openpyxl')
from openpyxl import Workbook
workbook = Workbook()
worksheet = workbook.active
for row in rows:
worksheet.append(row)
for col in worksheet.columns:
letter = col[0].column_letter
worksheet.column_dimensions[letter].auto_size = True
# Save the file
workbook.save(str(export_path))
if self.open_folder:
open_file(export_path, select=True)
return {"FINISHED"} return {"FINISHED"}
@ -230,7 +295,8 @@ class VSETB_OT_upload_to_tracker(Operator):
status : EnumProperty(items=get_task_status_items) status : EnumProperty(items=get_task_status_items)
comment : StringProperty() comment : StringProperty()
add_preview : BoolProperty(default=True) add_preview : BoolProperty(default=True)
preview_mode : EnumProperty(items=[(m, m.title().replace('_', ' '), '') for m in ('ONLY_NEW', 'REPLACE', 'ADD')]) preview_mode : EnumProperty(items=[(m, m.title().replace('_', ' '), '')
for m in ('ONLY_NEW', 'REPLACE', 'ADD')])
set_main_preview : BoolProperty(default=True) set_main_preview : BoolProperty(default=True)
casting : BoolProperty(default=True) casting : BoolProperty(default=True)
custom_data : BoolProperty(default=True) custom_data : BoolProperty(default=True)
@ -525,9 +591,8 @@ class VSETB_OT_load_assets(Operator):
asset.id = asset_data['id'] asset.id = asset_data['id']
asset.asset_type = asset_data['asset_type'] asset.asset_type = asset_data['asset_type']
datas = asset_data.get('data', {}) #for key, value in asset_data.get('data', {}).items():
for key, values in datas.items(): asset['metadata'] = asset_data.get('data', {})
asset[key] = values
preview_id = asset_data.get('preview_file_id') preview_id = asset_data.get('preview_file_id')
if preview_id: if preview_id:
@ -575,12 +640,13 @@ class VSETB_OT_load_projects(Operator):
episode.name = episode_data['name'] episode.name = episode_data['name']
episode.id = episode_data['id'] episode.id = episode_data['id']
for metadata_data in tracker.get_shots_metadata(project_data): for metadata_data in tracker.get_metadata_types(project_data):
pprint(metadata_data) #pprint(metadata_data)
metadata_type = project.metadata_types.add() metadata_type = project.metadata_types.add()
metadata_type.name = metadata_data['name'] metadata_type.name = metadata_data['name']
metadata_type.field_name = metadata_data['field_name'] metadata_type.field_name = metadata_data['field_name']
metadata_type['choices'] = metadata_data['choices'] metadata_type['choices'] = metadata_data['choices']
metadata_type['entity_type'] = metadata_data['entity_type'].upper()
for status_data in tracker.get_task_statuses(project_data): for status_data in tracker.get_task_statuses(project_data):
#print(metadata_data) #print(metadata_data)
@ -611,6 +677,8 @@ class VSETB_OT_load_projects(Operator):
#bpy.ops.vse_toolbox.load_assets() #bpy.ops.vse_toolbox.load_assets()
self.report({"INFO"}, 'Successfully Load Tracker Projects')
return {'FINISHED'} return {'FINISHED'}

View File

@ -1,8 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
from pathlib import Path
import bpy import bpy
from bpy.types import Panel from bpy.types import Panel
from pathlib import Path
from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings, get_strip_settings) from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings, get_strip_settings)
from vse_toolbox.constants import ASSET_PREVIEWS from vse_toolbox.constants import ASSET_PREVIEWS
from vse_toolbox.sequencer_utils import (set_active_strip, get_channel_name) from vse_toolbox.sequencer_utils import (set_active_strip, get_channel_name)
@ -219,15 +221,6 @@ class VSETB_PT_casting(VSETB_main, Panel):
col = row.column() col = row.column()
col.template_list("VSETB_UL_casting", "shot_casting", strip_settings, "casting", strip_settings, "casting_index", rows=6) col.template_list("VSETB_UL_casting", "shot_casting", strip_settings, "casting", strip_settings, "casting_index", rows=6)
if strip_settings.casting:
casting_item = strip_settings.casting[strip_settings.casting_index]
asset = casting_item.asset
if asset:
ico = ASSET_PREVIEWS.get(asset.preview)
if ico:
box = col.box()
box.template_icon(icon_value=ico.icon_id, scale=7.5)
col_tool = row.column(align=True) col_tool = row.column(align=True)
col_tool.operator('vse_toolbox.casting_add', icon='ADD', text="") col_tool.operator('vse_toolbox.casting_add', icon='ADD', text="")
col_tool.operator('vse_toolbox.casting_remove', icon='REMOVE', text="") col_tool.operator('vse_toolbox.casting_remove', icon='REMOVE', text="")
@ -238,6 +231,18 @@ class VSETB_PT_casting(VSETB_main, Panel):
col_tool.operator('vse_toolbox.copy_casting', icon='COPYDOWN', text="") col_tool.operator('vse_toolbox.copy_casting', icon='COPYDOWN', text="")
col_tool.operator('vse_toolbox.paste_casting', icon='PASTEDOWN', text="") col_tool.operator('vse_toolbox.paste_casting', icon='PASTEDOWN', text="")
if strip_settings.casting:
casting_item = strip_settings.casting[strip_settings.casting_index]
asset = casting_item.asset
if asset:
if asset.icon_id:
row = col.row(align=True)
#row.scale_y = 0.5
# box = col.box()
# box.template_icon(icon_value=ico.icon_id, scale=7.5)
row.template_icon_view(asset, "previews", show_labels=False)
class VSETB_PT_metadata(VSETB_main, Panel): class VSETB_PT_metadata(VSETB_main, Panel):
bl_label = "Shot Metadata" bl_label = "Shot Metadata"

View File

@ -2,6 +2,7 @@
import bpy import bpy
import os import os
from pathlib import Path
from bpy.props import ( from bpy.props import (
BoolProperty, BoolProperty,
@ -14,7 +15,7 @@ from bpy.props import (
from bpy.types import PropertyGroup, UIList from bpy.types import PropertyGroup, UIList
from pprint import pprint as pp from pprint import pprint as pp
from vse_toolbox.bl_utils import get_addon_prefs, get_scene_settings from vse_toolbox.bl_utils import get_addon_prefs, get_scene_settings
from vse_toolbox.constants import ASSET_PREVIEWS, TRACKERS from vse_toolbox.constants import ASSET_PREVIEWS, TRACKERS, PREVIEWS_DIR
from vse_toolbox.file_utils import norm_str from vse_toolbox.file_utils import norm_str
@ -79,6 +80,12 @@ class CollectionPropertyGroup(PropertyGroup):
return {k: getattr(self, k) for k in self.keys()} return {k: getattr(self, k) for k in self.keys()}
def get_preview_items(self, context):
if self.icon_id:
return [(self.preview, self.tracker_name, '', self.icon_id, 0)]
return []
class Asset(PropertyGroup): class Asset(PropertyGroup):
name : StringProperty(default='') name : StringProperty(default='')
id : StringProperty(default='') id : StringProperty(default='')
@ -86,6 +93,7 @@ class Asset(PropertyGroup):
asset_type : StringProperty(default='') asset_type : StringProperty(default='')
tracker_name : StringProperty(default='') tracker_name : StringProperty(default='')
preview : StringProperty(default='') preview : StringProperty(default='')
previews : EnumProperty(items=get_preview_items)
@property @property
def label(self): def label(self):
@ -112,9 +120,8 @@ class AssetCasting(PropertyGroup):
class SpreadsheetCell(PropertyGroup): class SpreadsheetCell(PropertyGroup):
export_name : StringProperty() export_name : StringProperty()
enabled : BoolProperty(default=True) enabled : BoolProperty(default=True)
is_metadata : BoolProperty(default=False)
field_name : StringProperty() field_name : StringProperty()
#type : EnumProperty(items=[(t, t, "")] for t in ('METADATA', 'STRIP_DATA')) type : EnumProperty(items=[(t, t, "") for t in ('METADATA', 'SHOT', 'ASSET_TYPE')])
class AssetType(PropertyGroup): class AssetType(PropertyGroup):
@ -124,7 +131,8 @@ class AssetType(PropertyGroup):
class MetadataType(PropertyGroup): class MetadataType(PropertyGroup):
choices = [] choices = []
choice : EnumProperty(items=lambda s, c: [(c, c.replace(' ', '_').upper(), '') for c in s['choices']]) choice : EnumProperty(items=lambda s, c: [(c, c.replace(' ', '_').upper(), '') for c in s['choices']])
field_name : bpy.props.StringProperty() field_name : StringProperty()
entity_type : StringProperty()
class TaskType(PropertyGroup): class TaskType(PropertyGroup):
@ -193,15 +201,25 @@ class Project(PropertyGroup):
for cell_name in cell_names: for cell_name in cell_names:
cell = self.spreadsheet.add() cell = self.spreadsheet.add()
cell.name = cell_name cell.name = cell_name
cell.export_name = cell_name cell.export_name = 'Name' if cell_name == 'Shot' else cell_name
cell.field_name = cell_name.upper() cell.field_name = cell_name.upper()
cell.type = "SHOT"
for metadata_type in self.metadata_types: for metadata_type in self.metadata_types:
if not metadata_type['entity_type'] == "SHOT":
continue
cell = self.spreadsheet.add() cell = self.spreadsheet.add()
cell.name = metadata_type.name cell.name = metadata_type.name
cell.export_name = metadata_type.name cell.export_name = metadata_type.name
cell.field_name = metadata_type.field_name cell.field_name = metadata_type.field_name
cell.is_metadata = True cell.type = "METADATA"
for asset_type in self.asset_types:
cell = self.spreadsheet.add()
cell.name = asset_type.name
cell.export_name = asset_type.name
cell.field_name = asset_type.name.upper()
cell.type = "ASSET_TYPE"
def set_strip_metadata(self): def set_strip_metadata(self):
@ -213,6 +231,9 @@ class Project(PropertyGroup):
del Metadata.__annotations__[attr] del Metadata.__annotations__[attr]
for metadata_type in self.metadata_types: for metadata_type in self.metadata_types:
if not metadata_type['entity_type'] == "SHOT":
continue
field_name = metadata_type.field_name field_name = metadata_type.field_name
name = metadata_type.name name = metadata_type.name
@ -237,7 +258,6 @@ class VSETB_UL_casting(UIList):
asset = item.asset asset = item.asset
if asset is None: if asset is None:
#TODO deal if asset was removed
layout.label(text=f'Asset not Found ({item.get("_name", "...")})') layout.label(text=f'Asset not Found ({item.get("_name", "...")})')
return return
@ -250,7 +270,9 @@ class VSETB_UL_casting(UIList):
split = layout.split(factor=0.6) split = layout.split(factor=0.6)
split.label(text=f"{asset.norm_name.title()}") split.label(text=f"{asset.norm_name.title()}")
split.label(text=f"{asset.asset_type.title()}") split.label(text=f"{asset.asset_type.title()}")
split.prop(item, 'instance', text='') sub = layout.row(align=True)
sub.alignment = 'RIGHT'
sub.prop(item, 'instance', text='')
elif self.layout_type in {'GRID'}: elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER' layout.alignment = 'CENTER'
@ -388,12 +410,25 @@ from bpy.app.handlers import persistent
@persistent @persistent
def load_handler(dummy): def load_handler(dummy):
settings = get_scene_settings() settings = get_scene_settings()
if settings.active_project: project = settings.active_project
settings.active_project.set_strip_metadata()
if project:
project.set_strip_metadata()
#settings.active_project.set_spreadsheet() #settings.active_project.set_spreadsheet()
os.environ['TRACKER_PROJECT_ID'] = settings.active_project.id os.environ['TRACKER_PROJECT_ID'] = settings.active_project.id
for asset in project.assets:
preview_id = asset.preview
preview_path = Path(PREVIEWS_DIR / project.id / preview_id).with_suffix('.png')
if preview_path.exists() and preview_id not in ASSET_PREVIEWS:
ASSET_PREVIEWS.load(preview_id, preview_path.as_posix(), 'IMAGE', True)
print(preview_path)
print(ASSET_PREVIEWS)
def register(): def register():
for cls in classes: for cls in classes:
bpy.utils.register_class(cls) bpy.utils.register_class(cls)

View File

@ -128,12 +128,12 @@ class Kitsu(Tracker):
task_types = gazu.task.all_task_types_for_project(project) task_types = gazu.task.all_task_types_for_project(project)
return [t for t in task_types if t['for_entity'].lower() == 'shot'] return [t for t in task_types if t['for_entity'].lower() == 'shot']
def get_shots_metadata(self, project=None): def get_metadata_types(self, project=None):
project = self.get_project(project) project = self.get_project(project)
metadatas = [] metadatas = []
for metadata in gazu.project.all_metadata_descriptors(project): for metadata in gazu.project.all_metadata_descriptors(project):
if metadata['entity_type'] == 'Shot' and metadata['name']: if metadata['name']:
metadatas.append(metadata) metadatas.append(metadata)
return metadatas return metadatas

View File

@ -52,7 +52,7 @@ class TrackerTest(Tracker):
return assets return assets
def get_shots_metadata(self, project): def get_metadata_types(self, project):
metadata = [] metadata = []
for md in gazu.project.all_metadata_descriptors(project): for md in gazu.project.all_metadata_descriptors(project):
asset_type = md.get('asset_type') asset_type = md.get('asset_type')

View File

@ -9,6 +9,7 @@ from bpy.app.handlers import persistent
from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings
from vse_toolbox.constants import SOUND_SUFFIXES from vse_toolbox.constants import SOUND_SUFFIXES
import multiprocessing
def new_text_strip(name='Text', channel=0, start=0, end=50, text='Text', font_size=48, def new_text_strip(name='Text', channel=0, start=0, end=50, text='Text', font_size=48,
@ -136,6 +137,14 @@ def render_strips(strips, template):
scene_end = scn.frame_end scene_end = scn.frame_end
render_path = scn.render.filepath render_path = scn.render.filepath
# pool = multiprocessing.Pool(4)
# p.map(func, range(1, 100))
# def render_strip_background(index):
# cmd = [bpy.app.binary_path, etc]
# process = subprocess.Popen(substr + " --index {}".format(index), shell=True, stdout=subprocess.PIPE)
for strip in strips: for strip in strips:
#print(render_template, strip.name, path) #print(render_template, strip.name, path)