From 024f97254ccf71950e0ee4ab8226b07bfdd34638 Mon Sep 17 00:00:00 2001 From: SteffeyDev Date: Thu, 8 Mar 2018 18:33:13 -0500 Subject: [PATCH] Deleting old files, fixing bugs --- OSCSceneController.py | 81 +++++---- README.md | 2 +- scenes.yaml.orig => example_scenes.yaml | 13 +- run_scenes.py | 211 ------------------------ scenes.yaml | 58 ------- setup.py | 2 +- 6 files changed, 57 insertions(+), 310 deletions(-) rename scenes.yaml.orig => example_scenes.yaml (84%) delete mode 100644 run_scenes.py delete mode 100644 scenes.yaml diff --git a/OSCSceneController.py b/OSCSceneController.py index bd18b8f..6fcbbca 100644 --- a/OSCSceneController.py +++ b/OSCSceneController.py @@ -25,7 +25,9 @@ def __init__(self, message, delay = 0): self._prefix = message.split("/")[1] self._addr = message.split(" ")[0] self._delay = delay - if value_types[self._prefix] == "float": + if self._prefix == "scene" or self._prefix not in value_types: + self._arg = int(message.split(" ")[1]) + elif value_types[self._prefix] == "float": self._arg = float(message.split(" ")[1]) elif value_types[self._prefix] == "int": self._arg = int(message.split(" ")[1]) @@ -170,6 +172,7 @@ def __init__(self, parser): self.server = None self.last_scene = None self.running = False + self.output_client = None def start(self, input_port): if not self.running: @@ -211,6 +214,7 @@ def respond_to_scene(self, addr, args = 1): elif addr.split("/")[1] == "midi-scene": new_scene = midi_map[int(round(float(addr.split("/")[2]) * 127))] else: + log_data.append("\nReceived invalid message: {0}".format(addr)) return if new_scene not in scene_map: @@ -226,24 +230,28 @@ def respond_to_scene(self, addr, args = 1): log_data.append("") log_data.append("Received: " + addr + " " + str(args)) - ### First we need to send message to turn on the new scene - self.send_msg(OSCMessage("/scene/" + new_scene + " 1")) + # Only send outgoing messages if we know where to send them to + if self.output_client is not None: - ### Then we need to send message to turn off current scene + ### First we need to send message to turn on the new scene + self.send_msg(OSCMessage("/scene/" + new_scene + " 1")) - # If we know what the last scene is, deselect it - if self.last_scene is not None: - self.send_msg(OSCMessage("/scene/" + self.last_scene + " 0")) - # If we don't know what the last scene is, turn them all off - # except for the current scene - else: - for key in scene_map: - if key != new_scene: - self.send_msg(OSCMessage("/scene/" + key +" 0")) - - self.last_scene = new_scene + ### Then we need to send message to turn off current scene + + # If we know what the last scene is, deselect it + if self.last_scene is not None: + self.send_msg(OSCMessage("/scene/" + self.last_scene + " 0")) + + # If we don't know what the last scene is, turn them all off + # except for the current scene + else: + for key in scene_map: + if key != new_scene: + self.send_msg(OSCMessage("/scene/" + key +" 0")) + + self.last_scene = new_scene # Update GUI global active_scene @@ -257,7 +265,8 @@ def respond_to_scene(self, addr, args = 1): def send_msg(self, message): if message.delay == 0: if message.prefix == "scene": - self.output_client.send_message(message.address, message.argument) + if self.output_client is not None: + self.output_client.send_message(message.address, message.argument) else: self.parser.getUdpClients()[message.prefix].send_message(message.address, message.argument) print("Sending message:", message.address, message.argument) @@ -285,11 +294,10 @@ def __init__(self, *args, **kwargs): self.output_port = None self.output_ip_address = None - self.minsize(450, 430) - self.configure(bg="#000000") + self.minsize(500, 430) menubar = tk.Menu(self) menubar.add_command(label="Quit", command=self.quit()) - self.config(menu=menubar) + self.config(menu=menubar, background="white") self.build() self.deiconify() @@ -302,17 +310,19 @@ def updateGUI(self): self.active_scene_text.set(active_scene) global log_data - log_data_store = log_data - log_data = [] - self.log_text_box.configure(state='normal') - for item in log_data_store: - self.log_text_box.insert('end', item + '\n') + if len(log_data) > 0: + log_data_store = log_data + log_data = [] + self.log_text_box.configure(state='normal') + for item in log_data_store: + self.log_text_box.insert('end', item + '\n') self.log_text_box.yview('end') - self.log_text_box.configure(state='disabled') + self.log_text_box.configure(state='disabled') self.after(1000, self.updateGUI) def reload_scene_handler(self): + self.focus() if (self.filename is not None): self.parser.parseFromFile(self.filename) self.log("Reloaded configuration from file: {0}".format(self.filename)) @@ -320,6 +330,7 @@ def reload_scene_handler(self): self.log("Cannot reload, no configuration loaded") def load_from_file_handler(self): + self.focus() new_filename = filedialog.askopenfilename() if new_filename != "": # User did not click cancel button if new_filename.split(".")[-1] != "yaml" and new_filename.split(".")[-1] != "yml": @@ -365,6 +376,7 @@ def log(self, text): self.log_text_box.configure(state='normal') self.log_text_box.insert('end', '\n' + text + '\n') self.log_text_box.configure(state='disabled') + self.log_text_box.yview('end') def input_port_changed(self, text): try: @@ -414,8 +426,7 @@ def build(self): style=ttk.Style() style.theme_use('alt') style.configure("TLabel", background="white") - style.configure("Left.TFrame", background="white") - style.configure("Right.TFrame", background="white") + style.configure("TFrame", background="white") style.configure("TButton", relief="flat", background="lightgray") style.map("TButton", background=[('pressed', 'lightgray'), ('active', 'gray')], @@ -432,7 +443,7 @@ def build(self): split = ttk.Frame(self) - left_side = ttk.Frame(split, width=250, height=400, borderwidth=5, style="Left.TFrame") + left_side = ttk.Frame(split, width=250, height=400, borderwidth=5) active_scene_box = tk.Frame(left_side, bg="white") #ttk.Frame(left_side, style="Left.TFrame") self.active_scene_text = tk.StringVar() @@ -443,7 +454,7 @@ def build(self): active_scene_box.pack(pady=10) active_scene_box.config() - input_box = ttk.Frame(left_side, style="Left.TFrame") + input_box = ttk.Frame(left_side) self.input_port_text = tk.StringVar() listening_address_entry = ttk.Entry(input_box, width=5, textvariable=self.input_port_text, font=largeBoldFont, justify="center", validate="key", validatecommand=isPortCommand) listening_address_entry.bind('', self.input_port_changed) @@ -453,8 +464,8 @@ def build(self): ttk.Label(input_box, text="Listening Port").pack() input_box.pack(pady=15) - output_box = ttk.Frame(left_side, style="Left.TFrame") - output_address_box = ttk.Frame(output_box, style="Left.TFrame") + output_box = ttk.Frame(left_side) + output_address_box = ttk.Frame(output_box) self.output_ip_text = tk.StringVar() outgoing_ip_entry = ttk.Entry(output_address_box, width=13, textvariable=self.output_ip_text, font=smallBoldFont, justify="center", validate="key", validatecommand=isIpAddressCommand) outgoing_ip_entry.bind('', self.output_ip_changed) @@ -469,13 +480,13 @@ def build(self): ttk.Label(output_box, text="Outgoing Reply").pack() output_box.pack(pady=15) - scene_box = ttk.Frame(left_side, style="Left.TFrame") + scene_box = ttk.Frame(left_side) self.scene_file_text = tk.StringVar() self.scene_file_text.set("None") ttk.Label(scene_box, wraplength=210, font=mediumBoldFont, textvariable=self.scene_file_text).pack() self.generateLine(scene_box, 140) ttk.Label(scene_box, text="Loaded Configuration").pack() - scene_button_box = ttk.Frame(scene_box, style="Left.TFrame") + scene_button_box = ttk.Frame(scene_box) ttk.Button(scene_button_box, text='Reload', command=lambda: self.reload_scene_handler()).pack(side='left', padx=2) ttk.Button(scene_button_box, text='Load from File', command=lambda: self.load_from_file_handler()).pack(side='right', padx=2) scene_button_box.pack(pady=10) @@ -485,7 +496,7 @@ def build(self): docLabel.bind("", self.open_documentation) docLabel.pack(side="bottom", anchor="s") - right_side = ttk.Frame(split, style="Right.TFrame") + right_side = ttk.Frame(split) self.log_text_box = ScrolledText(right_side, bg='lightgray', highlightthickness=10, highlightbackground='lightgray', wrap='word') self.log_text_box.pack(side="left", expand=1, fill="both", padx=(5,0)) self.log_text_box.insert('insert', """ @@ -499,7 +510,7 @@ def build(self): """) self.log_text_box.configure(state='disabled') - left_side.pack(side="left", fill="y") + left_side.pack(side="left", fill="y", padx=20) right_side.pack(side="right", fill="both", expand=1) split.grid(column=0, row=0, sticky='news') diff --git a/README.md b/README.md index ba7e339..1774574 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ OSC messages contain 2 parts: ## Getting Started -1. Download the `OSCSceneController.app` file from the [latest release](https://github.com/steffeydev/osc-scenes/releases/latest), and double-click it to run. +1. Download the `OSCSceneController.app.zip` file from the [latest release](https://github.com/steffeydev/osc-scenes/releases/latest). 2. Create the YAML Configuration File by following the format described below 3. Double-click the `OSCSceneController.app` file to run it, and load in your configuration diff --git a/scenes.yaml.orig b/example_scenes.yaml similarity index 84% rename from scenes.yaml.orig rename to example_scenes.yaml index 97153fa..cc014fc 100644 --- a/scenes.yaml.orig +++ b/example_scenes.yaml @@ -1,6 +1,3 @@ -server: - listen_port: 8002 - endpoints: - prefix: atem @@ -39,7 +36,7 @@ scenes: key: welcome video: camera: close - transition: cut + transition: cut 1s lights: color: starting spotlights: @@ -48,3 +45,11 @@ scenes: - name: Main key: main + video: + camera: wide + transition: auto 1s + lights: + color: ending + spotlights: + - center + diff --git a/run_scenes.py b/run_scenes.py deleted file mode 100644 index cb42c17..0000000 --- a/run_scenes.py +++ /dev/null @@ -1,211 +0,0 @@ -import yaml -import sys -from threading import Timer -from pythonosc import osc_server, dispatcher, udp_client - -debug = False - -config = yaml.load(open('scenes.yaml', 'r')) -scenes = config['scenes'] -endpoints = config['endpoints'] -mapping = config['map'] - -last_scene = None -input_port = config['server']['listen_port'] - -value_types = {} - -class OSCMessage: - def __init__(self, message, delay = 0): - if debug: - print("Creating OSCMessage from message:", message) - - self._prefix = message.split("/")[1] - self._addr = message.split(" ")[0] - self._delay = delay - if value_types[self._prefix] == "float": - self._arg = float(message.split(" ")[1]) - elif value_types[self._prefix] == "int": - self._arg = int(message.split(" ")[1]) - else: # By default use int - self._arg = int(message.split(" ")[1]) - - @property - def address(self): - return self._addr - - @property - def argument(self): - return self._arg - - @property - def delay(self): - return self._delay - - @delay.setter - def delay(self, delay): - self._delay = delay - - @property - def prefix(self): - return self._prefix - -print("\nInput Settings") -print("Listening on 0.0.0.0:" + str(input_port)) -print("Listening for OSC commands in the form:") -print("\t/scene/") -print("\t/midi-scene/") - -print("\nOutput Settings") -udp_clients = {} -for endpoint in endpoints: - udp_clients[endpoint['prefix']] = udp_client.SimpleUDPClient(endpoint['ip'], endpoint['port'], allow_broadcast=True) - value_types[endpoint['prefix']] = endpoint['valueType'] - print("Sending commands that start with /" + endpoint['prefix'] + " to " + endpoint['ip'] + ":" + str(endpoint['port'])) - - -### Generate the OSC commands that need to be sent for each scene -# by parsing the YAML file - -def is_osc_command(item): - return isinstance(item, str) and item.startswith("/") and len(item.split("/")) > 2 and len(item.split(" ")) > 1 - -def get_commands(key, value, map_value, array): - - def print_error(key, value, map_value): - print("Could not process item with key ", key, ", value:", value, ", and map value:", map_value) - - if debug: - print("Getting commands for key:", key, "and value:", value, "\nUsing map_value:", map_value) - - if isinstance(value, dict): - for _key, _value in value.items(): - get_commands(_key, _value, map_value[_key], array) - - elif isinstance(value, list): - delay = 0 - for item in value: - if 'delay' in item: - delay = int(item.split(" ")[1].replace("s", "")) - - for map_key, map_val in map_value.items(): - if map_key in value and map_key != "none": - if map_key in map_value and 'in' in map_value[map_key] and is_osc_command(map_value[map_key]['in']): - array.append(OSCMessage(map_value[map_key]['in'], delay)) - else: - print_error(key, value, map_value) - else: - if map_key in map_value and 'out' in map_value[map_key] and is_osc_command(map_value[map_key]['out']): - array.append(OSCMessage(map_value[map_key]['out'], delay)) - else: - print_error(key, value, map_value) - - elif isinstance(value, str): - string = value.split(" ")[0] - delay = 0 - if len(value.split(" ")) > 1: - delay = int(value.split(" ")[1].replace("s", "")) - - if string in map_value and is_osc_command(map_value[string]): - array.append(OSCMessage(map_value[string], delay)) - else: - print_error(key, value, map_value) - - elif isinstance(value, int): - array.append(OSCMessage(map_value.replace('x', str(value / 127)))) - - else: - print_error(key, value, map_value) - - -scene_map = {} -midi_map = {} -for scene in scenes: - arr = [] - - for key, value in scene.items(): - if not (key == "key" or key == "name" or key == "midi"): - get_commands(key, value, mapping[key], arr) - - if debug: - print("Array generated for scene " + scene['name'] + ":") - print(arr) - print() - - if 'midi' in scene: - midi_map[scene['midi']] = scene['key'] - - scene_map[scene['key']] = arr - -def send_msg(message): - if message.delay == 0: - udp_clients[message.prefix].send_message(message.address, message.argument) - print("Sending message:", message.address, message.argument) - - else: - wait = message.delay - message.delay = 0 - r = Timer(wait, send_msg, [message]) - r.start() - -#def trigger_atem_transition(): -# send_msg('/atem/transition/auto', 1) - -def respond_to_scene(addr, args): - global last_scene - - new_scene = "" - if addr.split("/")[1] == "scene": - new_scene = addr.split("/")[2] - elif addr.split("/")[1] == "midi-scene": - new_scene = midi_map[int(round(float(addr.split("/")[2]) * 127))] - else: - return - - # If we are recieving one of the turn off signals that - # we are sending, ignore it (prevent feedback loop) - if args == 0 or new_scene == last_scene: - return - - print("\nGot Message: ", addr, " ", args) - - ### First we need to send message to turn on the new scene - send_msg(OSCMessage("/scene/" + new_scene + " 1")) - - - ### Then we need to send message to turn off current scene - - # If we know what the last scene is, deselect it - if last_scene is not None: - send_msg(OSCMessage("/scene/" + last_scene + " 0")) - - # If we don't know what the last scene is, turn them all off - # except for the current scene - else: - for key in scene_map: - if key != new_scene: - send_msg(OSCMessage("/scene/" + key +" 0")) - - last_scene = new_scene - - - ### Finally we need to actual send the OSC messages that make up the scene change - for osc_command in scene_map[new_scene]: - send_msg(osc_command) - - ### We also need to send the video transition shortly after sending the above commands - #r = Timer(0.8, trigger_atem_transition) - #r.start() - -dispatcher = dispatcher.Dispatcher() -for key in scene_map: - dispatcher.map("/scene/" + key, respond_to_scene) -for number in midi_map: - dispatcher.map("/midi-scene/" + str(round(number / 127, 2)), respond_to_scene) - -try: - server = osc_server.ThreadingOSCUDPServer(("0.0.0.0", input_port), dispatcher) - server.serve_forever() -except KeyboardInterrupt: - print("Exiting...") - sys.exit(0) diff --git a/scenes.yaml b/scenes.yaml deleted file mode 100644 index f109c5e..0000000 --- a/scenes.yaml +++ /dev/null @@ -1,58 +0,0 @@ -server: - listen_port: 8002 - -endpoints: - - - prefix: atem - ip: 10.0.0.2 - port: 3456 - valueType: float - - - prefix: sc - ip: 127.0.0.1 - port: 8001 - valueType: int - - - prefix: scene - ip: 127.0.0.1 - port: 8400 - valueType: int - -map: - video: - camera: - wide: /atem/camera/7 1 - close: /atem/camera/2 1 - transition: - cut: /atem/transition/cut 1 - auto: /atem/transition/auto 1 - lights: - color: - starting: /sc/btn/run/3/5/5 1 - ending: /sc/btn/run/3/6/5 1 - spotlights: - overhead: - in: /sc/btn/2/2/4 1 - out: /sc/btn/2/2/4 0 - center: - in: /sc/btn/2/3/4 1 - out: /sc/btn/2/3/4 0 - -scenes: - - - name: Welcome - key: welcome - video: - camera: close - transition: cut - lights: - color: starting - spotlights: - - center - - overhead - - - name: Main - key: main - - - name: Worship - key: worship diff --git a/setup.py b/setup.py index 7210fa8..9a183ca 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ OPTIONS = { 'iconfile':'applet.icns', 'plist': { - 'CFBundleShortVersionString':'0.1.0', + 'CFBundleShortVersionString':'0.1.1', 'CFBundleDisplayName':'OSC Scene Controller' }, 'includes': 'tkinter'