From a1c9074a8981e269780cf1daa70f07afacf6e9e3 Mon Sep 17 00:00:00 2001 From: vanous Date: Sun, 17 Dec 2023 14:14:53 +0100 Subject: [PATCH] New Fixture UI List --- __init__.py | 27 +++-- fixture.py | 8 ++ panels/fixtures.py | 261 ++++++++++++++++++++++++++++++------------- panels/programmer.py | 40 +++++-- 4 files changed, 237 insertions(+), 99 deletions(-) diff --git a/__init__.py b/__init__.py index fc4228cd..1d1de26d 100644 --- a/__init__.py +++ b/__init__.py @@ -151,7 +151,6 @@ class DMX(PropertyGroup): DMX_OT_Fixture_Remove, DMX_OT_Fixture_Import_GDTF, DMX_OT_Fixture_Import_MVR, - DMX_PT_Fixtures, DMX_UL_Group, DMX_MT_Group, DMX_OT_Group_Create, @@ -161,6 +160,7 @@ class DMX(PropertyGroup): DMX_PT_Groups, DMX_OT_Programmer_DeselectAll, DMX_OT_Programmer_SelectAll, + DMX_OT_Programmer_SelectFiltered, DMX_OT_Programmer_SelectInvert, DMX_OT_Programmer_SelectEveryOther, DMX_OT_Programmer_Clear, @@ -168,10 +168,12 @@ class DMX(PropertyGroup): DMX_OT_Programmer_SelectTargets, DMX_OT_Programmer_SelectCamera, DMX_OT_Programmer_TargetsToZero, - DMX_PT_Fixture_Columns_Setup, DMX_OT_Programmer_Set_Ignore_Movement, DMX_OT_Programmer_Unset_Ignore_Movement, DMX_PT_DMX_OSC, + DMX_PT_Fixtures, + DMX_UL_Fixtures, + DMX_OP_Delete_Fixture, DMX_PT_DMX_MVR_X, DMX_UL_MVR_Commit, DMX_OP_MVR_Refresh, @@ -183,6 +185,7 @@ class DMX(PropertyGroup): linkedToFile = False _keymaps = [] + fixtures_filter = [] def register(): for cls in DMX.classes_setup: @@ -232,10 +235,6 @@ def unregister(): name = "DMX Address", default = True) - column_fixture_remove: BoolProperty( - name = "Remove Fixture", - default = False) - collection: PointerProperty( name = "DMX Collection", type = Collection) @@ -295,6 +294,12 @@ def prepare_empty_buffer(self, context): default = 4, ) + selected_fixture_index: IntProperty() # Just a fake value, we need as the Fixture list requires it + + fixture_properties_editable: BoolProperty( + name = "Editable", + default = False) + # New DMX Scene # - Remove any previous DMX objects/collections # - Create DMX collection @@ -1027,9 +1032,9 @@ def syncProgrammer(self): description= "Fixture sorting order", default = "ADDRESS", items= [ - ("ADDRESS", "DMX Address", "", "", 0), - ("NAME", "Name", "", "", 1), - ("FIXTURE_ID", "Fixture ID", "", "", 2), + ("NAME", "Name", "", "", 0), + ("FIXTURE_ID", "Fixture ID", "", "", 1), + ("ADDRESS", "DMX Address", "", "", 2), ("UNIT_NUMBER", "Unit Number", "", "", 3), ], ) @@ -1095,6 +1100,7 @@ def selectedFixtures(self): def sortedFixtures(self): + def string_to_pairs(s, pairs=re.compile(r"(\D*)(\d*)").findall): return [(text.lower(), int(digits or 0)) for (text, digits) in pairs(s)[:-1]] @@ -1324,7 +1330,8 @@ def render(self): for fixture in self.fixtures: fixture.render() - + def set_fixtures_filter(self, fixtures_filter): + DMX.fixtures_filter = fixtures_filter # Handlers # diff --git a/fixture.py b/fixture.py index 926e4e6d..67a04bda 100644 --- a/fixture.py +++ b/fixture.py @@ -840,6 +840,14 @@ def toggleSelect(self): if (selected): self.unselect() else: self.select() + def is_selected(self): + selected = False + for obj in self.objects: + if (obj.object in bpy.context.selected_objects): + selected = True + break + return selected + def clear(self): for i, ch in enumerate(self.channels): data = DMX_Data.set(self.universe, self.address+i, ch.default) diff --git a/panels/fixtures.py b/panels/fixtures.py index 68c6c2fe..cae2d1a8 100644 --- a/panels/fixtures.py +++ b/panels/fixtures.py @@ -10,18 +10,15 @@ import bpy import os import shutil -import uuid as py_uuid import re -from dmx import pygdtf from dmx.gdtf import * import dmx.panels.profiles as Profiles from bpy.props import (IntProperty, - FloatProperty, BoolProperty, FloatVectorProperty, - EnumProperty, + PointerProperty, StringProperty, CollectionProperty) @@ -29,10 +26,10 @@ Menu, Operator, UIList, - PropertyGroup) + ) -from dmx.model import DMX_Model from dmx.gdtf import DMX_GDTF +from dmx.fixture import DMX_Fixture # Menus # @@ -553,92 +550,202 @@ def execute(self, context): dmx.removeFixture(context.fixture) return {'FINISHED'} -class DMX_PT_Fixture_Columns_Setup(Panel): - bl_label = "Display Columns" - bl_idname = "DMX_PT_Fixture_Columns_Setup" - bl_parent_id = "DMX_PT_Fixtures" + + +class DMX_PT_Fixtures(Panel): + bl_label = "Fixtures" + #bl_parent_id = "DMX_PT_Profiles" + bl_idname = "DMX_PT_FixturesNEW" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "DMX" bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - + # bl_parent_id = "DMX_PT_Patch" + #bl_options = {"DEFAULT_CLOSED"} + def draw(self, context): layout = self.layout + scene = context.scene + dmx = scene.dmx + + row = layout.row() + c = row.column() + c.label(text="Name") + c.ui_units_x = 8 + + if dmx.column_fixture_id: + c = row.column() + c.label(text="F ID") + c.ui_units_x = 2 + + if dmx.column_unit_number: + c = row.column() + c.ui_units_x = 2 + c.label(text="Unit #") + + if dmx.column_fixture_id_numeric: + c = row.column() + c.label(text="F ID #") + c.ui_units_x = 2 + + if dmx.column_custom_id: + c = row.column() + c.label(text="Cst ID") + c.ui_units_x = 2 + + if dmx.column_dmx_address: + c = row.column() + c.ui_units_x = 2 + if dmx.fixture_properties_editable: + c.label(text="Uni") + c = row.column() + c.ui_units_x = 2 + c.label(text="Addr") + else: + c.label(text="Uni.Addr") + + if dmx.fixture_properties_editable: + c = row.column() + c.ui_units_x = 2 + c.label(text="Del") + + layout.template_list( + "DMX_UL_Fixtures", + "", + dmx, + "fixtures", + dmx, + "selected_fixture_index", + rows=4, + ) + + layout.menu('DMX_MT_Fixture', text="Fixtures", icon="OUTLINER_DATA_LIGHT") + +class DMX_UL_Fixtures(UIList): + + def str_to_digit(self, s): + out = 0 + try: + digs=re.compile(r"(\d*)").findall + out = int(digs(s)[-2]) or 0 + except Exception as e: + print("Error converting text to digit", e, s) + return out + + def draw_filter(self, context, layout): + dmx = context.scene.dmx row = layout.row() - row.prop(dmx, "column_fixture_id") - row = layout.row() - row.prop(dmx, "column_custom_id") - row = layout.row() - row.prop(dmx, "column_fixture_id_numeric") - row = layout.row() - row.prop(dmx, "column_unit_number") - row = layout.row() - row.prop(dmx, "column_dmx_address") + row.prop(self, "filter_name", text="") row = layout.row() - row.prop(dmx, "column_fixture_remove") + col = row.column() + col.prop(dmx, "column_fixture_id") + col.prop(dmx, "column_custom_id") + row = row.row() + col = row.column() + col.prop(dmx, "column_fixture_id_numeric") + col.prop(dmx, "column_unit_number") + row = row.row() + col = row.column() + col.prop(dmx, "column_dmx_address") + col.prop(dmx, "fixture_properties_editable") row = layout.row() row.prop(dmx, "fixtures_sorting_order") + def filter_items(self, context, data, propname): + vgroups = getattr(data, propname) + helper_funcs = bpy.types.UI_UL_list + dmx = context.scene.dmx + # Default return values. + flt_flags = [] + flt_neworder = [] + + flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, vgroups, "name") + if not flt_flags: + flt_flags = [self.bitflag_filter_item] * len(vgroups) + dmx.set_fixtures_filter(flt_flags) + + sorting_order = dmx.fixtures_sorting_order + + if sorting_order == "ADDRESS": + _sort = [(idx, vgroups[vg.name].universe*1000+vgroups[vg.name].address) for idx, vg in enumerate(vgroups)] + flt_neworder = helper_funcs.sort_items_helper(_sort, lambda e: e[1], False) + elif sorting_order == "NAME": + flt_neworder = helper_funcs.sort_items_by_name(vgroups, "name") + elif sorting_order == "FIXTURE_ID": + _sort = [(idx, self.str_to_digit(vgroups[vg.name].fixture_id)) for idx, vg in enumerate(vgroups)] + flt_neworder = helper_funcs.sort_items_helper(_sort, lambda e: e[1], False) + elif sorting_order == "UNIT_NUMBER": + _sort = [(idx, vgroups[vg.name].unit_number) for idx, vg in enumerate(vgroups)] + flt_neworder = helper_funcs.sort_items_helper(_sort, lambda e: e[1], False) + else: + flt_neworder=[] + return flt_flags, flt_neworder -class DMX_PT_Fixtures(Panel): - bl_label = "Fixtures" - bl_idname = "DMX_PT_Fixtures" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "DMX" - bl_context = "objectmode" - - def draw(self, context): - layout = self.layout + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + self.use_filter_show = True scene = context.scene dmx = scene.dmx - if (len(scene.dmx.fixtures)): - box = layout.box() - col = box.column() - fixtures = dmx.sortedFixtures() - - for fixture in fixtures: - selected = False - for obj in fixture.collection.objects: - if (obj in bpy.context.selected_objects): - selected = True - break - - row = col.row() - row.context_pointer_set("fixture", fixture) - row.operator('dmx.fixture_item', text=fixture.name, depress=selected, icon="LOCKED" if fixture.ignore_movement_dmx else 'OUTLINER_DATA_LIGHT') - - if dmx.column_fixture_id and fixture.fixture_id: - c = row.column() - c.label(text=f"{fixture.fixture_id}") - c.ui_units_x = 2 - - if dmx.column_unit_number: - c = row.column() - c.label(text=f"{fixture.unit_number}") - c.ui_units_x = 2 - - if dmx.column_fixture_id_numeric: - c = row.column() - c.label(text=f"{fixture.fixture_id_numeric}") - c.ui_units_x = 2 - - if dmx.column_custom_id: - c = row.column() - c.label(text=f"{fixture.custom_id}") - c.ui_units_x = 2 - - if dmx.column_dmx_address: - c = row.column() - c.label(text=f"{fixture.universe}.{fixture.address}") - c.ui_units_x = 2 - - if dmx.column_fixture_remove: - row.operator('dmx.force_remove_fixture', icon="CANCEL") - - layout.menu('DMX_MT_Fixture', text="Fixtures", icon="OUTLINER_DATA_LIGHT") + col = layout.column() + col.context_pointer_set("fixture", item) + col.operator('dmx.fixture_item', text=f"{item.name}", depress=item.is_selected(), icon="LOCKED" if item.ignore_movement_dmx else 'OUTLINER_DATA_LIGHT') + col.ui_units_x = 6 + + if dmx.column_fixture_id: + c = layout.column() + #c.label(text=f"{item.fixture_id}") + c.ui_units_x = 2 + c.prop(item, "fixture_id", text="") + c.enabled = dmx.fixture_properties_editable + + if dmx.column_unit_number: + c = layout.column() + c.ui_units_x = 2 + c.prop(item, "unit_number", text="") + c.enabled = dmx.fixture_properties_editable + + if dmx.column_fixture_id_numeric: + c = layout.column() + c.prop(item, "fixture_id_numeric", text="") + c.ui_units_x = 2 + c.enabled = dmx.fixture_properties_editable + + if dmx.column_custom_id: + c = layout.column() + c.prop(item, "custom_id", text="") + c.ui_units_x = 2 + c.enabled = dmx.fixture_properties_editable + + if dmx.column_dmx_address: + c = layout.column() + c.ui_units_x = 2 + if dmx.fixture_properties_editable: + c.prop(item, "universe", text="") + c = layout.column() + c.prop(item, "address", text="") + c.ui_units_x = 2 + else: + c.label(text=f"{item.universe}.{item.address}") + + if dmx.fixture_properties_editable: + col = layout.column() + col.context_pointer_set("fixture", item) + col.operator("dmx.force_remove_fixture", text="", icon="CANCEL") + + +class DMX_OP_Delete_Fixture(Operator): + bl_label = "Delete fixture" + bl_description = "Delete fixture from local filesystem" + bl_idname = "dmx.delete_local_fixture" + bl_options = {"UNDO"} + + index: IntProperty() + + def execute(self, context): + #Profiles.controller.DMX_Fixtures_Manager.delete_local_fixture(self, self.index) + #DMX_GDTF.getManufacturerList() + #Profiles.DMX_Fixtures_Local_Profile.loadLocal() + return {"FINISHED"} diff --git a/panels/programmer.py b/panels/programmer.py index 077777e1..189a06b8 100644 --- a/panels/programmer.py +++ b/panels/programmer.py @@ -17,7 +17,7 @@ # Operators # class DMX_OT_Programmer_Set_Ignore_Movement(Operator): - bl_label = "DMX > Programmer > Lock Movement" + bl_label = "Lock Movement" bl_idname = "dmx.ignore_movement_true" bl_description = "Ignore pan/tilt DMX data" bl_options = {'UNDO'} @@ -36,7 +36,7 @@ def execute(self, context): return {'FINISHED'} class DMX_OT_Programmer_Unset_Ignore_Movement(Operator): - bl_label = "DMX > Programmer > Unlock Movement" + bl_label = "Unlock Movement" bl_idname = "dmx.ignore_movement_false" bl_description = "Allow pan/tilt DMX data" bl_options = {'UNDO'} @@ -55,7 +55,7 @@ def execute(self, context): return {'FINISHED'} class DMX_OT_Programmer_DeselectAll(Operator): - bl_label = "DMX > Programmer > Deselect All" + bl_label = "Deselect All" bl_idname = "dmx.deselect_all" bl_description = "Deselect every object in the Scene" bl_options = {'UNDO'} @@ -68,20 +68,34 @@ def execute(self, context): return {'FINISHED'} class DMX_OT_Programmer_SelectAll(Operator): - bl_label = "DMX > Programmer > Select All" + bl_label = "Select All" bl_idname = "dmx.select_all" bl_description = "Select every object in the Scene" bl_options = {'UNDO'} def execute(self, context): dmx = context.scene.dmx - for fixture in dmx.fixtures: + for fixture, enabled in dmx.fixtures: fixture.select() bpy.context.scene.dmx.updatePreviewVolume() return {'FINISHED'} +class DMX_OT_Programmer_SelectFiltered(Operator): + bl_label = "Select Visible (only filtered)" + bl_idname = "dmx.select_filtered" + bl_description = "Select every object in the Scene which is visible in the fixtures list" + bl_options = {'UNDO'} + + def execute(self, context): + dmx = context.scene.dmx + for fixture, enabled in zip(dmx.fixtures, dmx.fixtures_filter): + if enabled: + fixture.select() + bpy.context.scene.dmx.updatePreviewVolume() + return {'FINISHED'} + class DMX_OT_Programmer_SelectInvert(Operator): - bl_label = "DMX > Programmer > Invert selection" + bl_label = "Invert selection" bl_idname = "dmx.select_invert" bl_description = "Invert the selection" bl_options = {'UNDO'} @@ -103,7 +117,7 @@ def execute(self, context): return {'FINISHED'} class DMX_OT_Programmer_SelectEveryOther(Operator): - bl_label = "DMX > Programmer > Select every other light" + bl_label = "Select every other light" bl_idname = "dmx.select_every_other" bl_description = "Select every other light" bl_options = {'UNDO'} @@ -119,7 +133,7 @@ def execute(self, context): return {'FINISHED'} class DMX_OT_Programmer_Clear(Operator): - bl_label = "DMX > Programmer > Clear" + bl_label = "Clear" bl_idname = "dmx.clear" bl_description = "Clear all DMX values to default and update fixtures" bl_options = {'UNDO'} @@ -144,7 +158,7 @@ def execute(self, context): return {'FINISHED'} class DMX_OT_Programmer_TargetsToZero(Operator): - bl_label = "DMX > Programmer > Targets to zero" + bl_label = "Targets to zero" bl_idname = "dmx.targets_to_zero" bl_description = "Set Targets to 0" bl_options = {'UNDO'} @@ -162,7 +176,7 @@ def execute(self, context): return {'FINISHED'} class DMX_OT_Programmer_SelectBodies(Operator): - bl_label = "DMX > Programmer > Select Bodies" + bl_label = "Select Bodies" bl_idname = "dmx.select_bodies" bl_description = "Select body from every fixture element selected" bl_options = {'UNDO'} @@ -186,7 +200,7 @@ def execute(self, context): return {'FINISHED'} class DMX_OT_Programmer_SelectTargets(Operator): - bl_label = "DMX > Programmer > Select Targets" + bl_label = "Select Targets" bl_idname = "dmx.select_targets" bl_description = "Select target from every fixture element selected" bl_options = {'UNDO'} @@ -197,7 +211,7 @@ def execute(self, context): return {'FINISHED'} class DMX_OT_Programmer_SelectCamera(Operator): - bl_label = "DMX > Programmer > Select Camera" + bl_label = "Select Camera" bl_idname = "dmx.toggle_camera" bl_description = "Select camera of the selected fixture" bl_options = {'UNDO'} @@ -248,10 +262,12 @@ def draw(self, context): row.operator("dmx.select_all", text='', icon='SELECT_EXTEND') row.operator("dmx.select_invert", text='', icon='SELECT_SUBTRACT') row.operator("dmx.select_every_other", text='', icon='SELECT_INTERSECT') + c0 = row.column() c1 = row.column() c2 = row.column() c3 = row.column() c4 = row.column() + c0.operator("dmx.select_filtered", text='', icon="SELECT_DIFFERENCE") c1.operator("dmx.deselect_all", text='', icon='SELECT_SET') c2.operator("dmx.targets_to_zero", text="", icon="LIGHT_POINT") c3.operator("dmx.ignore_movement_true", text="", icon="LOCKED")