diff --git a/dmx.py b/dmx.py index e912048..f4533bd 100644 --- a/dmx.py +++ b/dmx.py @@ -24,7 +24,14 @@ from .panels import recorder as recorder from .panels import setup as setup -from .panels import dmx as panels_dmx +from .panels.protocols import protocols as panels_protocols +from .panels.protocols import artnet as panels_artnet +from .panels.protocols import sacn as panels_sacn +from .panels.protocols import osc as panels_osc +from .panels.protocols import psn as panels_psn +from .panels.protocols import mvr as panels_mvr +from .panels.protocols import universes as panels_universes +from .panels.protocols import live as panels_live from .panels import fixtures as fixtures from .panels import groups as groups from .panels import programmer as programmer @@ -80,8 +87,8 @@ class DMX(PropertyGroup): DMX_Universe, DMX_Value, setup.DMX_PT_Setup, - panels_dmx.DMX_OP_MVR_Download, - panels_dmx.DMX_OP_MVR_Import, + panels_mvr.DMX_OP_MVR_Download, + panels_mvr.DMX_OP_MVR_Import, DMX_MVR_Xchange_Commit, DMX_MVR_Xchange_Client, DMX_MVR_Xchange, @@ -94,13 +101,13 @@ class DMX(PropertyGroup): classes_setup = (setup.DMX_OT_Setup_NewShow,) - classes = ( panels_dmx.DMX_UL_Universe, - panels_dmx.DMX_MT_Universe, - panels_dmx.DMX_PT_DMX, - panels_dmx.DMX_PT_DMX_Universes, - panels_dmx.DMX_PT_DMX_LiveDMX, - panels_dmx.DMX_PT_DMX_ArtNet, - panels_dmx.DMX_PT_DMX_sACN, + classes = ( panels_protocols.DMX_PT_DMX, + panels_universes.DMX_UL_Universe, + panels_universes.DMX_MT_Universe, + panels_universes.DMX_PT_DMX_Universes, + panels_live.DMX_PT_DMX_LiveDMX, + panels_artnet.DMX_PT_DMX_ArtNet, + panels_sacn.DMX_PT_DMX_sACN, setup.DMX_OT_Setup_Volume_Create, setup.DMX_PT_Setup_Volume, setup.DMX_PT_Setup_Viewport, @@ -118,7 +125,7 @@ class DMX(PropertyGroup): fixtures.DMX_OT_Fixture_Item, fixtures.DMX_OT_Fixture_Profiles, fixtures.DMX_OT_Fixture_Mode, - panels_dmx.DMX_UL_LiveDMX_items, + panels_live.DMX_UL_LiveDMX_items, fixtures.DMX_OT_Fixture_Add, fixtures.DMX_OT_Fixture_Edit, fixtures.DMX_OT_Fixture_Remove, @@ -151,22 +158,22 @@ class DMX(PropertyGroup): programmer.DMX_OT_Programmer_ResetTargets, programmer.DMX_MT_PIE_Reset, programmer.DMX_OT_Programmer_Unset_Ignore_Movement, - panels_dmx.DMX_PT_DMX_OSC, - panels_dmx.DMX_UL_Tracker, - panels_dmx.DMX_OP_DMX_Tracker_Add, - panels_dmx.DMX_OP_DMX_Tracker_Remove, - panels_dmx.DMX_PT_DMX_Trackers, - panels_dmx.DMX_OT_Tracker_Followers, - panels_dmx.DMX_OT_Tracker_Followers_Add_Target, - panels_dmx.DMX_OT_Tracker_Followers_Remove_Target, - panels_dmx.DMX_UL_Tracker_Followers, - panels_dmx.DMX_OP_Unlink_Fixture_Tracker, - panels_dmx.DMX_OP_Link_Fixture_Tracker, + panels_osc.DMX_PT_DMX_OSC, + panels_psn.DMX_UL_Tracker, + panels_psn.DMX_OP_DMX_Tracker_Add, + panels_psn.DMX_OP_DMX_Tracker_Remove, + panels_psn.DMX_PT_DMX_Trackers, + panels_psn.DMX_OT_Tracker_Followers, + panels_psn.DMX_OT_Tracker_Followers_Add_Target, + panels_psn.DMX_OT_Tracker_Followers_Remove_Target, + panels_psn.DMX_UL_Tracker_Followers, + panels_psn.DMX_OP_Unlink_Fixture_Tracker, + panels_psn.DMX_OP_Link_Fixture_Tracker, fixtures.DMX_UL_Fixtures, - panels_dmx.DMX_PT_DMX_MVR_X, - panels_dmx.DMX_UL_MVR_Commit, - panels_dmx.DMX_OP_MVR_Refresh, - panels_dmx.DMX_OP_MVR_Request, + panels_mvr.DMX_PT_DMX_MVR_X, + panels_mvr.DMX_UL_MVR_Commit, + panels_mvr.DMX_OP_MVR_Refresh, + panels_mvr.DMX_OP_MVR_Request, fixtures.DMX_OT_Fixture_ForceRemove, fixtures.DMX_OT_Fixture_SelectNext, fixtures.DMX_OT_Fixture_SelectPrevious, diff --git a/panels/dmx.py b/panels/dmx.py deleted file mode 100644 index 11ffe10..0000000 --- a/panels/dmx.py +++ /dev/null @@ -1,628 +0,0 @@ -# -# BlendexDMX > Panels > DMX -# -# - Setup DMX Universes -# - Setup ArtNet (Future) -# -# http://www.github.com/open-stage/BlenderDMX -# - -import os -import bpy -from bpy.props import StringProperty, IntProperty -from bpy.types import Menu, Operator, Panel, UIList -from ..mvrx_protocol import DMX_MVR_X_Client -from ..data import DMX_Data -from ..logging import DMX_Log -import uuid as py_uuid -from datetime import datetime -from ..tracker import DMX_Tracker - -from ..i18n import DMX_Lang -_ = DMX_Lang._ - -class DMX_OP_DMX_Tracker_Add(Operator): - bl_label = _("Add Tracker") - bl_description = _("Adding a tracker") - bl_idname = "dmx.dmx_add_tracker" - bl_options = {"UNDO"} - - def execute(self, context): - DMX_Tracker.add_tracker() - return {"FINISHED"} - -class DMX_OP_DMX_Tracker_Remove(Operator): - bl_label = _("Remove Tracker") - bl_description = _("Removing a tracker") - bl_idname = "dmx.dmx_remove_tracker" - bl_options = {"UNDO"} - - uuid: StringProperty() - - def execute(self, context): - DMX_Tracker.remove_tracker(self.uuid) - return {"FINISHED"} - - -class DMX_OP_MVR_Refresh(Operator): - bl_label = _("Refresh") - bl_description = _("Refresh connection") - bl_idname = "dmx.mvr_refresh" - bl_options = {"UNDO"} - - def execute(self, context): - DMX_MVR_X_Client.re_join() - return {"FINISHED"} - - -class DMX_OP_MVR_Request(Operator): - bl_label = _("Request latest version") - bl_description = _("Sends the Request message") - bl_idname = "dmx.mvr_request" - bl_options = {"UNDO"} - - station_uuid: StringProperty() - - def execute(self, context): - uuid = str(py_uuid.uuid4()) - mvr_commit = {"FileUUID": uuid, "StationUUID": self.station_uuid, - "Comment": datetime.now().strftime("%H:%M:%S %B %d, %Y"), "FileSize": 0} - - last_commit = DMX_MVR_X_Client.create_self_request_commit(mvr_commit) - if last_commit: - DMX_MVR_X_Client.request_file(last_commit) - - return {"FINISHED"} - -class DMX_OP_MVR_Import(Operator): - bl_label = _("Import") - bl_description = _("Import commit") - bl_idname = "dmx.mvr_import" - bl_options = {"UNDO"} - - uuid: StringProperty() - - def execute(self, context): - scene = context.scene - dmx = scene.dmx - ADDON_PATH = os.path.dirname(os.path.abspath(__file__)) - clients = context.window_manager.dmx.mvr_xchange - all_clients = context.window_manager.dmx.mvr_xchange.mvr_xchange_clients - selected = clients.selected_mvr_client - for client in all_clients: - if client.station_uuid == selected: - break - for commit in client.commits: - if commit.commit_uuid == self.uuid: - DMX_Log.log.info(f"import {commit}") - path = os.path.join(ADDON_PATH, "..", "assets", "mvrs", f"{commit.commit_uuid}.mvr") - DMX_Log.log.info(path) - dmx.addMVR(path) - break - return {"FINISHED"} - -class DMX_OP_MVR_Download(Operator): - bl_label = _("Download") - bl_description = _("Download commit") - bl_idname = "dmx.mvr_download" - bl_options = {"UNDO"} - - uuid: StringProperty() - - def execute(self, context): - DMX_Log.log.info("downloading") - - clients = context.window_manager.dmx.mvr_xchange - all_clients = clients.mvr_xchange_clients - selected = clients.selected_mvr_client - for client in all_clients: - if client.station_uuid == selected: - break - DMX_Log.log.info(f"got client {client.station_name}") - for commit in client.commits: - DMX_Log.log.info(commit.commit_uuid) - if commit.commit_uuid == self.uuid: - DMX_Log.log.info(f"downloading {commit}") - DMX_MVR_X_Client.request_file(commit) - break - - return {"FINISHED"} - -class DMX_UL_MVR_Commit(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - scene = context.scene - dmx = scene.dmx - icon = "GROUP_VERTEX" - #layout.context_pointer_set("mvr_xchange_clients", item) - col = layout.column() - col.label(text = f"{item.comment}", icon="CHECKBOX_HLT" if item.timestamp_saved else "CHECKBOX_DEHLT") - col = layout.column() - col.operator("dmx.mvr_download", text="", icon="IMPORT").uuid = item.commit_uuid - col.enabled = dmx.mvrx_enabled - col = layout.column() - col.operator("dmx.mvr_import", text="", icon="CHECKBOX_HLT").uuid = item.commit_uuid - col.enabled = item.timestamp_saved > 0 - - -# List # - -class DMX_UL_Tracker(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - ob = data - icon = "FILE_VOLUME" - if self.layout_type in {'DEFAULT', 'COMPACT'}: - col = layout.column() - col.ui_units_x = 3 - col = layout.column() - col.prop(item, "name", text="", emboss=False) - col = layout.column() - col.prop(item, "enabled", text="") - col = layout.column() - col.operator("dmx.dmx_remove_tracker", text="", icon="TRASH").uuid = item.uuid - elif self.layout_type in {'GRID'}: - layout.alignment = 'CENTER' - layout.label(text=str(item.id), icon=icon) - -class DMX_UL_Universe(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - ob = data - icon = "FILE_VOLUME" - if self.layout_type in {'DEFAULT', 'COMPACT'}: - col = layout.column() - col.label(text=f"{item.id}", icon=icon) - col.ui_units_x = 3 - col = layout.column() - col.prop(item, "name", text="", emboss=False) - col = layout.column() - col.label(text=item.input) - elif self.layout_type in {'GRID'}: - layout.alignment = 'CENTER' - layout.label(text=str(item.id), icon=icon) - - - -class DMX_OP_Link_Fixture_Tracker(Operator): - bl_label = _("Link Fixture to Tracker") - bl_description = _("Link fixture to a tracker") - bl_idname = "dmx.psn_tracker_follower_link" - bl_options = {"UNDO"} - - fixture_uuid: StringProperty() - tracker_uuid: StringProperty() - tracker_index: IntProperty() - - def execute(self, context): - scene = context.scene - dmx = scene.dmx - dmx = scene.dmx - layout = self.layout - target = None - for tracker in dmx.trackers: - if tracker.uuid == self.tracker_uuid: - for idx, obj in enumerate(tracker.collection.objects): - if idx == self.tracker_index: - target = obj - - if target is not None: - for fixture in dmx.fixtures: - if fixture.uuid == self.fixture_uuid: - for obj in fixture.objects: - if obj.name == "Target": - constraint = obj.object.constraints.new(type='COPY_LOCATION') - constraint.target = target - - return {"FINISHED"} - -class DMX_OP_Unlink_Fixture_Tracker(Operator): - bl_label = _("Unlink Fixture from Tracker") - bl_description = _("Unlink fixture from a tracker") - bl_idname = "dmx.psn_tracker_follower_unlink" - bl_options = {"UNDO"} - - fixture_uuid: StringProperty() - tracker_uuid: StringProperty() - - def execute(self, context): - scene = context.scene - dmx = scene.dmx - layout = self.layout - for fixture in dmx.fixtures: - if fixture.uuid == self.fixture_uuid: - for obj in fixture.objects: - if obj.name == "Target": - for constraint in obj.object.constraints: - if constraint.target != None: - if constraint.target.get("uuid", None) == self.tracker_uuid: - obj.object.constraints.remove(constraint) - - - return {"FINISHED"} - - - -class DMX_UL_Tracker_Followers(UIList): - - tracker_uuid: StringProperty() - tracker_index: IntProperty() - - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - fixture = item - scene = context.scene - dmx = scene.dmx - icon = "FILE_VOLUME" - self.tracker_uuid = context.window_manager.dmx.selected_tracker - self.tracker_index = context.window_manager.dmx.selected_tracker_index - - linked = None - for obj in fixture.objects: - if obj.name == "Target": - linked = False - for const in obj.object.constraints: - if const.target is not None: - if const.target.get("uuid", None) == self.tracker_uuid: - linked = True - - if self.layout_type in {'DEFAULT', 'COMPACT'}: - col = layout.column() - col.ui_units_x = 3 - col = layout.column() - col.prop(item, "name", text="", emboss=False) - col = layout.column() - if linked is None: - layout.label(text="", icon="CANCEL") - elif linked is True: - op = col.operator("dmx.psn_tracker_follower_unlink", icon="LINKED", text="") - op.fixture_uuid = fixture.uuid - op.tracker_uuid = self.tracker_uuid - else: - op = col.operator("dmx.psn_tracker_follower_link", icon="UNLINKED", text="") - op.fixture_uuid = fixture.uuid - op.tracker_uuid = self.tracker_uuid - op.tracker_index = self.tracker_index - elif self.layout_type in {'GRID'}: - layout.alignment = 'CENTER' - layout.label(text=item.name, icon=icon) - - -class DMX_OT_Tracker_Followers(Operator): - bl_label = _("Tracker Followers") - bl_idname = "dmx.psn_tracker_followers" - bl_description = _("Link followers to a tracker") - bl_options = {'UNDO'} - - tracker_uuid: StringProperty() - tracker_index: IntProperty() - fixture_i: IntProperty() - - def draw(self, context): - layout = self.layout - scene = context.scene - dmx = scene.dmx - context.window_manager.dmx.selected_tracker = self.tracker_uuid - context.window_manager.dmx.selected_tracker_index = self.tracker_index - layout.template_list("DMX_UL_Tracker_Followers", "", dmx, "fixtures", self, "fixture_i") - - - def execute(self, context): - return {'FINISHED'} - return {'CANCELLED'} - - def invoke(self, context, event): - #self.name = "Group " + str(len(context.scene.dmx.groups)+1) - wm = context.window_manager - return wm.invoke_props_dialog(self) - -# Menus # - -class DMX_MT_Universe(Menu): - bl_label = _("DMX > Universe Menu") - bl_idname = "DMX_MT_Universe" - - def draw(self, context): - layout = self.layout - scene = context.scene - dmx = scene.dmx - - # "Add" - row = layout.row() - #row.operator("dmx.add_universe", text="Add", icon="ADD") - - -# Sub-panels # -class DMX_PT_DMX_OSC(Panel): - bl_label = _("OSC") - bl_idname = "DMX_PT_DMX_OSC" - bl_parent_id = "DMX_PT_DMX" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "DMX" - bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - dmx = context.scene.dmx - - row = layout.row() - row.prop(dmx, "osc_enabled") - row = layout.row() - row.prop(dmx, "osc_target_address") - row.enabled = not dmx.osc_enabled - row = layout.row() - row.prop(dmx, "osc_target_port") - row.enabled = not dmx.osc_enabled - -class DMX_PT_DMX_Trackers(Panel): - bl_label = _("PSN") - bl_idname = "DMX_PT_DMX_Trackers" - bl_parent_id = "DMX_PT_DMX" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "DMX" - bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - dmx = context.scene.dmx - layout.operator("dmx.dmx_add_tracker", text="Add tracker", icon="PLUS") - layout.template_list("DMX_UL_Tracker", "", dmx, "trackers", dmx, "trackers_i") - - if (dmx.trackers_i < len(dmx.trackers)): - tracker = dmx.trackers[dmx.trackers_i] - layout.prop(tracker, "name") - layout.prop(tracker, "ip_address") - layout.prop(tracker, "ip_port") - layout.prop(tracker, "enabled") - add_trackers = True - for idx, obj in enumerate(tracker.collection.objects): - row = layout.row() - col = row.column() - op=col.operator("dmx.psn_tracker_followers", text=_(f"Link Followers to {obj.name}"), icon="LINKED") - op.tracker_uuid = tracker.uuid - op.tracker_index = idx - if len(tracker.collection.objects) > 1: - col = row.column() - op = col.operator("dmx.psn_remove_tracker_followers_target", text="", icon="TRASH") - op.object_name = obj.name - op.tracker_uuid = tracker.uuid - - if idx >= 9: # we support max 10 trackers, edit tracker.py and psn.py if more is needed - add_trackers = False - row = layout.row() - op = row.operator("dmx.psn_add_tracker_followers_target", text=_("Add Tracking Target"), icon="PLUS") - op.tracker_uuid = tracker.uuid - row.enabled = add_trackers - - - -class DMX_OT_Tracker_Followers_Remove_Target(Operator): - bl_label = _("Remove Followers Target") - bl_idname = "dmx.psn_remove_tracker_followers_target" - bl_description = _("Remove target") - bl_options = {'UNDO'} - - object_name: StringProperty() - tracker_uuid: StringProperty() - - def execute(self, context): - dmx = context.scene.dmx - for tracker in dmx.trackers: - if tracker.uuid == self.tracker_uuid: - if self.object_name in tracker.collection.objects: - rem_obj = tracker.collection.objects[self.object_name] - bpy.data.objects.remove(rem_obj) - - return {"FINISHED"} - -class DMX_OT_Tracker_Followers_Add_Target(Operator): - bl_label = _("Add Followers Target") - bl_idname = "dmx.psn_add_tracker_followers_target" - bl_description = _("Add target") - bl_options = {'UNDO'} - - tracker_uuid: StringProperty() - - def execute(self, context): - dmx = context.scene.dmx - for tracker in dmx.trackers: - if tracker.uuid == self.tracker_uuid: - for obj in tracker.collection.objects: - duplicate_obj = obj.copy() - tracker.collection.objects.link(duplicate_obj) - break - - return {"FINISHED"} - -class DMX_PT_DMX_MVR_X(Panel): - bl_label = _("MVR-xchange") - bl_idname = "DMX_PT_DMX_MVR_Xchange" - bl_parent_id = "DMX_PT_DMX" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "DMX" - bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - dmx = context.scene.dmx - - row = layout.row() - row.prop(dmx, "zeroconf_enabled") - row = layout.row() - - clients = context.window_manager.dmx.mvr_xchange - all_clients = clients.mvr_xchange_clients - if not all_clients: - selected = None - else: - selected = clients.selected_mvr_client - - client = None - for client in all_clients: - if client.station_uuid ==selected: - break - - row.prop(clients, "selected_mvr_client", text="") - row.enabled = not dmx.mvrx_enabled - row = layout.row() - col = row.column() - row.prop(dmx, "mvrx_enabled") - col1 = row.column() - col1.operator("dmx.mvr_refresh", text="", icon="FILE_REFRESH") - col2 = row.column() - if client: - col2.operator("dmx.mvr_request", text="", icon="IMPORT").station_uuid = client.station_uuid - col1.enabled = col2.enabled = dmx.mvrx_enabled - #row.operator("dmx.mvr_test", text="Test", icon="CANCEL") - row.enabled = len(all_clients) > 0 - if not client: - return - row = layout.row() - row.label(text = f"{client.station_name}", icon = "LINKED" if dmx.mvrx_enabled else "UNLINKED") - layout.template_list( - "DMX_UL_MVR_Commit", - "", - client, - "commits", - clients, - "selected_commit", - rows=4, - ) - -class DMX_PT_DMX_Universes(Panel): - bl_label = _("Universes") - bl_idname = "DMX_PT_DMX_Universes" - bl_parent_id = "DMX_PT_DMX" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "DMX" - bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - dmx = context.scene.dmx - - layout.prop(dmx, "universes_n", text=_("Universes")) - - layout.template_list("DMX_UL_Universe", "", dmx, "universes", dmx, "universe_list_i") - - if (dmx.universe_list_i < dmx.universes_n): - universe = dmx.universes[dmx.universe_list_i] - layout.prop(universe, "name") - layout.prop(universe, "input") - - #layout.menu("dmx.menu.universe", text="...", icon="FILE_VOLUME") - -class DMX_PT_DMX_ArtNet(Panel): - bl_label = _("Art-Net") - bl_idname = "DMX_PT_DMX_ArtNet" - bl_parent_id = "DMX_PT_DMX" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "DMX" - bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - dmx = context.scene.dmx - - artnet_universes = [] - for universe in dmx.universes: - if universe.input == "ARTNET": - artnet_universes.append(universe) - - row = layout.row() - row.prop(dmx, "artnet_ipaddr", text=_("IPv4")) - row.enabled = not dmx.artnet_enabled - - row = layout.row() - row.prop(dmx, "artnet_enabled") - row.enabled = len(artnet_universes)>0 - row = layout.row() - row.label(text=_("Art-Net set for {} universe(s)").format(len(artnet_universes))) - layout.label(text=_("Status") + ": " + layout.enum_item_name(dmx, 'artnet_status', dmx.artnet_status)) - -class DMX_PT_DMX_sACN(Panel): - bl_label = _("sACN") - bl_idname = "DMX_PT_DMX_sACN" - bl_parent_id = "DMX_PT_DMX" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "DMX" - bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - dmx = context.scene.dmx - - sacn_universes = [] - for index, universe in enumerate(dmx.universes): - if index == 0: # invalid for sACN - continue - if universe.input == "sACN": - sacn_universes.append(universe) - - row = layout.row() - row.prop(dmx, "sacn_enabled") - row.enabled = len(sacn_universes)>0 - row = layout.row() - row.label(text=_("sACN set for {} universe(s)").format(len(sacn_universes))) - layout.label(text=_("Status") + ": " + dmx.sacn_status) - -class DMX_UL_LiveDMX_items(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - layout.alignment = 'CENTER' - layout.label(text=f"{index+1}: {DMX_Data._live_view_data[index]}") - - def invoke(self, context, event): - pass - -class DMX_PT_DMX_LiveDMX(Panel): - bl_label = _("Live DMX") - bl_idname = "DMX_PT_DMX_LiveDMX" - bl_parent_id = "DMX_PT_DMX" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "DMX" - bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - - - def draw(self, context): - layout = self.layout - dmx = context.scene.dmx - selected_universe = dmx.get_selected_live_dmx_universe() - if selected_universe is None: # this should not happen - raise ValueError("Missing selected universe, as if DMX base class is empty...") - - row = layout.row() - row.prop(dmx, "selected_live_dmx", text=_("Source")) - row = layout.row() - col = row.column() - col.label(text=f"{selected_universe.id}") - col.ui_units_x = 2 - col = row.column() - row.label(text=f"{selected_universe.name}") - col = row.column() - row.label(text=f"{selected_universe.input}") - layout.template_list("DMX_UL_LiveDMX_items", "", dmx, "dmx_values", dmx, "dmx_value_index", type='GRID') - -# Panel # - -class DMX_PT_DMX(Panel): - bl_label = _("Protocols") - bl_idname = "DMX_PT_DMX" - bl_space_type = "VIEW_3D" - bl_region_type = "UI" - bl_category = "DMX" - bl_context = "objectmode" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - dmx = context.scene.dmx diff --git a/panels/protocols/artnet.py b/panels/protocols/artnet.py new file mode 100644 index 0000000..27c3aaf --- /dev/null +++ b/panels/protocols/artnet.py @@ -0,0 +1,52 @@ +# Copyright Hugo Aboud +# +# This file is part of BlenderDMX. +# +# BlenderDMX 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. +# +# BlenderDMX 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 this program. If not, see . + +from bpy.types import Panel + +from ...i18n import DMX_Lang +_ = DMX_Lang._ + +class DMX_PT_DMX_ArtNet(Panel): + bl_label = _("Art-Net") + bl_idname = "DMX_PT_DMX_ArtNet" + bl_parent_id = "DMX_PT_DMX" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "DMX" + bl_context = "objectmode" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + dmx = context.scene.dmx + + artnet_universes = [] + for universe in dmx.universes: + if universe.input == "ARTNET": + artnet_universes.append(universe) + + row = layout.row() + row.prop(dmx, "artnet_ipaddr", text=_("IPv4")) + row.enabled = not dmx.artnet_enabled + + row = layout.row() + row.prop(dmx, "artnet_enabled") + row.enabled = len(artnet_universes)>0 + row = layout.row() + row.label(text=_("Art-Net set for {} universe(s)").format(len(artnet_universes))) + layout.label(text=_("Status") + ": " + layout.enum_item_name(dmx, 'artnet_status', dmx.artnet_status)) + diff --git a/panels/protocols/live.py b/panels/protocols/live.py new file mode 100644 index 0000000..77a84b5 --- /dev/null +++ b/panels/protocols/live.py @@ -0,0 +1,62 @@ +# Copyright vanous +# +# This file is part of BlenderDMX. +# +# BlenderDMX 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. +# +# BlenderDMX 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 this program. If not, see . + +from bpy.types import Menu, Operator, Panel, UIList +from ...data import DMX_Data + +from ...i18n import DMX_Lang +_ = DMX_Lang._ + + +class DMX_UL_LiveDMX_items(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + layout.alignment = 'CENTER' + layout.label(text=f"{index+1}: {DMX_Data._live_view_data[index]}") + + def invoke(self, context, event): + pass + +class DMX_PT_DMX_LiveDMX(Panel): + bl_label = _("Live DMX") + bl_idname = "DMX_PT_DMX_LiveDMX" + bl_parent_id = "DMX_PT_DMX" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "DMX" + bl_context = "objectmode" + bl_options = {'DEFAULT_CLOSED'} + + + def draw(self, context): + layout = self.layout + dmx = context.scene.dmx + selected_universe = dmx.get_selected_live_dmx_universe() + if selected_universe is None: # this should not happen + raise ValueError("Missing selected universe, as if DMX base class is empty...") + + row = layout.row() + row.prop(dmx, "selected_live_dmx", text=_("Source")) + row = layout.row() + col = row.column() + col.label(text=f"{selected_universe.id}") + col.ui_units_x = 2 + col = row.column() + row.label(text=f"{selected_universe.name}") + col = row.column() + row.label(text=f"{selected_universe.input}") + layout.template_list("DMX_UL_LiveDMX_items", "", dmx, "dmx_values", dmx, "dmx_value_index", type='GRID') + diff --git a/panels/protocols/mvr.py b/panels/protocols/mvr.py new file mode 100644 index 0000000..9924e38 --- /dev/null +++ b/panels/protocols/mvr.py @@ -0,0 +1,187 @@ +# Copyright vanous +# +# This file is part of BlenderDMX. +# +# BlenderDMX 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. +# +# BlenderDMX 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 this program. If not, see . + +import os +from bpy.props import StringProperty +from bpy.types import Operator, Panel, UIList +from ...mvrx_protocol import DMX_MVR_X_Client +from ...logging import DMX_Log +import uuid as py_uuid +from datetime import datetime + +from ...i18n import DMX_Lang +_ = DMX_Lang._ + + +class DMX_OP_MVR_Refresh(Operator): + bl_label = _("Refresh") + bl_description = _("Refresh connection") + bl_idname = "dmx.mvr_refresh" + bl_options = {"UNDO"} + + def execute(self, context): + DMX_MVR_X_Client.re_join() + return {"FINISHED"} + + +class DMX_OP_MVR_Request(Operator): + bl_label = _("Request latest version") + bl_description = _("Sends the Request message") + bl_idname = "dmx.mvr_request" + bl_options = {"UNDO"} + + station_uuid: StringProperty() + + def execute(self, context): + uuid = str(py_uuid.uuid4()) + mvr_commit = {"FileUUID": uuid, "StationUUID": self.station_uuid, + "Comment": datetime.now().strftime("%H:%M:%S %B %d, %Y"), "FileSize": 0} + + last_commit = DMX_MVR_X_Client.create_self_request_commit(mvr_commit) + if last_commit: + DMX_MVR_X_Client.request_file(last_commit) + + return {"FINISHED"} + +class DMX_OP_MVR_Import(Operator): + bl_label = _("Import") + bl_description = _("Import commit") + bl_idname = "dmx.mvr_import" + bl_options = {"UNDO"} + + uuid: StringProperty() + + def execute(self, context): + scene = context.scene + dmx = scene.dmx + ADDON_PATH = os.path.dirname(os.path.abspath(__file__)) + clients = context.window_manager.dmx.mvr_xchange + all_clients = context.window_manager.dmx.mvr_xchange.mvr_xchange_clients + selected = clients.selected_mvr_client + for client in all_clients: + if client.station_uuid == selected: + break + for commit in client.commits: + if commit.commit_uuid == self.uuid: + DMX_Log.log.info(f"import {commit}") + path = os.path.join(ADDON_PATH, "..", "..", "assets", "mvrs", f"{commit.commit_uuid}.mvr") + DMX_Log.log.info(path) + dmx.addMVR(path) + break + return {"FINISHED"} + +class DMX_OP_MVR_Download(Operator): + bl_label = _("Download") + bl_description = _("Download commit") + bl_idname = "dmx.mvr_download" + bl_options = {"UNDO"} + + uuid: StringProperty() + + def execute(self, context): + DMX_Log.log.info("downloading") + + clients = context.window_manager.dmx.mvr_xchange + all_clients = clients.mvr_xchange_clients + selected = clients.selected_mvr_client + for client in all_clients: + if client.station_uuid == selected: + break + DMX_Log.log.info(f"got client {client.station_name}") + for commit in client.commits: + DMX_Log.log.info(commit.commit_uuid) + if commit.commit_uuid == self.uuid: + DMX_Log.log.info(f"downloading {commit}") + DMX_MVR_X_Client.request_file(commit) + break + + return {"FINISHED"} + +class DMX_UL_MVR_Commit(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + scene = context.scene + dmx = scene.dmx + icon = "GROUP_VERTEX" + #layout.context_pointer_set("mvr_xchange_clients", item) + col = layout.column() + col.label(text = f"{item.comment}", icon="CHECKBOX_HLT" if item.timestamp_saved else "CHECKBOX_DEHLT") + col = layout.column() + col.operator("dmx.mvr_download", text="", icon="IMPORT").uuid = item.commit_uuid + col.enabled = dmx.mvrx_enabled + col = layout.column() + col.operator("dmx.mvr_import", text="", icon="CHECKBOX_HLT").uuid = item.commit_uuid + col.enabled = item.timestamp_saved > 0 + + + +class DMX_PT_DMX_MVR_X(Panel): + bl_label = _("MVR-xchange") + bl_idname = "DMX_PT_DMX_MVR_Xchange" + bl_parent_id = "DMX_PT_DMX" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "DMX" + bl_context = "objectmode" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + dmx = context.scene.dmx + + row = layout.row() + row.prop(dmx, "zeroconf_enabled") + row = layout.row() + + clients = context.window_manager.dmx.mvr_xchange + all_clients = clients.mvr_xchange_clients + if not all_clients: + selected = None + else: + selected = clients.selected_mvr_client + + client = None + for client in all_clients: + if client.station_uuid ==selected: + break + + row.prop(clients, "selected_mvr_client", text="") + row.enabled = not dmx.mvrx_enabled + row = layout.row() + col = row.column() + row.prop(dmx, "mvrx_enabled") + col1 = row.column() + col1.operator("dmx.mvr_refresh", text="", icon="FILE_REFRESH") + col2 = row.column() + if client: + col2.operator("dmx.mvr_request", text="", icon="IMPORT").station_uuid = client.station_uuid + col1.enabled = col2.enabled = dmx.mvrx_enabled + #row.operator("dmx.mvr_test", text="Test", icon="CANCEL") + row.enabled = len(all_clients) > 0 + if not client: + return + row = layout.row() + row.label(text = f"{client.station_name}", icon = "LINKED" if dmx.mvrx_enabled else "UNLINKED") + layout.template_list( + "DMX_UL_MVR_Commit", + "", + client, + "commits", + clients, + "selected_commit", + rows=4, + ) + diff --git a/panels/protocols/osc.py b/panels/protocols/osc.py new file mode 100644 index 0000000..b30ffce --- /dev/null +++ b/panels/protocols/osc.py @@ -0,0 +1,45 @@ +# Copyright vanous +# +# This file is part of BlenderDMX. +# +# BlenderDMX 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. +# +# BlenderDMX 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 this program. If not, see . + +from bpy.types import Panel + +from ...i18n import DMX_Lang +_ = DMX_Lang._ + +class DMX_PT_DMX_OSC(Panel): + bl_label = _("OSC") + bl_idname = "DMX_PT_DMX_OSC" + bl_parent_id = "DMX_PT_DMX" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "DMX" + bl_context = "objectmode" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + dmx = context.scene.dmx + + row = layout.row() + row.prop(dmx, "osc_enabled") + row = layout.row() + row.prop(dmx, "osc_target_address") + row.enabled = not dmx.osc_enabled + row = layout.row() + row.prop(dmx, "osc_target_port") + row.enabled = not dmx.osc_enabled + diff --git a/panels/protocols/protocols.py b/panels/protocols/protocols.py new file mode 100644 index 0000000..cedb8e7 --- /dev/null +++ b/panels/protocols/protocols.py @@ -0,0 +1,35 @@ +# Copyright Hugo Aboud, vanous +# +# This file is part of BlenderDMX. +# +# BlenderDMX 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. +# +# BlenderDMX 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 this program. If not, see . + +from bpy.types import Panel + +from ...i18n import DMX_Lang + +_ = DMX_Lang._ + + +class DMX_PT_DMX(Panel): + bl_label = _("Protocols") + bl_idname = "DMX_PT_DMX" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "DMX" + bl_context = "objectmode" + bl_options = {"DEFAULT_CLOSED"} + + def draw(self, context): + pass diff --git a/panels/protocols/psn.py b/panels/protocols/psn.py new file mode 100644 index 0000000..e00f4a5 --- /dev/null +++ b/panels/protocols/psn.py @@ -0,0 +1,289 @@ +# Copyright vanous +# +# This file is part of BlenderDMX. +# +# BlenderDMX 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. +# +# BlenderDMX 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 this program. If not, see . + +import os +import bpy +from bpy.props import StringProperty, IntProperty +from bpy.types import Menu, Operator, Panel, UIList +from ...mvrx_protocol import DMX_MVR_X_Client +from ...data import DMX_Data +from ...logging import DMX_Log +import uuid as py_uuid +from datetime import datetime +from ...tracker import DMX_Tracker + +from ...i18n import DMX_Lang +_ = DMX_Lang._ + +class DMX_OP_DMX_Tracker_Add(Operator): + bl_label = _("Add Tracker") + bl_description = _("Adding a tracker") + bl_idname = "dmx.dmx_add_tracker" + bl_options = {"UNDO"} + + def execute(self, context): + DMX_Tracker.add_tracker() + return {"FINISHED"} + +class DMX_OP_DMX_Tracker_Remove(Operator): + bl_label = _("Remove Tracker") + bl_description = _("Removing a tracker") + bl_idname = "dmx.dmx_remove_tracker" + bl_options = {"UNDO"} + + uuid: StringProperty() + + def execute(self, context): + DMX_Tracker.remove_tracker(self.uuid) + return {"FINISHED"} + + + +class DMX_UL_Tracker(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + ob = data + icon = "FILE_VOLUME" + if self.layout_type in {'DEFAULT', 'COMPACT'}: + col = layout.column() + col.ui_units_x = 3 + col = layout.column() + col.prop(item, "name", text="", emboss=False) + col = layout.column() + col.prop(item, "enabled", text="") + col = layout.column() + col.operator("dmx.dmx_remove_tracker", text="", icon="TRASH").uuid = item.uuid + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.label(text=str(item.id), icon=icon) + + + + +class DMX_OP_Link_Fixture_Tracker(Operator): + bl_label = _("Link Fixture to Tracker") + bl_description = _("Link fixture to a tracker") + bl_idname = "dmx.psn_tracker_follower_link" + bl_options = {"UNDO"} + + fixture_uuid: StringProperty() + tracker_uuid: StringProperty() + tracker_index: IntProperty() + + def execute(self, context): + scene = context.scene + dmx = scene.dmx + dmx = scene.dmx + layout = self.layout + target = None + for tracker in dmx.trackers: + if tracker.uuid == self.tracker_uuid: + for idx, obj in enumerate(tracker.collection.objects): + if idx == self.tracker_index: + target = obj + + if target is not None: + for fixture in dmx.fixtures: + if fixture.uuid == self.fixture_uuid: + for obj in fixture.objects: + if obj.name == "Target": + constraint = obj.object.constraints.new(type='COPY_LOCATION') + constraint.target = target + + return {"FINISHED"} + +class DMX_OP_Unlink_Fixture_Tracker(Operator): + bl_label = _("Unlink Fixture from Tracker") + bl_description = _("Unlink fixture from a tracker") + bl_idname = "dmx.psn_tracker_follower_unlink" + bl_options = {"UNDO"} + + fixture_uuid: StringProperty() + tracker_uuid: StringProperty() + + def execute(self, context): + scene = context.scene + dmx = scene.dmx + layout = self.layout + for fixture in dmx.fixtures: + if fixture.uuid == self.fixture_uuid: + for obj in fixture.objects: + if obj.name == "Target": + for constraint in obj.object.constraints: + if constraint.target != None: + if constraint.target.get("uuid", None) == self.tracker_uuid: + obj.object.constraints.remove(constraint) + + + return {"FINISHED"} + + + +class DMX_UL_Tracker_Followers(UIList): + + tracker_uuid: StringProperty() + tracker_index: IntProperty() + + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + fixture = item + scene = context.scene + dmx = scene.dmx + icon = "FILE_VOLUME" + self.tracker_uuid = context.window_manager.dmx.selected_tracker + self.tracker_index = context.window_manager.dmx.selected_tracker_index + + linked = None + for obj in fixture.objects: + if obj.name == "Target": + linked = False + for const in obj.object.constraints: + if const.target is not None: + if const.target.get("uuid", None) == self.tracker_uuid: + linked = True + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + col = layout.column() + col.ui_units_x = 3 + col = layout.column() + col.prop(item, "name", text="", emboss=False) + col = layout.column() + if linked is None: + layout.label(text="", icon="CANCEL") + elif linked is True: + op = col.operator("dmx.psn_tracker_follower_unlink", icon="LINKED", text="") + op.fixture_uuid = fixture.uuid + op.tracker_uuid = self.tracker_uuid + else: + op = col.operator("dmx.psn_tracker_follower_link", icon="UNLINKED", text="") + op.fixture_uuid = fixture.uuid + op.tracker_uuid = self.tracker_uuid + op.tracker_index = self.tracker_index + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.label(text=item.name, icon=icon) + + +class DMX_OT_Tracker_Followers(Operator): + bl_label = _("Tracker Followers") + bl_idname = "dmx.psn_tracker_followers" + bl_description = _("Link followers to a tracker") + bl_options = {'UNDO'} + + tracker_uuid: StringProperty() + tracker_index: IntProperty() + fixture_i: IntProperty() + + def draw(self, context): + layout = self.layout + scene = context.scene + dmx = scene.dmx + context.window_manager.dmx.selected_tracker = self.tracker_uuid + context.window_manager.dmx.selected_tracker_index = self.tracker_index + layout.template_list("DMX_UL_Tracker_Followers", "", dmx, "fixtures", self, "fixture_i") + + + def execute(self, context): + return {'FINISHED'} + return {'CANCELLED'} + + def invoke(self, context, event): + #self.name = "Group " + str(len(context.scene.dmx.groups)+1) + wm = context.window_manager + return wm.invoke_props_dialog(self) + + +class DMX_PT_DMX_Trackers(Panel): + bl_label = _("PSN") + bl_idname = "DMX_PT_DMX_Trackers" + bl_parent_id = "DMX_PT_DMX" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "DMX" + bl_context = "objectmode" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + dmx = context.scene.dmx + layout.operator("dmx.dmx_add_tracker", text="Add tracker", icon="PLUS") + layout.template_list("DMX_UL_Tracker", "", dmx, "trackers", dmx, "trackers_i") + + if (dmx.trackers_i < len(dmx.trackers)): + tracker = dmx.trackers[dmx.trackers_i] + layout.prop(tracker, "name") + layout.prop(tracker, "ip_address") + layout.prop(tracker, "ip_port") + layout.prop(tracker, "enabled") + add_trackers = True + for idx, obj in enumerate(tracker.collection.objects): + row = layout.row() + col = row.column() + op=col.operator("dmx.psn_tracker_followers", text=_(f"Link Followers to {obj.name}"), icon="LINKED") + op.tracker_uuid = tracker.uuid + op.tracker_index = idx + if len(tracker.collection.objects) > 1: + col = row.column() + op = col.operator("dmx.psn_remove_tracker_followers_target", text="", icon="TRASH") + op.object_name = obj.name + op.tracker_uuid = tracker.uuid + + if idx >= 9: # we support max 10 trackers, edit tracker.py and psn.py if more is needed + add_trackers = False + row = layout.row() + op = row.operator("dmx.psn_add_tracker_followers_target", text=_("Add Tracking Target"), icon="PLUS") + op.tracker_uuid = tracker.uuid + row.enabled = add_trackers + + + +class DMX_OT_Tracker_Followers_Remove_Target(Operator): + bl_label = _("Remove Followers Target") + bl_idname = "dmx.psn_remove_tracker_followers_target" + bl_description = _("Remove target") + bl_options = {'UNDO'} + + object_name: StringProperty() + tracker_uuid: StringProperty() + + def execute(self, context): + dmx = context.scene.dmx + for tracker in dmx.trackers: + if tracker.uuid == self.tracker_uuid: + if self.object_name in tracker.collection.objects: + rem_obj = tracker.collection.objects[self.object_name] + bpy.data.objects.remove(rem_obj) + + return {"FINISHED"} + +class DMX_OT_Tracker_Followers_Add_Target(Operator): + bl_label = _("Add Followers Target") + bl_idname = "dmx.psn_add_tracker_followers_target" + bl_description = _("Add target") + bl_options = {'UNDO'} + + tracker_uuid: StringProperty() + + def execute(self, context): + dmx = context.scene.dmx + for tracker in dmx.trackers: + if tracker.uuid == self.tracker_uuid: + for obj in tracker.collection.objects: + duplicate_obj = obj.copy() + tracker.collection.objects.link(duplicate_obj) + break + + return {"FINISHED"} + diff --git a/panels/protocols/sacn.py b/panels/protocols/sacn.py new file mode 100644 index 0000000..da59063 --- /dev/null +++ b/panels/protocols/sacn.py @@ -0,0 +1,50 @@ +# Copyright vanous +# +# This file is part of BlenderDMX. +# +# BlenderDMX 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. +# +# BlenderDMX 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 this program. If not, see . + +from bpy.types import Panel + +from ...i18n import DMX_Lang +_ = DMX_Lang._ + +class DMX_PT_DMX_sACN(Panel): + bl_label = _("sACN") + bl_idname = "DMX_PT_DMX_sACN" + bl_parent_id = "DMX_PT_DMX" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "DMX" + bl_context = "objectmode" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + dmx = context.scene.dmx + + sacn_universes = [] + for index, universe in enumerate(dmx.universes): + if index == 0: # invalid for sACN + continue + if universe.input == "sACN": + sacn_universes.append(universe) + + row = layout.row() + row.prop(dmx, "sacn_enabled") + row.enabled = len(sacn_universes)>0 + row = layout.row() + row.label(text=_("sACN set for {} universe(s)").format(len(sacn_universes))) + layout.label(text=_("Status") + ": " + dmx.sacn_status) + diff --git a/panels/protocols/universes.py b/panels/protocols/universes.py new file mode 100644 index 0000000..7c18b68 --- /dev/null +++ b/panels/protocols/universes.py @@ -0,0 +1,77 @@ +# Copyright Hugo Aboud +# +# This file is part of BlenderDMX. +# +# BlenderDMX 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. +# +# BlenderDMX 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 this program. If not, see . + +from bpy.types import Menu, Panel, UIList + +from ...i18n import DMX_Lang + +_ = DMX_Lang._ + + +class DMX_MT_Universe(Menu): + bl_label = _("DMX > Universe Menu") + bl_idname = "DMX_MT_Universe" + + def draw(self, context): + layout = self.layout + scene = context.scene + dmx = scene.dmx + + # "Add" + row = layout.row() + # row.operator("dmx.add_universe", text="Add", icon="ADD") + + +class DMX_UL_Universe(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + ob = data + icon = "FILE_VOLUME" + if self.layout_type in {"DEFAULT", "COMPACT"}: + col = layout.column() + col.label(text=f"{item.id}", icon=icon) + col.ui_units_x = 3 + col = layout.column() + col.prop(item, "name", text="", emboss=False) + col = layout.column() + col.label(text=item.input) + elif self.layout_type in {"GRID"}: + layout.alignment = "CENTER" + layout.label(text=str(item.id), icon=icon) + + +class DMX_PT_DMX_Universes(Panel): + bl_label = _("Universes") + bl_idname = "DMX_PT_DMX_Universes" + bl_parent_id = "DMX_PT_DMX" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_category = "DMX" + bl_context = "objectmode" + bl_options = {"DEFAULT_CLOSED"} + + def draw(self, context): + layout = self.layout + dmx = context.scene.dmx + + layout.prop(dmx, "universes_n", text=_("Universes")) + + layout.template_list("DMX_UL_Universe", "", dmx, "universes", dmx, "universe_list_i") + + if dmx.universe_list_i < dmx.universes_n: + universe = dmx.universes[dmx.universe_list_i] + layout.prop(universe, "name") + layout.prop(universe, "input")