2025-07-01
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
Copyright (C) 2019 Remington Creative
|
||||
|
||||
This file is part of Atomic Data Manager.
|
||||
|
||||
Atomic Data Manager 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.
|
||||
|
||||
Atomic Data Manager is distributed in the hope that it will
|
||||
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY 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 Atomic Data Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
This file handles the registration of the atomic_data_manager.ui package
|
||||
|
||||
"""
|
||||
|
||||
from atomic_data_manager.ui import main_panel_ui
|
||||
from atomic_data_manager.ui import stats_panel_ui
|
||||
from atomic_data_manager.ui import inspect_ui
|
||||
from atomic_data_manager.ui import missing_file_ui
|
||||
from atomic_data_manager.ui import missing_file_ui
|
||||
from atomic_data_manager.ui import pie_menu_ui
|
||||
from atomic_data_manager.ui import preferences_ui
|
||||
from atomic_data_manager.ui import support_me_ui
|
||||
|
||||
|
||||
def register():
|
||||
# register preferences first so we can access variables in config.py
|
||||
preferences_ui.register()
|
||||
|
||||
# register everything else
|
||||
main_panel_ui.register()
|
||||
stats_panel_ui.register()
|
||||
inspect_ui.register()
|
||||
missing_file_ui.register()
|
||||
pie_menu_ui.register()
|
||||
support_me_ui.register()
|
||||
|
||||
|
||||
def unregister():
|
||||
main_panel_ui.unregister()
|
||||
stats_panel_ui.unregister()
|
||||
inspect_ui.unregister()
|
||||
missing_file_ui.unregister()
|
||||
pie_menu_ui.unregister()
|
||||
preferences_ui.unregister()
|
||||
support_me_ui.unregister()
|
||||
@@ -0,0 +1,722 @@
|
||||
"""
|
||||
Copyright (C) 2019 Remington Creative
|
||||
|
||||
This file is part of Atomic Data Manager.
|
||||
|
||||
Atomic Data Manager 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.
|
||||
|
||||
Atomic Data Manager is distributed in the hope that it will
|
||||
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY 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 Atomic Data Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
This file contains the inspection user interface.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from bpy.utils import register_class
|
||||
from bpy.utils import unregister_class
|
||||
from atomic_data_manager.stats import users
|
||||
from atomic_data_manager.ui.utils import ui_layouts
|
||||
|
||||
|
||||
# bool that triggers an inspection update if it is True when the
|
||||
# inspection's draw() method is called
|
||||
inspection_update_trigger = False
|
||||
|
||||
|
||||
def update_inspection(self, context):
|
||||
global inspection_update_trigger
|
||||
inspection_update_trigger = True
|
||||
|
||||
|
||||
# Atomic Data Manager Inspect Collections UI Operator
|
||||
class ATOMIC_OT_inspect_collections(bpy.types.Operator):
|
||||
"""Inspect Collections"""
|
||||
bl_idname = "atomic.inspect_collections"
|
||||
bl_label = "Inspect Collections"
|
||||
|
||||
# user lists
|
||||
users_meshes = []
|
||||
users_lights = []
|
||||
users_cameras = []
|
||||
users_others = []
|
||||
users_children = []
|
||||
|
||||
def draw(self, context):
|
||||
global inspection_update_trigger
|
||||
atom = bpy.context.scene.atomic
|
||||
|
||||
layout = self.layout
|
||||
|
||||
# inspect collections box list
|
||||
ui_layouts.inspect_header(
|
||||
layout=layout,
|
||||
atom_prop="collections_field",
|
||||
data="collections"
|
||||
)
|
||||
|
||||
# inspection update code
|
||||
if inspection_update_trigger:
|
||||
|
||||
# if key is valid, update the user lists
|
||||
if atom.collections_field in bpy.data.collections.keys():
|
||||
self.users_meshes = \
|
||||
users.collection_meshes(atom.collections_field)
|
||||
self.users_lights = \
|
||||
users.collection_lights(atom.collections_field)
|
||||
self.users_cameras = \
|
||||
users.collection_cameras(atom.collections_field)
|
||||
self.users_others = \
|
||||
users.collection_others(atom.collections_field)
|
||||
self.users_children = \
|
||||
users.collection_children(atom.collections_field)
|
||||
|
||||
# if key is invalid, empty the user lists
|
||||
else:
|
||||
self.users_meshes = []
|
||||
self.users_lights = []
|
||||
self.users_cameras = []
|
||||
self.users_others = []
|
||||
self.users_children = []
|
||||
|
||||
inspection_update_trigger = False
|
||||
|
||||
# mesh box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Meshes",
|
||||
items=self.users_meshes,
|
||||
icon="OUTLINER_OB_MESH"
|
||||
)
|
||||
|
||||
# light box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Lights",
|
||||
items=self.users_lights,
|
||||
icon="OUTLINER_OB_LIGHT"
|
||||
)
|
||||
|
||||
# camera box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Cameras",
|
||||
items=self.users_cameras,
|
||||
icon="OUTLINER_OB_CAMERA"
|
||||
)
|
||||
|
||||
# other objects box list
|
||||
ui_layouts.box_list_diverse(
|
||||
layout=layout,
|
||||
title="Other",
|
||||
items=self.users_others
|
||||
)
|
||||
|
||||
# child collections box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Child Collections",
|
||||
items=self.users_children,
|
||||
icon="OUTLINER_OB_GROUP_INSTANCE"
|
||||
)
|
||||
|
||||
row = layout.row() # extra row for spacing
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
# update inspection context
|
||||
atom = bpy.context.scene.atomic
|
||||
atom.active_inspection = "COLLECTIONS"
|
||||
|
||||
# trigger update on invoke
|
||||
global inspection_update_trigger
|
||||
inspection_update_trigger = True
|
||||
|
||||
# invoke inspect dialog
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
# Atomic Data Manager Inspect Images UI Operator
|
||||
class ATOMIC_OT_inspect_images(bpy.types.Operator):
|
||||
"""Inspect Images"""
|
||||
bl_idname = "atomic.inspect_images"
|
||||
bl_label = "Inspect Images"
|
||||
|
||||
# user lists
|
||||
users_compositors = []
|
||||
users_materials = []
|
||||
users_node_groups = []
|
||||
users_textures = []
|
||||
users_worlds = []
|
||||
|
||||
def draw(self, context):
|
||||
global inspection_update_trigger
|
||||
atom = bpy.context.scene.atomic
|
||||
|
||||
layout = self.layout
|
||||
|
||||
# inspect images header
|
||||
ui_layouts.inspect_header(
|
||||
layout=layout,
|
||||
atom_prop="images_field",
|
||||
data="images"
|
||||
)
|
||||
|
||||
# inspection update code
|
||||
if inspection_update_trigger:
|
||||
|
||||
# if key is valid, update the user lists
|
||||
if atom.images_field in bpy.data.images.keys():
|
||||
self.users_compositors = \
|
||||
users.image_compositors(atom.images_field)
|
||||
self.users_materials = \
|
||||
users.image_materials(atom.images_field)
|
||||
self.users_node_groups = \
|
||||
users.image_node_groups(atom.images_field)
|
||||
self.users_textures = \
|
||||
users.image_textures(atom.images_field)
|
||||
self.users_worlds = \
|
||||
users.image_worlds(atom.images_field)
|
||||
|
||||
# if key is invalid, empty the user lists
|
||||
else:
|
||||
self.users_compositors = []
|
||||
self.users_materials = []
|
||||
self.users_node_groups = []
|
||||
self.users_textures = []
|
||||
self.users_worlds = []
|
||||
|
||||
inspection_update_trigger = False
|
||||
|
||||
# compositors box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Compositors",
|
||||
items=self.users_compositors,
|
||||
icon="NODE_COMPOSITING"
|
||||
)
|
||||
|
||||
# materials box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Materials",
|
||||
items=self.users_materials,
|
||||
icon="MATERIAL"
|
||||
)
|
||||
|
||||
# node groups box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Node Groups",
|
||||
items=self.users_node_groups,
|
||||
icon="NODETREE"
|
||||
)
|
||||
|
||||
# textures box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Textures",
|
||||
items=self.users_textures,
|
||||
icon="TEXTURE"
|
||||
)
|
||||
|
||||
# worlds box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Worlds",
|
||||
items=self.users_worlds,
|
||||
icon="WORLD"
|
||||
)
|
||||
|
||||
row = layout.row() # extra row for spacing
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
# update inspection context
|
||||
atom = bpy.context.scene.atomic
|
||||
atom.active_inspection = "IMAGES"
|
||||
|
||||
# trigger update on invoke
|
||||
global inspection_update_trigger
|
||||
inspection_update_trigger = True
|
||||
|
||||
# invoke inspect dialog
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
# Atomic Data Manager Inspect Lights UI Operator
|
||||
class ATOMIC_OT_inspect_lights(bpy.types.Operator):
|
||||
"""Inspect Lights"""
|
||||
bl_idname = "atomic.inspect_lights"
|
||||
bl_label = "Inspect Lights"
|
||||
|
||||
# user lists
|
||||
users_objects = []
|
||||
|
||||
def draw(self, context):
|
||||
global inspection_update_trigger
|
||||
atom = bpy.context.scene.atomic
|
||||
|
||||
layout = self.layout
|
||||
|
||||
# inspect lights header
|
||||
ui_layouts.inspect_header(
|
||||
layout=layout,
|
||||
atom_prop="lights_field",
|
||||
data="lights"
|
||||
)
|
||||
|
||||
# inspection update code
|
||||
if inspection_update_trigger:
|
||||
# if key is valid, update the user lists
|
||||
if atom.lights_field in bpy.data.lights.keys():
|
||||
self.users_objects = users.light_objects(atom.lights_field)
|
||||
|
||||
# if key is invalid, empty the user lists
|
||||
else:
|
||||
self.users_objects = []
|
||||
|
||||
inspection_update_trigger = False
|
||||
|
||||
# light objects box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Light Objects",
|
||||
items=self.users_objects,
|
||||
icon="OUTLINER_OB_LIGHT"
|
||||
)
|
||||
|
||||
row = layout.row() # extra row for spacing
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
# update inspection context
|
||||
atom = bpy.context.scene.atomic
|
||||
atom.active_inspection = "LIGHTS"
|
||||
|
||||
# trigger update on invoke
|
||||
global inspection_update_trigger
|
||||
inspection_update_trigger = True
|
||||
|
||||
# invoke inspect dialog
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
# Atomic Data Manager Inspect Materials UI Operator
|
||||
class ATOMIC_OT_inspect_materials(bpy.types.Operator):
|
||||
"""Inspect Materials"""
|
||||
bl_idname = "atomic.inspect_materials"
|
||||
bl_label = "Inspect Materials"
|
||||
|
||||
# user lists
|
||||
users_objects = []
|
||||
|
||||
def draw(self, context):
|
||||
global inspection_update_trigger
|
||||
atom = bpy.context.scene.atomic
|
||||
|
||||
layout = self.layout
|
||||
|
||||
# inspect materials header
|
||||
ui_layouts.inspect_header(
|
||||
layout=layout,
|
||||
atom_prop="materials_field",
|
||||
data="materials"
|
||||
)
|
||||
|
||||
# inspection update code
|
||||
if inspection_update_trigger:
|
||||
|
||||
# if key is valid, update the user lists
|
||||
if atom.materials_field in bpy.data.materials.keys():
|
||||
self.users_objects = \
|
||||
users.material_objects(atom.materials_field)
|
||||
|
||||
# if key is invalid, empty the user lists
|
||||
else:
|
||||
self.users_objects = []
|
||||
|
||||
inspection_update_trigger = False
|
||||
|
||||
# objects box list
|
||||
ui_layouts.box_list_diverse(
|
||||
layout=layout,
|
||||
title="Objects",
|
||||
items=self.users_objects
|
||||
)
|
||||
|
||||
row = layout.row() # extra row for spacing
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
# update inspection context
|
||||
atom = bpy.context.scene.atomic
|
||||
atom.active_inspection = "MATERIALS"
|
||||
|
||||
# trigger update on invoke
|
||||
global inspection_update_trigger
|
||||
inspection_update_trigger = True
|
||||
|
||||
# invoke inspect dialog
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
# Atomic Data Manager Inspect Node Groups UI Operator
|
||||
class ATOMIC_OT_inspect_node_groups(bpy.types.Operator):
|
||||
"""Inspect Node Groups"""
|
||||
bl_idname = "atomic.inspect_node_groups"
|
||||
bl_label = "Inspect Node Groups"
|
||||
|
||||
# user lists
|
||||
users_compositors = []
|
||||
users_materials = []
|
||||
users_node_groups = []
|
||||
users_textures = []
|
||||
users_worlds = []
|
||||
|
||||
def draw(self, context):
|
||||
global inspection_update_trigger
|
||||
atom = bpy.context.scene.atomic
|
||||
|
||||
layout = self.layout
|
||||
|
||||
# inspect node groups header
|
||||
ui_layouts.inspect_header(
|
||||
layout=layout,
|
||||
atom_prop="node_groups_field",
|
||||
data="node_groups"
|
||||
)
|
||||
|
||||
# inspection update code
|
||||
if inspection_update_trigger:
|
||||
|
||||
# if key is valid, update the user lists
|
||||
if atom.node_groups_field in bpy.data.node_groups.keys():
|
||||
|
||||
self.users_compositors = \
|
||||
users.node_group_compositors(atom.node_groups_field)
|
||||
self.users_materials = \
|
||||
users.node_group_materials(atom.node_groups_field)
|
||||
self.users_node_groups = \
|
||||
users.node_group_node_groups(atom.node_groups_field)
|
||||
self.users_textures = \
|
||||
users.node_group_textures(atom.node_groups_field)
|
||||
self.users_worlds = \
|
||||
users.node_group_worlds(atom.node_groups_field)
|
||||
|
||||
# if key is invalid, empty the user lists
|
||||
else:
|
||||
self.users_compositors = []
|
||||
self.users_materials = []
|
||||
self.users_node_groups = []
|
||||
self.users_textures = []
|
||||
self.users_worlds = []
|
||||
|
||||
inspection_update_trigger = False
|
||||
|
||||
# compositors box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Compositors",
|
||||
items=self.users_compositors,
|
||||
icon="NODE_COMPOSITING"
|
||||
)
|
||||
|
||||
# materials box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Materials",
|
||||
items=self.users_materials,
|
||||
icon="MATERIAL"
|
||||
)
|
||||
|
||||
# node groups box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Node Groups",
|
||||
items=self.users_node_groups,
|
||||
icon="NODETREE"
|
||||
)
|
||||
|
||||
# textures box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Textures",
|
||||
items=self.users_textures,
|
||||
icon="TEXTURE"
|
||||
)
|
||||
|
||||
# world box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Worlds",
|
||||
items=self.users_worlds,
|
||||
icon="WORLD"
|
||||
)
|
||||
|
||||
row = layout.row() # extra row for spacing
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
# update inspection context
|
||||
atom = bpy.context.scene.atomic
|
||||
atom.active_inspection = "NODE_GROUPS"
|
||||
|
||||
# trigger update on invoke
|
||||
global inspection_update_trigger
|
||||
inspection_update_trigger = True
|
||||
|
||||
# invoke inspect dialog
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
# Atomic Data Manager Inspect Particles UI Operator
|
||||
class ATOMIC_OT_inspect_particles(bpy.types.Operator):
|
||||
"""Inspect Particle Systems"""
|
||||
bl_idname = "atomic.inspect_particles"
|
||||
bl_label = "Inspect Particles"
|
||||
|
||||
# user lists
|
||||
users_objects = []
|
||||
|
||||
def draw(self, context):
|
||||
global inspection_update_trigger
|
||||
atom = bpy.context.scene.atomic
|
||||
|
||||
layout = self.layout
|
||||
|
||||
# inspect particles header
|
||||
ui_layouts.inspect_header(
|
||||
layout=layout,
|
||||
atom_prop="particles_field",
|
||||
data="particles"
|
||||
)
|
||||
|
||||
# inspection update code
|
||||
if inspection_update_trigger:
|
||||
|
||||
# if key is valid, update the user lists
|
||||
if atom.particles_field in bpy.data.particles.keys():
|
||||
|
||||
self.users_objects = \
|
||||
users.particle_objects(atom.particles_field)
|
||||
|
||||
# if key is invalid, empty the user lists
|
||||
else:
|
||||
self.users_objects = []
|
||||
|
||||
inspection_update_trigger = False
|
||||
|
||||
# objects box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Objects",
|
||||
items=self.users_objects,
|
||||
icon="OUTLINER_OB_MESH"
|
||||
)
|
||||
|
||||
row = layout.row() # extra row for spacing
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
# update inspection context
|
||||
atom = bpy.context.scene.atomic
|
||||
atom.active_inspection = "PARTICLES"
|
||||
|
||||
# trigger update on invoke
|
||||
global inspection_update_trigger
|
||||
inspection_update_trigger = True
|
||||
|
||||
# invoke inspect dialog
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
# Atomic Data Manager Inspect Textures UI Operator
|
||||
class ATOMIC_OT_inspect_textures(bpy.types.Operator):
|
||||
"""Inspect Textures"""
|
||||
bl_idname = "atomic.inspect_textures"
|
||||
bl_label = "Inspect Textures"
|
||||
|
||||
# user lists
|
||||
users_compositors = []
|
||||
users_brushes = []
|
||||
users_particles = []
|
||||
users_objects = []
|
||||
|
||||
def draw(self, context):
|
||||
global inspection_update_trigger
|
||||
atom = bpy.context.scene.atomic
|
||||
|
||||
layout = self.layout
|
||||
|
||||
# inspect textures header
|
||||
ui_layouts.inspect_header(
|
||||
layout=layout,
|
||||
atom_prop="textures_field",
|
||||
data="textures"
|
||||
)
|
||||
|
||||
# inspection update code
|
||||
if inspection_update_trigger:
|
||||
|
||||
# if the key is valid, update the user lists
|
||||
if atom.textures_field in bpy.data.textures.keys():
|
||||
|
||||
self.users_compositors = \
|
||||
users.texture_compositor(atom.textures_field)
|
||||
self.users_brushes = \
|
||||
users.texture_brushes(atom.textures_field)
|
||||
self.users_objects = \
|
||||
users.texture_objects(atom.textures_field)
|
||||
self.users_particles = \
|
||||
users.texture_particles(atom.textures_field)
|
||||
|
||||
# if the key is invalid, set empty the user lists
|
||||
else:
|
||||
self.users_compositors = []
|
||||
self.users_brushes = []
|
||||
self.users_particles = []
|
||||
self.users_objects = []
|
||||
|
||||
inspection_update_trigger = False
|
||||
|
||||
# brushes box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Brushes",
|
||||
items=self.users_brushes,
|
||||
icon="BRUSH_DATA"
|
||||
)
|
||||
|
||||
# compositors box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Compositors",
|
||||
items=self.users_compositors,
|
||||
icon="NODE_COMPOSITING"
|
||||
)
|
||||
|
||||
# particles box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Particles",
|
||||
items=self.users_particles,
|
||||
icon="PARTICLES"
|
||||
)
|
||||
|
||||
# objects box list
|
||||
ui_layouts.box_list_diverse(
|
||||
layout=layout,
|
||||
title="Objects",
|
||||
items=self.users_objects,
|
||||
)
|
||||
|
||||
row = layout.row() # extra row for spacing
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
# update inspection context
|
||||
atom = bpy.context.scene.atomic
|
||||
atom.active_inspection = "TEXTURES"
|
||||
|
||||
# trigger update on invoke
|
||||
global inspection_update_trigger
|
||||
inspection_update_trigger = True
|
||||
|
||||
# invoke inspect dialog
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
# Atomic Data Manager Inspect Worlds UI Operator
|
||||
class ATOMIC_OT_inspect_worlds(bpy.types.Operator):
|
||||
"""Inspect Worlds"""
|
||||
bl_idname = "atomic.inspect_worlds"
|
||||
bl_label = "Inspect Worlds"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# inspect worlds header
|
||||
ui_layouts.inspect_header(
|
||||
layout=layout,
|
||||
atom_prop="worlds_field",
|
||||
data="worlds"
|
||||
)
|
||||
|
||||
# worlds box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Worlds in Scene",
|
||||
items=bpy.data.worlds.keys(),
|
||||
icon="WORLD"
|
||||
)
|
||||
|
||||
row = layout.row() # extra row for spacing
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
# update inspection context
|
||||
atom = bpy.context.scene.atomic
|
||||
atom.active_inspection = "WORLDS"
|
||||
|
||||
# trigger update on invoke
|
||||
global inspection_update_trigger
|
||||
inspection_update_trigger = True
|
||||
|
||||
# invoke inspect dialog
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
reg_list = [
|
||||
ATOMIC_OT_inspect_collections,
|
||||
ATOMIC_OT_inspect_images,
|
||||
ATOMIC_OT_inspect_lights,
|
||||
ATOMIC_OT_inspect_materials,
|
||||
ATOMIC_OT_inspect_node_groups,
|
||||
ATOMIC_OT_inspect_particles,
|
||||
ATOMIC_OT_inspect_textures,
|
||||
ATOMIC_OT_inspect_worlds
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in reg_list:
|
||||
register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reg_list:
|
||||
unregister_class(cls)
|
||||
@@ -0,0 +1,245 @@
|
||||
"""
|
||||
Copyright (C) 2019 Remington Creative
|
||||
|
||||
This file is part of Atomic Data Manager.
|
||||
|
||||
Atomic Data Manager 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.
|
||||
|
||||
Atomic Data Manager is distributed in the hope that it will
|
||||
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY 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 Atomic Data Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
This file contains the primary Atomic Data Manager panel that will
|
||||
appear in the Scene tab of the Properties panel.
|
||||
|
||||
This panel contains the Nuke/Clean/Undo buttons as well as the data
|
||||
category toggles and the category selection tools.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from bpy.utils import register_class
|
||||
from bpy.utils import unregister_class
|
||||
from atomic_data_manager.stats import count
|
||||
from atomic_data_manager.ui.utils import ui_layouts
|
||||
|
||||
|
||||
# Atomic Data Manager Main Panel
|
||||
class ATOMIC_PT_main_panel(bpy.types.Panel):
|
||||
"""The main Atomic Data Manager panel"""
|
||||
bl_label = "Atomic Data Manager"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
atom = bpy.context.scene.atomic
|
||||
category_props = [
|
||||
atom.collections,
|
||||
atom.images,
|
||||
atom.lights,
|
||||
atom.materials,
|
||||
atom.node_groups,
|
||||
atom.particles,
|
||||
atom.textures,
|
||||
atom.worlds
|
||||
]
|
||||
|
||||
# nuke and clean buttons
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 2.0
|
||||
row.operator("atomic.nuke", text="Nuke", icon="GHOST_ENABLED")
|
||||
row.operator("atomic.clean", text="Clean", icon="PARTICLEMODE")
|
||||
row.operator("atomic.undo", text="Undo", icon="LOOP_BACK")
|
||||
|
||||
row = layout.row()
|
||||
|
||||
# category toggles
|
||||
split = layout.split(align=False)
|
||||
|
||||
# left column
|
||||
col = split.column(align=True)
|
||||
|
||||
# collections buttons
|
||||
splitcol = col.split(factor=0.8, align=True)
|
||||
|
||||
splitcol.prop(
|
||||
atom,
|
||||
"collections",
|
||||
text="Collections",
|
||||
icon='GROUP',
|
||||
toggle=True
|
||||
)
|
||||
|
||||
splitcol.operator(
|
||||
"atomic.inspect_collections",
|
||||
icon='VIEWZOOM',
|
||||
text=""
|
||||
)
|
||||
|
||||
# lights buttons
|
||||
splitcol = col.split(factor=0.8, align=True)
|
||||
|
||||
splitcol.prop(
|
||||
atom,
|
||||
"lights",
|
||||
text="Lights",
|
||||
icon='LIGHT',
|
||||
toggle=True
|
||||
)
|
||||
|
||||
splitcol.operator(
|
||||
"atomic.inspect_lights",
|
||||
icon='VIEWZOOM',
|
||||
text=""
|
||||
)
|
||||
|
||||
# node groups buttons
|
||||
splitcol = col.split(factor=0.8, align=True)
|
||||
|
||||
splitcol.prop(
|
||||
atom,
|
||||
"node_groups",
|
||||
text="Node Groups",
|
||||
icon='NODETREE',
|
||||
toggle=True
|
||||
)
|
||||
|
||||
splitcol.operator(
|
||||
"atomic.inspect_node_groups",
|
||||
icon='VIEWZOOM',
|
||||
text=""
|
||||
)
|
||||
|
||||
# textures button
|
||||
splitcol = col.split(factor=0.8, align=True)
|
||||
|
||||
splitcol.prop(
|
||||
atom,
|
||||
"textures",
|
||||
text="Textures",
|
||||
icon='TEXTURE',
|
||||
toggle=True
|
||||
)
|
||||
|
||||
splitcol.operator(
|
||||
"atomic.inspect_textures",
|
||||
icon='VIEWZOOM',
|
||||
text=""
|
||||
)
|
||||
|
||||
# right column
|
||||
col = split.column(align=True)
|
||||
|
||||
# images buttons
|
||||
splitcol = col.split(factor=0.8, align=True)
|
||||
|
||||
splitcol.prop(
|
||||
atom,
|
||||
"images",
|
||||
text="Images",
|
||||
toggle=True,
|
||||
icon='IMAGE_DATA'
|
||||
)
|
||||
|
||||
splitcol.operator(
|
||||
"atomic.inspect_images",
|
||||
icon='VIEWZOOM',
|
||||
text=""
|
||||
)
|
||||
|
||||
# materials buttons
|
||||
splitcol = col.split(factor=0.8, align=True)
|
||||
|
||||
splitcol.prop(
|
||||
atom,
|
||||
"materials",
|
||||
text="Materials",
|
||||
icon='MATERIAL',
|
||||
toggle=True
|
||||
)
|
||||
|
||||
splitcol.operator(
|
||||
"atomic.inspect_materials",
|
||||
icon='VIEWZOOM',
|
||||
text=""
|
||||
)
|
||||
|
||||
# particles buttons
|
||||
splitcol = col.split(factor=0.8, align=True)
|
||||
|
||||
splitcol.prop(
|
||||
atom,
|
||||
"particles",
|
||||
text="Particles",
|
||||
icon='PARTICLES',
|
||||
toggle=True
|
||||
)
|
||||
|
||||
splitcol.operator(
|
||||
"atomic.inspect_particles",
|
||||
icon='VIEWZOOM',
|
||||
text=""
|
||||
)
|
||||
|
||||
# worlds buttons
|
||||
splitcol = col.split(factor=0.8, align=True)
|
||||
splitcol.prop(
|
||||
atom,
|
||||
"worlds",
|
||||
text="Worlds",
|
||||
icon='WORLD',
|
||||
toggle=True
|
||||
)
|
||||
|
||||
splitcol.operator(
|
||||
"atomic.inspect_worlds",
|
||||
icon='VIEWZOOM',
|
||||
text=""
|
||||
)
|
||||
|
||||
# selection operators
|
||||
row = layout.row(align=True)
|
||||
|
||||
row.operator(
|
||||
"atomic.smart_select",
|
||||
text='Smart Select',
|
||||
icon='ZOOM_SELECTED'
|
||||
)
|
||||
|
||||
if all(prop is True for prop in category_props):
|
||||
row.operator(
|
||||
"atomic.deselect_all",
|
||||
text="Deselect All",
|
||||
icon='RESTRICT_SELECT_ON'
|
||||
)
|
||||
|
||||
else:
|
||||
row.operator(
|
||||
"atomic.select_all",
|
||||
text="Select All",
|
||||
icon='RESTRICT_SELECT_OFF'
|
||||
)
|
||||
|
||||
|
||||
reg_list = [ATOMIC_PT_main_panel]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in reg_list:
|
||||
register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reg_list:
|
||||
unregister_class(cls)
|
||||
@@ -0,0 +1,195 @@
|
||||
"""
|
||||
Copyright (C) 2019 Remington Creative
|
||||
|
||||
This file is part of Atomic Data Manager.
|
||||
|
||||
Atomic Data Manager 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.
|
||||
|
||||
Atomic Data Manager is distributed in the hope that it will
|
||||
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY 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 Atomic Data Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
This file contains the user interface for the missing file dialog that
|
||||
pops up when missing files are detected on file load.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from bpy.utils import register_class
|
||||
from bpy.utils import unregister_class
|
||||
from bpy.app.handlers import persistent
|
||||
from atomic_data_manager import config
|
||||
from atomic_data_manager.stats import missing
|
||||
from atomic_data_manager.ui.utils import ui_layouts
|
||||
|
||||
|
||||
# Atomic Data Manager Detect Missing Files Popup
|
||||
class ATOMIC_OT_detect_missing(bpy.types.Operator):
|
||||
"""Detect missing files in this project"""
|
||||
bl_idname = "atomic.detect_missing"
|
||||
bl_label = "Missing File Detection"
|
||||
|
||||
# missing file lists
|
||||
missing_images = []
|
||||
missing_libraries = []
|
||||
|
||||
# missing file recovery option enum property
|
||||
recovery_option: bpy.props.EnumProperty(
|
||||
items=[
|
||||
(
|
||||
'IGNORE',
|
||||
'Ignore Missing Files',
|
||||
'Ignore the missing files and leave them offline'
|
||||
),
|
||||
(
|
||||
'RELOAD',
|
||||
'Reload Missing Files',
|
||||
'Reload the missing files from their existing file paths'
|
||||
),
|
||||
(
|
||||
'REMOVE',
|
||||
'Remove Missing Files',
|
||||
'Remove the missing files from the project'
|
||||
),
|
||||
(
|
||||
'SEARCH',
|
||||
'Search for Missing Files (under development)',
|
||||
'Search for the missing files in a directory'
|
||||
),
|
||||
(
|
||||
'REPLACE',
|
||||
'Specify Replacement Files (under development)',
|
||||
'Replace missing files with new files'
|
||||
),
|
||||
],
|
||||
default='IGNORE'
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# missing files interface if missing files are found
|
||||
if self.missing_images or self.missing_libraries:
|
||||
|
||||
# header warning
|
||||
row = layout.row()
|
||||
row.label(
|
||||
text="Atomic has detected one or more missing files in "
|
||||
"your project!"
|
||||
)
|
||||
|
||||
# missing images box list
|
||||
if self.missing_images:
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Images",
|
||||
items=self.missing_images,
|
||||
icon="IMAGE_DATA",
|
||||
columns=3
|
||||
)
|
||||
|
||||
# missing libraries box list
|
||||
if self.missing_libraries:
|
||||
ui_layouts.box_list(
|
||||
layout=layout,
|
||||
title="Libraries",
|
||||
items=self.missing_libraries,
|
||||
icon="LIBRARY_DATA_DIRECT",
|
||||
columns=3
|
||||
)
|
||||
|
||||
row = layout.separator() # extra space
|
||||
|
||||
# recovery option selection
|
||||
row = layout.row()
|
||||
row.label(text="What would you like to do?")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self, 'recovery_option', text="")
|
||||
|
||||
# missing files interface if no missing files are found
|
||||
else:
|
||||
row = layout.row()
|
||||
row.label(text="No missing files were found!")
|
||||
|
||||
# empty box list
|
||||
ui_layouts.box_list(
|
||||
layout=layout
|
||||
)
|
||||
|
||||
row = layout.separator() # extra space
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
# ignore missing files will take no action
|
||||
|
||||
# reload missing files
|
||||
if self.recovery_option == 'RELOAD':
|
||||
bpy.ops.atomic.reload_missing('INVOKE_DEFAULT')
|
||||
|
||||
# remove missing files
|
||||
elif self.recovery_option == 'REMOVE':
|
||||
bpy.ops.atomic.remove_missing('INVOKE_DEFAULT')
|
||||
|
||||
# search for missing files
|
||||
elif self.recovery_option == 'SEARCH':
|
||||
bpy.ops.atomic.search_missing('INVOKE_DEFAULT')
|
||||
|
||||
# replace missing files
|
||||
elif self.recovery_option == 'REPLACE':
|
||||
bpy.ops.atomic.replace_missing('INVOKE_DEFAULT')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
|
||||
# update missing file lists
|
||||
self.missing_images = missing.images()
|
||||
self.missing_libraries = missing.libraries()
|
||||
|
||||
wm = context.window_manager
|
||||
|
||||
# invoke large dialog if there are missing files
|
||||
if self.missing_images or self.missing_libraries:
|
||||
return wm.invoke_props_dialog(self, width=500)
|
||||
|
||||
# invoke small dialog if there are no missing files
|
||||
else:
|
||||
return wm.invoke_popup(self, width=300)
|
||||
|
||||
|
||||
@persistent
|
||||
def autodetect_missing_files(dummy=None):
|
||||
# invokes the detect missing popup when missing files are detected upon
|
||||
# loading a new Blender project
|
||||
if config.enable_missing_file_warning and \
|
||||
(missing.images() or missing.libraries()):
|
||||
bpy.ops.atomic.detect_missing('INVOKE_DEFAULT')
|
||||
|
||||
|
||||
reg_list = [ATOMIC_OT_detect_missing]
|
||||
|
||||
|
||||
def register():
|
||||
for item in reg_list:
|
||||
register_class(item)
|
||||
|
||||
# run missing file auto-detection after loading a Blender file
|
||||
bpy.app.handlers.load_post.append(autodetect_missing_files)
|
||||
|
||||
|
||||
def unregister():
|
||||
for item in reg_list:
|
||||
unregister_class(item)
|
||||
|
||||
# stop running missing file auto-detection after loading a Blender file
|
||||
bpy.app.handlers.load_post.remove(autodetect_missing_files)
|
||||
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
Copyright (C) 2019 Remington Creative
|
||||
|
||||
This file is part of Atomic Data Manager.
|
||||
|
||||
Atomic Data Manager 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.
|
||||
|
||||
Atomic Data Manager is distributed in the hope that it will
|
||||
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY 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 Atomic Data Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
This file contains Atomic's pie menu UI and its pie menu keymap
|
||||
registration.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from bpy.utils import register_class
|
||||
from bpy.utils import unregister_class
|
||||
|
||||
|
||||
# Atomic Data Manager Main Pie Menu
|
||||
class ATOMIC_MT_main_pie(bpy.types.Menu):
|
||||
bl_idname = "ATOMIC_MT_main_pie"
|
||||
bl_label = "Atomic Data Manager"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
|
||||
# nuke all operator
|
||||
pie.operator(
|
||||
"atomic.nuke_all",
|
||||
text="Nuke All",
|
||||
icon="GHOST_ENABLED"
|
||||
)
|
||||
|
||||
# clean all operator
|
||||
pie.operator(
|
||||
"atomic.clean_all",
|
||||
text="Clean All",
|
||||
icon="PARTICLEMODE"
|
||||
)
|
||||
|
||||
# undo operator
|
||||
pie.operator(
|
||||
"atomic.detect_missing",
|
||||
text="Detect Missing Files",
|
||||
icon="SHADERFX"
|
||||
)
|
||||
|
||||
# inspect category operator
|
||||
pie.operator(
|
||||
"wm.call_menu_pie",
|
||||
text="Inspect",
|
||||
icon="VIEWZOOM"
|
||||
).name = "ATOMIC_MT_inspect_pie"
|
||||
|
||||
# nuke category operator
|
||||
pie.operator(
|
||||
"wm.call_menu_pie",
|
||||
text="Nuke",
|
||||
icon="GHOST_ENABLED"
|
||||
).name = "ATOMIC_MT_nuke_pie"
|
||||
|
||||
# clean category operator
|
||||
pie.operator(
|
||||
"wm.call_menu_pie",
|
||||
text="Clean",
|
||||
icon="PARTICLEMODE"
|
||||
).name = "ATOMIC_MT_clean_pie"
|
||||
|
||||
|
||||
# Atomic Data Manager Nuke Pie Menu
|
||||
class ATOMIC_MT_nuke_pie(bpy.types.Menu):
|
||||
bl_idname = "ATOMIC_MT_nuke_pie"
|
||||
bl_label = "Atomic Nuke"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
|
||||
# nuke node groups operator
|
||||
pie.operator("atomic.nuke_node_groups", icon="NODETREE")
|
||||
|
||||
# nuke materials operator
|
||||
pie.operator("atomic.nuke_materials", icon="MATERIAL")
|
||||
|
||||
# nuke worlds operator
|
||||
pie.operator("atomic.nuke_worlds", icon="WORLD")
|
||||
|
||||
# nuke collections operator
|
||||
pie.operator("atomic.nuke_collections", icon="GROUP")
|
||||
|
||||
# nuke lights operator
|
||||
pie.operator("atomic.nuke_lights", icon="LIGHT")
|
||||
|
||||
# nuke images operator
|
||||
pie.operator("atomic.nuke_images", icon="IMAGE_DATA")
|
||||
|
||||
# nuke textures operator
|
||||
pie.operator("atomic.nuke_textures", icon="TEXTURE")
|
||||
|
||||
# nuke particles operator
|
||||
pie.operator("atomic.nuke_particles", icon="PARTICLES")
|
||||
|
||||
|
||||
# Atomic Data Manager Clean Pie Menu
|
||||
class ATOMIC_MT_clean_pie(bpy.types.Menu):
|
||||
bl_idname = "ATOMIC_MT_clean_pie"
|
||||
bl_label = "Atomic Clean"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
|
||||
# clean node groups operator
|
||||
pie.operator("atomic.clean_node_groups", icon="NODETREE")
|
||||
|
||||
# clean materials operator
|
||||
pie.operator("atomic.clean_materials", icon="MATERIAL")
|
||||
|
||||
# clean worlds operator
|
||||
pie.operator("atomic.clean_worlds", icon="WORLD")
|
||||
|
||||
# clean collections operator
|
||||
pie.operator("atomic.clean_collections", icon="GROUP")
|
||||
|
||||
# clean lights operator
|
||||
pie.operator("atomic.clean_lights", icon="LIGHT")
|
||||
|
||||
# clean images operator
|
||||
pie.operator("atomic.clean_images", icon="IMAGE_DATA")
|
||||
|
||||
# clean textures operator
|
||||
pie.operator("atomic.clean_textures", icon="TEXTURE")
|
||||
|
||||
# clean materials operator
|
||||
pie.operator("atomic.clean_particles", icon="PARTICLES")
|
||||
|
||||
|
||||
# Atomic Data Manager Inspect Pie Menu
|
||||
class ATOMIC_MT_inspect_pie(bpy.types.Menu):
|
||||
bl_idname = "ATOMIC_MT_inspect_pie"
|
||||
bl_label = "Atomic Inspect"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pie = layout.menu_pie()
|
||||
|
||||
# inspect node groups operator
|
||||
pie.operator("atomic.inspect_node_groups", icon="NODETREE")
|
||||
|
||||
# inspect materials operator
|
||||
pie.operator("atomic.inspect_materials", icon="MATERIAL")
|
||||
|
||||
# inspect worlds operator
|
||||
pie.operator("atomic.inspect_worlds", icon="WORLD")
|
||||
|
||||
# inspect groups operator
|
||||
pie.operator("atomic.inspect_collections", icon="GROUP")
|
||||
|
||||
# inspect lights operator
|
||||
pie.operator("atomic.inspect_lights", icon="LIGHT")
|
||||
|
||||
# inspect images operator
|
||||
pie.operator("atomic.inspect_images", icon="IMAGE_DATA")
|
||||
|
||||
# inspect textures operator
|
||||
pie.operator("atomic.inspect_textures", icon="TEXTURE")
|
||||
|
||||
# inspect particles operator
|
||||
pie.operator("atomic.inspect_particles", icon="PARTICLES")
|
||||
|
||||
|
||||
reg_list = [
|
||||
ATOMIC_MT_main_pie,
|
||||
ATOMIC_MT_nuke_pie,
|
||||
ATOMIC_MT_clean_pie,
|
||||
ATOMIC_MT_inspect_pie
|
||||
]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in reg_list:
|
||||
register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reg_list:
|
||||
unregister_class(cls)
|
||||
@@ -0,0 +1,350 @@
|
||||
"""
|
||||
Copyright (C) 2019 Remington Creative
|
||||
|
||||
This file is part of Atomic Data Manager.
|
||||
|
||||
Atomic Data Manager 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.
|
||||
|
||||
Atomic Data Manager is distributed in the hope that it will
|
||||
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY 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 Atomic Data Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
This file contains the Atomic preferences UI, preferences properties, and
|
||||
some functions for syncing the preference properties with external factors.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from bpy.utils import register_class
|
||||
from bpy.utils import unregister_class
|
||||
from atomic_data_manager import config
|
||||
from atomic_data_manager.updater import addon_updater_ops
|
||||
|
||||
|
||||
def set_enable_support_me_popup(value):
|
||||
# sets the value of the enable_support_me_popup boolean property
|
||||
|
||||
bpy.context.preferences.addons["atomic_data_manager"]\
|
||||
.preferences.enable_support_me_popup = value
|
||||
copy_prefs_to_config(None, None)
|
||||
bpy.ops.wm.save_userpref()
|
||||
|
||||
|
||||
def set_last_popup_day(day):
|
||||
# sets the value of the last_popup_day float property
|
||||
|
||||
bpy.context.preferences.addons["atomic_data_manager"]\
|
||||
.preferences.last_popup_day = day
|
||||
copy_prefs_to_config(None, None)
|
||||
|
||||
|
||||
def copy_prefs_to_config(self, context):
|
||||
# copies the values of Atomic's preferences to the variables in
|
||||
# config.py for global use
|
||||
|
||||
preferences = bpy.context.preferences
|
||||
|
||||
atomic_preferences = preferences.addons['atomic_data_manager']\
|
||||
.preferences
|
||||
|
||||
# visible atomic preferences
|
||||
config.enable_missing_file_warning = \
|
||||
atomic_preferences.enable_missing_file_warning
|
||||
|
||||
config.enable_pie_menu_ui = \
|
||||
atomic_preferences.enable_pie_menu_ui
|
||||
|
||||
config.enable_support_me_popup = \
|
||||
atomic_preferences.enable_support_me_popup
|
||||
|
||||
config.include_fake_users = \
|
||||
atomic_preferences.include_fake_users
|
||||
|
||||
# hidden atomic preferences
|
||||
config.pie_menu_type = \
|
||||
atomic_preferences.pie_menu_type
|
||||
|
||||
config.pie_menu_alt = \
|
||||
atomic_preferences.pie_menu_alt
|
||||
|
||||
config.pie_menu_any = \
|
||||
atomic_preferences.pie_menu_any
|
||||
|
||||
config.pie_menu_ctrl = \
|
||||
atomic_preferences.pie_menu_ctrl
|
||||
|
||||
config.pie_menu_oskey = \
|
||||
atomic_preferences.pie_menu_oskey
|
||||
|
||||
config.pie_menu_shift = \
|
||||
atomic_preferences.pie_menu_shift
|
||||
|
||||
config.last_popup_day = \
|
||||
atomic_preferences.last_popup_day
|
||||
|
||||
|
||||
def update_pie_menu_hotkeys(self, context):
|
||||
preferences = bpy.context.preferences
|
||||
atomic_preferences = preferences.addons['atomic_data_manager'] \
|
||||
.preferences
|
||||
|
||||
# add the hotkeys if the preference is enabled
|
||||
if atomic_preferences.enable_pie_menu_ui:
|
||||
add_pie_menu_hotkeys()
|
||||
|
||||
# remove the hotkeys otherwise
|
||||
else:
|
||||
remove_pie_menu_hotkeys()
|
||||
|
||||
|
||||
def add_pie_menu_hotkeys():
|
||||
# adds the pie menu hotkeys to blender's addon keymaps
|
||||
|
||||
global keymaps
|
||||
keyconfigs = bpy.context.window_manager.keyconfigs.addon
|
||||
|
||||
# check to see if a window keymap already exists
|
||||
if "Window" in keyconfigs.keymaps.keys():
|
||||
km = keyconfigs.keymaps['Window']
|
||||
|
||||
# if not, crate a new one
|
||||
else:
|
||||
km = keyconfigs.keymaps.new(
|
||||
name="Window",
|
||||
space_type='EMPTY',
|
||||
region_type='WINDOW'
|
||||
)
|
||||
|
||||
# add a new keymap item to that keymap
|
||||
kmi = km.keymap_items.new(
|
||||
idname="atomic.invoke_pie_menu_ui",
|
||||
type=config.pie_menu_type,
|
||||
value="PRESS",
|
||||
alt=config.pie_menu_alt,
|
||||
any=config.pie_menu_any,
|
||||
ctrl=config.pie_menu_ctrl,
|
||||
oskey=config.pie_menu_oskey,
|
||||
shift=config.pie_menu_shift,
|
||||
)
|
||||
|
||||
# # point the keymap item to our pie menu
|
||||
# kmi.properties.name = "ATOMIC_MT_main_pie"
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
|
||||
def remove_pie_menu_hotkeys():
|
||||
# removes the pie menu hotkeys from blender's addon keymaps if they
|
||||
# exist there
|
||||
|
||||
global keymaps
|
||||
|
||||
# remove each hotkey in our keymaps list if it exists in blenders
|
||||
# addon keymaps
|
||||
for km, kmi in keymaps:
|
||||
km.keymap_items.remove(kmi)
|
||||
|
||||
# clear our keymaps list
|
||||
keymaps.clear()
|
||||
|
||||
|
||||
# Atomic Data Manager Preference Panel UI
|
||||
class ATOMIC_PT_preferences_panel(bpy.types.AddonPreferences):
|
||||
bl_idname = "atomic_data_manager"
|
||||
|
||||
# visible atomic preferences
|
||||
enable_missing_file_warning: bpy.props.BoolProperty(
|
||||
description="Display a warning on startup if Atomic detects "
|
||||
"missing files in your project",
|
||||
default=True
|
||||
)
|
||||
|
||||
enable_support_me_popup: bpy.props.BoolProperty(
|
||||
description="Occasionally display a popup asking if you would "
|
||||
"like to support Remington Creative",
|
||||
default=True
|
||||
)
|
||||
|
||||
include_fake_users: bpy.props.BoolProperty(
|
||||
description="Include data-blocks with only fake users in unused "
|
||||
"data detection",
|
||||
default=False
|
||||
)
|
||||
|
||||
enable_pie_menu_ui: bpy.props.BoolProperty(
|
||||
description="Enable the Atomic pie menu UI, so you can clean "
|
||||
"your project from anywhere.",
|
||||
default=True,
|
||||
update=update_pie_menu_hotkeys
|
||||
)
|
||||
|
||||
# hidden atomic preferences
|
||||
pie_menu_type: bpy.props.StringProperty(
|
||||
default="D"
|
||||
)
|
||||
|
||||
pie_menu_alt: bpy.props.BoolProperty(
|
||||
default=False
|
||||
)
|
||||
|
||||
pie_menu_any: bpy.props.BoolProperty(
|
||||
default=False
|
||||
)
|
||||
|
||||
pie_menu_ctrl: bpy.props.BoolProperty(
|
||||
default=False
|
||||
)
|
||||
|
||||
pie_menu_oskey: bpy.props.BoolProperty(
|
||||
default=False
|
||||
)
|
||||
|
||||
pie_menu_shift: bpy.props.BoolProperty(
|
||||
default=False
|
||||
)
|
||||
|
||||
last_popup_day: bpy.props.FloatProperty(
|
||||
default=0
|
||||
)
|
||||
|
||||
# add-on updater properties
|
||||
auto_check_update: bpy.props.BoolProperty(
|
||||
name="Auto-check for Update",
|
||||
description="If enabled, auto-check for updates using an interval",
|
||||
default=True,
|
||||
)
|
||||
|
||||
updater_intrval_months: bpy.props.IntProperty(
|
||||
name='Months',
|
||||
description="Number of months between checking for updates",
|
||||
default=0,
|
||||
min=0,
|
||||
max=6
|
||||
)
|
||||
updater_intrval_days: bpy.props.IntProperty(
|
||||
name='Days',
|
||||
description="Number of days between checking for updates",
|
||||
default=7,
|
||||
min=0,
|
||||
)
|
||||
updater_intrval_hours: bpy.props.IntProperty(
|
||||
name='Hours',
|
||||
description="Number of hours between checking for updates",
|
||||
default=0,
|
||||
min=0,
|
||||
max=23
|
||||
)
|
||||
updater_intrval_minutes: bpy.props.IntProperty(
|
||||
name='Minutes',
|
||||
description="Number of minutes between checking for updates",
|
||||
default=0,
|
||||
min=0,
|
||||
max=59
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
split = layout.split()
|
||||
|
||||
# left column
|
||||
col = split.column()
|
||||
|
||||
# enable missing file warning toggle
|
||||
col.prop(
|
||||
self,
|
||||
"enable_missing_file_warning",
|
||||
text="Show Missing File Warning"
|
||||
)
|
||||
|
||||
# enable support me popup toggle
|
||||
col.prop(
|
||||
self,
|
||||
"enable_support_me_popup",
|
||||
text="Show \"Support Me\" Popup"
|
||||
)
|
||||
|
||||
# right column
|
||||
col = split.column()
|
||||
|
||||
# ignore fake users toggle
|
||||
col.prop(
|
||||
self,
|
||||
"include_fake_users",
|
||||
text="Include Fake Users"
|
||||
)
|
||||
|
||||
# pie menu settings
|
||||
pie_split = col.split(factor=0.55) # nice
|
||||
|
||||
# enable pie menu ui toggle
|
||||
pie_split.prop(
|
||||
self,
|
||||
"enable_pie_menu_ui",
|
||||
text="Enable Pie Menu"
|
||||
)
|
||||
|
||||
# put the property in a row so it can be disabled
|
||||
pie_row = pie_split.row()
|
||||
pie_row.enabled = self.enable_pie_menu_ui
|
||||
|
||||
if pie_row.enabled:
|
||||
# keymap item that contains our pie menu hotkey
|
||||
# note: keymap item index hardcoded with an index -- may be
|
||||
# dangerous if more keymap items are added
|
||||
kmi = bpy.context.window_manager.keyconfigs.addon.keymaps[
|
||||
'Window'].keymap_items[0]
|
||||
|
||||
# hotkey property
|
||||
pie_row.prop(
|
||||
kmi,
|
||||
"type",
|
||||
text="",
|
||||
full_event=True
|
||||
)
|
||||
|
||||
# update hotkey preferences
|
||||
self.pie_menu_type = kmi.type
|
||||
self.pie_menu_any = kmi.any
|
||||
self.pie_menu_alt = kmi.alt
|
||||
self.pie_menu_ctrl = kmi.ctrl
|
||||
self.pie_menu_oskey = kmi.oskey
|
||||
self.pie_menu_shift = kmi.shift
|
||||
|
||||
separator = layout.row() # extra space
|
||||
|
||||
# add-on updater box
|
||||
addon_updater_ops.update_settings_ui(self, context)
|
||||
|
||||
# update config with any new preferences
|
||||
copy_prefs_to_config(None, None)
|
||||
|
||||
|
||||
reg_list = [ATOMIC_PT_preferences_panel]
|
||||
keymaps = []
|
||||
|
||||
|
||||
def register():
|
||||
for cls in reg_list:
|
||||
register_class(cls)
|
||||
|
||||
# make sure global preferences are updated on registration
|
||||
copy_prefs_to_config(None, None)
|
||||
|
||||
# update keymaps
|
||||
add_pie_menu_hotkeys()
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reg_list:
|
||||
unregister_class(cls)
|
||||
|
||||
remove_pie_menu_hotkeys()
|
||||
@@ -0,0 +1,376 @@
|
||||
"""
|
||||
Copyright (C) 2019 Remington Creative
|
||||
|
||||
This file is part of Atomic Data Manager.
|
||||
|
||||
Atomic Data Manager 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.
|
||||
|
||||
Atomic Data Manager is distributed in the hope that it will
|
||||
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY 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 Atomic Data Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
This file contains the user interface for Atomic's statistics subpanel.
|
||||
|
||||
The statistics panel is nested in the main Atomic Data Manager panel. This
|
||||
panel contains statistics about the Blender file and each data category in
|
||||
it.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from bpy.utils import register_class
|
||||
from bpy.utils import unregister_class
|
||||
from atomic_data_manager.stats import count
|
||||
from atomic_data_manager.stats import misc
|
||||
from atomic_data_manager.ui.utils import ui_layouts
|
||||
|
||||
|
||||
# Atomic Data Manager Statistics SubPanel
|
||||
class ATOMIC_PT_stats_panel(bpy.types.Panel):
|
||||
"""The Atomic Data Manager \"Stats for Nerds\" panel"""
|
||||
bl_idname = "ATOMIC_PT_stats_panel"
|
||||
bl_label = "Stats for Nerds"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_parent_id = "ATOMIC_PT_main_panel"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
atom = bpy.context.scene.atomic
|
||||
|
||||
# categories selector / header
|
||||
row = layout.row()
|
||||
row.label(text="Categories:")
|
||||
row.prop(atom, "stats_mode", expand=True, icon_only=True)
|
||||
|
||||
# statistics box
|
||||
box = layout.box()
|
||||
|
||||
# overview statistics
|
||||
if atom.stats_mode == 'OVERVIEW':
|
||||
|
||||
# category header label
|
||||
row = box.row()
|
||||
row.label(text="Overview", icon='FILE')
|
||||
|
||||
# blender project file size statistic
|
||||
row = box.row()
|
||||
row.label(text="Blend File Size: " + misc.blend_size())
|
||||
|
||||
# cateogry statistics
|
||||
split = box.split()
|
||||
|
||||
# left column
|
||||
col = split.column()
|
||||
|
||||
# left column category labels
|
||||
col.label(text="Collections")
|
||||
col.label(text="Lights")
|
||||
col.label(text="Node Groups")
|
||||
col.label(text="Textures")
|
||||
|
||||
col = split.column()
|
||||
|
||||
# collection count
|
||||
col.label(text=str(count.collections()))
|
||||
|
||||
# light count
|
||||
col.label(text=str(count.lights()))
|
||||
|
||||
# node group count
|
||||
col.label(text=str(count.node_groups()))
|
||||
|
||||
# texture count
|
||||
col.label(text=str(count.textures()))
|
||||
|
||||
# right column
|
||||
col = split.column()
|
||||
|
||||
# right column category labels
|
||||
col.label(text="Images")
|
||||
col.label(text="Materials")
|
||||
col.label(text="Particles")
|
||||
col.label(text="Worlds")
|
||||
|
||||
col = split.column()
|
||||
|
||||
# image count
|
||||
col.label(text=str(count.images()))
|
||||
|
||||
# material count
|
||||
col.label(text=str(count.materials()))
|
||||
|
||||
# particle system count
|
||||
col.label(text=str(count.particles()))
|
||||
|
||||
# world count
|
||||
col.label(text=str(count.worlds()))
|
||||
|
||||
# collection statistics
|
||||
elif atom.stats_mode == 'COLLECTIONS':
|
||||
|
||||
# category header label
|
||||
row = box.row()
|
||||
row.label(text="Collections", icon='GROUP')
|
||||
|
||||
split = box.split()
|
||||
|
||||
# total and placeholder count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Total: {0}".format(count.collections())
|
||||
)
|
||||
|
||||
# col.label(text="Placeholder") # TODO: remove placeholder
|
||||
|
||||
# unused and unnamed count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Unused: {0}".format(count.collections_unused())
|
||||
)
|
||||
|
||||
col.label(
|
||||
text="Unnamed: {0}".format(count.collections_unnamed())
|
||||
)
|
||||
|
||||
# image statistics
|
||||
elif atom.stats_mode == 'IMAGES':
|
||||
|
||||
# category header label
|
||||
row = box.row()
|
||||
row.label(text="Images", icon='IMAGE_DATA')
|
||||
|
||||
split = box.split()
|
||||
|
||||
# total and missing count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Total: {0}".format(count.images())
|
||||
)
|
||||
|
||||
col.label(
|
||||
text="Missing: {0}".format(count.images_missing())
|
||||
)
|
||||
|
||||
# unused and unnamed count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Unused: {0}".format(count.images_unused())
|
||||
)
|
||||
col.label(
|
||||
text="Unnamed: {0}".format(count.images_unnamed())
|
||||
)
|
||||
|
||||
# light statistics
|
||||
elif atom.stats_mode == 'LIGHTS':
|
||||
row = box.row()
|
||||
row.label(text="Lights", icon='LIGHT')
|
||||
|
||||
split = box.split()
|
||||
|
||||
# total and placeholder count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Total: {0}".format(count.lights())
|
||||
)
|
||||
|
||||
# col.label(text="Placeholder") # TODO: remove placeholder
|
||||
|
||||
# unused and unnamed count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Unused: {0}".format(count.lights_unused())
|
||||
)
|
||||
|
||||
col.label(
|
||||
text="Unnamed: {0}".format(count.lights_unnamed())
|
||||
)
|
||||
|
||||
# material statistics
|
||||
elif atom.stats_mode == 'MATERIALS':
|
||||
|
||||
# category header label
|
||||
row = box.row()
|
||||
row.label(text="Materials", icon='MATERIAL')
|
||||
|
||||
split = box.split()
|
||||
|
||||
# total and placeholder count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Total: {0}".format(count.materials())
|
||||
)
|
||||
|
||||
# col.label(text="Placeholder") # TODO: remove placeholder
|
||||
|
||||
# unused and unnamed count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Unused: {0}".format(count.materials_unused())
|
||||
)
|
||||
|
||||
col.label(
|
||||
text="Unnamed: {0}".format(count.materials_unnamed())
|
||||
)
|
||||
|
||||
# object statistics
|
||||
elif atom.stats_mode == 'OBJECTS':
|
||||
|
||||
# category header label
|
||||
row = box.row()
|
||||
row.label(text="Objects", icon='OBJECT_DATA')
|
||||
|
||||
# total count
|
||||
split = box.split()
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Total: {0}".format(count.objects())
|
||||
)
|
||||
|
||||
# unnamed count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Unnamed: {0}".format(count.objects_unnamed())
|
||||
)
|
||||
|
||||
# node group statistics
|
||||
elif atom.stats_mode == 'NODE_GROUPS':
|
||||
|
||||
# category header label
|
||||
row = box.row()
|
||||
row.label(text="Node Groups", icon='NODETREE')
|
||||
|
||||
split = box.split()
|
||||
|
||||
# total and placeholder count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Total: {0}".format(count.node_groups())
|
||||
)
|
||||
|
||||
# col.label(text="Placeholder") # TODO: remove placeholder
|
||||
|
||||
# unused and unnamed count
|
||||
col = split.column()
|
||||
col.label(
|
||||
text="Unused: {0}".format(count.node_groups_unused())
|
||||
)
|
||||
col.label(
|
||||
text="Unnamed: {0}".format(count.node_groups_unnamed())
|
||||
)
|
||||
|
||||
# particle statistics
|
||||
elif atom.stats_mode == 'PARTICLES':
|
||||
|
||||
# category header label
|
||||
row = box.row()
|
||||
row.label(text="Particle Systems", icon='PARTICLES')
|
||||
|
||||
split = box.split()
|
||||
|
||||
# total and placeholder count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Total: {0}".format(count.particles())
|
||||
)
|
||||
|
||||
# col.label(text="Placeholder") # TODO: remove placeholder
|
||||
|
||||
# unused and unnamed count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Unused: {0}".format(count.particles_unused())
|
||||
)
|
||||
|
||||
col.label(
|
||||
text="Unnamed: {0}".format(count.particles_unnamed())
|
||||
)
|
||||
|
||||
# texture statistics
|
||||
elif atom.stats_mode == 'TEXTURES':
|
||||
row = box.row()
|
||||
row.label(text="Textures", icon='TEXTURE')
|
||||
|
||||
split = box.split()
|
||||
|
||||
# total and placeholder count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Total: {0}".format(count.textures())
|
||||
)
|
||||
|
||||
# col.label(text="Placeholder") # TODO: remove placeholder
|
||||
|
||||
# unused and unnamed count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Unused: {0}".format(count.textures_unused())
|
||||
)
|
||||
|
||||
col.label(
|
||||
text="Unnamed: {0}".format(count.textures_unnamed())
|
||||
)
|
||||
|
||||
# world statistics
|
||||
elif atom.stats_mode == 'WORLDS':
|
||||
row = box.row()
|
||||
row.label(text="Worlds", icon='WORLD')
|
||||
|
||||
split = box.split()
|
||||
|
||||
# total and placeholder count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Total: {0}".format(count.worlds())
|
||||
)
|
||||
|
||||
# # col.label(text="Placeholder") # TODO: remove placeholder
|
||||
|
||||
# unused and unnamed count
|
||||
col = split.column()
|
||||
|
||||
col.label(
|
||||
text="Unused: {0}".format(count.worlds_unused())
|
||||
)
|
||||
|
||||
col.label(
|
||||
text="Unnamed: {0}".format(count.worlds_unnamed())
|
||||
)
|
||||
|
||||
|
||||
reg_list = [ATOMIC_PT_stats_panel]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in reg_list:
|
||||
register_class(cls)
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reg_list:
|
||||
unregister_class(cls)
|
||||
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
Copyright (C) 2019 Remington Creative
|
||||
|
||||
This file is part of Atomic Data Manager.
|
||||
|
||||
Atomic Data Manager 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.
|
||||
|
||||
Atomic Data Manager is distributed in the hope that it will
|
||||
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY 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 Atomic Data Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
This file contains the user interface and some helper functions for the
|
||||
support Remington Creative popup.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
import time
|
||||
from bpy.utils import register_class
|
||||
from bpy.utils import unregister_class
|
||||
from bpy.app.handlers import persistent
|
||||
from atomic_data_manager import config
|
||||
from atomic_data_manager.ui import preferences_ui
|
||||
|
||||
|
||||
def get_current_day():
|
||||
# returns the current day since the start of the computer clock
|
||||
seconds_per_day = 86400
|
||||
return int(time.time() / seconds_per_day)
|
||||
|
||||
|
||||
def update_enable_show_support_me_popup(self, context):
|
||||
# copy the inverse of the stop show support popup property to Atomic's
|
||||
# enable support me popup preference
|
||||
preferences_ui.set_enable_support_me_popup(
|
||||
not self.stop_showing_support_popup)
|
||||
|
||||
|
||||
@persistent
|
||||
def show_support_me_popup(dummy=None):
|
||||
# shows the support me popup if the 5 day interval has expired and the
|
||||
# enable support me popup preference is enabled
|
||||
|
||||
popup_interval = 5 # days
|
||||
|
||||
current_day = get_current_day()
|
||||
next_day = config.last_popup_day + popup_interval
|
||||
|
||||
if config.enable_support_me_popup and current_day >= next_day:
|
||||
preferences_ui.set_last_popup_day(current_day)
|
||||
bpy.ops.atomic.show_support_me('INVOKE_DEFAULT')
|
||||
|
||||
|
||||
# Atomic Data Manager Support Me Popup Operator
|
||||
class ATOMIC_OT_support_me_popup(bpy.types.Operator):
|
||||
"""Displays the Atomic \"Support Me\" popup"""
|
||||
bl_idname = "atomic.show_support_me"
|
||||
bl_label = "Like Atomic Data Manager?"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
stop_showing_support_popup: bpy.props.BoolProperty(
|
||||
default=False,
|
||||
update=update_enable_show_support_me_popup
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# call to action label
|
||||
col = layout.column(align=True)
|
||||
col.label(
|
||||
text="Consider supporting our free software development!"
|
||||
)
|
||||
|
||||
separator = layout.separator() # extra space
|
||||
|
||||
# never show again toggle
|
||||
row = layout.row()
|
||||
row.prop(
|
||||
self, "stop_showing_support_popup", text="Never Show Again"
|
||||
)
|
||||
|
||||
# support remington creative button
|
||||
row = layout.row()
|
||||
row.scale_y = 2
|
||||
row.operator(
|
||||
"atomic.open_support_me",
|
||||
text="Support Remington Creative",
|
||||
icon="FUND"
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
reg_list = [ATOMIC_OT_support_me_popup]
|
||||
|
||||
|
||||
def register():
|
||||
for cls in reg_list:
|
||||
register_class(cls)
|
||||
|
||||
bpy.app.handlers.load_post.append(show_support_me_popup)
|
||||
|
||||
# reset day counter if it equals zero of if it is in the future
|
||||
if config.last_popup_day == 0 \
|
||||
or config.last_popup_day > get_current_day():
|
||||
preferences_ui.set_last_popup_day(get_current_day())
|
||||
|
||||
|
||||
def unregister():
|
||||
for cls in reg_list:
|
||||
unregister_class(cls)
|
||||
|
||||
bpy.app.handlers.load_post.remove(show_support_me_popup)
|
||||
@@ -0,0 +1,224 @@
|
||||
"""
|
||||
Copyright (C) 2019 Remington Creative
|
||||
|
||||
This file is part of Atomic Data Manager.
|
||||
|
||||
Atomic Data Manager 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.
|
||||
|
||||
Atomic Data Manager is distributed in the hope that it will
|
||||
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
of MERCHANTABILITY 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 Atomic Data Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
---
|
||||
|
||||
This file contains basic UI layouts for the Atomic add-on that can be
|
||||
used throughout the interface.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def box_list(layout, title=None, items=None, columns=2, icon=None):
|
||||
# a title label followed by a box that contains a two column list of
|
||||
# items, each of which is preceded by a uniform icon that does not
|
||||
# change depending on the objects type
|
||||
|
||||
# box list title
|
||||
row = layout.row() # extra row for additional spacing
|
||||
|
||||
if title is not None:
|
||||
row = layout.row()
|
||||
row.label(text=title)
|
||||
|
||||
box = layout.box()
|
||||
|
||||
# if the list has elements
|
||||
if items is not None and len(items) != 0:
|
||||
|
||||
# display the list
|
||||
flow = box.column_flow(columns=columns)
|
||||
for item in items:
|
||||
if icon is not None:
|
||||
flow.label(text=item, icon=icon)
|
||||
else:
|
||||
flow.label(text=item)
|
||||
|
||||
# if the list has no elements
|
||||
else:
|
||||
|
||||
# display the none label
|
||||
row = box.row()
|
||||
row.enabled = False
|
||||
row.label(text="none")
|
||||
|
||||
|
||||
def box_list_diverse(layout, title, items, columns=2):
|
||||
# a title label followed by a box that contains a two column list of
|
||||
# items, each of which is preceded by an icon that changes depending
|
||||
# on the type of object that is being listed
|
||||
|
||||
# box list title
|
||||
row = layout.row() # extra row for additional spacing
|
||||
row = layout.row()
|
||||
row.label(text=title)
|
||||
box = layout.box()
|
||||
|
||||
# if the list has elements
|
||||
if len(items) != 0:
|
||||
|
||||
# display the list
|
||||
flow = box.column_flow(columns=columns)
|
||||
objects = bpy.data.objects
|
||||
for item in items:
|
||||
if objects[item].type == 'ARMATURE':
|
||||
flow.label(text=item, icon="OUTLINER_OB_ARMATURE")
|
||||
|
||||
elif objects[item].type == 'CAMERA':
|
||||
flow.label(text=item, icon="OUTLINER_OB_CAMERA")
|
||||
|
||||
elif objects[item].type == 'CURVE':
|
||||
flow.label(text=item, icon="OUTLINER_OB_CURVE")
|
||||
|
||||
elif objects[item].type == 'EMPTY':
|
||||
flow.label(text=item, icon="OUTLINER_OB_EMPTY")
|
||||
|
||||
elif objects[item].type == 'FONT':
|
||||
flow.label(text=item, icon="OUTLINER_OB_FONT")
|
||||
|
||||
elif objects[item].type == 'GPENCIL':
|
||||
flow.label(text=item, icon="OUTLINER_OB_GREASEPENCIL")
|
||||
|
||||
elif objects[item].type == 'LATTICE':
|
||||
flow.label(text=item, icon="OUTLINER_OB_LATTICE")
|
||||
|
||||
elif objects[item].type == 'LIGHT':
|
||||
flow.label(text=item, icon="OUTLINER_OB_LIGHT")
|
||||
|
||||
elif objects[item].type == 'LIGHT_PROBE':
|
||||
flow.label(text=item, icon="OUTLINER_OB_LIGHTPROBE")
|
||||
|
||||
elif objects[item].type == 'MESH':
|
||||
flow.label(text=item, icon="OUTLINER_OB_MESH")
|
||||
|
||||
elif objects[item].type == 'META':
|
||||
flow.label(text=item, icon="OUTLINER_OB_META")
|
||||
|
||||
elif objects[item].type == 'SPEAKER':
|
||||
flow.label(text=item, icon="OUTLINER_OB_SPEAKER")
|
||||
|
||||
elif objects[item].type == 'SURFACE':
|
||||
flow.label(text=item, icon="OUTLINER_OB_SURFACE")
|
||||
|
||||
# if the object doesn't fit any of the previous types
|
||||
else:
|
||||
flow.label(text=item, icon="QUESTION")
|
||||
|
||||
# if the list has no elements
|
||||
else:
|
||||
|
||||
# display the none label
|
||||
row = box.row()
|
||||
row.enabled = False
|
||||
row.label(text="none")
|
||||
|
||||
|
||||
def inspect_header(layout, atom_prop, data):
|
||||
# a single column containing a search property and basic data
|
||||
# manipulation functions that appears at the top of all inspect data
|
||||
# set dialogs
|
||||
|
||||
atom = bpy.context.scene.atomic
|
||||
|
||||
# exterior box and prop search for data-blocks
|
||||
col = layout.column(align=True)
|
||||
box = col.box()
|
||||
row = box.row()
|
||||
split = row.split()
|
||||
split.prop_search(atom, atom_prop, bpy.data, data, text="")
|
||||
|
||||
# convert the data set string into an actual data set reference
|
||||
data = getattr(bpy.data, data)
|
||||
|
||||
# get the string value of the string property
|
||||
text_field = getattr(atom, atom_prop)
|
||||
|
||||
# determine whether or not the text entered in the string property
|
||||
# is a valid key
|
||||
is_valid_key = text_field in data.keys()
|
||||
|
||||
# determine whether or not the piece of data is using a fake user
|
||||
has_fake_user = is_valid_key and data[text_field].use_fake_user
|
||||
|
||||
# buttons that follow the prop search
|
||||
split = row.split()
|
||||
row = split.row(align=True)
|
||||
|
||||
# disable the buttons if the key in the search property is invalid
|
||||
row.enabled = is_valid_key
|
||||
|
||||
# toggle fake user button (do not show for collections)
|
||||
# icon and depression changes depending on whether or not the object
|
||||
# is using a fake user
|
||||
if data != bpy.data.collections:
|
||||
|
||||
# has fake user
|
||||
if has_fake_user:
|
||||
row.operator(
|
||||
"atomic.toggle_fake_user",
|
||||
text="",
|
||||
icon="FAKE_USER_ON",
|
||||
depress=True
|
||||
)
|
||||
|
||||
# does not have fake user
|
||||
else:
|
||||
row.operator(
|
||||
"atomic.toggle_fake_user",
|
||||
text="",
|
||||
icon="FAKE_USER_OFF",
|
||||
depress=False
|
||||
)
|
||||
|
||||
# duplicate button
|
||||
row.operator(
|
||||
"atomic.inspection_duplicate",
|
||||
text="",
|
||||
icon="DUPLICATE"
|
||||
)
|
||||
|
||||
# replace button (do not show for collections)
|
||||
if data != bpy.data.collections:
|
||||
row.operator(
|
||||
"atomic.replace",
|
||||
text="",
|
||||
icon="UV_SYNC_SELECT"
|
||||
)
|
||||
|
||||
# rename button
|
||||
row.operator(
|
||||
"atomic.rename",
|
||||
text="",
|
||||
icon="GREASEPENCIL"
|
||||
)
|
||||
|
||||
# delete button
|
||||
row.operator(
|
||||
"atomic.inspection_delete",
|
||||
text="",
|
||||
icon="TRASH"
|
||||
)
|
||||
|
||||
|
||||
def number_suffix(text, number):
|
||||
# returns the text properly formatted as a suffix
|
||||
# e.g. passing in "hello" and "100" will result in "hello (100)"
|
||||
|
||||
return text + " ({0})".format(number) if int(number) != 0 else text
|
||||
Reference in New Issue
Block a user