def build_cntrl(self): ''' Builds the top bar of the GUI. Initializes all the general tkinter control widgets ''' tk.Label(self.cntrl_frame, text = 'Select\nUniverse:').pack(side = tk.LEFT) self.universe_menu = RDMMenu(self.cntrl_frame, 'No Universes Available', 'Select Universe') self.universe_menu.pack(side = tk.LEFT) discover_button = tk.Button(self.cntrl_frame, text = 'Discover', command = self.discover) discover_button.pack(side = tk.LEFT) self.device_menu = RDMMenu(self.cntrl_frame, "No Devices", "Select Device") self.device_menu.pack(side = tk.LEFT) self.id_state = tk.IntVar(self.root) self.id_state.set(0) self.id_box = tk.Checkbutton(self.cntrl_frame, text = 'Identify', variable = self.id_state, command = self.identify) self.id_box.pack(side = tk.LEFT) self.auto_disc = tk.BooleanVar(self.root) self.auto_disc.set(False) self.auto_disc_box = tk.Checkbutton(self.cntrl_frame, text = 'Automatic\nDiscovery', variable = self.auto_disc, command = self.discover) self.auto_disc_box.pack(side = tk.LEFT)
def _init_dmx(self): """ initalize the widgets for the dmx tab """ # Text Variables self.dmx_footprint = tk.StringVar(self.dmx_tab) self.dmx_start_address = tk.StringVar(self.dmx_tab) self.slot_required = tk.StringVar(self.dmx_tab) self.personality_name = tk.StringVar(self.dmx_tab) self.slot_number = tk.StringVar(self.dmx_tab) self.slot_name = tk.StringVar(self.dmx_tab) self.slot_type = tk.StringVar(self.dmx_tab) self.slot_label_id = tk.StringVar(self.dmx_tab) self.default_slot_value = tk.StringVar(self.dmx_tab) # Widgets self.start_address_entry = tk.Entry( self.dmx_tab, textvariable = self.dmx_start_address) self.start_address_button = tk.Button(self.dmx_tab, text = 'Set Start Address', command = self.set_start_address) # validatecommand make sure between 1 and 512 self.dmx_personality_menu = RDMMenu( self.dmx_tab, "Personality description not supported.", "") self.slot_menu = RDMMenu( self.dmx_tab, "No slot description.", "Choose Slot") self.objects["DMX512_SETUP"] = [ tk.Label(self.dmx_tab, text = "DMX Footprint:"), tk.Label(self.dmx_tab, textvariable = self.dmx_footprint), tk.Label(self.dmx_tab, text = "DMX Start Address:"), self.start_address_entry, tk.Label(self.dmx_tab, text = ""), self.start_address_button, tk.Label(self.dmx_tab, text = "Current Personality:"), self.dmx_personality_menu, tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.slot_required), tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.personality_name), tk.Label(self.dmx_tab, text = "Slot Info:"), self.slot_menu, tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.slot_type), tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.slot_label_id), tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.slot_name), tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.default_slot_value), ]
def _init_sensor(self): """ initalize the widgets for the sensor tab """ # Text Variable self.sensor_type = tk.StringVar(self.sensor_tab) self.sensor_unit = tk.StringVar(self.sensor_tab) self.sensor_prefix = tk.StringVar(self.sensor_tab) self.sensor_range = tk.StringVar(self.sensor_tab) self.normal_range = tk.StringVar(self.sensor_tab) self.supports_recording = tk.StringVar(self.sensor_tab) self.supports_lowest_highest = tk.StringVar(self.sensor_tab) self.sensor_name = tk.StringVar(self.sensor_tab) self.sensor_number = tk.StringVar(self.sensor_tab) self.present_value = tk.StringVar(self.sensor_tab) self.lowest = tk.StringVar(self.sensor_tab) self.highest = tk.StringVar(self.sensor_tab) self.recorded = tk.StringVar(self.sensor_tab) # Widgets self.sensor_menu = RDMMenu( self.sensor_tab, "Sensor information not provided.", "Choose Sensor") self.record_sensor_button = tk.Button( self.sensor_tab, text="Record Sensor", state=tk.DISABLED) self.clear_sensor_button = tk.Button( self.sensor_tab, text='Clear Sensor', state=tk.DISABLED) self.refresh_sensor_button = tk.Button( self.sensor_tab, text = 'Refresh', state=tk.DISABLED) self.objects["SENSORS"] = [ tk.Label(self.sensor_tab, text = "Choose Sensor"), self.sensor_menu, tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.sensor_type), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.sensor_unit), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.sensor_prefix), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.sensor_range), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.normal_range), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.supports_recording), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.supports_lowest_highest), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.present_value), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.lowest), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.highest), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.recorded), tk.Label(self.sensor_tab, text = ""), self.record_sensor_button, tk.Label(self.sensor_tab, text = ""), self.clear_sensor_button, tk.Label(self.sensor_tab, text = ""), self.refresh_sensor_button ]
def _init_config(self): """ initalize the widgets for the config tab """ # Variables self.display_invert = tk.StringVar(self.config_tab) self.display_level = tk.IntVar(self.config_tab) self.pan_invert = tk.BooleanVar(self.config_tab) self.tilt_invert = tk.BooleanVar(self.config_tab) self.pan_tilt_swap = tk.BooleanVar(self.config_tab) self.real_time_clock = tk.StringVar(self.config_tab) # Widgets self.language_menu = RDMMenu( self.config_tab, "Languages not supported.", "") self.display_invert_menu = tk.OptionMenu(self.config_tab, self.display_invert, *PIDDict.DISPLAY_INVERT.values(), command = self._set_display_invert) self.display_level_menu = tk.Scale( self.config_tab, from_ = 0, to = 255, variable=self.display_level, orient=tk.HORIZONTAL, command = self._controller.set_display_level, length = 255, state = tk.DISABLED, tickinterval = 255) self.pan_invert_button = tk.Checkbutton(self.config_tab, variable = self.pan_invert, command = self._set_pan_invert, state = tk.DISABLED) self.tilt_invert_button = tk.Checkbutton(self.config_tab, variable = self.tilt_invert, command = self._set_tilt_invert, state = tk.DISABLED) self.pan_tilt_swap_button = tk.Checkbutton( self.config_tab, variable = self.pan_tilt_swap, command = self._set_pan_tilt_swap, state = tk.DISABLED ) self.objects["CONFIGURATION"] = [ tk.Label(self.config_tab, text = "Device Language:"), self.language_menu, tk.Label(self.config_tab, text = "Display Invert:"), self.display_invert_menu, tk.Label(self.config_tab, text = "Display Level:"), self.display_level_menu, tk.Label(self.config_tab, text = "Pan Invert:"), self.pan_invert_button, tk.Label(self.config_tab, text = "Tilt Invert:"), self.tilt_invert_button, tk.Label(self.config_tab, text = "Pan Tilt Swap"), self.pan_tilt_swap_button, tk.Label(self.config_tab, text = "Real Time Clock"), tk.Label(self.config_tab, textvariable = self.real_time_clock) ]
def _init_setting(self): """ initalize the widgets for the setting tab """ # sText Varibles self.device_hours = tk.StringVar(self.setting_tab) self.lamp_hours = tk.StringVar(self.setting_tab) self.lamp_strikes = tk.StringVar(self.setting_tab) self.lamp_state = tk.StringVar(self.setting_tab) self.lamp_on_mode = tk.StringVar(self.setting_tab) self.device_power_cycles = tk.StringVar(self.setting_tab) self.power_state = tk.StringVar(self.setting_tab) # Widgets self.lamp_state_menu = RDMMenu(self.setting_tab, 'Lamp state not supported.', '') self.lamp_on_mode_menu = RDMMenu(self.setting_tab, 'Lamp on mode not supported', '') self.power_state_menu = RDMMenu(self.setting_tab, 'Power state not supported.', '') self.objects["POWER_LAMP_SETTINGS"] = [ tk.Label(self.setting_tab, text = "Device Hours:"), tk.Label(self.setting_tab, textvariable = self.device_hours), tk.Label(self.setting_tab, text = "Device Power Cycles:"), tk.Label(self.setting_tab, textvariable = self.device_power_cycles), tk.Label(self.setting_tab, text = "Lamp Hours:"), tk.Label(self.setting_tab, textvariable = self.lamp_hours), tk.Label(self.setting_tab, text = "Lamp Strikes:"), tk.Label(self.setting_tab, textvariable = self.lamp_strikes), tk.Label(self.setting_tab, text = "Lamp State:"), self.lamp_state_menu, tk.Label(self.setting_tab, text = "Lamp On Mode:"), self.lamp_on_mode_menu, tk.Label(self.setting_tab, text = "Power State:"), self.power_state_menu ]
class DisplayApp(object): ''' Creates the GUI for sending and receiving RDM messages through the ola thread. ''' def __init__(self, width, height): ''' initializes the GUI and the ola thread Args: width: the int value of the width of the tkinter window height: the int value of the height of the tkinter window ''' # ================== Initialize the tk root window ========================= self._controller = Controller(self) self.root = tk.Tk() self.init_dx = width self.init_dy = height self.root.geometry('%dx%d+50+30'%(self.init_dx, self.init_dy)) self.root.title('RDM user interface version: 1.0') self.root.maxsize(1600, 900) self.root.lift() self.root.update_idletasks() # ================== Initialize Variables and Ola Thread =================== self.universe = tk.IntVar(self.root) self.universe_dict = {} self._cur_uid = None self._uid_dict = {} self._pid_store = PidStore.GetStore() self.ola_thread = olathread.OLAThread(self._pid_store) self.ola_thread.start() self.build_frames() self.build_cntrl() self._notebook = notebook.RDMNotebook(self.root, self._controller) # ================== Call Fetch Universes ================================== self.fetch_universes(self.fetch_universes_complete) print 'currently in thread: %d' % threading.currentThread().ident time.sleep(1) print 'back from sleep' def build_frames(self): ''' builds the two tkinter frames that are used as parents for the tkinter widgets that both control and display the RDM messages. ''' self.cntrl_frame = tk.PanedWindow(self.root) self.cntrl_frame.pack(side = tk.TOP, padx = 1, pady = 1, fill = tk.Y) self.info_frame_1 = tk.PanedWindow(self.root) self.info_frame_1.pack(side = tk.TOP, padx = 1, pady = 2, fill = tk.Y) def build_cntrl(self): ''' Builds the top bar of the GUI. Initializes all the general tkinter control widgets ''' tk.Label(self.cntrl_frame, text = 'Select\nUniverse:').pack(side = tk.LEFT) self.universe_menu = RDMMenu(self.cntrl_frame, 'No Universes Available', 'Select Universe') self.universe_menu.pack(side = tk.LEFT) discover_button = tk.Button(self.cntrl_frame, text = 'Discover', command = self.discover) discover_button.pack(side = tk.LEFT) self.device_menu = RDMMenu(self.cntrl_frame, "No Devices", "Select Device") self.device_menu.pack(side = tk.LEFT) self.id_state = tk.IntVar(self.root) self.id_state.set(0) self.id_box = tk.Checkbutton(self.cntrl_frame, text = 'Identify', variable = self.id_state, command = self.identify) self.id_box.pack(side = tk.LEFT) self.auto_disc = tk.BooleanVar(self.root) self.auto_disc.set(False) self.auto_disc_box = tk.Checkbutton(self.cntrl_frame, text = 'Automatic\nDiscovery', variable = self.auto_disc, command = self.discover) self.auto_disc_box.pack(side = tk.LEFT) def device_selected(self, uid): ''' called when a new device is chosen from dev_menu. Args: uid: the uid of the newly selected device ''' if uid == self._cur_uid: print 'Already Selected' return self._cur_uid = uid pid_key = 'DEVICE_LABEL' self.ola_thread.rdm_get(self.universe.get(), uid, 0, 'IDENTIFY_DEVICE', lambda b, s, uid = uid:self._get_identify_complete(uid, b, s)) data = self._uid_dict[uid] flow = controlflow.RDMControlFlow( self.universe.get(), uid, [ actions.GetSupportedParams(data, self.ola_thread.rdm_get), actions.GetDeviceInfo(data, self.ola_thread.rdm_get) ], self._device_changed_complete) flow.run() def _device_changed_complete(self): '''called once the control flow created in device_selected completes ''' self._uid_dict[self._cur_uid]['PARAM_NAMES'] = set() for pid_key in self._uid_dict[self._cur_uid]['SUPPORTED_PARAMETERS']: pid = self._pid_store.GetPid(pid_key) if pid is not None: self._uid_dict[self._cur_uid]['PARAM_NAMES'].add(pid.name) print 'Device selected: %s' % self._uid_dict[self._cur_uid] self._notebook.update() def _get_identify_complete(self, uid, succeeded, value): ''' Callback for rdm_get in device_selected. Sets the checkbox's state to that of the currently selected device ''' if succeeded: self.id_state.set(value['identify_state']) def fetch_universes(self, callback): self.ola_thread.fetch_universes(self.fetch_universes_complete) def fetch_universes_complete(self, succeeded, universes): if succeeded: for universe in universes: self.universe_dict[universe.id] = UniverseObj(universe.id, universe.name) self.universe_menu.add_item(universe.name, lambda i = universe.id: self._set_universe(i)) else: print 'could not find active universe' def _set_universe(self, universe_id): ' sets the int var self.universe to the value of i ' print universe_id self.universe.set(universe_id) self.device_menu.clear_menu() if not self.universe_dict[universe_id].discovery_run: self.discover() else: for uid in self.universe_dict[universe_id].uids: self._add_device_to_menu(uid) def discover(self): ' runs discovery for the current universe. ' universe_id = self.universe.get() self.ola_thread.run_discovery(universe_id, lambda status, uids:self._upon_discover(status, uids, universe_id)) if self.auto_disc.get(): self.ola_thread.add_event(5000, self.discover) else: print 'Automatic discovery is off.' def _upon_discover(self, status, uids, universe_id): ' callback for client.runRDMDiscovery. ' if self.universe.get() != universe_id: return self.universe_dict[universe_id].set_uids(uids) if not uids: self.device_menu.clear_menu() for uid in uids: if uid not in self._uid_dict.keys(): self._uid_dict[uid] = {} self.ola_thread.rdm_get(self.universe.get(), uid, 0, 'DEVICE_LABEL', lambda b, s, uid = uid:self._add_device(uid, b, s), []) def identify(self): ''' Command is called by id_box. sets the value of the device's identify field based on the value of id_box. ''' if self._cur_uid is None: return self.ola_thread.rdm_set(self.universe.get(), self._cur_uid, 0, 'IDENTIFY_DEVICE', lambda b, s, uid = self._cur_uid :self._set_identify_complete(uid, b, s), [self.id_state.get()]) def _add_device(self, uid, error, data): ''' callback for the rdm_get in upon_discover. populates self.device_menu ''' if error is None: self._uid_dict.setdefault(uid, {})['DEVICE_LABEL'] = data['label'] else: self._uid_dict.setdefault(uid, {})['DEVICE_LABEL'] = '' self._add_device_to_menu(uid) def _set_identify_complete(self, uid, succeded, value): ''' callback for the rdm_set in identify. ''' print 'identify %s' % value self._uid_dict[self._cur_uid] = value # ============================================================================ # ============================ RDM Gets ====================================== def get_basic_information(self): ''' creates and calls the action flow for retrieving information for the first tab of the notebook. Triggered by: the control flow class, originally from the notebook initialization and "Device Information" tab selection. ''' if self._cur_uid is None: print 'you need to select a device' return data = self._uid_dict[self._cur_uid] flow = controlflow.RDMControlFlow( self.universe.get(), self._cur_uid, [ actions.GetProductDetailIds(data, self.ola_thread.rdm_get), actions.GetDeviceModel(data, self.ola_thread.rdm_get), actions.GetManufacturerLabel(data, self.ola_thread.rdm_get), actions.GetFactoryDefaults(data, self.ola_thread.rdm_get), actions.GetSoftwareVersion(data, self.ola_thread.rdm_get), actions.GetBootSoftwareLabel(data, self.ola_thread.rdm_get), actions.GetBootSoftwareVersion(data, self.ola_thread.rdm_get) ], self.update_basic_information) flow.run() def get_dmx_information(self): ''' creates and calls the action flow for retrieving information for the second tab of the notebook. Triggered by: the control flow class, originally from the notebook initialization and "DMX512 Information" tab selection. ''' if self._cur_uid is None: print 'you need to select a device.' return dmx_actions = [] data = self._uid_dict[self._cur_uid] dmx_actions.append(actions.GetDmxPersonality(data, self.ola_thread.rdm_get)) for i in xrange(data['DEVICE_INFO']['personality_count']): dmx_actions.append(actions.GetPersonalityDescription( data, self.ola_thread.rdm_get, [i + 1])) dmx_actions.append(actions.GetStartAddress(data, self.ola_thread.rdm_get)) dmx_actions.append(actions.GetSlotInfo(data, self.ola_thread.rdm_get)) print 'dmx_footprint: %d' % data['DEVICE_INFO']['dmx_footprint'] for i in xrange(data['DEVICE_INFO']['dmx_footprint']): dmx_actions.append(actions.GetSlotDescription( data, self.ola_thread.rdm_get, [i])) dmx_actions.append( actions.GetDefaultSlotValue(data, self.ola_thread.rdm_get)) print i flow = controlflow.RDMControlFlow( self.universe.get(), self._cur_uid, dmx_actions, self.update_dmx_information) flow.run() def get_sensor_definitions(self): ''' gets the sensor definition for each sensor in the sensor count ''' if self._cur_uid is None: return sensor_actions = [] data = self._uid_dict[self._cur_uid] for i in xrange(data['DEVICE_INFO']['sensor_count']): sensor_actions.append(actions.GetSensorDefinition(data, self.ola_thread.rdm_get, [i])) flow = controlflow.RDMControlFlow( self.universe.get(), self._cur_uid, sensor_actions, self.update_sensor_information) flow.run() def get_sensor_value(self, sensor_number): """ gets the sensor value for the currently selected sensor Args: sensor_number: the number associated with the currently selected sensor. call originates in render_sensor_information in the notebook class. """ if self._cur_uid is None: return sensor_actions = [] data = self._uid_dict[self._cur_uid] sensor_actions = [actions.GetSensorValue(data, self.ola_thread.rdm_get, [sensor_number])] flow = controlflow.RDMControlFlow( self.universe.get(), self._cur_uid, sensor_actions, lambda: self.display_sensor_data(sensor_number)) flow.run() def get_setting_information(self): ''' creates and calls the action flow for retrieving information for the forth tab of the notebook. Triggered by: the control flow class, originally from the notebook initialization and "Power and Lamp Settings" tab selection. ''' if self._cur_uid is None: print 'you need to select a device' return data = self._uid_dict[self._cur_uid] flow = controlflow.RDMControlFlow( self.universe.get(), self._cur_uid, [ actions.GetDeviceHours(data, self.ola_thread.rdm_get), actions.GetLampHours(data, self.ola_thread.rdm_get), actions.GetLampState(data, self.ola_thread.rdm_get), actions.GetLampStrikes(data, self.ola_thread.rdm_get), actions.GetLampOnMode(data, self.ola_thread.rdm_get), actions.GetPowerCycles(data, self.ola_thread.rdm_get), actions.GetPowerState(data, self.ola_thread.rdm_get), ], self.update_setting_information) flow.run() def get_config_information(self): ''' creates and calls the action flow for retrieving information for the fifth tab of the notebook. Triggered by: the control flow class, originally from the notebook initialization and "Configure" tab selection. ''' if self._cur_uid is None: print 'you need to select a device' return data = self._uid_dict[self._cur_uid] flow = controlflow.RDMControlFlow( self.universe.get(), self._cur_uid, [ actions.GetLanguageCapabilities(data, self.ola_thread.rdm_get), actions.GetLanguage(data, self.ola_thread.rdm_get), actions.GetDisplayInvert(data, self.ola_thread.rdm_get), actions.GetDisplayLevel(data, self.ola_thread.rdm_get), actions.GetPanInvert(data, self.ola_thread.rdm_get), actions.GetTiltInvert(data, self.ola_thread.rdm_get), actions.GetPanTiltSwap(data, self.ola_thread.rdm_get), actions.GetRealTimeClock(data, self.ola_thread.rdm_get) ], self.update_config_information) flow.run() # ============================ Notebook Updates ============================== # The following methods call into the notebook class and popluate the # associted tab with RDM information def update_basic_information(self): # print "uid_dict: %s" % self._uid_dict[self._cur_uid] self._notebook.render_basic_information(self._uid_dict[self._cur_uid]) def update_dmx_information(self): self._notebook.render_dmx_information(self._uid_dict[self._cur_uid]) def update_setting_information(self): self._notebook.render_setting_information(self._uid_dict[self._cur_uid]) def update_config_information(self): self._notebook.render_config_information(self._uid_dict[self._cur_uid]) # the sensor tab is slightly different that those above. The first method # populates (update_sensor_information) the RDM menu on the sensor tab and the # second method displays the values being recorded/sensed by the sensor device def update_sensor_information(self): self._notebook.render_sensor_information(self._uid_dict[self._cur_uid]) def display_sensor_data(self,sensor_number): self._notebook.display_sensor_data( self._uid_dict[self._cur_uid],sensor_number) # ============================ RDM Sets ====================================== def set_start_address(self, start_address): ''' RDM set call for DMX_START_ADDRESS call originates from the start address button in the notebook class Args: start_address: INT 16 bit; the DMX address that is set as the start address. ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._set_address_complete(start_address, b, s) self.ola_thread.rdm_set( self.universe.get(), uid, 0, 'DMX_START_ADDRESS', callback, [start_address]) def _set_address_complete(self, start_address, error, data): ''' Callback method from DMX_START_ADDRESS RDM set. Args: start_address: INT 16 bit; the new DMX start address of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' print 'set start address callback' if error is None: pid_dict = self._uid_dict[self._cur_uid] pid_dict['DEVICE_INFO']['dmx_start_address'] = start_address pid_dict['DMX_START_ADDRESS'] = start_address print 'DMX start address set to %s' % start_address else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def set_dmx_personality(self, personality): ''' RDM set call for DMX_PERSONALITY call originates from an optionMenu in the DMX tab in the notebook class Args: personaility: INT 8 bit; the new personality for the current device ''' print "setting DMX persaonality..." if self._cur_uid is None: return data = self._uid_dict[self._cur_uid] flow_actions = [actions.SetDMXPersonality(data, self.ola_thread.rdm_set, [personality])] flow = controlflow.RDMControlFlow( self.universe.get(), self._cur_uid, flow_actions, lambda: self._get_slot_info(personality, None, data)) flow.run() def _get_slot_info(self, personality, error, data): ''' Args: personality: INT 8 bit; the personality of the current device error: the error returned if the previous ola call fails, otherwise this will be None data: 16 bit slot number, data to be passed to the ola thread ''' print 'getting slot info...' if error is None: data = self._uid_dict[self._cur_uid] flow_actions = [] for slot in xrange(self._uid_dict[self._cur_uid] ['DMX_PERSONALITY_DESCRIPTION'] [personality]['slots_required']): flow_actions.append(actions.GetSlotDescription( data, self.ola_thread.rdm_get, [slot])) flow_actions.append(actions.GetSlotInfo(data, self.ola_thread.rdm_get)) flow_actions.append(actions.GetDefaultSlotValue( data, self.ola_thread.rdm_get)) flow = controlflow.RDMControlFlow( self.universe.get(), self._cur_uid, flow_actions, lambda: self._set_dmx_personality_complete(None, data)) flow.run() else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def _set_dmx_personality_complete(self, error, data): ''' Callback method from DMX_PERSONALITY RDM set. Args: error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' #print self._uid_dict[self._cur_uid] if error is None: self._notebook.set_dmx_personality_complete(self._uid_dict[self._cur_uid]) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) def set_display_level(self, level): ''' RDM set call for DISPLAY_LEVEL call originates from a scale in the config tab of the notebook class Args: level: INT 8 bit; the new display level for the current device ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._display_level_complete(uid, level, b, s) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'DISPLAY_LEVEL', callback, [level]) def _display_level_complete(self, uid, level, error, data): ''' Callback method from DISPLAY_LEVEL RDM set. Args: uid: The uid of the current uid level: INT 8 bit, the new display level of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: self._uid_dict[uid]['DISPLAY_LEVEL'] = level self._notebook.set_display_level_complete(level) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def set_lamp_state(self, state): ''' RDM set call for LAMP_STATE call originates from an optionMenu in the lamp/power settings tab of the notebook class Args: state: INT [0, 3]; the new lamp state for the RDM device ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._set_lamp_state_complete(uid, state, b, s) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'LAMP_STATE', callback, [state]) def _set_lamp_state_complete(self, uid, state, error, data): '''Callback method from LAMP_STATE RDM set. Args: uid: The uid of the RDM device state: INT [0, 3]; the new lamp state of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: self._uid_dict[uid]['LAMP_STATE'] = state self._notebook.set_lamp_state_complete(state) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def set_lamp_on_mode(self, mode): ''' RDM set call for LAMP_ON_MODE call originates from an optionMenu in the lamp/power settings tab of the notebook class Args: mode: INT [0, 3]; the new lamp on mode for the RDM device ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._set_lamp_on_mode_complete(uid, mode, b, s) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'LAMP_ON_MODE', callback, [mode]) def _set_lamp_on_mode_complete(self, uid, mode, error, data): '''Callback method from LAMP_ON_MODE RDM set. Args: uid: The uid of the RDM device mode: INT [0, 3]; the new lamp on mode of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: self._uid_dict[uid]['LAMP_ON_MODE'] = mode self._notebook.set_lamp_on_mode_complete(mode) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def set_power_state(self, state): ''' RDM set call for POWER_STATE call originates from an optionMenu in the lamp/power settings tab of the notebook class Args: state: INT {[0,2], 255}; the new power state for the RDM device ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._set_power_state_complete(uid, state, b, s) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'POWER_STATE', callback, [state]) def _set_power_state_complete(self, uid, state, error, data): '''Callback method from POWER_STATE RDM set. Args: uid: The uid of the RDM device state: INT {[0,2], 255}; the new power state of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: self._uid_dict[uid]['POWER_STATE'] = state self._notebook.set_power_state_complete(state) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def set_language(self, language): ''' RDM set call for LANGUAGE call originates from an optionMenu in the config tab of the notebook class Args: language: STRING length 2; the new language for the RDM device ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._language_complete(uid, language, b, s) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'LANGUAGE', callback, [language]) def _language_complete(self, uid, language, error, data): '''Callback method from LANGUAGE RDM set. Args: uid: The uid of the RDM device language: STRING length 2; the new language of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: self._uid_dict[uid]['LANGUAGE'] = language self._notebook.set_language_complete(language) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def set_display_invert(self, invert): ''' RDM set call for DISPLAY_INVERT call originates from an optionMenu in the config tab of the notebook class Args: invert: INT [0,2]; the new invert state for the RDM device ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._display_invert_complete(uid, invert, b, s) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'DISPLAY_INVERT', callback, [invert]) def _display_invert_complete(self, uid, invert, error, data): '''Callback method from DISPLAY_INVERT RDM set. Args: uid: The uid of the RDM device invert: INT [0,2]; the new invert state of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: self._uid_dict[uid]['DISPLAY_INVERT'] = invert self._notebook.set_display_invert_complete(invert) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def set_pan_invert(self, invert): ''' RDM set call for PAN_INVERT call originates from a checkbox in the config tab of the notebook class Args: invert: BOOL; the new pan invert state for the RDM device ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._pan_invert_complete(uid, invert, b, s) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'PAN_INVERT', callback, [invert]) def _pan_invert_complete(self, uid, invert, error, data): '''Callback method from PAN_INVERT RDM set. Args: uid: The uid of the RDM device invert: BOOL; the new pan invert state of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: self._uid_dict[uid]['PAN_INVERT'] = invert self._notebook.set_pan_invert_complete(invert) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def set_tilt_invert(self, invert): ''' RDM set call for TILT_INVERT call originates from a checkbox in the config tab of the notebook class Args: invert: BOOL; the new tilt invert state for the RDM device ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._tilt_invert_complete(uid, invert, b, s) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'TILT_INVERT', callback, [invert]) def _tilt_invert_complete(self, uid, invert, error, data): '''Callback method from TILT_INVERT RDM set. Args: uid: The uid of the RDM device invert: BOOL; the new tilt invert state of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: self._uid_dict[uid]['TILT_INVERT'] = invert self._notebook.set_tilt_invertComplete(invert) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def set_pan_tilt_swap(self, swap): ''' RDM set call for PAN_TILT_SWAP call originates from a checkbox in the config tab of the notebook class Args: swap: BOOL; the new pan/tilt swap state of the RDM device ''' if self._cur_uid is None: return uid = self._cur_uid callback = lambda b, s: self._pan_tilt_swap_complete(uid, swap, b, s) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'PAN_TILT_SWAP', callback, [swap]) def _pan_tilt_swap_complete(self, uid, swap, error, data): '''Callback method from PAN_TILT_SWAP RDM set. Args: uid: The uid of the RDM device swap: BOOL; the new pan/tilt swap state of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: self._uid_dict[uid]['PAN_TILT_SWAP'] = swap self._notebook.set_pan_tilt_swap_complete(swap) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def record_sensor(self, sensor_number): ''' RDM set call for RECORD_SENSORS call originates from a button in the sensor tab of the notebook class Args: sensor_number: INT; the number of the sensor whose values are being recorded ''' self.ola_thread.rdm_set(self.universe.get(), self._cur_uid, 0, 'RECORD_SENSORS', lambda b, s: self._record_sensor_complete(b, s), [sensor_number]) def _record_sensor_complete(self, error, data): ''' Callback method from RECORD_SENSORS RDM set. Args: error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: pass else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() def clear_sensor(self, sensor_number): ''' clears the values of the RDM sensor device Args: sensor_number: INT; the number of the sensor whose values are being cleared ''' sensor_actions = [] data = self._uid_dict[self._cur_uid] sensor_actions.append(actions.SetSensorValue( data, self.ola_thread.rdm_set, [sensor_number])) sensor_actions.append(actions.GetSensorValue( data, self.ola_thread.rdm_get, [sensor_number])) sensor_actions.append(actions.GetSensorDefinition( data, self.ola_thread.rdm_get, [sensor_number])) flow = controlflow.RDMControlFlow( self.universe.get(), self._cur_uid, sensor_actions, lambda: self.display_sensor_data(sensor_number)) flow.run() # def clear_sensor_complete(self, error, data): # ''' Callback method from CLEA RDM set. # Args: # error: If the RDM set fails, this will be the error returned by OLA # otherwise this value will be None # data: Not Present # ''' # if error is None: # pass # else: # d = RDMDialog(self.root, error) # self.root.wait_window(d.top) # self._notebook.update() # ================================ Callbacks ================================= def set_device_label(self, label): ''' RDM set call for DEVICE_LABEL call originates from a button in the device information tab of the notebook class Args: label: STRING maxsize 32; the new device label for the RDM device ''' uid = self._cur_uid callback = (lambda b, s: self.set_device_label_complete(uid, label, b, s)) self.ola_thread.rdm_set(self.universe.get(), uid, 0, 'DEVICE_LABEL', callback, [label] ) def set_device_label_complete(self, uid, label, error, data): ''' Callback method from DEVICE_LABEL RDM set. Args: uid: The uid of the RDM device label: STRING maxsize 32; the new device label of the RDM device error: If the RDM set fails, this will be the error returned by OLA otherwise this value will be None data: Not Present ''' if error is None: index = self._uid_dict[self._cur_uid]['index'] self._uid_dict[self._cur_uid]['DEVICE_LABEL'] = label self.device_menu.entryconfigure(index, label = '%s (%s)'%( self._uid_dict[uid]['DEVICE_LABEL'], uid)) else: d = RDMDialog(self.root, error) self.root.wait_window(d.top) self._notebook.update() # store the results in the uid dict self.root.update_idletasks() self._notebook.update() def _add_device_to_menu(self, uid): ''' adds a device to the device menu at the top of the GUI Args: uid: 48-bit Unique ID (UID) consisting of a 16-bit Manufacturer ID and a 32-bit Device ID ''' label = self._uid_dict[uid]['DEVICE_LABEL'] if label == '': menu_label = '%s' % uid else: menu_label = '%s (%s)' % (label, uid) index = self.device_menu.add_item(menu_label, lambda:self.device_selected(uid)) self._uid_dict[uid]['index'] = index def main(self): print 'Entering main loop' self.root.mainloop()
class RDMNotebook(object): def __init__(self, root, controller, width=800, height=500, side=tk.TOP): """ Builds the ttk.Notebook """ self.root = root self._controller = controller self.init_dx = width self.init_dy = height self.side = side self.objects = {} self.pid_location_dict = {} self._notebook = ttk.Notebook(self.root, name="nb", height=height, width=width) # self._notebook.config(state = tk.DISABLED) self._notebook.bind('<<NotebookTabChanged>>', self._tab_changed) self.populate_defaults() def populate_defaults(self): """ creates the default frames. """ self.info_tab = self._create_tab("info_tab", "Device Information") self._init_info() self.dmx_tab = self._create_tab("dmx_tab", "DMX512 Setup") self._init_dmx() self.sensor_tab = self._create_tab("sensor_tab", "Sensors") self._init_sensor() self.setting_tab = self._create_tab("setting_tab", "Power and Lamp Settings") self._init_setting() self.config_tab = self._create_tab("config_tab", "Configuration") self._init_config() tabs = ["PRODUCT_INFO", "DMX512_SETUP", "SENSORS", "POWER_LAMP_SETTINGS", "CONFIGURATION"] for tab in tabs: self._grid_info(self.objects[tab]) self._notebook.pack(side = self.side) # ============================================================================== # ============================ Tab Inits ======================================= # ============================================================================== def _init_info(self): """ initalize the widgets for the info tab """ # Text Variables: self.protocol_version = tk.StringVar(self.info_tab) self.device_model = tk.StringVar(self.info_tab) self.product_category = tk.StringVar(self.info_tab) self.software_version = tk.StringVar(self.info_tab) self.sub_device_count = tk.StringVar(self.info_tab) self.product_detail_ids = tk.StringVar(self.info_tab) self.manufacturer_label = tk.StringVar(self.info_tab) self.device_label = tk.StringVar(self.info_tab) self.boot_software = tk.StringVar(self.info_tab) # Widgets: #device label entry boc value should be capped at 32 self.factory_defaults = tk.BooleanVar(self.info_tab) self.factory_defaults_button = tk.Checkbutton( self.info_tab, variable = self.factory_defaults) self.device_label_button = tk.Button(self.info_tab, text = "Update Device Label", command = self.device_label_set) self.objects["PRODUCT_INFO"] = [ tk.Label(self.info_tab, text = "RDM Protocol Version"), tk.Label(self.info_tab, textvariable = self.protocol_version), tk.Label(self.info_tab, text = "Device Model"), tk.Label(self.info_tab, textvariable = self.device_model), tk.Label(self.info_tab, text = "Product Category:"), tk.Label(self.info_tab, textvariable = self.product_category), tk.Label(self.info_tab, text = "Software Version:"), tk.Label(self.info_tab, textvariable = self.software_version), tk.Label(self.info_tab, text = "Product Details:"), tk.Label(self.info_tab, textvariable = self.product_detail_ids), tk.Label(self.info_tab, text = "Sub-Device Count"), tk.Label(self.info_tab, textvariable = self.sub_device_count), tk.Label(self.info_tab, text = "Manufacturer:"), tk.Label(self.info_tab, textvariable = self.manufacturer_label), tk.Label(self.info_tab, text = "Device Label:"), tk.Entry(self.info_tab, textvariable = self.device_label), tk.Label(self.info_tab, text = "Factory Defaults:"), tk.Checkbutton(self.info_tab, variable = self.factory_defaults), tk.Label(self.info_tab, text = "Boot Software Version:"), tk.Label(self.info_tab, textvariable = self.boot_software), self.device_label_button, tk.Label(self.info_tab, text = ""), ] def _init_dmx(self): """ initalize the widgets for the dmx tab """ # Text Variables self.dmx_footprint = tk.StringVar(self.dmx_tab) self.dmx_start_address = tk.StringVar(self.dmx_tab) self.slot_required = tk.StringVar(self.dmx_tab) self.personality_name = tk.StringVar(self.dmx_tab) self.slot_number = tk.StringVar(self.dmx_tab) self.slot_name = tk.StringVar(self.dmx_tab) self.slot_type = tk.StringVar(self.dmx_tab) self.slot_label_id = tk.StringVar(self.dmx_tab) self.default_slot_value = tk.StringVar(self.dmx_tab) # Widgets self.start_address_entry = tk.Entry( self.dmx_tab, textvariable = self.dmx_start_address) self.start_address_button = tk.Button(self.dmx_tab, text = 'Set Start Address', command = self.set_start_address) # validatecommand make sure between 1 and 512 self.dmx_personality_menu = RDMMenu( self.dmx_tab, "Personality description not supported.", "") self.slot_menu = RDMMenu( self.dmx_tab, "No slot description.", "Choose Slot") self.objects["DMX512_SETUP"] = [ tk.Label(self.dmx_tab, text = "DMX Footprint:"), tk.Label(self.dmx_tab, textvariable = self.dmx_footprint), tk.Label(self.dmx_tab, text = "DMX Start Address:"), self.start_address_entry, tk.Label(self.dmx_tab, text = ""), self.start_address_button, tk.Label(self.dmx_tab, text = "Current Personality:"), self.dmx_personality_menu, tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.slot_required), tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.personality_name), tk.Label(self.dmx_tab, text = "Slot Info:"), self.slot_menu, tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.slot_type), tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.slot_label_id), tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.slot_name), tk.Label(self.dmx_tab, text = ""), tk.Label(self.dmx_tab, textvariable = self.default_slot_value), ] def _init_sensor(self): """ initalize the widgets for the sensor tab """ # Text Variable self.sensor_type = tk.StringVar(self.sensor_tab) self.sensor_unit = tk.StringVar(self.sensor_tab) self.sensor_prefix = tk.StringVar(self.sensor_tab) self.sensor_range = tk.StringVar(self.sensor_tab) self.normal_range = tk.StringVar(self.sensor_tab) self.supports_recording = tk.StringVar(self.sensor_tab) self.supports_lowest_highest = tk.StringVar(self.sensor_tab) self.sensor_name = tk.StringVar(self.sensor_tab) self.sensor_number = tk.StringVar(self.sensor_tab) self.present_value = tk.StringVar(self.sensor_tab) self.lowest = tk.StringVar(self.sensor_tab) self.highest = tk.StringVar(self.sensor_tab) self.recorded = tk.StringVar(self.sensor_tab) # Widgets self.sensor_menu = RDMMenu( self.sensor_tab, "Sensor information not provided.", "Choose Sensor") self.record_sensor_button = tk.Button( self.sensor_tab, text="Record Sensor", state=tk.DISABLED) self.clear_sensor_button = tk.Button( self.sensor_tab, text='Clear Sensor', state=tk.DISABLED) self.refresh_sensor_button = tk.Button( self.sensor_tab, text = 'Refresh', state=tk.DISABLED) self.objects["SENSORS"] = [ tk.Label(self.sensor_tab, text = "Choose Sensor"), self.sensor_menu, tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.sensor_type), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.sensor_unit), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.sensor_prefix), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.sensor_range), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.normal_range), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.supports_recording), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.supports_lowest_highest), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.present_value), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.lowest), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.highest), tk.Label(self.sensor_tab, text = ""), tk.Label(self.sensor_tab, textvariable = self.recorded), tk.Label(self.sensor_tab, text = ""), self.record_sensor_button, tk.Label(self.sensor_tab, text = ""), self.clear_sensor_button, tk.Label(self.sensor_tab, text = ""), self.refresh_sensor_button ] def _init_setting(self): """ initalize the widgets for the setting tab """ # sText Varibles self.device_hours = tk.StringVar(self.setting_tab) self.lamp_hours = tk.StringVar(self.setting_tab) self.lamp_strikes = tk.StringVar(self.setting_tab) self.lamp_state = tk.StringVar(self.setting_tab) self.lamp_on_mode = tk.StringVar(self.setting_tab) self.device_power_cycles = tk.StringVar(self.setting_tab) self.power_state = tk.StringVar(self.setting_tab) # Widgets self.lamp_state_menu = RDMMenu(self.setting_tab, 'Lamp state not supported.', '') self.lamp_on_mode_menu = RDMMenu(self.setting_tab, 'Lamp on mode not supported', '') self.power_state_menu = RDMMenu(self.setting_tab, 'Power state not supported.', '') self.objects["POWER_LAMP_SETTINGS"] = [ tk.Label(self.setting_tab, text = "Device Hours:"), tk.Label(self.setting_tab, textvariable = self.device_hours), tk.Label(self.setting_tab, text = "Device Power Cycles:"), tk.Label(self.setting_tab, textvariable = self.device_power_cycles), tk.Label(self.setting_tab, text = "Lamp Hours:"), tk.Label(self.setting_tab, textvariable = self.lamp_hours), tk.Label(self.setting_tab, text = "Lamp Strikes:"), tk.Label(self.setting_tab, textvariable = self.lamp_strikes), tk.Label(self.setting_tab, text = "Lamp State:"), self.lamp_state_menu, tk.Label(self.setting_tab, text = "Lamp On Mode:"), self.lamp_on_mode_menu, tk.Label(self.setting_tab, text = "Power State:"), self.power_state_menu ] def _init_config(self): """ initalize the widgets for the config tab """ # Variables self.display_invert = tk.StringVar(self.config_tab) self.display_level = tk.IntVar(self.config_tab) self.pan_invert = tk.BooleanVar(self.config_tab) self.tilt_invert = tk.BooleanVar(self.config_tab) self.pan_tilt_swap = tk.BooleanVar(self.config_tab) self.real_time_clock = tk.StringVar(self.config_tab) # Widgets self.language_menu = RDMMenu( self.config_tab, "Languages not supported.", "") self.display_invert_menu = tk.OptionMenu(self.config_tab, self.display_invert, *PIDDict.DISPLAY_INVERT.values(), command = self._set_display_invert) self.display_level_menu = tk.Scale( self.config_tab, from_ = 0, to = 255, variable=self.display_level, orient=tk.HORIZONTAL, command = self._controller.set_display_level, length = 255, state = tk.DISABLED, tickinterval = 255) self.pan_invert_button = tk.Checkbutton(self.config_tab, variable = self.pan_invert, command = self._set_pan_invert, state = tk.DISABLED) self.tilt_invert_button = tk.Checkbutton(self.config_tab, variable = self.tilt_invert, command = self._set_tilt_invert, state = tk.DISABLED) self.pan_tilt_swap_button = tk.Checkbutton( self.config_tab, variable = self.pan_tilt_swap, command = self._set_pan_tilt_swap, state = tk.DISABLED ) self.objects["CONFIGURATION"] = [ tk.Label(self.config_tab, text = "Device Language:"), self.language_menu, tk.Label(self.config_tab, text = "Display Invert:"), self.display_invert_menu, tk.Label(self.config_tab, text = "Display Level:"), self.display_level_menu, tk.Label(self.config_tab, text = "Pan Invert:"), self.pan_invert_button, tk.Label(self.config_tab, text = "Tilt Invert:"), self.tilt_invert_button, tk.Label(self.config_tab, text = "Pan Tilt Swap"), self.pan_tilt_swap_button, tk.Label(self.config_tab, text = "Real Time Clock"), tk.Label(self.config_tab, textvariable = self.real_time_clock) ] # ============================================================================ # ============================== Update Tabs ================================= # ============================================================================ def update(self): ''' using the value of the current tab (self._notebook.index('current')), calls the control for obtaining the information for that tab. ''' index = self._notebook.index('current') print 'The selected tab changed to %d' % index if index == 0: self._controller.get_basic_information() elif index == 1: self._controller.get_dmx_information() elif index == 2: self._controller.get_sensor_definitions() elif index == 3: self._controller.get_setting_information() elif index == 4: self._controller.get_config_information() # ========================= Information Rendering ============================ def render_basic_information(self, param_dict): ''' Uses the data in param_dict to display the DMX information for the device Called when the tab is selected, or the device changed. Ultimate callback function for control flow, 'get_basic_information'. Args: param_dict: dictionary of pids for the current uid. In the form: {PID: data} NOTE: data may be in the form of a int, string or dict and is treated differently in each case. ''' device_info = param_dict["DEVICE_INFO"] self.protocol_version.set( "Version %d.%d" % ( device_info["protocol_major"], device_info["protocol_minor"])) self.device_model.set(param_dict["DEVICE_INFO"]["device_model"]) self.device_model.set("%s (%d)" % ( param_dict.get("DEVICE_MODEL_DESCRIPTION", 'N/A'), device_info["device_model"])) index = device_info["product_category"] self.product_category.set(RDMConstants.PRODUCT_CATEGORY_TO_NAME.get(index, "").replace("_"," ")) self.sub_device_count.set(param_dict["DEVICE_INFO"]["sub_device_count"]) self.software_version.set("%s (%d)" % ( param_dict.get("SOFTWARE_VERSION_LABEL", "N/A"), device_info["software_version"])) self.sub_device_count.set(device_info["sub_device_count"]) if "PRODUCT_DETAIL_ID_LIST" in param_dict: ids = param_dict["PRODUCT_DETAIL_ID_LIST"] names = ', '.join(RDMConstants.PRODUCT_DETAIL_IDS_TO_NAME[id] for id in ids).replace("_", " ") self.product_detail_ids.set(names) self.manufacturer_label.set(param_dict.get("MANUFACTURER_LABEL", "N/A")) self.device_label.set(param_dict.get("DEVICE_LABEL", "N/A")) self.factory_defaults.set(param_dict.get("FACTORY_DEFAULTS", "N/A")) boot_software = 'N/A' boot_software_version = param_dict.get('BOOT_SOFTWARE_VERSION') boot_software_label = param_dict.get('BOOT_SOFTWARE_LABEL') if boot_software_version and boot_software_label: boot_software = '%s (%d)' % (boot_software_label, boot_software_version) elif boot_software_label: boot_software = boot_software_label elif boot_software_version: boot_software = boot_software_version self.boot_software.set(boot_software) def render_dmx_information(self, param_dict): ''' Uses the data in param_dict to display the DMX information for the device Called when the tab is selected, or the device changed. Ultimate callback function for control flow, 'get_dmx_information'. Args: param_dict: dictionary of pids for the current uid. In the form: {PID: data} NOTE: data may be in the form of a int, string or dict and is treated differently in each case. ''' print "param_dict: %s" % param_dict device_info = param_dict["DEVICE_INFO"] self.dmx_personality_menu.clear_menu() self.slot_menu.clear_menu() self._display_personality_decription('N/A', 'N/A') if "DMX_PERSONALITY_DESCRIPTION" in param_dict: personalities = param_dict["DMX_PERSONALITY_DESCRIPTION"] for personality in personalities.iteritems(): print personality self.dmx_personality_menu.add_item( self._get_personality_string(personality[1]), lambda i = personality[0]:self._controller.set_dmx_personality(i)) personality_id = device_info['current_personality'] self.dmx_personality_menu.set(self._get_personality_string( personalities[personality_id])) s = personalities[personality_id]['slots_required'] p = personality_id self._display_personality_decription(s, p) self.dmx_footprint.set(param_dict["DEVICE_INFO"]["dmx_footprint"]) start_address = param_dict["DEVICE_INFO"]["dmx_start_address"] if start_address == 0xffff: self.dmx_start_address.set('N/A') self.start_address_entry.config(state=tk.DISABLED) self.start_address_button.config(state=tk.DISABLED) else: self.dmx_start_address.set(start_address) self.start_address_entry.config(state=tk.NORMAL) self.start_address_button.config(state=tk.NORMAL) if "SLOT_INFO" in param_dict: for index in xrange(param_dict["DEVICE_INFO"]["dmx_footprint"]): self.slot_menu.add_item( "Slot Number %d" % index, lambda i = index:self._display_slot_info(i, param_dict)) def render_sensor_information(self, param_dict): ''' Uses the data in param_dict to display the DMX information for the device Called when the tab is selected, or the device changed. Ultimate callback function for control flow, 'get_sensor_information'. Args: param_dict: dictionary of pids for the current uid. In the form: {PID: data} NOTE: data may be in the form of a int, string or dict and is treated differently in each case. ''' self.sensor_type.set('') self.sensor_unit.set('') self.sensor_prefix.set('') self.sensor_range.set('') self.normal_range.set('') self.supports_recording.set('') self.present_value.set('') self.lowest.set('') self.highest.set('') self.recorded.set('') sensor_info = {} self.sensor_menu.clear_menu() for index, sensor in param_dict.get('SENSOR_DEFINITION', {}).iteritems(): self.sensor_menu.add_item('%s' % sensor['name'], lambda i=index: self._populate_sensor_tab(i)) def render_setting_information(self, param_dict): ''' Uses the data in param_dict to display the DMX information for the device Called when the tab is selected, or the device changed. Ultimate callback function for control flow, 'GetSettingsInformation'. Args: param_dict: dictionary of pids for the current uid. In the form: {PID: data} NOTE: data may be in the form of a int, string or dict and is treated differently in each case. ''' print "PARAM_DICT: %s" % param_dict self.device_hours.set(param_dict.get('DEVICE_HOURS', 'N/A')) self.lamp_hours.set(param_dict.get('LAMP_HOURS', 'N/A')) self.device_power_cycles.set(param_dict.get("DEVICE_POWER_CYCLES", "N/A")) self.lamp_strikes.set(param_dict.get('LAMP_STRIKES', 'N/A')) self.lamp_state_menu.config(state = tk.DISABLED) self.lamp_on_mode_menu.config(state = tk.DISABLED) self.power_state_menu.config(state = tk.DISABLED) if 'LAMP_STATE' in param_dict: self.lamp_state_menu.config(state = tk.NORMAL) for key, value in PIDDict.LAMP_STATE.iteritems(): self.lamp_state_menu.add_item( value, lambda k=key: self._controller.set_lamp_state(k)) self.lamp_state_menu.set(PIDDict.LAMP_STATE[param_dict['LAMP_STATE']]) if 'LAMP_ON_MODE' in param_dict: self.lamp_on_mode_menu.config(state = tk.NORMAL) for key, value in PIDDict.LAMP_ON_MODE.iteritems(): self.lamp_on_mode_menu.add_item(value, lambda k=key: self._set_lamp_on_mode(k)) self.lamp_on_mode_menu.set( PIDDict.LAMP_ON_MODE[param_dict['LAMP_ON_MODE']]) if 'POWER_STATE' in param_dict: self.power_state_menu.config(state = tk.NORMAL) for key, value in PIDDict.POWER_STATE.iteritems(): self.power_state_menu.add_item(value, lambda k=key: self._set_power_state(k)) self.power_state_menu.set(PIDDict.POWER_STATE[param_dict['POWER_STATE']]) def render_config_information(self, param_dict): ''' Uses the data in param_dict to display the DMX information for the device Called when the tab is selected, or the device changed. Ultimate callback function for control flow, 'get_config_information'. Args: param_dict: dictionary of pids for the current uid. In the form: {PID: data} NOTE: data may be in the form of a int, string or dict and is treated differently in each case. ''' self.language_menu.clear_menu() if 'LANGUAGE_CAPABILITIES' in param_dict: self.language_menu.config(state = tk.NORMAL) for value in param_dict['LANGUAGE_CAPABILITIES']: language = value['language'] self.language_menu.add_item(language, lambda l = language: self._set_language(l)) self.language_menu.set(param_dict.get('LANGUAGE', 'N/A')) if "DISPLAY_LEVEL" in param_dict: self.display_level.set(param_dict['DISPLAY_LEVEL']) self.display_level_menu.config(state = tk.NORMAL) else: self.display_level.set(0) self.display_level_menu.config(state = tk.DISABLED) if 'DISPLAY_INVERT' in param_dict: display_invert = PIDDict.DISPLAY_INVERT [param_dict['DISPLAY_INVERT']] self.display_invert.set(display_invert) self.display_invert_menu.config(state = tk.NORMAL) else: self.display_invert.set('N/A') self.display_invert_menu.config(state = tk.DISABLED) if 'PAN_INVERT' in param_dict: self.pan_invert.set(param_dict['PAN_INVERT']) self.pan_invert_button.config(state = tk.NORMAL) else: self.pan_invert.set(False) self.pan_invert_button.config(state = tk.DISABLED) if 'TILT_INVERT' in param_dict: self.tilt_invert.set(param_dict['TILT_INVERT']) self.tilt_invert_button.config(state = tk.NORMAL) else: self.tilt_invert.set(False) self.tilt_invert_button.config(state = tk.DISABLED) if 'PAN_TILT_SWAP' in param_dict: self.pan_tilt_swap.set(param_dict['PAN_TILT_SWAP']) self.pan_tilt_swap_button.config(state = tk.NORMAL) else: self.pan_tilt_swap.set(False) self.pan_tilt_swap_button.config(state = tk.DISABLED) if 'REAL_TIME_CLOCK' in param_dict: clock = param_dict['REAL_TIME_CLOCK'] self.real_time_clock.set("%d:%d:%d %d/%d/%d" % (clock['hour'], clock['minute'], clock['second'], clock['day'], clock['month'], clock['year'] )) # ============================================================================ # ============================ RDM Set Methods =============================== # ============================================================================ def device_label_set(self): """ calls the control flow for setting the device's label to a new value, using the value of self.device_label """ self._controller.set_device_label(self.device_label.get()) def set_start_address(self): ''' start of control flow for setting the dmx_start_address of a device. ''' try: start_address = int(self.dmx_start_address.get()) except ValueError: d = RDMDialog(self.root, "Start address must be an int between 1 and 512") self.root.wait_window(d.top) self._notebook.update() return if start_address > 512 or start_address < 1: d = RDMDialog(self.root, "Start address must be between 1 and 512") self.root.wait_window(d.top) self._notebook.update() return self._controller.set_start_address(start_address) def _set_lamp_state(self, state): ''' Internal Function, first function in the 'SetLampState' control flow. Args: state: int, see PIDDict.LAMP_STATE for state name. ''' self._controller.set_lamp_state(state) def set_lamp_state_complete(self, state): ''' Ultimate callback for, 'SetLampState' control flow. Args: state: int, see PIDDict.LAMP_STATE for state name. ''' if self.lamp_state_menu.get() != PIDDict.LAMP_STATE[state]: self.lamp_state_menu.set(PIDDict.LAMP_STATE[state]) def _set_lamp_on_mode(self, mode): ''' Internal Function, first function in the 'SetLampOnMode' control flow. Args: mode: int, see PIDDict.LAMP_ON_MODE for mode names. ''' self._controller.set_lamp_on_mode(mode) def set_lamp_on_mode_complete(self, mode): ''' Ulitmate callback for 'SetLampOnMode' control flow Args: mode: int, see PIDDict.LAMP_ON_MODE for mode names. ''' if self.lamp_on_mode_menu.get() != PIDDict.LAMP_ON_MODE[mode]: self.lamp_on_mode_menu.set(PIDDict.LAMP_ON_MODE[mode]) def _set_power_state(self, state): ''' Internal Function, first function in the 'SetPowerState' control flow. Args: state: int, see PIDDict.LAMP_STATE for state name. ''' self._controller.set_power_state(state) def set_power_state_complete(self, state): ''' ''' if self.power_state_menu.get() != PIDDict.POWER_STATE[state]: self.power_state_menu.set(PIDDict.POWER_STATE[state]) def set_dmx_personality_complete(self, param_dict): print param_dict personality = param_dict['DEVICE_INFO']['current_personality'] slots_required = param_dict.get("DMX_PERSONALITY_DESCRIPTION", {})[personality].get( "slots_required", "N/A") self._display_personality_decription(slots_required, personality) self.slot_menu.clear_menu() self.slot_name.set('') self.slot_type.set('') self.slot_label_id.set('') self.default_slot_value.set('') if 'SLOT_INFO' or 'SLOT_DESCRIPTION' or 'DEFAULT_SLOT_VALUE' in param_dict['PARAM_NAMES']: for index in xrange(slots_required): self.slot_menu.add_item('Slot %d' % index, lambda i=index: self._display_slot_info(i, param_dict)) return def _set_display_invert(self, invert): ''' Internal Function, first function in the 'SetDisplayInvert' control flow. Args: state: int, see PIDDict.DISPLAY_INVERT for state name. ''' self._controller.set_display_invert(invert) # self._controller.SetDisplayInvert(self.display_invert.get()) def set_display_invert_complete(self, invert): if self.display_invert.get() != invert: self.display_invert.set(invert) def _set_pan_invert(self): ''' Internal Function, first function in the 'SetPanInvert' control flow. Args: state: boolean, true: inverted, false: normal. ''' self._controller.set_pan_invert(self.pan_invert.get()) def set_pan_invert_complete(self, invert): if self.pan_invert.get() != invert: self.pan_invert.set(invert) def _set_tilt_invert(self): ''' Internal Function, first function in the 'SetTiltInvert' control flow. Args: state: boolean, true: inverted, false: normal. ''' self._controller.set_tilt_invert(self.tilt_invert.get()) def set_tilt_invert_complete(self, invert): if self.tilt_invert.get() != invert: self.tilt_invert.set(invert) def _set_pan_tilt_swap(self): """ Swaps the pan and tilt in the device. Calls set_pan_tilt_swap in the controller. The final callback for this control flow is set_pan_tilt_swap_complete (below). """ self._controller.set_pan_tilt_swap(self.pan_tilt_swap.get()) def set_pan_tilt_swap_complete(self, swap): """ Sets the value of the pan-tilt-swap Checkbutton Only sets the value of the Checkbutton if the current value is does not match the value passed into the method. Args: swap: boolean value True, when pan and tilt are swapped False, when they are unswapped, or normal """ if self.pan_tilt_swap.get() != swap: self.pan_tilt_swap.set(swap) def _set_language(self, language): self._controller.set_language(language) def set_language_complete(self, language): if self.language_menu.get() != language: self.language_menu.set(language) def record_sensor(self, sensor_number): self._controller.record_sensor(sensor_number) def clear_sensor(self, sensor_number): self._controller.clear_sensor(sensor_number) def set_display_level_complete(self, level): if level != self.display_level: self.display_level.set(level) return # ============================================================================ # ========================== Internal Methods ================================ # ============================================================================ def _tab_changed(self, event): ''' Method bound to tab change event, calls self.update ''' # Note that this will be called when the program starts self.update() def _grid_info(self, obj_list): """ places the widgets subject to change upon completion of controlflows """ obj_list.reverse() for r in xrange((len(obj_list) + 1) / 2): for c in xrange(2): obj_list.pop().grid(row=r, column=c) def _create_tab(self, tab_name, tab_label=None): """ Creates a tab. will want to have all the options allowed by the ttk notebook widget to be args for this method Args: tab_name: string, cannot begin with a capital letter pid_list: list of strings, tab_label: string that will be displayed on the tab, default set to None, and tab_name will be on the tab Returns: tab: the Frame """ if tab_label is None: tab_label = tab_name tab = tk.Frame(self._notebook, name=tab_name) self._notebook.add(tab, text=tab_label) return tab def _display_slot_info(self, slot_number, param_dict): """ """ if param_dict['SLOT_INFO'][slot_number]['slot_type'] != 0: self.slot_label_id.set('Primary Slot Index: %d' % param_dict.get( 'SLOT_INFO', {})[slot_number].get('slot_label_id', "N/A")) else: label_id = RDMConstants.SLOT_DEFINITION_TO_NAME[ param_dict.get('SLOT_INFO', {})[slot_number].get('slot_label_id', "N/A") ].replace('_', ' ') self.slot_label_id.set('Slot Label: %s' % label_id) slot_name = param_dict.get('SLOT_DESCRIPTION', {}).get('slot_number') if slot_name is not None: self.slot_name.set('Name: %s' % slot_name) else: if param_dict['SLOT_INFO'][slot_number]['slot_type'] == 0: self.slot_name.set('Description: %s' % PIDDict.SLOT_ID_DEFINITIONS.get( param_dict['SLOT_INFO'][slot_number]['slot_label_id'], 'N/A')) else: self.slot_name.set('Name: N/A') type_name = RDMConstants.SLOT_TYPE_TO_NAME[ param_dict.get('SLOT_INFO', {})[slot_number].get('slot_type', "N/A") ].replace('_', ' ') self.slot_type.set('Type: %s' % type_name) if 'DEFAULT_SLOT_VALUE' in param_dict: print param_dict['DEFAULT_SLOT_VALUE'] self.default_slot_value.set('Default Slot Value: %d' % param_dict.get('DEFAULT_SLOT_VALUE', {})[slot_number]) def _display_personality_decription(self, slots_required, personality): """ Changes the displayed DMX personality information Called after a device's DMX personality has been changed. Changes the values for both the personality name and the number of DMX slots required. Args: slots_required: the number of DMX slots required for the new DMX personality personality: the personality ID for the new DMX personality assigned to the device """ self.slot_required.set("Slots Required: %s" % slots_required) self.personality_name.set("Personality ID: %s" % personality) def _get_personality_string(self, personality): """ """ return '%s (%d)' % (personality['name'], personality['slots_required']) def _populate_sensor_tab(self, sensor_number): self._controller.get_sensor_value(sensor_number) def display_sensor_data(self, param_dict, sensor_number): self.record_sensor_button.config( command = lambda: self.record_sensor(sensor_number), state = tk.NORMAL) self.clear_sensor_button.config( command = lambda: self.clear_sensor(sensor_number), state = tk.NORMAL) self.refresh_sensor_button.config( command = lambda: self._populate_sensor_tab(sensor_number), state = tk.NORMAL) definition = param_dict['SENSOR_DEFINITION'][sensor_number] TYPE = RDMConstants.SENSOR_TYPE_TO_NAME[definition['type']].replace("_", " ") UNIT = RDMConstants.UNIT_TO_NAME[definition['unit']].replace("_", " ") PREFIX = RDMConstants.PREFIX_TO_NAME[definition['prefix']].replace("_", " ") self.sensor_type.set('Type: %s' % TYPE) self.sensor_unit.set('Unit: %s' % UNIT) self.sensor_prefix.set('Prefix: %s' % PREFIX) self.sensor_range.set('Range: %d - %d' % (definition['range_min'], definition['range_max'])) self.normal_range.set( 'Normal Range: %d - %d' % (definition['normal_min'], definition['normal_max'])) self.supports_recording.set( 'Supports Recording: %s' % bool(PIDDict.RECORDED_SUPPORTED & definition['supports_recording'])) self.supports_lowest_highest.set('Supports Lowest/Highest: %s' % bool(PIDDict.LOWEST_HIGHEST_SUPPORTED & definition['supports_recording'])) if 'SENSOR_VALUE' in param_dict: value = param_dict['SENSOR_VALUE'][sensor_number] self.present_value.set('Value: %d' % value['present_value']) self.lowest.set('Lowest Value: %d' % value['lowest']) self.highest.set('Highest Value: %d' % value['highest']) self.recorded.set('Recorded Value: %d' % value['recorded'])