diff --git a/__init__.py b/__init__.py index 8c80cd2..3b41675 100644 --- a/__init__.py +++ b/__init__.py @@ -1,248 +1,332 @@ -# 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 . - -import bpy - -bl_info = { - "name" : "Image Menus", - "author" : "Yianni Papazis", - "description" : "Some menus for making working with images easier.", - "blender" : (2, 90, 0), - "version" : (0, 0, 1), - "location" : "", - "warning" : "", - "category" : "Generic" -} - -addon_keymaps = [] - -class reload_images_menu(bpy.types.Menu): - bl_idname = "reload_images_menu" - bl_label = "Reload Images" - bl_space_type = "3D_VIEW" - - def draw(self, context): - layout = self.layout - # TODO make recent images reload - # layout.label(text="Recent") - # layout.label(text="Images") - image_list = get_images_from_objects() - - if image_list: - image_data = [img.image.name for img in image_list] - o = layout.operator(reload_image_by_name.bl_idname, text="Reload All", icon="FILE_REFRESH") - o.reload_list = True - o.image_name = str(image_data) - - layout.separator() - - for image_node in image_list: - image = image_node.image - o = layout.operator(reload_image_by_name.bl_idname, text=image.name, icon='IMAGE_DATA') - o.reload_list = False - o.image_name = image.name - else: - layout.label("No images") - -class reload_image_by_name(bpy.types.Operator): - bl_idname = "object.reload_image_by_name" - bl_label = "Reload Image by Name" - image_name = bpy.props.StringProperty() - reload_list = bpy.props.BoolProperty(default=False) - - def execute(self, context): - if self.image_name: - if not self.reload_list: - bpy.data.images[self.image_name].reload() - else: - # converting string list to list - # probably doing this in a stupid - image_list = self.image_name[1:-1].split(',') - image_list = [img.strip(' ')[1:-1] for img in image_list] - - for image in image_list: - bpy.data.images[image].reload() - return {'FINISHED'} - -class load_from_selected_menu(bpy.types.Menu): - bl_idname = "load_from_selected_menu" - bl_label = "Load from Selected" - bl_space_type = "IMAGE_EDITOR" - - class operator(bpy.types.Operator): - bl_idname = "load_from_selected.my_class_name" - bl_label = "Load From Selected" - image_name = bpy.props.StringProperty() - - @classmethod - def poll(cls, context): - return True - - def execute(self, context): - image = bpy.data.images[self.image_name] - bpy.context.area.spaces.active.image = image - return {"FINISHED"} - - - def draw(self, context): - layout = self.layout - - material_list = get_materials_from_selected() - if material_list: - for mat in material_list: - layout.label(text=mat, icon='MATERIAL') - layout.separator() - mat_image_nodes = look_for_images_from_mat( - bpy.data.materials[mat]) - - # build menu for each material - if mat_image_nodes: - for image_node in mat_image_nodes: - o = layout.operator( - self.operator.bl_idname, text=image_node.image.name, icon='IMAGE_DATA') - o.image_name = image_node.image.name - layout.separator() - else: - layout.label(text="No images") - else: - layout.label(text='No materials') - -class make_active_from_selected_menu(bpy.types.Menu): - bl_idname = "object.make_active_from_selected_menu" - bl_label = "Make Image Active" - image_name = "" - - class operator(bpy.types.Operator): - bl_idname = "object.make_active_from_selected" - bl_label = "Make Image Active" - mat_name = bpy.props.StringProperty() - node_name = bpy.props.StringProperty() - - def execute(self, context): - - node_tree = bpy.data.materials[self.mat_name].node_tree - # TODO find a cleaner way of getting out node - for each in node_tree.nodes: - if str(each) == self.node_name: - node = each - node.select = True - node_tree.nodes.active = node - return {'FINISHED'} - - def draw(self, context): - layout = self.layout - material_list = get_materials_from_selected() - if material_list: - layout.menu(reload_images_menu.bl_idname) - for mat in material_list: - layout.label(text=mat, icon='MATERIAL') - layout.separator() - mat_image_nodes = look_for_images_from_mat( - bpy.data.materials[mat]) - - # build menu for each material - if mat_image_nodes: - for image_node in mat_image_nodes: - o = layout.operator( - self.operator.bl_idname, text=image_node.image.name, icon='IMAGE_DATA') - o.mat_name = mat - o.node_name = str(image_node) - layout.separator() - else: - layout.label(text="No images") - else: - layout.label(text='No materials') - -def get_images_from_objects(): - checked_mats = [] - images = [] - active_object = bpy.context.active_object - materials = active_object.material_slots - for mat in materials: - mat = mat.material - if mat not in checked_mats: - checked_mats.append(mat) - found_images = look_for_images_from_mat(mat) - for img in found_images: - if img not in images: - images.append(img) - return images - -def look_for_images_from_mat(mat = bpy.types.Material): - image_list = [] - for node in mat.node_tree.nodes: - if node.type == 'TEX_IMAGE' and node.image not in image_list: - image_list.append(node) - return image_list - -def get_materials_from_selected(): - object_materials = [] - checked_mats = [] - active_object = bpy.context.active_object - if active_object: - materials = active_object.material_slots - for mat in materials: - mat = mat.name - if mat not in checked_mats: - checked_mats.append(mat) - object_materials.append(mat) - return object_materials - -classes = ( - reload_images_menu, - load_from_selected_menu, - load_from_selected_menu.operator, - reload_image_by_name, - make_active_from_selected_menu.operator, - make_active_from_selected_menu -) - -def register(): - - for cls in classes: - bpy.utils.register_class(cls) - - # register keymaps - wm = bpy.context.window_manager - kc = wm.keyconfigs.addon - - km = kc.keymaps.new(name="Image", space_type="IMAGE_EDITOR") - kmi = km.keymap_items.new( - "wm.call_menu", - type="I", - value="PRESS", - shift=True - ) - kmi.properties.name = load_from_selected_menu.bl_idname - addon_keymaps.append((km,kmi)) - - km = kc.keymaps.new(name="3D View", space_type="VIEW_3D") - kmi = km.keymap_items.new( - "wm.call_menu", - type="I", - value="PRESS", - shift=True - ) - kmi.properties.name = make_active_from_selected_menu.bl_idname - addon_keymaps.append((km,kmi)) - -def unregister(): - - for cls in classes: - bpy.utils.unregister_class(cls) - - # Unregister keymaps - for km, kmi in addon_keymaps: - km.keymap_items.remove(kmi) - - addon_keymaps.clear() +# 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 . + +import bpy + +from bpy.props import IntProperty, FloatProperty, BoolProperty, EnumProperty, StringProperty +import rna_keymap_ui +from bpy.types import AddonPreferences + +bl_info = { + "name" : "Image Menus", + "author" : "Yianni Papazis, Zuorion", + "description" : "Some menus for making working with images easier.", + "blender" : (2, 93, 0), + "version" : (0, 0, 3), + "location" : "Shift+I in 3d view and image editor", + "warning" : "", + "wiki_url": "https://github.com/yiannipapazis/image_menus", + "tracker_url": "https://github.com/yiannipapazis/image_menus/issues", + "category" : "Generic" +} + +addon_keymaps = [] + +class reload_images_menu(bpy.types.Menu): + bl_idname = "reload_images_menu" + bl_label = "Reload Images" + bl_space_type = "3D_VIEW" + + def draw(self, context): + layout = self.layout + # TODO make recent images reload + # layout.label(text="Recent") + # layout.label(text="Images") + image_list = get_images_from_objects() + + if image_list: + image_data = [img.image.name for img in image_list] + o = layout.operator("object.reload_image_by_name", text="Reload All", icon="FILE_REFRESH") + o.reload_list = True + o.image_name = str(image_data) + + layout.separator() + + for image_node in image_list: + image = image_node.image + o = layout.operator("object.reload_image_by_name", text=image.name, icon='IMAGE_DATA') + o.reload_list = False + o.image_name = image.name + else: + layout.label("No images") + +class reload_image_by_name(bpy.types.Operator): + bl_idname = "object.reload_image_by_name" + bl_label = "Reload Image by Name" + image_name: StringProperty( + name = 'Image Name', + default = '' + ) + reload_list: BoolProperty( + default=False + ) + + def execute(self, context): + if self.image_name: + if not self.reload_list: + bpy.data.images[self.image_name].reload() + else: + # converting string list to list + # probably doing this in a stupid + image_list = self.image_name[1:-1].split(',') + image_list = [img.strip(' ')[1:-1] for img in image_list] + + for image in image_list: + bpy.data.images[image].reload() + return {'FINISHED'} + +class load_from_selected_menu(bpy.types.Menu): + bl_idname = "load_from_selected_menu" + bl_label = "Load from Selected" + bl_space_type = "IMAGE_EDITOR" + + class operator(bpy.types.Operator): + bl_idname = "load_from_selected.my_class_name" + bl_label = "Load From Selected" + image_name: bpy.props.StringProperty( + name = 'Image Name', + default = '' + ) + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + image = bpy.data.images[self.image_name] + bpy.context.area.spaces.active.image = image + return {"FINISHED"} + + + def draw(self, context): + layout = self.layout + + material_list = get_materials_from_selected() + if material_list: + for mat in material_list: + layout.label(text=mat, icon='MATERIAL') + layout.separator() + mat_image_nodes = look_for_images_from_mat( + bpy.data.materials[mat]) + + # build menu for each material + if mat_image_nodes: + for image_node in mat_image_nodes: + o = layout.operator( + self.operator.bl_idname, text=image_node.image.name, icon='IMAGE_DATA') + o.image_name = image_node.image.name + layout.separator() + else: + layout.label(text="No images") + else: + layout.label(text='No materials') + +class make_active_from_selected_menu(bpy.types.Menu): + bl_idname = "object.make_active_from_selected_menu" + bl_label = "Make Image Active" + image_name = "" + + class operator(bpy.types.Operator): + bl_idname = "object.make_active_from_selected" + bl_label = "Make Image Active" + mat_name: bpy.props.StringProperty( + name = 'Material Name', + default = '' + ) + node_name: bpy.props.StringProperty( + name = 'Node Name', + default = '' + ) + + def execute(self, context): + + node_tree = bpy.data.materials[self.mat_name].node_tree + # TODO find a cleaner way of getting out node + for each in node_tree.nodes: + if str(each) == self.node_name: + node = each + node.select = True + node_tree.nodes.active = node + bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) + return {'FINISHED'} + + def draw(self, context): + layout = self.layout + material_list = get_materials_from_selected() + if material_list: + layout.menu(reload_images_menu.bl_idname) + for mat in material_list: + layout.label(text=mat, icon='MATERIAL') + layout.separator() + mat_image_nodes = look_for_images_from_mat( + bpy.data.materials[mat]) + + # build menu for each material + if mat_image_nodes: + for image_node in mat_image_nodes: + o = layout.operator( + self.operator.bl_idname, text=image_node.image.name, icon='IMAGE_DATA') + o.mat_name = mat + o.node_name = str(image_node) + layout.separator() + else: + layout.label(text="No images") + else: + layout.label(text='No materials') + +def get_images_from_objects(): + checked_mats = [] + images = [] + active_object = bpy.context.active_object + materials = active_object.material_slots + for mat in materials: + mat = mat.material + if mat not in checked_mats: + checked_mats.append(mat) + found_images = look_for_images_from_mat(mat) + for img in found_images: + if img not in images: + images.append(img) + return images + +def look_for_images_from_mat(mat = bpy.types.Material): + image_list = [] + for node in mat.node_tree.nodes: + if node.type == 'TEX_IMAGE' and node.image not in image_list: + image_list.append(node) + return image_list + +def get_materials_from_selected(): + object_materials = [] + checked_mats = [] + active_object = bpy.context.active_object + if active_object: + materials = active_object.material_slots + for mat in materials: + mat = mat.name + if mat not in checked_mats: + checked_mats.append(mat) + object_materials.append(mat) + return object_materials + +class Prefs(AddonPreferences): + bl_idname = __name__ + + + def draw(self, context): + def get_menu_hotkey(km, kmi_name, kmi_value): + for i, km_item in enumerate(km.keymap_items): + if km.keymap_items.keys()[i] == kmi_name: + if km.keymap_items[i].properties.name == kmi_value: + return km_item + + layout = self.layout + wm = bpy.context.window_manager + + + box = layout.box() + split = box.split() + col = split.column() + col.label(text='Hotkey') + col.separator() + kc = wm.keyconfigs.addon + + km = kc.keymaps['Image'] + kmi = get_menu_hotkey(km, 'wm.call_menu', "load_from_selected_menu") + if kmi: + col.context_pointer_set("keymap", km) + rna_keymap_ui.draw_kmi([], kc, km, kmi, col, 0) + else: + col.label(text="No keymap entry found") + + + km = kc.keymaps['3D View'] + kmi = get_menu_hotkey(km, 'wm.call_menu', "object.make_active_from_selected_menu") + if kmi: + col.context_pointer_set("keymap", km) + rna_keymap_ui.draw_kmi([], kc, km, kmi, col, 0) + else: + col.label(text="No keymap entry found") + + km = kc.keymaps['3D View'] + kmi = get_menu_hotkey(km, 'wm.call_menu', "reload_images_menu") + if kmi: + col.context_pointer_set("keymap", km) + rna_keymap_ui.draw_kmi([], kc, km, kmi, col, 0) + else: + col.label(text="No keymap entry found") + #object.make_active_from_selected_menu + + + + +classes = ( + reload_images_menu, + load_from_selected_menu, + load_from_selected_menu.operator, + reload_image_by_name, + make_active_from_selected_menu.operator, + make_active_from_selected_menu, + Prefs +) + +def register(): + + for cls in classes: + bpy.utils.register_class(cls) + + # register keymaps + wm = bpy.context.window_manager + kc = wm.keyconfigs.addon + + km = kc.keymaps.new(name="Image", space_type="IMAGE_EDITOR") + kmi = km.keymap_items.new( + "wm.call_menu", + type="I", + value="PRESS", + shift=True + ) + kmi.properties.name = load_from_selected_menu.bl_idname + addon_keymaps.append((km,kmi)) + + km = kc.keymaps.new(name="3D View", space_type="VIEW_3D") + kmi = km.keymap_items.new( + "wm.call_menu", + type="I", + value="PRESS", + shift=True + ) + kmi.properties.name = make_active_from_selected_menu.bl_idname + addon_keymaps.append((km,kmi)) + + kmi = km.keymap_items.new( + "wm.call_menu", + type="R", + value="PRESS", + shift=True, + alt=True + ) + kmi.properties.name = reload_images_menu.bl_idname + kmi.active = False + addon_keymaps.append((km,kmi)) + +def unregister(): + + for cls in classes: + bpy.utils.unregister_class(cls) + + # Unregister keymaps + for km, kmi in addon_keymaps: + km.keymap_items.remove(kmi) + + addon_keymaps.clear()