329 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| 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
 | |
|         if context.mode in ("EDIT_GPENCIL", "SCULPT_GPENCIL"):
 | |
|             tool = fix.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) |