import bpy import os from pathlib import Path from .utils import show_message_box, get_addon_prefs class GPTB_OT_file_checker(bpy.types.Operator): bl_idname = "gp.file_checker" bl_label = "File check" bl_description = "Check / correct some aspect of the file, properties and such and report" bl_options = {"REGISTER"} # @classmethod # def poll(cls, context): # return context.region_data.view_perspective == 'CAMERA' ## list of action : # Lock main cam: # set scene res # set scene percentage at 100: # set show slider and sync range # set fps # set cursor type # GP use additive drawing (else creating a frame in dopesheet makes it blank...) # GP stroke placement/projection check # Disabled animation # Set onion skin filter to 'All type' def execute(self, context): prefs = get_addon_prefs() fix = prefs.fixprops problems = [] ## Lock main cam: if fix.lock_main_cam: if not 'layout' in Path(bpy.data.filepath).stem.lower():#dont touch layout cameras if context.scene.camera: cam = context.scene.camera if cam.name == 'draw_cam' and cam.parent: if cam.parent.type == 'CAMERA': cam = cam.parent else: cam = None if cam: triple = (True,True,True) if cam.lock_location[:] != triple or cam.lock_rotation[:] != triple: problems.append('Lock main camera') cam.lock_location = cam.lock_rotation = triple ## set scene res at pref res according to addon pref if fix.set_scene_res: rx, ry = prefs.render_res_x, prefs.render_res_y # TODO set (rx, ry) to camera resolution if specified in camera name if context.scene.render.resolution_x != rx or context.scene.render.resolution_y != ry: problems.append(f'Resolution {context.scene.render.resolution_x}x{context.scene.render.resolution_y} >> {rx}x{ry}') context.scene.render.resolution_x, context.scene.render.resolution_y = rx, ry ## set scene percentage at 100: if fix.set_res_percentage: if context.scene.render.resolution_percentage != 100: problems.append('Resolution output to 100%') context.scene.render.resolution_percentage = 100 ## set fps according to preferences settings if fix.set_fps: if context.scene.render.fps != prefs.fps: problems.append( (f"framerate corrected {context.scene.render.fps} >> {prefs.fps}", 'ERROR') ) context.scene.render.fps = prefs.fps ## set show slider and sync range if fix.set_slider_n_sync: for window in bpy.context.window_manager.windows: screen = window.screen for area in screen.areas: if area.type == 'DOPESHEET_EDITOR': if hasattr(area.spaces[0], 'show_sliders'): setattr(area.spaces[0], 'show_sliders', True) if hasattr(area.spaces[0], 'show_locked_time'): setattr(area.spaces[0], 'show_locked_time', True) ## set cursor type (according to prefs ?) if fix.set_cursor_type: if context.mode in ("EDIT_GPENCIL", "SCULPT_GPENCIL"): tool = prefs.select_active_tool if tool != 'none': if bpy.context.workspace.tools.from_space_view3d_mode(bpy.context.mode, create=False).idname != tool: bpy.ops.wm.tool_set_by_id(name=tool)# Tweaktoolcode problems.append(f'tool changed to {tool.split(".")[1]}') # ## GP use additive drawing (else creating a frame in dopesheet makes it blank...) # if not context.scene.tool_settings.use_gpencil_draw_additive: # problems.append(f'Activated Gp additive drawing mode (snowflake)') # context.scene.tool_settings.use_gpencil_draw_additive = True ## GP stroke placement/projection check if fix.check_front_axis: if context.scene.tool_settings.gpencil_sculpt.lock_axis != 'AXIS_Y': problems.append('/!\\ Draw axis not "Front" (Need Manual change if not Ok)') if fix.check_placement: if bpy.context.scene.tool_settings.gpencil_stroke_placement_view3d != 'ORIGIN': problems.append('/!\\ Draw placement not "Origin" (Need Manual change if not Ok)') ## Disabled animation if fix.list_disabled_anim: fcu_ct = 0 for act in bpy.data.actions: if not act.users: continue for fcu in act.fcurves: if fcu.mute: fcu_ct += 1 print(f"muted: {act.name} > {fcu.data_path}") if fcu_ct: problems.append(f'{fcu_ct} anim channel disabled (details -> console)') ## Use median point if fix.set_pivot_median_point: if context.scene.tool_settings.transform_pivot_point != 'MEDIAN_POINT': problems.append(f"Pivot changed from '{context.scene.tool_settings.transform_pivot_point}' to 'MEDIAN_POINT'") context.scene.tool_settings.transform_pivot_point = 'MEDIAN_POINT' if fix.disable_guide: if context.scene.tool_settings.gpencil_sculpt.guide.use_guide == True: problems.append(f"Disabled Draw Guide") context.scene.tool_settings.gpencil_sculpt.guide.use_guide = False if fix.autokey_add_n_replace: if context.scene.tool_settings.auto_keying_mode != 'ADD_REPLACE_KEYS': problems.append(f"Autokey mode reset to 'Add & Replace'") context.scene.tool_settings.auto_keying_mode = 'ADD_REPLACE_KEYS' # ## Set onion skin filter to 'All type' # fix_kf_type = 0 # for gp in bpy.data.grease_pencils:#from data # if not gp.is_annotation: # if gp.onion_keyframe_type != 'ALL': # gp.onion_keyframe_type = 'ALL' # fix_kf_type += 1 # if fix_kf_type: # problems.append(f"{fix_kf_type} GP onion skin filter to 'All type'") # for ob in context.scene.objects:#from object # if ob.type == 'GPENCIL': # ob.data.onion_keyframe_type = 'ALL' #### --- print fix/problems report if problems: print('===File check===') for p in problems: if isinstance(p, str): print(p) else: print(p[0]) # Show in viewport show_message_box(problems, _title = "Changed Settings", _icon = 'INFO') else: self.report({'INFO'}, 'All good') return {"FINISHED"} """ OLD links checker with show_message_box class GPTB_OT_links_checker(bpy.types.Operator): bl_idname = "gp.links_checker" bl_label = "Links check" bl_description = "Check states of file direct links" bl_options = {"REGISTER"} def execute(self, context): all_lnks = [] has_broken_link = False ## check for broken links for current, lib in zip(bpy.utils.blend_paths(local=True), bpy.utils.blend_paths(absolute=True, local=True)): lfp = Path(lib) realib = Path(current) if not lfp.exists(): has_broken_link = True all_lnks.append( (f"Broken link: {realib.as_posix()}", 'LIBRARY_DATA_BROKEN') )#lfp.as_posix() else: if realib.as_posix().startswith('//'): all_lnks.append( (f"Link: {realib.as_posix()}", 'LINKED') )#lfp.as_posix() else: all_lnks.append( (f"Link: {realib.as_posix()}", 'LIBRARY_DATA_INDIRECT') )#lfp.as_posix() all_lnks.sort(key=lambda x: x[1], reverse=True) if all_lnks: print('===File check===') for p in all_lnks: if isinstance(p, str): print(p) else: print(p[0]) # Show in viewport show_message_box(all_lnks, _title = "Links", _icon = 'INFO') return {"FINISHED"} """ class GPTB_OT_links_checker(bpy.types.Operator): bl_idname = "gp.links_checker" bl_label = "Links check" bl_description = "Check states of file direct links" bl_options = {"REGISTER"} def execute(self, context): return {"FINISHED"} def draw(self, context): layout = self.layout layout.label(text=self.title) if self.broke_ct: layout.label(text="You can try to scan for missing files:") ## How to launch directly without filebrowser ? # in Shot folder layout.operator('file.find_missing_files', text='in parent hierarchy').directory = Path(bpy.data.filepath).parents[1].as_posix() if self.proj: # In Library layout.operator('file.find_missing_files', text='in library').directory = (Path(self.proj)/'library').as_posix() # In all project layout.operator('file.find_missing_files', text='in all project (last resort)').directory = self.proj layout.separator() for l in self.all_lnks: if l[1] == 'LIBRARY_DATA_BROKEN': layout.label(text=l[0], icon=l[1]) else: split=layout.split(factor=0.75) split.label(text=l[0], icon=l[1]) split.operator('wm.path_open', text='Open folder', icon='FILE_FOLDER').filepath = Path(bpy.path.abspath(l[0])).resolve().parent.as_posix() split.operator('wm.path_open', text='Open file', icon='FILE_TICK').filepath = Path(bpy.path.abspath(l[0])).resolve().as_posix()#os.path.abspath(bpy.path.abspath(dirname(l[0]))) def invoke(self, context, event): self.all_lnks = [] self.title = '' self.broke_ct = 0 abs_ct = 0 rel_ct = 0 ## check for broken links for current, lib in zip(bpy.utils.blend_paths(local=True), bpy.utils.blend_paths(absolute=True, local=True)): lfp = Path(lib) realib = Path(current) if not lfp.exists(): self.broke_ct += 1 self.all_lnks.append( (f"{realib.as_posix()}", 'LIBRARY_DATA_BROKEN') )#lfp.as_posix() else: if realib.as_posix().startswith('//'): rel_ct += 1 self.all_lnks.append( (f"{realib.as_posix()}", 'LINKED') )#lfp.as_posix() else: abs_ct += 1 self.all_lnks.append( (f"{realib.as_posix()}", 'LIBRARY_DATA_INDIRECT') )#lfp.as_posix() if not self.all_lnks: self.report({'INFO'}, 'No external links in files') return {"FINISHED"} bct = f"{self.broke_ct} broken " if self.broke_ct else '' act = f"{abs_ct} absolute " if abs_ct else '' rct = f"{rel_ct} clean " if rel_ct else '' self.title = f"{bct}{act}{rct}" self.all_lnks.sort(key=lambda x: x[1], reverse=True) if self.all_lnks: print('===File check===') for p in self.all_lnks: if isinstance(p, str): print(p) else: print(p[0]) # Show in viewport # if broke_ct == 0: # show_message_box(self.all_lnks, _title = self.title, _icon = 'INFO')# Links # return {"FINISHED"} try: self.proj = context.preferences.addons['pipe_sync'].preferences['local_folder'] except: self.proj = None return context.window_manager.invoke_props_dialog(self, width=800) '''### OLD class GPTB_OT_check_scene(bpy.types.Operator): bl_idname = "gp.scene_check" bl_label = "Check GP scene" bl_description = "Check and fix scene settings" bl_options = {"REGISTER"} @classmethod def poll(cls, context): return True def execute(self, context): ## check scene resolution / 100% / framerate context.scene.render.resolution_percentage = 100 context.scene.render.resolution_x = 3072# define addon properties to make generic ? context.scene.render.resolution_y = 1620# define addon properties to make generic ? context.scene.render.fps = 24# define addon properties to make generic ? ## check GP datas name gp_os = [o for o in context.scene.objects if o.type == 'GPENCIL' if o.data.users == 1]#no multiple users for gpo in gp_os: if gpo.data.name.startswith('Stroke'):# dont touch already renamed group if gpo.data.name != gpo.name: print('renaming GP data:', gpo.data.name, '-->', gpo.name) gpo.data.name = gpo.name ## disable autolock context.scene.tool_settings.lock_object_mode = False return {"FINISHED"} ''' classes = ( # GPTB_OT_check_scene, GPTB_OT_file_checker, GPTB_OT_links_checker, ) def register(): for cls in classes: bpy.utils.register_class(cls) def unregister(): for cls in reversed(classes): bpy.utils.unregister_class(cls)