gp_toolbox/__init__.py

620 lines
22 KiB
Python
Raw Normal View History

2021-01-10 16:47:17 +01:00
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
bl_info = {
"name": "GP toolbox",
2021-01-10 16:47:17 +01:00
"description": "Set of tools for Grease Pencil in animation production",
"author": "Samuel Bernou, Christophe Seux",
"version": (1, 6, 0),
2021-01-10 16:47:17 +01:00
"blender": (2, 91, 0),
2021-06-05 01:20:35 +02:00
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
2021-01-10 16:47:17 +01:00
"warning": "",
"doc_url": "https://gitlab.com/autour-de-minuit/blender/gp_toolbox",
"tracker_url": "https://gitlab.com/autour-de-minuit/blender/gp_toolbox/-/issues",
2021-01-10 16:47:17 +01:00
"category": "3D View",
}
from . import addon_updater_ops
from .utils import *
from .functions import *
## GMIC
from .GP_guided_colorize import GP_colorize
## direct tools
from . import OP_breakdowner
from . import OP_temp_cutter
from . import OP_playblast_bg
from . import OP_playblast
from . import OP_helpers
from . import OP_keyframe_jump
2021-01-10 16:47:17 +01:00
from . import OP_cursor_snap_canvas
from . import OP_palettes
from . import OP_brushes
2021-01-10 16:47:17 +01:00
from . import OP_file_checker
from . import OP_render
from . import OP_copy_paste
from . import OP_realign
from . import OP_depth_move
from . import OP_key_duplicate_send
from . import OP_layer_manager
from . import OP_material_picker
from . import OP_eraser_brush
from . import TOOL_eraser_brush
from . import handler_draw_cam
2021-01-10 16:47:17 +01:00
from . import keymaps
from .OP_pseudo_tint import GPT_OT_auto_tint_gp_layers
from . import UI_tools
from .properties import GP_PG_ToolsSettings, GP_PG_FixSettings
2021-01-10 16:47:17 +01:00
from bpy.props import (FloatProperty,
BoolProperty,
EnumProperty,
StringProperty,
IntProperty)
import bpy
import os
2021-01-10 16:47:17 +01:00
from bpy.app.handlers import persistent
from pathlib import Path
# from .eyedrop import EyeDropper
# from .properties import load_icons,remove_icons
### prefs
# def set_palette_path(self, context):
# print('value set')
# self.palette_path = Path(bpy.path.abspath(self["palette_path"])).as_posix()
@persistent
def remap_relative(dummy):
# try:
all_path = [lib for lib in bpy.utils.blend_paths(local=True)]
bpy.ops.file.make_paths_relative()
for i, lib in enumerate(bpy.utils.blend_paths(local=True)):
if all_path[i] != lib:
print('Remapped:', all_path[i], '\n>> ', lib)
# except Exception as e:
# print(e)
def remap_on_save_update(self, context):
pref = get_addon_prefs()
if pref.use_relative_remap_on_save:
if not 'remap_relative' in [hand.__name__ for hand in bpy.app.handlers.save_pre]:
bpy.app.handlers.save_pre.append(remap_relative)
else:
if 'remap_relative' in [hand.__name__ for hand in bpy.app.handlers.save_pre]:
bpy.app.handlers.save_pre.remove(remap_relative)
def update_use_precise_eraser(self, context):
km, kmi = TOOL_eraser_brush.addon_keymaps[0]
kmi.active = self.use_precise_eraser
2021-01-10 16:47:17 +01:00
class GPTB_prefs(bpy.types.AddonPreferences):
bl_idname = __name__
use_precise_eraser : BoolProperty(
name='Precise Eraser',
default=False,
update=update_use_precise_eraser
)
2021-01-10 16:47:17 +01:00
## tabs
pref_tabs : EnumProperty(
2021-01-10 16:47:17 +01:00
items=(('PREF', "Preferences", "Change some preferences of the modal"),
('MAN_OPS', "Operator", "Operator to add Manually"),
# ('TUTO', "Tutorial", "How to use the tool"),
('CHECKS', "Check List", "Customise what should happend when hitting 'check fix' button"),
2021-01-10 16:47:17 +01:00
('UPDATE', "Update", "Check and apply updates"),
# ('KEYMAP', "Keymap", "customise the default keymap"),
),
default='PREF')
## addon pref updater props
auto_check_update : BoolProperty(
name="Auto-check for Update",
description="If enabled, auto-check for updates using an interval",
default=False,
)
updater_intrval_months : IntProperty(
name='Months',
description="Number of months between checking for updates",
default=0,
min=0
)
updater_intrval_days : IntProperty(
name='Days',
description="Number of days between checking for updates",
default=7,
min=0,
max=31
)
updater_intrval_hours : IntProperty(
name='Hours',
description="Number of hours between checking for updates",
default=0,
min=0,
max=23
)
updater_intrval_minutes : IntProperty(
name='Minutes',
description="Number of minutes between checking for updates",
default=0,
min=0,
max=59
)
## addon prefs
#--# PROJECT PREFERENCES #--#
2021-01-10 16:47:17 +01:00
# subtype (string) Enumerator in ['FILE_PATH', 'DIR_PATH', 'FILE_NAME', 'BYTE_STRING', 'PASSWORD', 'NONE'].
## fps
use_relative_remap_on_save : BoolProperty(
name="Relative Remap On Save",
description="Always remap all external path to relative when saving\nNeed blender restart if changed",
default=False,
update=remap_on_save_update
)
2021-01-10 16:47:17 +01:00
fps : IntProperty(
name='Frame Rate',
description="Fps of the project, Used to conform the file when you use Check file operator",
default=24,
2021-01-10 16:47:17 +01:00
min=1,
max=10000
)
## output settings for automated renders
output_parent_level = IntProperty(
name='Parent level',
description="Go up in folder to define a render path relative to the file in upper directotys",
default=0,
min=0,
max=20
)
output_path : StringProperty(
name="Output path",
description="Path relative to blend to place render",
default="//render", maxlen=0, subtype='DIR_PATH')
use_env_palettes : BoolProperty(
name="Use Project Palettes",
description="Load the palette path in environnement at startup (key 'PALETTES')",
default=True,
)
2021-01-10 16:47:17 +01:00
palette_path : StringProperty(
name="Palettes directory",
description="Path to palette containing palette.json files to save and load",
default="//", maxlen=0, subtype='DIR_PATH')#, update = set_palette_path
use_env_brushes : BoolProperty(
name="Use Project Brushes",
description="Load the brushes path in environnement at startup (key 'BRUSHES')",
default=True,
)
brush_path : StringProperty(
name="Brushes directory",
description="Path to brushes containing the blends holding the brushes",
default="//", maxlen=0, subtype='DIR_PATH')#, update = set_palette_path
separator : StringProperty(
name="Separator",
description="Character delimiter to use for detecting namespace (prefix), default is '_', space if nothing specified",
default="_", maxlen=0, subtype='NONE')
prefixes : StringProperty(
name="Layers Prefixes",
description="List of prefixes (two capital letters) available for layers(ex: AN,CO,CL)",
default="", maxlen=0)
suffixes : StringProperty(
name="Layers Suffixes",
description="List of suffixes (two capital letters) available for layers(ex: OL,UL)",
default="", maxlen=0)
show_prefix_buttons : BoolProperty(
name="Show Prefix Buttons",
description="Show prefix and suffix buttons above layer stack",
default=False,
)
2021-01-10 16:47:17 +01:00
## Playblast prefs
playblast_auto_play : BoolProperty(
name="Playblast auto play",
description="Open rendered playblast when finished",
default=True,
)
playblast_auto_open_folder : BoolProperty(
name="Playblast auto open location",
description="Open folder of rendered playblast when finished",
default=False,
)
## render settings
render_obj_exclusion : StringProperty(
name="GP obj exclude filter",
description="List comma separated words to exclude from render list",
default="old,rough,trash,test")#, subtype='FILE_PATH')
render_res_x : IntProperty(
name='Resolution X',
description="Resolution on X",
default=2048,
min=1,
max=10000
)
render_res_y : IntProperty(
name='Resolution Y',
description="Resolution on Y",
default=1080,
min=1,
max=10000
)
## KF jumper
kfj_use_shortcut: BoolProperty(
name = "Use Keyframe Jump Shortcut",
description = "Auto bind shotcut for keyframe jump (else you can bind manually using 'screen.gp_keyframe_jump' id_name)",
default = True)
kfj_prev_keycode : StringProperty(
name="Jump Prev Shortcut",
description="Shortcut to trigger previous keyframe jump",
default="F5")
kfj_prev_shift: BoolProperty(
name = "Shift",
description = "add shift",
default = False)
kfj_prev_alt: BoolProperty(
name = "Alt",
description = "add alt",
default = False)
kfj_prev_ctrl: BoolProperty(
name = "combine with ctrl",
description = "add ctrl",
default = False)
kfj_next_keycode : StringProperty(
name="Jump Next Shortcut",
description="Shortcut to trigger keyframe jump",
default="F6")
kfj_next_shift: BoolProperty(
name = "Shift",
description = "add shift",
default = False)
kfj_next_alt: BoolProperty(
name = "Alt",
description = "add alt",
default = False)
kfj_next_ctrl: BoolProperty(
name = "combine with ctrl",
description = "add ctrl",
default = False)
fixprops: bpy.props.PointerProperty(type = GP_PG_FixSettings)
2021-01-10 16:47:17 +01:00
## Temp cutter
# temp_cutter_use_shortcut: BoolProperty(
# name = "Use temp cutter Shortcut",
# description = "Auto assign shortcut for temp_cutter",
# default = True)
def draw(self, context):
layout = self.layout## random color
# layout.use_property_split = True
row= layout.row(align=True)
row.prop(self, "pref_tabs", expand=True)
if self.pref_tabs == 'PREF':
box = layout.box()
box.label(text='Project settings')
## Render
# box.label(text='Render option:')
box.prop(self, 'fps')
row = box.row(align = True)
row.label(text='Render Resolution')
row.prop(self, 'render_res_x', text='Width')
row.prop(self, 'render_res_y', text='Height')
box.prop(self, 'use_relative_remap_on_save')
box.prop(self, "render_obj_exclusion", icon='FILTER')#
subbox = box.box()
2021-06-23 16:46:30 +02:00
subbox.label(text='Project folders:')
## Palette
subbox.prop(self, 'use_env_palettes', text='Use Palettes Environnement Path')
subbox.prop(self, 'palette_path')
## Brushes
subbox.prop(self, 'use_env_brushes', text='Use Brushes Environnement Path')
subbox.prop(self, 'brush_path')
2021-01-10 16:47:17 +01:00
## render output
subbox.prop(self, 'output_path')
## render output
subbox = box.box()
subbox.label(text='Namespace:')
subbox.prop(self, 'separator')
subbox.prop(self, 'show_prefix_buttons', text='Use Prefixes Toggles')
if self.show_prefix_buttons:
subbox.prop(self, 'prefixes')
subbox.prop(self, 'suffixes')
2021-01-10 16:47:17 +01:00
### TODO add render settings
# layout.separator()## Playblast
box = layout.box()
box.label(text='Playblast options:')
box.prop(self, 'playblast_auto_play')
box.prop(self, 'playblast_auto_open_folder')
# box.separator()## Keyframe jumper
box = layout.box()
box.label(text='Keyframe Jump options:')
box.prop(self, "kfj_use_shortcut", text='Bind shortcuts')
if self.kfj_use_shortcut:
prompt = '[TYPE SHORTCUT TO USE (can be with modifiers)]'
if self.kfj_prev_keycode:
mods = '+'.join([m for m, b in [('Ctrl', self.kfj_prev_ctrl), ('Shift', self.kfj_prev_shift), ('Alt', self.kfj_prev_alt)] if b])
text = f'{mods}+{self.kfj_prev_keycode}' if mods else self.kfj_prev_keycode
text = f'Jump Keyframe Prev: {text} (Click to change)'
else:
text = prompt
ops = box.operator('prefs.shortcut_rebinder', text=text, icon='FILE_REFRESH')
ops.s_keycode = 'kfj_prev_keycode'
ops.s_ctrl = 'kfj_prev_ctrl'
ops.s_shift = 'kfj_prev_shift'
ops.s_alt = 'kfj_prev_alt'
if self.kfj_next_keycode:
mods = '+'.join([m for m, b in [('Ctrl', self.kfj_next_ctrl), ('Shift', self.kfj_next_shift), ('Alt', self.kfj_next_alt)] if b])
text = f'{mods}+{self.kfj_next_keycode}' if mods else self.kfj_next_keycode
text = f'Jump Keyframe Next: {text} (Click to change)'
else:
text = prompt
ops = box.operator('prefs.shortcut_rebinder', text=text, icon='FILE_REFRESH')
ops.s_keycode = 'kfj_next_keycode'
ops.s_ctrl = 'kfj_next_ctrl'
ops.s_shift = 'kfj_next_shift'
ops.s_alt = 'kfj_next_alt'
else:
box.label(text="No Jump hotkey auto set. Following operators needs to be set manually", icon="ERROR")
box.label(text="screen.gp_keyframe_jump - preferably in 'screen' category to jump from any editor")
box = layout.box()
box.label(text='Tools options:')
box.prop(self, 'use_precise_eraser')
2021-01-10 16:47:17 +01:00
if self.pref_tabs == 'MAN_OPS':
# layout.separator()## notes
# layout.label(text='Notes:')
layout.label(text='Following operators ID have to be set manually in keymap if needed :')
2021-01-10 16:47:17 +01:00
## keyframe jump
box = layout.box()
box.label(text='GP keyframe jump (consider only GP keyframe, multiple options available at setup)')
row = box.row()
row.label(text='screen.gp_keyframe_jump')
row.operator('wm.copytext', icon='COPYDOWN').text = 'screen.gp_keyframe_jump'
# layout.separator()
## Snap cursor to GP
box = layout.box()
box.label(text='Snap cursor to GP canvas (if not autoset)')
row = box.row()
row.label(text='Look for default 3d snap operators by searching "view3d.cursor3d"')
row.operator('wm.copytext', text='Copy "view3d.cursor3d"', icon='COPYDOWN').text = 'view3d.cursor3d'
row = box.row()
row.label(text='Replace wanted by "view3d.cusor_snap"')
row.operator('wm.copytext', text='Copy "view3d.cusor_snap"', icon='COPYDOWN').text = 'view3d.cusor_snap'
box.label(text='Or just create a new shortcut using cursor_snap')
## Clear keyframe
box = layout.box()
box.label(text='Clear active frame (delete all strokes without deleting the frame)')
row = box.row()
row.label(text='gp.clear_active_frame')
row.operator('wm.copytext', icon='COPYDOWN').text = 'gp.clear_active_frame'
2021-01-10 16:47:17 +01:00
## user prefs
box = layout.box()
box.label(text='Note: You can access user pref file and startup file in config folder')
box.operator("wm.path_open", text='Open config location').filepath = bpy.utils.user_resource('CONFIG')
if self.pref_tabs == 'CHECKS':
layout.label(text='Following checks will be made when clicking "Check File" button:')
col = layout.column()
# row = col.row()
col.prop(self.fixprops, 'check_only')
col.label(text='If dry run is checked, no modification is done')
col.label(text='Note: you can use Ctrl+Click on Check file button to invert the behavior')
col.separator()
col.prop(self.fixprops, 'lock_main_cam')
col.prop(self.fixprops, 'set_scene_res', text=f'Reset Scene Resolution (to {self.render_res_x}x{self.render_res_y})')
col.prop(self.fixprops, 'set_res_percentage')
col.prop(self.fixprops, 'set_fps', text=f'Reset FPS (to {self.fps})')
col.prop(self.fixprops, 'set_slider_n_sync')
col.prop(self.fixprops, 'check_front_axis')
col.prop(self.fixprops, 'check_placement')
col.prop(self.fixprops, 'set_pivot_median_point')
col.prop(self.fixprops, 'disable_guide')
col.prop(self.fixprops, 'list_disabled_anim')
col.prop(self.fixprops, 'autokey_add_n_replace')
#-# col.prop(self.fixprops, 'set_cursor_type')
col.prop(self.fixprops, "select_active_tool", icon='RESTRICT_SELECT_OFF')
col.prop(self.fixprops, "file_path_type")
col.prop(self.fixprops, "lock_object_mode")
# row.label(text='lock the active camera if not a draw cam (and if not "layout" in blendfile name)')
2021-01-10 16:47:17 +01:00
if self.pref_tabs == 'UPDATE':
addon_updater_ops.update_settings_ui(self, context)
### --- ENV_PROP ---
def set_env_properties():
prefs = get_addon_prefs()
fps = os.getenv('FPS')
2021-06-23 16:46:30 +02:00
prefs.fps = int(fps) if fps else prefs.fps
render_height = os.getenv('RENDER_HEIGHT')
2021-06-23 16:46:30 +02:00
prefs.render_res_x = int(render_height) if render_height else prefs.render_res_x
render_width = os.getenv('RENDER_WIDTH')
2021-06-23 16:46:30 +02:00
prefs.render_res_y = int(render_width) if render_width else prefs.render_res_y
palettes = os.getenv('PALETTES')
if prefs.use_env_palettes:
prefs.palette_path = palettes if palettes else prefs.palette_path
brushes = os.getenv('BRUSHES')
if prefs.use_env_brushes:
prefs.brush_path = brushes if brushes else prefs.brush_path
prefix_list = os.getenv('PREFIXES')
prefs.prefixes = prefix_list if prefix_list else prefs.prefixes
suffix_list = os.getenv('SUFFIXES')
prefs.suffixes = suffix_list if suffix_list else prefs.suffixes
separator = os.getenv('SEPARATOR')
prefs.separator = separator if separator else prefs.separator
2021-01-10 16:47:17 +01:00
### --- REGISTER ---
# class GP_PG_ToolsSettings(bpy.types.PropertyGroup) :
# autotint_offset = bpy.props.IntProperty(name="Tint hue offset", description="offset the tint by this value for better color", default=0, min=-5000, max=5000, soft_min=-999, soft_max=999, step=1)#, subtype='PERCENTAGE'
classes = (
GP_PG_FixSettings,
2021-01-10 16:47:17 +01:00
GP_PG_ToolsSettings,
GPTB_prefs,
2021-01-10 16:47:17 +01:00
GPT_OT_auto_tint_gp_layers,
)
# register, unregister = bpy.utils.register_classes_factory(classes)
def register():
addon_updater_ops.register(bl_info)
# bpy.types.Scene.gpfixprops = bpy.props.PointerProperty(type = GP_PG_FixSettings) # used in prefs
2021-01-10 16:47:17 +01:00
for cls in classes:
bpy.utils.register_class(cls)
OP_helpers.register()
OP_keyframe_jump.register()
2021-01-10 16:47:17 +01:00
OP_file_checker.register()
OP_breakdowner.register()
OP_temp_cutter.register()
GP_colorize.register()## GP_guided_colorize.
OP_playblast_bg.register()
OP_playblast.register()
OP_palettes.register()
OP_brushes.register()
2021-01-10 16:47:17 +01:00
OP_cursor_snap_canvas.register()
OP_render.register()
OP_copy_paste.register()
OP_realign.register()
OP_depth_move.register()
OP_key_duplicate_send.register()
OP_layer_manager.register()
OP_eraser_brush.register()
OP_material_picker.register()
TOOL_eraser_brush.register()
handler_draw_cam.register()
2021-01-10 16:47:17 +01:00
UI_tools.register()
keymaps.register()
bpy.types.Scene.gptoolprops = bpy.props.PointerProperty(type = GP_PG_ToolsSettings)
set_env_properties()
## add handler (if option is on)
if get_addon_prefs().use_relative_remap_on_save:
if not 'remap_relative' in [hand.__name__ for hand in bpy.app.handlers.save_pre]:
bpy.app.handlers.save_pre.append(remap_relative)
2021-01-10 16:47:17 +01:00
def unregister():
# remove handler
if 'remap_relative' in [hand.__name__ for hand in bpy.app.handlers.save_pre]:
bpy.app.handlers.save_pre.remove(remap_relative)
2021-01-10 16:47:17 +01:00
keymaps.unregister()
addon_updater_ops.unregister()
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
UI_tools.unregister()
handler_draw_cam.unregister()
OP_material_picker.unregister()
OP_eraser_brush.unregister()
TOOL_eraser_brush.unregister()
OP_layer_manager.unregister()
OP_key_duplicate_send.unregister()
OP_depth_move.unregister()
OP_realign.unregister()
2021-01-10 16:47:17 +01:00
OP_copy_paste.unregister()
OP_render.unregister()
OP_cursor_snap_canvas.unregister()
OP_brushes.unregister()
2021-01-10 16:47:17 +01:00
OP_palettes.unregister()
OP_file_checker.unregister()
OP_helpers.unregister()
OP_keyframe_jump.unregister()
2021-01-10 16:47:17 +01:00
OP_breakdowner.unregister()
OP_temp_cutter.unregister()
GP_colorize.unregister()## GP_guided_colorize.
OP_playblast_bg.unregister()
OP_playblast.unregister()
# del bpy.types.Scene.gpfixprops
2021-01-10 16:47:17 +01:00
del bpy.types.Scene.gptoolprops
if __name__ == "__main__":
register()