def create_voices(self): voice_ids = ['1', '2', '3', '4'] SCALES = OrderedDict([ ('pan_pos', {'min': -1, 'max': 1, 'start': 0.5, 'res': 0.001}), ('volume', {'min': 0, 'max': 1, 'start': 0.666, 'res': 0.001}), ('slide_duration_msecs', {'min': 0, 'max': 2000, 'start': 60, 'res': 1}), ('slide_duration_prop', {'min': 0, 'max': 2, 'start': 0.666, 'res': 0.001}), ('binaural_diff', {'min': 0, 'max': 66, 'start': 0.2, 'res': 0.01}) ]) for vid in voice_ids: counter = 0 for sca in SCALES: name = 'voice_' + vid + '_' + sca setattr(self, 'min_' + name, SCALES[sca]['min']) setattr(self, 'max_' + name, SCALES[sca]['max']) this_sca = Scale(self, label=sca, orient=HORIZONTAL, from_=getattr(self, 'min_' + name), to=getattr(self, 'max_' + name), resolution=SCALES[sca]['res']) this_sca.enable = ('enable' in SCALES[sca].keys() and SCALES[sca]['enable'] or None) this_sca.disable = ('disable' in SCALES[sca].keys() and SCALES[sca]['disable'] or None) this_sca.grid(column=int(2 + int(vid)), row=counter, sticky=E + W) this_sca.bind("<ButtonRelease>", self.scale_handler) this_sca.ref = name counter += 1 CHECK_BUTTONS = OrderedDict( [('mute', False), ('automate_binaural_diffs', True), ('automate_note_duration_prop', True), ('use_proportional_slide_duration', {'val': True, 'label': 'proportional slide'}), ('automate_pan', True), ('automate_wavetables', True)]) for vid in voice_ids: counter = 0 cb_frame = LabelFrame(self, text="Voice {0} - Automation".format(vid)) setattr(self, 'voice_' + vid + '_cb_frame', cb_frame) for cb in CHECK_BUTTONS: options = CHECK_BUTTONS[cb] name = 'voice_' + vid + '_' + cb label = (options['label'] if isinstance(options, dict) and 'label' in options.keys() else (cb[9:] if cb[:9] == 'automate_' else cb)) setattr(self, name, IntVar(value=type(options) == dict and options['val'] or options)) self.this_cb = Checkbutton(cb_frame, text=label, variable=getattr(self, name)) self.this_cb.bind('<Button-1>', self.check_boxes_handler) self.this_cb.disable = None self.this_cb.grid(sticky=W, column=0, row=counter) self.this_cb.ref = name counter += 1 # add trigger wavetable-button trigWavetableButton = Button(cb_frame, text='Next Wavetable') trigWavetableButton.bind('<Button-1>', self.trigger_waveform_handler) trigWavetableButton.ref = 'voice_' + vid + "_trigger_wavetable" trigWavetableButton.grid(row=counter) cb_frame.grid(column=int(vid) + 2, row=5, sticky=E + W + N, rowspan=8) for vid in voice_ids: generation_types = ["random", "random_harmonic", "harmonic"] partial_pools = ["even", "odd", "all"] prefix = 'voice_' + vid + '_' types_name = prefix + 'wavetable_generation_type' pools_name = prefix + 'partial_pool' setattr(self, types_name, StringVar()) getattr(self, types_name).set("random") setattr(self, pools_name, StringVar()) getattr(self, pools_name).set("all") target_frame = getattr(self, 'voice_' + vid + '_cb_frame') gen_typ_frame = LabelFrame(target_frame, text="type") gen_typ_frame.grid(row=len(target_frame.winfo_children()), sticky=W) for gen_t in generation_types: gen_t_entry = Radiobutton(gen_typ_frame, value=gen_t, text=gen_t, anchor=W, variable=getattr(self, types_name)) gen_t_entry.bind('<ButtonRelease-1>', self.wt_handler) gen_t_entry.ref = types_name gen_t_entry.grid(row=len(gen_typ_frame.winfo_children()), sticky=W) pp_frame = LabelFrame(target_frame, text="harmonics") for pp in partial_pools: pp_entry = Radiobutton(pp_frame, value=pp, text=pp, anchor=W, variable=getattr(self, pools_name)) pp_entry.bind('<ButtonRelease-1>', self.wt_handler) pp_entry.ref = pools_name pp_entry.grid(row=len(pp_frame.winfo_children()), sticky=E + W) this_num_partials = Scale(pp_frame, label='number of harmonics', orient=HORIZONTAL, from_=1, to=24, resolution=1) this_num_partials.ref = prefix + 'num_partials' this_num_partials.grid(column=0, row=len(pp_frame.winfo_children()), sticky=E + W) this_num_partials.bind("<ButtonRelease>", self.scale_handler) pp_frame.grid(row=len(target_frame.winfo_children()), sticky=E + W)
def create_monitor(self): self.monitor_frame = LabelFrame(self, text="Monitor and Transport") this_cycle = Scale(self.monitor_frame, label='cycle_pos', orient=HORIZONTAL, from_=1, to=16, resolution=1) this_cycle.disable, this_cycle.enable = (None, None) this_cycle.ref = 'cycle_pos' this_cycle.grid(column=0, row=0, sticky=E + W) self.updateButton = Button(self.monitor_frame, text='Reload all Settings', command=self.request_update) self.updateButton.grid(row=1, sticky=E + W) self.ForceCaesuraButton = Button(self.monitor_frame, text='Force Caesura', command=self.force_caesura) self.ForceCaesuraButton.grid(row=2, sticky=E + W) self.saveBehaviourButton = Button(self.monitor_frame, text='Save current behaviour', command=self.request_saving_behaviour) self.saveBehaviourButton.grid(row=3, sticky=E + W) self.saveBehaviourNameEntry = Entry(self.monitor_frame) self.saveBehaviourNameEntry.grid(row=4, sticky=E + W) self.saveBehaviourNameEntry.bind('<KeyRelease>', self.request_saving_behaviour) self.selected_behaviour = StringVar() self.selected_behaviour.trace('w', self.new_behaviour_chosen) self.savedBehavioursMenu = OptionMenu(self.monitor_frame, self.selected_behaviour, None,) self.savedBehavioursMenu.grid(row=5, sticky=E + W) self.monitor_frame.grid(column=0, row=10, sticky=E + W)
class JoystickFrame(LabelFrame): def __init__(self, master, tracker, text="Joystick", **options): LabelFrame.__init__(self, master, text=text, **options) self.tracker = tracker self.width = 400 self.height = 400 self.canvas = Canvas(self, height=self.height, width=self.width) self.canvas.grid() self.canvas.create_oval((self.width/2 - 3, self.height/2 - 3, self.width/2 + 3, self.height/2 + 3)) self.canvas.bind("<Button-1>", bg_caller(lambda event: self.move_tracker(event))) self.canvas.bind("<Motion>", self.update_label) self.motion_label = Label(self, text="", font=tkFont.Font(family="Courier")) self.motion_label.grid() f = LabelFrame(self, text="Sensitivity") self.sensitivity_scale = Scale(f, from_=0, to=10, resolution=0.01, orient=HORIZONTAL, length=self.width) self.sensitivity_scale.set(5) self.sensitivity_scale.grid() f.grid() @property def sensitivity(self): return self.sensitivity_scale.get() / 2000. def get_delta(self, event): dx = event.x - int(self.canvas['width'])/2. dy = event.y - int(self.canvas['height'])/2. dx_rad = dx*self.sensitivity dy_rad = dy*self.sensitivity dtheta = dy_rad dphi = -dx_rad return (dtheta, dphi) def update_label(self, event): dtheta, dphi = self.get_delta(event) self.motion_label.configure(text="<{:8.5f}, {:8.5f}>".format(dtheta, dphi)) def move_tracker(self, event): dtheta, dphi = self.get_delta(event) self.tracker.move(0, dtheta, dphi) logger.info("Moved tracker by ({}, {})".format(dtheta, dphi))
def draw_game_info_grid(self): ''' Draw control and inform part of game to right side of the window. ''' self.info = Canvas(self.root, height=self.h - self.gridh, width=self.w - self.gridw) self.info.pack(side="left") label_stones = Label(self.info, text="Current stones:", font=("Helvetica", 10)) label_stones.grid(row=1, column=0) label_max_time = Label(self.info, text="Max time:", font=("Helvetica", 10)) label_max_time.grid(row=2, column=0) label_scale = Label(self.info, text='Game speed [ms]:', font=("Helvetica", 10), foreground='black') label_scale.grid(row=5, column=0) helv36 = font.Font(family="helvetica", size=16, weight='bold') self.scale_var = IntVar() scale = Scale(self.info, variable=self.scale_var, command=self.sleep_time_change_handler, from_=0, to=1000, resolution=10, width="15", orient=HORIZONTAL, length="225") scale.set(200) scale.grid(row=5, column=1, columnspan=3) self.button = Button(self.info, text="Play", width="20", height="2", command=self.play_button_click_handler) self.button['font'] = helv36 self.button.grid(row=6, column=0, columnspan=4) # labels for num stones, max time of move, etc self.label_player_stones = [-1, -1] self.label_player_max_time = [-1, -1] self.labels_inform = [-1, -1] self.labels_player_name = [-1, -1] self.option_menus = [-1, -1] self.option_menus_vars = [-1, -1] for i in range(2): self.label_player_stones[i] = Label(self.info, text='2', font=("Helvetica", 10), foreground=self.colors[i]) self.label_player_stones[i].grid(row=1, column=2 * (i + 1) - 1, columnspan=2) self.label_player_max_time[i] = Label(self.info, text="%.2f [ms]" % 0.0, font=("Helvetica", 10), foreground=self.colors[i]) self.label_player_max_time[i].grid(row=2, column=2 * (i + 1) - 1, columnspan=2) self.labels_inform[i] = Label(self.info, text='', font=("Helvetica", 10), foreground='black') self.labels_inform[i].grid(row=i + 3, column=0, columnspan=4) self.labels_player_name[i] = Label(self.info, text="Player%d:" % (i), font=("Helvetica", 12), foreground=self.colors[i]) self.labels_player_name[i].grid(row=0, column=2 * i) self.option_menus_vars[i] = StringVar(self.root) self.option_menus_vars[i].set(self.interractivePlayerName) self.option_menus[i] = OptionMenu(self.info, self.option_menus_vars[i], *self.possiblePlayers) self.option_menus[i].grid(row=0, column=2 * i + 1)
class Screen(Observer): def __init__(self, parent, model_x, model_y, bg="white"): self.canvas = Canvas(parent, bg=bg) self.model_x = model_x self.model_y = model_y print("parent", parent.cget("width"), parent.cget("height")) self.showX = True self.showY = True self.frame = Frame(parent) # Signal X self.magnitude_x = Scale(self.frame, length=250, orient="horizontal", name="m_x", label="Magnitude X", sliderlength=20, showvalue=0, from_=0, to=5, tickinterval=25) self.frequency_x = Scale(self.frame, length=250, orient="horizontal", name="f_x", label="Frequency X", sliderlength=20, showvalue=0, from_=0, to=5, tickinterval=25) self.phase_x = Scale(self.frame, length=250, orient="horizontal", name="p_x", label="Phase X", sliderlength=20, showvalue=0, from_=0, to=5, tickinterval=25) # Signal Y self.magnitude_y = Scale(self.frame, length=250, orient="horizontal", name="m_y", label="Magnitude Y", sliderlength=20, showvalue=0, from_=0, to=5, tickinterval=25) self.frequency_y = Scale(self.frame, length=250, orient="horizontal", name="f_y", label="Frequency Y", sliderlength=20, showvalue=0, from_=0, to=5, tickinterval=25) self.phase_y = Scale(self.frame, length=250, orient="horizontal", name="p_y", label="Phase Y", sliderlength=20, showvalue=0, from_=0, to=5, tickinterval=25) self.frame2 = Frame(parent, bg="black") self.varX = IntVar() self.varY = IntVar() self.varXY = IntVar() self.lbl = Label(self.frame2, text="Courbes", fg="black") # Boutons de sélection (X, Y ou X-Y) self.caseX = Checkbutton(self.frame2, text="X", variable=self.varX, command=self.getX) self.caseY = Checkbutton(self.frame2, text="Y", variable=self.varY, command=self.getY) self.caseXY = Checkbutton(self.frame2, text="XY", variable=self.varXY, command=self.getXY) self.caseXY.select() self.wi = self.canvas.cget("width") self.hi = self.canvas.cget("height") self.stepx = 0 self.stepy = 0 # Step x self.step_x = Entry(parent, name="x") # Step y self.step_y = Entry(parent, name="y") def update(self, model): print("View update") if model.getId() == 0: signal = model.get_signal() self.plot_signal(signal) elif model.getId() == 1: signal = model.get_signal() self.plot_signal(signal, "blue") else: raise ("Error") # Signal X def get_magnitude(self, whichOne): if whichOne == 0: return self.magnitude_x elif whichOne == 1: return self.magnitude_y else: raise ("Error") def get_frequency(self, whichOne): if whichOne == 0: return self.frequency_x elif whichOne == 1: return self.frequency_y else: raise ("Error") def get_phase(self, whichOne): if whichOne == 0: return self.phase_x elif whichOne == 1: return self.phase_y else: raise ("Error") def get_step_x(self): return self.step_x def get_step_y(self): return self.step_y def getX(self): print("update_X(self,event)") self.caseY.deselect() self.caseXY.deselect() self.showX = True self.showY = False self.update(self.model_x) if self.canvas.find_withtag("signal_y"): self.canvas.delete("signal_y") def getY(self): print("update_Y(self,event)") self.caseX.deselect() self.caseXY.deselect() self.showX = False self.showY = True self.update(self.model_y) if self.canvas.find_withtag("signal_x"): self.canvas.delete("signal_x") def getXY(self): print("update_XY(self,event)") self.caseX.deselect() self.caseY.deselect() self.showX = True self.showY = True self.update(self.model_x) self.update(self.model_y) def plot_signal(self, signal, color="red"): w, h = self.wi, self.hi width, height = int(w), int(h) if color == "red" and self.showX == True: if self.canvas.find_withtag("signal_x"): self.canvas.delete("signal_x") if signal and len(signal) > 1: plot = [(x * width, height / 2.0 * (y + 1)) for (x, y) in signal] signal_id = self.canvas.create_line(plot, fill=color, smooth=1, width=3, tags="signal_x") elif color == "blue" and self.showY == True: if self.canvas.find_withtag("signal_y"): self.canvas.delete("signal_y") if signal and len(signal) > 1: plot = [(x * width, height / 2.0 * (y + 1)) for (x, y) in signal] signal_id = self.canvas.create_line(plot, fill=color, smooth=1, width=3, tags="signal_y") def grid(self, step_x, step_y): w, h = self.wi, self.hi width, height = int(w), int(h) self.stepx = (width - 10) / step_x * 1. self.stepy = (height - 10) / step_y * 1. for t in range(1, step_x + 2): x = t * self.stepx self.canvas.create_line(x, 0, x, height, tags="grid") #self.canvas.create_line(x,height/2-4,x,height/2+4) for t in range(1, step_y + 2): y = t * self.stepy self.canvas.create_line(0, y, width, y, tags="grid") #self.canvas.create_line(width/2-4,y,width/2+4,y) def resize(self, event): if event: self.wi = event.width self.hi = event.height self.canvas.delete("grid") self.plot_signal(self.model_x.get_signal()) self.plot_signal(self.model_y.get_signal(), "blue") self.grid(25, 25) def packing(self): self.canvas.pack(fill="both", expand=1) self.step_x.pack(expand=1, fill="both") self.step_y.pack(expand=1, fill="both") self.frame.pack(expand=1, fill="both") self.magnitude_x.grid(row=0, column=0) self.magnitude_y.grid(row=0, column=1) self.frequency_x.grid(row=1, column=0) self.frequency_y.grid(row=1, column=1) self.phase_x.grid(row=2, column=0) self.phase_y.grid(row=2, column=1) self.frame2.pack(side="bottom", expand=1) self.lbl.grid(row=0, column=0) self.caseX.grid(row=0, column=1) self.caseY.grid(row=0, column=2) self.caseXY.grid(row=0, column=3)
class ObjectDialog(Dialog): """ A custom dialog that allows the user to configure placing objects in the virtual environment """ def __init__(self, parent, x=None, y=None, populator=None): """ Construct the instance of the object dialog :param parent: The parent tk instance that spawns the dialog """ self._entries = { "x_coordinate" : None, "y_coordinate" : None, "name" : None, "mesh" : None, "scale" : None } self._scale_text = StringVar() self._scale_text.set(str(1)) Dialog.__init__(self, parent, "ObjectBuilder", True, x, y, populator) def body(self, parent): """ Define the custom body of the dialog :param parent: The parent instance of the dialog """ # Define the labels of all of the sub widgets that are to be used Label(parent, text="Name:").grid(row=0, column=0, sticky=W) Label(parent, text="X-Coord:").grid(row=1, column=0, sticky=W) Label(parent, text="Y-Coord:").grid(row=1, column=2, sticky=W) Label(parent, text="Mesh:").grid(row=2, column=0, sticky=W) Label(parent, text="Scale:").grid(row=3, column=0, sticky=W) Label(parent, textvariable=self._scale_text, bg="grey").grid(row=3, column=1, sticky=W) #Define the text entry widgets self._object_name = Label(parent, text=self._entries["name"]) self._object_name.grid(column=1, row=0, sticky=W) self._x_coord = Label(parent, text=self._entries["x_coordinate"]) self._x_coord.grid(column=1, row=1, sticky=W) self._y_coord = Label(parent, text=self._entries["y_coordinate"]) self._y_coord.grid(column=3, row=1, stick=W) self._mesh = Entry(parent, width=15, text=self._entries["mesh"]) if self._entries["mesh"] is None: self._mesh.insert(0, "No mesh loaded") else: self._mesh.insert(0, self._entries["mesh"]) self._mesh.grid(column=1, row=2, columnspan=2, sticky=W) Button(parent, text="Load", width=5, command=self._load_mesh, default=ACTIVE).grid(column=3, row=2) self._scale = Scale(parent, from_=1, to=100, orient=HORIZONTAL, length=140, variable=self._scale_text, showvalue=0) if self._entries["scale"] is not None: self._scale.set(self._entries["scale"]) self._scale_text.set(str(self._entries["scale"])) self._scale.grid(row=3, column=2, columnspan=2, sticky=W) def validate(self): (result, message) = DataValidator.validate(DataStore.EVENT.OBJECT_EDIT, { "name" : self._entries["name"], "mesh" : self._mesh.get() }) if result is not True: tkMessageBox.showerror("Input Error", message) return result def apply(self): self._entries["scale"] = self._scale.get() self._entries["mesh"] = self._mesh.get() def _load_mesh(self): """ Open a file dialog to load a mesh filepath :return: """ Debug.printi("Load Mesh called", Debug.Level.INFO) types = \ [ ("DirectX", "*.x") ] dialog = tkFileDialog.Open(self, filetypes=types) file_path = dialog.show() file_path = self._move_img(file_path) self._mesh.delete(0, END) self._mesh.insert(0, str(file_path)) Debug.printi("Mesh Filepath:" + file_path, Debug.Level.INFO) def _move_img(self, file_path): """ Move the DirectX file to the Data folder automagically :param file_path: The file path of the file to be moved :return: """ try: src = file_path file_name = self._scrub_name(file_path) dest = os.path.dirname(os.path.realpath(__file__)) + "/" + file_name shutil.copy(src, dest) Debug.printi("Moving file " + file_path + " to location " + os.path.dirname(os.path.realpath(__file__)) + "/" + file_name, Debug.Level.INFO) return file_name # eg. src and dest are the same file except shutil.Error as e: print('Error: %s' % e + " " + dest) return file_name # eg. source or destination doesn't exist except IOError as e: print('Error: %s' % e.strerror + " " + dest) return file_name def _scrub_name(self, file_path): """ Scrubs the file name so taht it is /really/long/abs/path/fil.x to Data/file.x """ split = file_path.split("/") f_name = "Data/" + split[-1] return f_name def populate(self, manager): self._entries["x_coordinate"] = manager.x_coordinate self._entries["y_coordinate"] = manager.y_coordinate self._entries["name"] = manager.name self._entries["mesh"] = manager.mesh self._entries["scale"] = manager.scale def auto_populate(self): self._mesh.delete(0, END) self._mesh.insert(0, Defaults.Object.MESH) self._scale.set(Defaults.Object.SCALE)
class ToolBar(Frame): def __init__(self, root, printer, settings, logger, *arg): self.app = root self.printer = printer self.settings = settings self.logger = logger self.app.printing = False self.app.connected = False self.app.paused = False Frame.__init__(self, root, *arg) topBar = Frame(self) topBar.grid(row=1, column=1, columnspan=3, sticky=W) speedBar = Frame(self) speedBar.grid(row=1, column=5, sticky=E) bottomBar = Frame(self) bottomBar.grid(row=2, column=1, columnspan=6, sticky=W+E) self.bPort = Button(topBar, text="Port", width=BWIDTH, command=self.doPort) self.bPort.pack(side=LEFT, padx=2, pady=2) ports = self.scanSerial() self.spPort = Spinbox(topBar, values=ports, state="readonly") self.spPort.pack(side=LEFT, padx=2, pady=2) l = Label(topBar, text=" @ ") l.pack(side=LEFT, padx=2, pady=2) self.spBaud = Spinbox(topBar, values=baudChoices) self.spBaud.pack(side=LEFT, padx=2, pady=2) self.spBaud.delete(0, END) self.spBaud.insert(0, 115200) self.spBaud.config(state="readonly") self.bConnectMode = CM_CONNECT self.bConnect = Button(topBar, text=connectText[CM_CONNECT], width=BWIDTH, command=self.doConnect) self.bConnect.pack(side=LEFT, padx=2, pady=2) if len(ports) == 0: self.bConnect.config(state=DISABLED) else: self.bConnect.config(state=NORMAL) self.bReset = Button(topBar, text="Reset", width=BWIDTH, command=self.doReset, state=DISABLED) self.bReset.pack(side=LEFT, padx=2, pady=2) l = Label(speedBar, text="Speed:", justify=RIGHT) l.grid(row=1, column=1, sticky=E) self._speedJob = None self.currentSpeed = self.app.FeedMultiply self.scSpeed = Scale(speedBar, from_=MINSPEED, to=MAXSPEED, orient=HORIZONTAL, command=self.updateSpeedCommand) self.scSpeed.grid(row=1, column=2) self.scSpeed.set(self.currentSpeed); l = Label(speedBar, text="Fan:", width=10, anchor=E, justify=RIGHT) l.grid(row=1, column=3, sticky=E) self._fanJob = None self.currentFanSpeed = self.app.FanSpeed self.scFan = Scale(speedBar, from_=0, to=255, orient=HORIZONTAL, command=self.updateFanSpeedCommand) self.scFan.grid(row=1, column=4) self.scFan.set(self.currentFanSpeed); if self.settings.speedcommand is not None: self.cbvAssertFan = IntVar() if self.settings.forcefanspeed: self.cbvAssertFan.set(1) else: self.cbvAssertFan.set(0) self.cbAssertFan = Checkbutton(speedBar, text="Force", variable=self.cbvAssertFan, command=self.clickAssertFan) self.cbAssertFan.grid(row=1, column=5) self.bSliceMode = SM_SLICE self.bSlice = Button(bottomBar, text=sliceText[SM_SLICE], width=BWIDTH*2, command=self.doSlice) self.bSlice.pack(side=LEFT, padx=2, pady=2) self.bLoad = Button(bottomBar, text="Load GCode", width=BWIDTH, command=self.doLoad) self.bLoad.pack(side=LEFT, padx=2, pady=2) self.setSliceText() self.bSD = Button(bottomBar, text="SD", width=BWIDTH, command=self.doSD, state=DISABLED) self.bSD.pack(side=LEFT, padx=2, pady=2) self.bPrintMode = PR_PRINT self.bPrint = Button(bottomBar, text=printText[PR_PRINT], width=BWIDTH, command=self.doPrint, state=DISABLED) self.bPrint.pack(side=LEFT, padx=2, pady=2) self.bPauseMode = PM_PAUSE self.bPause = Button(bottomBar, text=pauseText[PM_PAUSE], width=BWIDTH, command=self.doPause, state=DISABLED) self.bPause.pack(side=LEFT, padx=2, pady=2) self.bAbandon = Button(bottomBar, text="Abandon SD Print", width=BWIDTH+8, command=self.doAbandon, state=DISABLED) self.bAbandon.pack(side=LEFT, padx=2, pady=2) self.cbvLiftOnPause = IntVar() if self.settings.liftonpause: self.cbvLiftOnPause.set(1) else: self.cbvLiftOnPause.set(0) self.cbLiftOnPause = Checkbutton(bottomBar, text="Lift Head/Retract on Pause", variable=self.cbvLiftOnPause, command=self.clickLiftOnPause) self.cbLiftOnPause.pack(side=LEFT, padx=2) self.cbvResumeAtPause = IntVar() if self.settings.resumeatpausepoint: self.cbvResumeAtPause.set(1) else: self.cbvResumeAtPause.set(0) self.cbResumeAtPause = Checkbutton(bottomBar, text="Resume print at pause point", variable=self.cbvResumeAtPause, command=self.clickResumeAtPause) self.cbResumeAtPause.pack(side=LEFT, padx=2) def clickAssertFan(self): if self.cbvAssertFan.get() == 1: self.settings.forcefanspeed = True self.settings.setModified() else: self.settings.forcefanspeed = False self.settings.setModified() def clickLiftOnPause(self): if self.cbvLiftOnPause.get() == 1: self.settings.liftonpause = True self.settings.setModified() else: self.settings.liftonpause = False self.settings.setModified() def clickResumeAtPause(self): if self.cbvResumeAtPause.get() == 1: self.settings.resumeatpausepoint = True self.settings.setModified() else: self.settings.resumeatpausepoint = False self.settings.setModified() def setSliceText(self): if self.settings.slicer == SLIC3R: sl = "slic3r:%s" % self.app.slic3r.getProfile() else: sl = "skeinforge:%s" % self.app.skeinforge.getProfile() sliceText[SM_SLICE] = "Slice (%s)" % sl if self.bSliceMode == SM_SLICE: self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) lt = len(sliceText[SM_SLICE])+2 if lt < BWIDTH: lt = BWIDTH self.bSlice.config(text=sliceText[SM_SLICE], width=lt) def updateSpeedCommand(self, *arg): if self._speedJob: self.app.master.after_cancel(self._speedJob) self._speedJob = self.app.master.after(500, self.updateSpeed) def updateSpeed(self, *arg): v = self.scSpeed.get() self.setSpeed(v) def setSpeed(self, v): if v < MINSPEED or v > MAXSPEED: self.logger.logMsg("Attempt to change speed outside of allowable range (%d-%d)" % (MINSPEED, MAXSPEED)) elif int(v) != self.currentSpeed: if self.app.connected: self.currentSpeed = int(v) self.logger.logMsg("changing speed percentage to %d%%" % self.currentSpeed) cmd = "M220 S%d" % self.currentSpeed self.printer.send_now(cmd) else: self.logger.logMsg("Printer is off-line") self.scSpeed.set(self.currentSpeed) def updateFanSpeedCommand(self, *arg): if self._fanJob: self.app.master.after_cancel(self._fanJob) self._fanJob = self.app.master.after(500, self.updateFanSpeed) def updateFanSpeed(self, *arg): v = self.scFan.get() self.setFanSpeed(v) self.app.FanSpeed = v def forceFanSpeed(self, v): self.currentFanSpeed = -1 self.setFanSpeed(v) def setFanSpeed(self, v): if int(v) != self.currentFanSpeed: if self.app.connected: self.currentFanSpeed = int(v) cmd = "M106 S%d" % self.currentFanSpeed self.printer.send_now(cmd) else: self.logger.logMsg("Printer is off-line") self.scFan.set(self.currentFanSpeed) def syncSpeeds(self): self.currentSpeed = self.app.FeedMultiply self.scSpeed.set(self.currentSpeed) self.currentFanSpeed = self.app.FanSpeed self.scFan.set(self.currentFanSpeed) def initializeToolbar(self): self.bReset.config(state=DISABLED) self.bSliceMode = SM_SLICE self.bSlice.config(text=sliceText[SM_SLICE]) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) if not self.app.sdprinting and not self.app.sdpaused: self.bPrintMode = PR_PRINT self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPauseMode = PM_PAUSE self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) def setSDPrint(self): self.bPause.config(text=pauseText[PM_PAUSE], state=NORMAL) self.bPauseMode = PM_PAUSE self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT self.bAbandon.config(state=NORMAL) def clearSDPrint(self): self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) self.bPauseMode = PM_PAUSE self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT self.bAbandon.config(state=DISABLED) self.checkAllowPrint() def setCancelMode(self): self.bSliceMode = SM_CANCEL self.bSlice.config(text=sliceText[SM_CANCEL], width=BWIDTH) self.bLoad.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.allowSliceMenu(False) def setLoading(self, flag): if flag: self.bLoad.config(state=DISABLED) self.bSlice.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.allowSliceMenu(False) else: self.bLoad.config(state=NORMAL) self.bSlice.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) def clearCancelMode(self): self.bSliceMode = SM_SLICE lt = len(sliceText[SM_SLICE])+2 if lt < BWIDTH: lt = BWIDTH self.bSlice.config(text=sliceText[SM_SLICE], width=lt) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) def doConnect(self): if self.bConnectMode == CM_CONNECT: port = self.spPort.get() baud = int(self.spBaud.get()) self.printer.onlinecb = self.onlinecb try: self.printer.connect(port, baud) except SerialException: self.logger.logMsg("Unable to open printer port %s" % port) else: if self.app.printing: self.logger.logMsg("Please wait until printing has finished or is paused") else: self.printer.disconnect() self.printer.onlinecb = None self.app.printerConnected(False) # self.app.connected = False self.bConnectMode = CM_CONNECT self.bConnect.config(text=connectText[CM_CONNECT]) self.bReset.config(state=DISABLED) self.bSD.config(state=DISABLED) if self.app.paused: self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) self.bPauseMode = PM_PAUSE self.app.printing = False self.app.paused = False def doReset(self): if tkMessageBox.askyesno("Reset?", "Are you sure you want to reset the printer?", parent=self.app): self.printer.reset() self.printer.printing = 0 self.app.printing = False self.bSlice.config(state=NORMAL) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) self.bPrintMode = PR_PRINT self.bPrint.config(text=printText[PR_PRINT], state=NORMAL) if self.app.paused: self.printer.paused = 0 self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) self.bPauseMode = PM_PAUSE self.app.paused = False def onlinecb(self): self.logger.logMsg("Printer is on-line") self.app.printerConnected(True) # self.app.connected = True self.bConnectMode = CM_DISCONNECT self.bConnect.config(text=connectText[CM_DISCONNECT]) self.bReset.config(state=NORMAL) self.bSD.config(state=NORMAL) self.checkAllowPrint() def checkAllowPrint(self): if self.app.connected and len(self.app.gcode) != 0 and not self.app.printing and not self.app.sdprinting: self.bPrint.config(text=printText[PR_PRINT], state=NORMAL) self.bPrintMode = PR_PRINT def printComplete(self): self.app.endTime = time.time() self.app.elapsedTime = self.app.endTime - self.app.startTime self.logger.logMsg("Printing completed at %s" % time.strftime('%H:%M:%S', time.localtime())) self.logger.logMsg("Total elapsed time: %s" % formatElapsed(self.app.elapsedTime)) self.bPrintMode = PR_PRINT self.bPrint.config(text=printText[PR_PRINT], state=NORMAL) self.bPauseMode = PM_PAUSE self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) self.app.printing = False self.bSlice.config(state=NORMAL) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) self.app.paused = False self.app.gc.updatePrintProgress(0) self.app.closeAllReports() if self.settings.endprintmacro is not None: self.app.doMacro(name=self.settings.endprintmacro, silent=True) def doPause(self): if self.bPauseMode == PM_PAUSE: if self.app.printing: self.app.paused = True self.printer.pause() elif self.app.sdprinting: self.app.sdpaused = True self.printer.send_now("M25") self.app.sdprinting = False self.bPause.config(text=pauseText[PM_RESUME]) self.bPauseMode = PM_RESUME self.bPrint.config(text=printText[PR_RESTART], state=NORMAL) self.bPrintMode = PR_RESTART else: if self.app.sdpaused: self.printer.send_now("M24") self.app.sdpaused = False self.app.sdprinting = True self.bPause.config(text=pauseText[PM_PAUSE]) self.bPauseMode = PM_PAUSE self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT else: self.hitPrint = False if self.settings.resumeatpausepoint: self.printer.send_now("G1 X%s Y%s F%s" % (self.app.pausePoint[XAxis], self.app.pausePoint[YAxis], self.settings.xyfeed)) self.printer.send_now("G1 Z%s F%s" % (self.app.pausePoint[ZAxis], self.settings.zfeed)) self.printer.send_now("G92 E%s" % self.app.pausePoint[EAxis]) self.printer.send_now("G1 F%s" % self.settings.xyfeed) self.printer.startcb = self.startcb self.printer.resume() def doAbandon(self): self.printer.send_now("M25") self.app.sdpaused = False self.app.sdprinting = False self.clearSDPrint() def doSlice(self): if self.bSliceMode == SM_SLICE: self.bLoad.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.openSTLFile() else: self.app.slicerCancel = True self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) def doLoad(self): self.app.openGCodeFile() def doPrint(self): if self.app.sdpaused: self.printer.send_now("M26 S0") self.printer.send_now("M24") self.app.sdpaused = False self.app.sdprinting = True self.bPause.config(text=pauseText[PM_PAUSE]) self.bPauseMode = PM_PAUSE self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT else: if len(self.app.gcode) == 0: self.logger.logMsg("Nothing to print") else: #if not self.app.paused: self.app.gc.updatePrintProgress(0, restart=True) self.bPauseMode = PM_PAUSE self.bPause.config(text=pauseText[PM_PAUSE], state=NORMAL) self.hitPrint = True self.printer.startcb = self.startcb self.printer.startprint(self.app.gcode) def startcb(self): self.printer.startcb = None if not self.app.paused: self.app.startTime = time.time() self.logger.logMsg("Printing Started at %s" % time.strftime('%H:%M:%S', time.localtime(self.app.startTime))) self.app.printing = True self.bSlice.config(state=DISABLED) self.bLoad.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.allowSliceMenu(False) self.bPrint.config(text=printText[PR_RESTART], state=DISABLED) self.bPrintMode = PR_RESTART self.printer.endcb = self.endcb else: if self.hitPrint: self.app.startTime = time.time() self.logger.logMsg("Printing restarted at %s" % time.strftime('%H:%M:%S', time.localtime())) else: self.logger.logMsg("Printing resumed at %s" % time.strftime('%H:%M:%S', time.localtime())) self.bPause.config(text=pauseText[PM_PAUSE]) self.bPauseMode = PM_PAUSE self.app.printing = True self.bSlice.config(state=DISABLED) self.bLoad.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.allowSliceMenu(False) self.app.paused = False self.bPrint.config(state=DISABLED) self.printer.endcb = self.endcb def endcb(self): self.printer.endcb = None if not self.app.paused: self.printComplete() if self.app.sduploading: self.app.sduploading = False self.printer.send_now("M29") else: self.app.event_generate(MWM_REQUESTPOSITIONREPORT) # record the current printer position and how many intervals were willing to wait fo the response self.maxWait114 = MAXWAIT114; self.waitPos = self.printer.queueindex self.check114Response() def check114Response(self) : # tick off 1 interval, and make sure we haven't either received the report or we've waited max intervals self.maxWait114 -= 1 if self.app.m114count == 0 or self.maxWait114 <= 0: # one way or the other we're done waiting here if self.maxWait114 <= 0: self.app.m114count = 0 self.logger.logMsg("Location report not received - proceeding") self.app.pausePoint[XAxis] = self.app.location[XAxis] self.app.pausePoint[YAxis] = self.app.location[YAxis] self.app.pausePoint[ZAxis] = self.app.location[ZAxis] self.app.pausePoint[EAxis] = self.app.location[EAxis] self.logger.logMsg("Pause location: X:%f Y:%f Z:%f E:%f" % (self.app.pausePoint[XAxis], self.app.pausePoint[YAxis], self.app.pausePoint[ZAxis], self.app.pausePoint[EAxis])) if self.settings.liftonpause: self.printer.send_now("G1 E%s F%s" % (self.app.pausePoint[EAxis]-2, self.settings.efeed)) self.printer.send_now("G1 Z%s F%s" % (self.app.pausePoint[ZAxis]+10, self.settings.zfeed)) self.bPause.config(text=pauseText[PM_RESUME]) self.bPauseMode = PM_RESUME self.app.printing = False self.bSlice.config(state=NORMAL) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) if self.app.sduploading: self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT else: self.bPrint.config(text=printText[PR_RESTART], state=NORMAL) self.bPrintMode = PR_RESTART else: # we still are waiting for the report, but reset everything if the printer is still moving if self.waitPos != self.printer.queueindex: self.waitPos = self.printer.queueindex self.maxWait114 = MAXWAIT114 self.app.master.after(500, self.check114Response) def doPort(self): l = self.scanSerial() self.spPort.config(values=l) if len(l) == 0: self.bConnect.config(state=DISABLED) else: self.bConnect.config(state=NORMAL) def scanSerial(self): """scan for available ports. return a list of device names.""" baselist=[] if os.name=="nt": try: key=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM") i=0 while(1): baselist+=[_winreg.EnumValue(key,i)[1]] i+=1 except: pass return baselist+glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyACM*') +glob.glob("/dev/tty.*")+glob.glob("/dev/cu.*")+glob.glob("/dev/rfcomm*") def doSD(self): self.app.doSD()
def __init__(self, root): """ Create a DSP interface instance. :param root: is the Tk() interface where the DPS will be drawedstring with the prot name i.e. /dev/ttyUSB0 or COM5 for Windows :returns: a new instance of DPS graphical interface """ self.root = root root.title("DPS power supplier interface") root.protocol("WM_DELETE_WINDOW", self.wnwcmdclose) self.dps = None self.poller = None self.waver = None self.strtme = time() self.dpsfwave = None self.maxoutv = 5 self.maxoutc = 5 menubar = Menu(root) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Exit", command=self.wnwcmdclose) menubar.add_cascade(label="File", menu=filemenu) scopemenu = Menu(menubar, tearoff=0) scopemenu.add_command(label="Load sampled points...", command=self.mnucmdloadsmppts) scopemenu.add_command(label="Save sampled points as...", command=self.mnucmdsavesmppts) menubar.add_cascade(label="Scope", menu=scopemenu) wavemenu = Menu(menubar, tearoff=0) wavemenu.add_command(label="New wave", command=self.mnucmdnewwve) wavemenu.add_command(label="Load wave...", command=self.mnucmdloadwve) wavemenu.add_command(label="Edit wave...", command=self.mnucmdedtwve) wavemenu.add_command(label="Save wave as...", command=self.mnucmdsavewve) menubar.add_cascade(label="Wave", menu=wavemenu) memmenu = Menu(menubar, tearoff=0) memmenu.add_command(label="Edit memories...", command=self.mnucmdedtmem) menubar.add_cascade(label="Memory", menu=memmenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label="Help...", command=self.mnucmdhelp) helpmenu.add_command(label="About...", command=self.mnucmdabout) menubar.add_cascade(label="Help", menu=helpmenu) root.config(menu=menubar) row = 0 col = 0 rowspan = 1 colspan = 1 insertlabelrow(root, row, col, ("Serial: ", None, "Addr,Baud: "), E) col += colspan self.svardpsport = StringVar() self.svardpsport.set('/dev/ttyUSB0') self.entryserport = Entry(root, textvariable=self.svardpsport, width=ENTRYWIDTH, justify='right') self.entryserport.grid(row=row, column=col, sticky=W) col += colspan col += colspan self.svardpsaddbrt = StringVar() self.svardpsaddbrt.set('1, 9600') self.entrydpsadd = Entry(root, textvariable=self.svardpsaddbrt, width=ENTRYWIDTH, justify='right') self.entrydpsadd.grid(row=row, column=col, sticky=W) col += colspan colspan = 2 self.ivarconctd = IntVar() self.ivarconctd.set(0) Checkbutton(root, variable=self.ivarconctd, text='Connect', command=self.butcmdconnect).grid(row=row, column=col, columnspan=colspan, sticky=E + W) row += rowspan col = 0 colspan = 1 Separator(root, orient='horizontal').grid(row=row, columnspan=8, sticky=E + W, pady=8) row += rowspan rowspan = 1 colspan = 2 col = 0 self.ivarbrghtnes = IntVar() s = Scale(root, label='Brightness', variable=self.ivarbrghtnes, from_=0, to=5, resolution=1, orient="horizontal") s.bind("<ButtonRelease-1>", self.sclbndbrghtnss) s.grid(row=row, column=col, columnspan=colspan, sticky=E + W) col += colspan colspan = 1 Label(root, text="Model: ").grid(row=row, column=col, sticky=E) col += colspan self.ivarmodel = IntVar() Entry(root, textvariable=self.ivarmodel, state="readonly", width=ENTRYWIDTH, justify='right').grid(row=row, column=col, sticky=W) col += colspan colspan = 2 self.ivarsetmem = IntVar() s = Scale(root, label='Mem Recall', variable=self.ivarsetmem, from_=1, to=9, resolution=1, orient="horizontal") s.bind("<ButtonRelease-1>", self.sclbndmemory) s.grid(row=row, column=col, columnspan=colspan, sticky=E + W) row += rowspan colspan = 1 col = 0 insertlabelrow( root, row, col, (("Vinp [V]: ", VCOL), None, "Out Mode: ", None, "Protection: "), E) self.dvarvinp = DoubleVar() self.svarwrmde = StringVar() self.setworkmode(0) self.svarprot = StringVar() self.setprotection(0) insertentryrow( root, row, col, (None, self.dvarvinp, None, self.svarwrmde, None, self.svarprot), 'right', W, 'readonly') colspan = 1 row += rowspan col = 0 insertlabelrow(root, row, col, (("Vmax [V]: ", VCOL), None, ("Cmax [A]: ", CCOL), None, ("Pmax [W]: ", PCOL)), E) self.dvarvmaxm0 = DoubleVar() self.dvarcmaxm0 = DoubleVar() self.dvarpmaxm0 = DoubleVar() entries = insertentryrow(root, row, col, (None, self.dvarvmaxm0, None, self.dvarcmaxm0, None, self.dvarpmaxm0), 'right', W) for e, f in zip(entries, (self.entbndvmax, self.entbndcmax, self.entbndpmax)): e.bind('<FocusOut>', f) e.bind('<Return>', f) row += rowspan col = 0 insertlabelrow(root, row, col, (("Vout [V]: ", VCOL), None, ("Cout [A]: ", CCOL), None, ("Pout [W]: ", PCOL)), E) self.dvarvout = DoubleVar() self.dvarcout = DoubleVar() self.dvarpout = DoubleVar() insertentryrow( root, row, col, (None, self.dvarvout, None, self.dvarcout, None, self.dvarpout), 'right', W, 'readonly') row += rowspan col = 0 self.scope = Scope(root, [], row, col) row += 9 col = 4 Label(root, text="Rte[s/Sa]: ").grid(row=row, column=col, sticky=E) col += colspan self.dvarsecsmp = DoubleVar() self.dvarsecsmp.set(self.scope.sampletime()) e = Entry(root, textvariable=self.dvarsecsmp, width=ENTRYWIDTH, justify='right').grid(row=row, column=col, sticky=W) row += rowspan col = 0 colspan = 2 self.ivaracquire = IntVar() self.ivaracquire.set(0) Checkbutton(root, variable=self.ivaracquire, text='Run Acquisition', command=self.butcmdacquire).grid(row=row, column=col, columnspan=2, sticky=E + W) col += colspan self.ivarkeylock = IntVar() self.ivarkeylock.set(0) Checkbutton(root, variable=self.ivarkeylock, text="Key Lock", command=self.butcmdkeylock).grid(row=row, column=col, sticky=E + W, columnspan=colspan) col += colspan self.ivaroutenab = IntVar() self.ivaroutenab.set(0) Checkbutton(root, variable=self.ivaroutenab, text="Output Enable", command=self.butcmdoutenable).grid(row=row, column=col, sticky=E + W, columnspan=colspan) row += rowspan col = 0 rowspan = 1 colspan = 3 self.dvarvscale = DoubleVar() self.voltscale = Scale(root, label='Vset [V]', foreground=VCOL, variable=self.dvarvscale, from_=0, to=self.maxoutv, resolution=1, orient="horizontal") #, label='Vset[V]' self.voltscale.bind("<ButtonRelease-1>", self.sclbndvolt) self.voltscale.grid(row=row, column=col, columnspan=colspan, sticky=E + W) col += colspan self.dvarcscale = DoubleVar() self.curntscale = Scale(root, label='Cset[A]', foreground=CCOL, variable=self.dvarcscale, from_=0, to=self.maxoutc, resolution=1, orient="horizontal") #,label='Cset[A]' self.curntscale.bind("<ButtonRelease-1>", self.sclbndcrnt) self.curntscale.grid(row=row, column=col, columnspan=colspan, sticky=E + W) row += rowspan col = 0 self.dvarvscalef = DoubleVar() sc = Scale(root, foreground=VCOL, variable=self.dvarvscalef, from_=0, to=0.99, resolution=0.01, orient="horizontal") sc.bind("<ButtonRelease-1>", self.sclbndvolt) sc.grid(row=row, column=col, columnspan=colspan, sticky=E + W) col += colspan self.dvarcscalef = DoubleVar() sc = Scale(root, foreground=CCOL, variable=self.dvarcscalef, from_=0, to=0.99, resolution=0.01, orient="horizontal") sc.bind("<ButtonRelease-1>", self.sclbndcrnt) sc.grid(row=row, column=col, columnspan=colspan, sticky=E + W) row += rowspan col = 0 colspan = 1 Separator(root, orient='horizontal').grid(row=row, columnspan=6, sticky=E + W, pady=8) row += rowspan colspan = 1 col = 0 Label(root, text="Waveform: ").grid(row=row, column=col, sticky=E) col += colspan colspan = 2 self.svarwave = StringVar() Entry(root, textvariable=self.svarwave, width=ENTRYWIDTH, justify='right', state='readonly').grid(row=row, column=col, columnspan=colspan, sticky=E + W) col += colspan colspan = 1 self.ivarplaywv = IntVar() self.ivarplaywv.set(0) Checkbutton(root, variable=self.ivarplaywv, text='Play', command=self.butcmdplaywave).grid(row=row, column=col, sticky=E + W) col += colspan self.ivarpausewv = IntVar() self.ivarpausewv.set(0) Checkbutton(root, variable=self.ivarpausewv, text='Pause', command=self.butcmdpausewave).grid(row=row, column=col, sticky=E + W) col += colspan self.ivarloopwv = IntVar() self.ivarloopwv.set(0) Checkbutton(root, variable=self.ivarloopwv, text='Loop').grid(row=row, column=col, sticky=E + W) self.scope.update() self.scope.redraw()
class PVapplicaton(Frame): """ classdocs """ def __init__(self, master=None): """ Constructor """ Frame.__init__(self, master, name='pvApplication', bg='black', padx=5, pady=5) # set black background, pad sides with 15 points, top/bottom 5 points # fill=BOTH fills in padding with background color # w/o fill=BOTH padding is default color # side=TOP is the default self.pack(fill=BOTH) master.resizable(False, False) # not resizable in x or y master.title(PVAPP_TXT) # set title bar of master (a.k.a. root) master.protocol("WM_DELETE_WINDOW", self._quit) # close window to quit self.validationConstants = self.readJSON('validationConstants') self.messagetext = self.readJSON('messagetext' + '.' + LANGUAGE) MAX_STRINGS = self.validationConstants["pvapplication"]["numStrs"] MAX_MODULES = self.validationConstants["pvapplication"]["numMods"] MAX_SUNS = self.validationConstants["pvapplication"]["sysEe"] CAPTION_FONT = nametofont('TkCaptionFont') # font for titles # PVsystem pvSys = self.pvSys = PVsystem() # variables numStrs = self.numStrs = IntVar(self, NUMBERSTRS, 'numStrs') numMods = self.numMods = IntVar(self, NUMBERMODS, 'numMods') numCells = self.numCells = IntVar(self, NUMBERCELLS, 'numCells') txtIsys = self.txtIsys = DoubleVar(self, name='txtIsys') txtVsys = self.txtVsys = DoubleVar(self, name='txtVsys') txtPsys = self.txtPsys = DoubleVar(self, name='txtPsys') txtImp = self.txtImp = StringVar(self, name='txtImp') txtVmp = self.txtVmp = StringVar(self, name='txtVmp') txtPmp = self.txtPmp = StringVar(self, name='txtPmp') txtIsc = self.txtIsc = StringVar(self, name='txtIsc') txtVoc = self.txtVoc = StringVar(self, name='txtVoc') txtFF = self.txtFF = StringVar(self, name='txtFF') txtEff = self.txtEff = StringVar(self, name='txtEff') sysEe = self.sysEe = DoubleVar(self, 1, name='sysEe') txtImp.set("{:7.3f}".format(self.pvSys.Imp)) # [A] txtVmp.set("{:7.3f}".format(self.pvSys.Vmp)) # [V] txtPmp.set("{:7.3f}".format(self.pvSys.Pmp / 1000)) # [kW] txtIsc.set("{:7.3f}".format(self.pvSys.Isc)) # [A] txtVoc.set("{:7.3f}".format(self.pvSys.Voc)) # [V] txtFF.set("{:7.3f}".format(self.pvSys.FF * 100)) # [%] txtEff.set("{:7.3f}".format(self.pvSys.eff * 100)) # [%] self.msgtext = StringVar(self, READY_MSG, 'msgtext') # must register vcmd and invcmd as Tcl functions vcmd = (self.register(self.validateWidget), '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W') invcmd = (self.register(self.invalidWidget), '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W') # SP logo # convert image to tk-compatible format (.gif, .pgm, or .ppm) self.SPlogo = ImageTk.PhotoImage(Image.open(SPLOGO)) # bg='black' fills extra space with black # anchor=W aligns photoimage on left side, NW is no different # padding is ignored by images, use borderwidth Label(self, image=self.SPlogo, borderwidth=5, bg='black', anchor=W).pack(fill=BOTH) # fill=BOTH expands the photoimage to fill parent frame # w/o fill=BOTH photoimage is centered in frame even with anchor=W # Intro text introText = 'PVmismatch calculates I-V and P-V curves as well as the' introText += ' max power point (MPP) for any sized system.\nSet the' introText += ' number of strings in the system, the number of modules' introText += ' per string and the number cells per module.' # anchor=W aligns message on left side, NW is no different # fg='white' sets text color to white, default is black, so it doesn't # show on black background # default aspect is 150%, about as wide as high, or set width>0 Message(self, text=introText, width=750, bg='black', fg='white', anchor=W).pack(fill=BOTH) # fill=BOTH expands the message to fill parent frame # w/o fill=BOTH message is centered in frame even with anchor=W # PVsystem frame pvSysFrame = self.pvSysFrame = Frame(master, name='pvSysFrame') # fill=BOTH keeps widgets in frame on left when window is resized pvSysFrame.pack(fill=BOTH) # PVsystem matplotlib figure canvas self.pvSysPlotFrame = Frame(pvSysFrame, name='pvSysPlotFrame') pvSysPlotFrame = self.pvSysPlotFrame pvSysPlotFrame.pack(side=RIGHT) pvSysPlot = self.pvSysPlot = pvSys.plotSys() self.pvSysFigCanvas = FigureCanvasTkAgg(pvSysPlot, master=pvSysPlotFrame, resize_callback=None) pvSysFigCanvas = self.pvSysFigCanvas pvSysFigCanvas.get_tk_widget()._name = 'pvSysFigCanvas' # IGNORE:W0212 pvSysFigCanvas.show() # NB: FigureCanvasTkAgg._tkcanvas is FigureCanvasTkAgg.get_tk_widget() pvSysFigCanvas.get_tk_widget().pack(fill=BOTH) pvSysToolbar = NavigationToolbar2TkAgg(pvSysFigCanvas, pvSysPlotFrame) pvSysToolbar.update() pvSysToolbar.pack(fill=BOTH) # PVsystem data frame pvSysDataFrame = self.pvSysDataFrame = Frame(pvSysFrame, name='pvSysDataFrame') pvSysDataFrame.pack(side=LEFT) _row = 0 Label(pvSysDataFrame, text='PVsystem', font=CAPTION_FONT).grid(row=_row, columnspan=3, sticky=W) # number of strings _row += 1 # row 1 Label(pvSysDataFrame, text='Number of Strings').grid(row=_row, columnspan=2, sticky=W) # use textVar to set number of strings from LOAD, RESET or default spinboxCnf = { 'name': 'numStrSpinbox', 'from_': 1, 'to': MAX_STRINGS, 'textvariable': numStrs, 'width': 5, 'validate': 'all', 'validatecommand': vcmd, 'invalidcommand': invcmd, 'command': self.updatePVsys } self.numStrSpinbox = Spinbox(pvSysDataFrame, cnf=spinboxCnf) self.numStrSpinbox.bind("<Return>", self.keyBinding) self.numStrSpinbox.grid(row=_row, column=2) # number of modules _row += 1 # row 2 Label(pvSysDataFrame, text='Number of Modules').grid(row=_row, columnspan=2, sticky=W) # number of modules spinbox spinboxCnf = { 'name': 'numModSpinbox', 'from_': 1, 'to': MAX_MODULES, 'textvariable': numMods, 'width': 5, 'validate': 'all', 'validatecommand': vcmd, 'invalidcommand': invcmd, 'command': self.updatePVsys } self.numModSpinbox = Spinbox(pvSysDataFrame, cnf=spinboxCnf) self.numModSpinbox.bind("<Return>", self.keyBinding) self.numModSpinbox.grid(row=_row, column=2) # number of cells _row += 1 # row 3 Label(pvSysDataFrame, text='Number of Cells').grid(row=_row, columnspan=2, sticky=W) # http://www.logilab.org/card/pylintfeatures#basic-checker # pylint: disable = W0142 self.numCellOption = OptionMenu(pvSysDataFrame, numCells, *MODSIZES, command=self.updatePVsys) # pylint: enable = W0142 self.numCellOption._name = 'numCellOption' # IGNORE:W0212 self.numCellOption.grid(row=_row, column=2) # Advanced Configuration button _row += 1 # row 14 buttonCnf = { 'name': 'advCnfButton', 'text': 'Advanced Configuration', 'command': self.startAdvCnf_tk } pvStrButton = self.pvStrButton = Button(pvSysDataFrame, buttonCnf) pvStrButton.grid(row=_row, columnspan=3, sticky=(E + W)) # slider to explore IV curves _row += 1 # row 4, 5 & 6 self.pvSysScale = Scale(pvSysDataFrame, orient=HORIZONTAL, label='I-V Curve', font=CAPTION_FONT, command=self.getIV, showvalue=False, from_=0, to=(pvSys.pvconst.npts - 1)) self.pvSysScale.grid(row=_row, columnspan=3, sticky=(E + W)) # Isys Label(pvSysDataFrame, text='Isys [A]').grid(row=(_row + 1)) self.pvIsys = Entry(pvSysDataFrame, textvariable=txtIsys, width=7) self.pvIsys.grid(row=(_row + 2)) # Vsys Label(pvSysDataFrame, text='Vsys [V]').grid(row=(_row + 1), column=1) self.pvVsys = Entry(pvSysDataFrame, textvariable=txtVsys, width=7) self.pvVsys.grid(row=(_row + 2), column=1) # Psys Label(pvSysDataFrame, text='Psys [kW]').grid(row=(_row + 1), column=2) self.pvPsys = Entry(pvSysDataFrame, textvariable=txtPsys, width=7) self.pvPsys.grid(row=(_row + 2), column=2) # Imp, Vmp & Pmp _row += 3 # row 7, 8, 9, 10, 11 & 12 Label(pvSysDataFrame, text='I-V Characteristics', font=CAPTION_FONT).grid(row=_row, columnspan=3, sticky=W) Label(pvSysDataFrame, text='Imp [A]').grid(row=(_row + 1)) Label(pvSysDataFrame, text='Vmp [V]').grid(row=(_row + 1), column=1) Label(pvSysDataFrame, text='Pmp [kW]').grid(row=(_row + 1), column=2) self.pvImp = Entry(pvSysDataFrame, textvariable=txtImp, width=7, state='readonly') self.pvImp.grid(row=(_row + 2)) self.pvVmp = Entry(pvSysDataFrame, textvariable=txtVmp, width=7, state='readonly') self.pvVmp.grid(row=(_row + 2), column=1) self.pvPmp = Entry(pvSysDataFrame, textvariable=txtPmp, width=7, state='readonly') self.pvPmp.grid(row=(_row + 2), column=2) # Isc, Voc & FF Label(pvSysDataFrame, text='Isc [A]').grid(row=(_row + 3)) Label(pvSysDataFrame, text='Voc [V]').grid(row=(_row + 3), column=1) Label(pvSysDataFrame, text='FF [%]').grid(row=(_row + 3), column=2) self.pvIsc = Entry(pvSysDataFrame, textvariable=txtIsc, width=7, state='readonly') self.pvIsc.grid(row=(_row + 4)) self.pvVoc = Entry(pvSysDataFrame, textvariable=txtVoc, width=7, state='readonly') self.pvVoc.grid(row=(_row + 4), column=1) self.pvFF = Entry(pvSysDataFrame, textvariable=txtFF, width=7, state='readonly') self.pvFF.grid(row=(_row + 4), column=2) Label(pvSysDataFrame, text='Efficiency [%]').grid(row=(_row + 5), columnspan=2) self.pvEff = Entry(pvSysDataFrame, textvariable=txtEff, width=7, state='readonly') self.pvEff.grid(row=(_row + 5), column=2) # set suns _row += 6 # row 13 Label(pvSysDataFrame, text='Irradiance [suns]', font=CAPTION_FONT).grid(row=_row, columnspan=2, sticky=W) # number of modules spinbox spinboxCnf = { 'name': 'sunSpinbox', 'from_': 0.2, 'to': MAX_SUNS, 'increment': 0.1, 'textvariable': sysEe, 'width': 5, 'validate': 'all', 'validatecommand': vcmd, 'invalidcommand': invcmd, 'command': self.updatePVsys } self.sunSpinbox = Spinbox(pvSysDataFrame, cnf=spinboxCnf) self.sunSpinbox.bind("<Return>", self.keyBinding) self.sunSpinbox.grid(row=_row, column=2) # PVstring button _row += 1 # row 14 buttonCnf = { 'name': 'pvStrButton', 'text': 'PVstring', 'command': self.startPVstring_tk } pvStrButton = self.pvStrButton = Button(pvSysDataFrame, buttonCnf) pvStrButton.grid(row=_row, columnspan=3, sticky=(E + W)) # toolbar toolbar = self.toolbarframe = Frame(master, name='toolbar') toolbar.pack(fill=BOTH) self.QUIT = Button(toolbar, text='Quit', command=self._quit) self.QUIT.pack(side=RIGHT) self.SAVE = Button(toolbar, text='Save', command=self._save) self.SAVE.pack(side=RIGHT) self.LOAD = Button(toolbar, text='Load', command=self._load) self.LOAD.pack(side=RIGHT) self.RESET = Button(toolbar, text='Reset', command=self._reset) self.RESET.pack(side=RIGHT) self.UPDATE = Button(toolbar, text='Update', command=self._update) self.UPDATE.pack(side=RIGHT) self.HELP = Button(toolbar, text='Help', command=self._help) self.HELP.pack(side=RIGHT) self.MESSAGE = Message(toolbar, textvariable=self.msgtext, width=500, fg='red') self.MESSAGE.pack(side=LEFT) # Validation substitutions # %d Type of action: 1 for insert, 0 for delete, or -1 for focus, forced or # textvariable validation. # %i Index of char string to be inserted/deleted, if any, otherwise -1. # %P The value of the spinbox should edition occur. If you are configuring # the spinbox widget to have a new textvariable, this will be the value # of that textvariable. # %s The current value of spinbox before edition. # %S The text string being inserted/deleted, if any. Otherwise it is an # empty string. # %v The type of validation currently set. # %V The type of validation that triggered the callback (key, focusin, # focusout, forced). # %W The name of the spinbox widget. # TODO: Fix these functions so that delete and overwrite work def validateWidget(self, *args): # W = Tkinter.W = 'w' is already used, so use W_ instead (d, i, P, s, S, v, V, W_) = args # @UnusedVariable # IGNORE:W0612 logging.debug( "OnValidate: d={}, i={}, P={}, s={}, S={}, v={}, V={}, W={}". format(*args)) if W_ == ".pvSysFrame.pvSysDataFrame.numStrSpinbox": valType = INTEGERS valTest = lambda val: int(val) # IGNORE:W0108 elif W_ == ".pvSysFrame.pvSysDataFrame.numModSpinbox": valType = INTEGERS valTest = lambda val: int(val) # IGNORE:W0108 elif W_ == ".pvSysFrame.pvSysDataFrame.sunSpinbox": valType = FLOATS valTest = lambda val: float(val) # IGNORE:W0108 else: return False w = self.nametowidget(W_) w.config(validate=v) if S in valType: try: valTest(P) except ValueError: return False return True else: return False def invalidWidget(self, *args): (d, i, P, s, S, v, V, W_) = args # @UnusedVariable # IGNORE:W0612 logging.debug( "OnInvalid: d={}, i={}, P={}, s={}, S={}, v={}, V={}, W={}".format( *args)) if W_ == ".pvSysFrame.pvSysDataFrame.numStrSpinbox": errText = 'Invalid number of strings!' elif W_ == ".pvSysFrame.pvSysDataFrame.numModSpinbox": errText = 'Invalid number of modules!' elif W_ == ".pvSysFrame.pvSysDataFrame.sunSpinbox": errText = 'Invalid irradiance!' else: errText = 'Unknown widget!' w = self.nametowidget(W_) w.config(validate=v) self.msgtext.set(errText) self.bell() def getIV(self, *args): logging.debug('args:\n\t%r', args) x = np.float64(float(args[0]) / self.pvSys.pvconst.npts / 2.) xp = np.concatenate( (self.pvSys.pvconst.negpts, self.pvSys.pvconst.pts), axis=0).flatten() Vsys = np.interp(x, xp, self.pvSys.Vsys) Isys = np.interp(x, xp, self.pvSys.Isys) Psys = Vsys * Isys / 1000 self.txtVsys.set("{:7.3f}".format(Vsys)) self.txtIsys.set("{:7.3f}".format(Isys)) self.txtPsys.set("{:7.3f}".format(Psys)) def startPVstring_tk(self): top = Toplevel() app = PVstring_tk(self, top) app.mainloop() # please destroy me or I'll continue to run in background top.destroy() def startAdvCnf_tk(self): """ open advnaced config window """ top = Toplevel(name='advCnfTop') app = AdvCnf_tk(self, top) app.mainloop() # please destroy me or I'll continue to run in background top.destroy() def keyBinding(self, event): logging.debug('event widget:\n\t%r', event.widget) logging.debug('event widget get:\n\t%r', event.widget.get()) self.updatePVsys() def updatePVsys(self, *args, **kwargs): logging.debug('args:\n\t%r', args) logging.debug('kwargs:\n\t%r', kwargs) if args and isinstance(args[0], PVsystem_cls): pvsys = args[0] for n, pvstr in enumerate(pvsys.pvstrs): for pvmod in pvstr.pvmods: pvmod.calcMod() pvstr.calcString() logging.debug('updating pvstring #%d: Pmp = %g[W]', n, pvstr.Pstring.max()) return PVAPP = "pvapplication" try: numStrs = self.numStrs.get() if not (0 < numStrs <= self.validationConstants[PVAPP]["numStrs"]): raise PVValidationError('numStrs', numStrs) numMods = self.numMods.get() if not (0 < numMods <= self.validationConstants[PVAPP]["numMods"]): raise PVValidationError('numMods', numMods) sysEe = self.sysEe.get() if not (0 < sysEe <= self.validationConstants[PVAPP]["sysEe"]): raise PVValidationError('sysEe', sysEe) except PVValidationError as err: logging.debug('err:\n\t%r', err) errtext = self.messagetext[PVAPP][err.argname] self.msgtext.set(errtext) self.bell() return numCells = self.numCells.get() self.msgtext.set(self.messagetext[PVAPP]["Ready"]) pvconst = self.pvSys.pvconst pvcell = PVcell(Ee=sysEe) if numCells == 24: numCells = STD24 elif numCells == 72: numCells = STD72 elif numCells == 96: numCells = STD96 elif numCells == 128: numCells = STD128 pvmods = PVmodule(cell_pos=numCells, pvcells=pvcell) self.pvSys = PVsystem(pvconst, numStrs, numberMods=numMods, pvmods=pvmods) self.updateIVstats() def updateIVstats(self): # reuse sysPlot figure and update pvSysFigCanvas self.pvSysPlot = self.pvSys.plotSys(self.pvSysPlot) self.pvSysFigCanvas.show() self.txtImp.set("{:7.3f}".format(self.pvSys.Imp)) # [A] self.txtVmp.set("{:7.3f}".format(self.pvSys.Vmp)) # [V] self.txtPmp.set("{:7.3f}".format(self.pvSys.Pmp / 1000)) # [kW] self.txtIsc.set("{:7.3f}".format(self.pvSys.Isc)) # [A] self.txtVoc.set("{:7.3f}".format(self.pvSys.Voc)) # [V] self.txtFF.set("{:7.3f}".format(self.pvSys.FF * 100)) # [%] self.txtEff.set("{:7.3f}".format(self.pvSys.eff * 100)) # [%] def _help(self): logging.debug('show docs in browser') webbrowser.open(DOCS) def _update(self): self.msgtext.set(READY_MSG) self.updatePVsys() def _reset(self): # number of strings integer variable self.numStrs.set(NUMBERSTRS) # default # number of modules integer variable self.numMods.set(NUMBERMODS) # default # number of cells integer variable self.numCells.set(NUMBERCELLS) # default value is 96 self.msgtext.set(READY_MSG) # TODO: need to reset advCnf too logging.debug('reset') def _load(self): logging.debug('load *.pv file') def _save(self): logging.debug('save *.pv file') def _quit(self): # this is necessary on Windows to prevent # Fatal Python Error: PyEval_RestoreThread: NULL tstate self.master.quit() # stops mainloop self.master.destroy() def readJSON(self, JSONfilename): if not JSONfilename.endswith('json'): JSONfilename += '.json' JSONfullpath = os.path.join(JSONDIR, JSONfilename) with open(JSONfullpath, 'r') as JSONfile: JSONObjects = json.load(JSONfile) logging.debug('JSON objects loaded from %s.', JSONfullpath) return JSONObjects
spd = 50 def changeSpeed(ev=None): tmp = 'speed' global spd spd = speed.get() data = tmp + str( spd ) # Change the integers into strings and combine them with the string 'speed'. print 'sendData = %s' % data tcpCliSock.send(data) # Send the speed data to the server(Raspberry Pi) label = Label(top, text='Speed:', fg='red') # Create a label label.grid(row=6, column=0) # Label layout speed = Scale(top, from_=0, to=100, orient=HORIZONTAL, command=changeSpeed) # Create a scale speed.set(50) speed.grid(row=6, column=1) def main(): top.mainloop() if __name__ == '__main__': main()
class DataCollector: def __init__(self, master): self.master = master master.title("Robostats Data Collector") global Labels self.grassLabels = Labels #class vars self.isRecording = False self.directory_name = StringVar() self.grassLabel = StringVar() self.grassLabel.set(self.grassLabels[0]) self.lastcaptime = 0 self.count = [IntVar() for i in range(len(self.grassLabels))] #self.countText = [StringVar(), StringVar(), StringVar()] self.countText = [StringVar() for i in range(len(self.grassLabels))] for i in range(len(self.grassLabels)): self.countText[i].set(self.grassLabels[i] + ":" + str(self.count[i].get())) self.capRate = 10 global CameraNum self.cap = cv2.VideoCapture(CameraNum) self.controlFrame = Frame(self.master) self.directory_label = Label(self.controlFrame, text="Save Directory:") self.dir_box = Entry(self.controlFrame, width=50, textvariable=self.directory_name) self.directoryButton = Button(self.controlFrame, text="Browse", command=self.set_directory) self.rateFrame = Frame(self.master) self.rateLabel = Label(self.rateFrame, text="Captures per second: ") self.rateSlider = Scale(self.rateFrame, from_=.1, to=30, orient=HORIZONTAL, resolution=0.1, sliderlength=15, length=300, command=self.set_rate) self.buttonFrame = Frame(self.master) self.startButton = Button(self.buttonFrame, text="Start", command=self.start_record, height=5, width=12, bg="#47BD5C") self.stopButton = Button(self.buttonFrame, text="Stop", command=self.stop_record, height=5, width=12, bg="red") self.labelButtonFrame = Frame(self.master) self.typeButton = [ Button(self.labelButtonFrame, text=self.grassLabels[i], height=3, command=lambda num=i: self.set_label(num), width=int(50 / len(self.grassLabels))) for i in range(len(self.grassLabels)) ] self.typeButton[0].config(relief=SUNKEN, bg="#F7C45D") # self.typeButton = [ # Button(self.labelButtonFrame, text=self.grassLabels[0], command=lambda:self.set_label(0), height=3, width=10, relief=SUNKEN, bg="#F7C45D"), # Button(self.labelButtonFrame, text=self.grassLabels[1], command=lambda:self.set_label(1), height=3, width=10), # Button(self.labelButtonFrame, text=self.grassLabels[2], command=lambda:self.set_label(2), height=3, width=10) # ] self.infoFrame = Frame(self.master) self.countLabel = [ Label(self.infoFrame, textvariable=self.countText[i]) for i in range(len(self.grassLabels)) ] # self.countLabel = [ # Label(self.infoFrame, textvariable=self.countText[0]), # Label(self.infoFrame, textvariable=self.countText[1]), # Label(self.infoFrame, textvariable=self.countText[2]) # ] #layout #self.imageFrame.grid(row=0, column=0) #self.lmain.grid(row=0, column=0) curFrameRow = 0 self.controlFrame.grid(row=curFrameRow, column=0) self.directory_label.grid(row=1, column=0) self.dir_box.grid(row=1, column=1) self.directoryButton.grid(row=1, column=2) curFrameRow += 1 self.rateFrame.grid(row=curFrameRow, column=0) self.rateLabel.grid(row=0, column=0) self.rateSlider.grid(row=0, column=1) curFrameRow += 1 self.buttonFrame.grid(row=curFrameRow, column=0, pady=10) self.startButton.grid(row=0, padx=5) self.stopButton.grid(row=0, column=1, padx=5) curFrameRow += 1 self.labelButtonFrame.grid(row=curFrameRow, column=0) col = 0 for button in self.typeButton: button.grid(row=0, column=col, padx=10) col += 1 curFrameRow += 1 self.infoFrame.grid(row=curFrameRow, column=0, pady=10) col = 0 for label in self.countLabel: label.grid(row=0, column=col) col += 1 def set_directory(self): filename = tkFileDialog.askdirectory() self.directory_name.set(filename) self.update_counters() def set_label(self, num): print "num: " + str(num) for i, button in enumerate(self.typeButton): if i != num: button.config(relief=RAISED, bg=self.master.cget('bg')) else: button.config(relief=SUNKEN, bg="#F7C45D") self.grassLabel.set(self.grassLabels[num]) def set_rate(self, val): self.capRate = float(val) def show_frame(self): _, frame = self.cap.read() #frame = cv2.flip(frame, 1) cv2.imshow("Robostats Data Collector", frame) cv2.waitKey(1) #cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) #img = Image.fromarray(cv2image) #imgtk = ImageTk.PhotoImage(image=img) #self.lmain.imgtk = imgtk #self.lmain.configure(image=imgtk) #save if started if self.isRecording: if os.path.exists(self.directory_name.get()): millis = int(round(time.time() * 1000)) if millis > self.lastcaptime + (1000 / self.capRate): labelDir = self.directory_name.get( ) + "/" + self.grassLabel.get() if not os.path.exists(labelDir): os.makedirs(labelDir) #check existing files listOfFiles = [f for f in os.listdir(labelDir)] print listOfFiles maxNum = 0 if len(listOfFiles) > 0: fileNums = [ int(re.search(r'\d+', n).group()) for n in listOfFiles ] maxNum = max(fileNums) filename = labelDir + "/" + self.grassLabel.get() + str( maxNum + 1) + ".png" #print("saved" + str(filename)) cv2.imwrite(filename, frame) self.lastcaptime = millis self.update_counters() self.controlFrame.after(10, self.show_frame) def start_record(self): self.isRecording = True def stop_record(self): self.isRecording = False def update_counters(self): for i, label in enumerate(self.grassLabels): labelDir = self.directory_name.get() + "/" + label if not os.path.exists(labelDir): os.makedirs(labelDir) #check existing files listOfFiles = [f for f in os.listdir(labelDir)] maxNum = 0 if len(listOfFiles) > 0: fileNums = [ int(re.search(r'\d+', n).group()) for n in listOfFiles ] maxNum = max(fileNums) #print maxNum self.count[i].set(maxNum) for i in range(len(self.grassLabels)): self.countText[i].set(self.grassLabels[i] + ":" + str(self.count[i].get()))
class OfflineVisualiser(Visualiser): """A VTK-powered offline visualiser which runs in its own thread. In addition to the functions provided by the standard visualiser, the following additional functions are provided: precache_height_quantities() - Precache all the vtkpoints structures for any dynamic height based quantities to render. """ def __init__(self, source, frameDelay=100, frameStep=1): """The source parameter is assumed to be a NetCDF sww file. The frameDelay parameter is the number of milliseconds waited between frames. """ Visualiser.__init__(self, source) self.frameNumber = 0 fin = NetCDFFile(self.source, 'r') self.maxFrameNumber = fin.variables['time'].shape[0] - 1 fin.close() #self.frameNumberTkVariable = StringVar() #self.frameNumberTkVariable.set('Frame - %05g'%self.framNumber) self.frameDelay = frameDelay self.xmin = None self.xmax = None self.ymin = None self.ymax = None self.zmin = None self.zmax = None self.frameStep= frameStep self.vtk_heightQuantityCache = [] for i in range(self.maxFrameNumber + 1): # maxFrameNumber is zero indexed. self.vtk_heightQuantityCache.append({}) self.paused = False self.movie = False def setup_grid(self): fin = NetCDFFile(self.source, 'r') self.vtk_cells = vtkCellArray() N_tri = fin.variables['volumes'].shape[0] for v in range(N_tri): self.vtk_cells.InsertNextCell(3) for i in range(3): self.vtk_cells.InsertCellPoint(fin.variables['volumes'][v][i]) fin.close() def update_height_quantity(self, quantityName, dynamic=True): polydata = self.vtk_polyData[quantityName] = vtkPolyData() if dynamic is True: #print ' - Frame',self.frameNumber,'of',self.maxFrameNumber if not self.vtk_heightQuantityCache[self.frameNumber].has_key(quantityName): self.vtk_heightQuantityCache[self.frameNumber][quantityName]\ = self.read_height_quantity(quantityName, True, self.frameNumber); polydata.SetPoints(self.vtk_heightQuantityCache[self.frameNumber][quantityName]) else: polydata.SetPoints(self.read_height_quantity(quantityName, False)) polydata.SetPolys(self.vtk_cells) def get_3d_bounds(self): return [self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax] def read_height_quantity(self, quantityName, dynamic=True, frameNumber=0): """Read in a height based quantity from the NetCDF source file and return a vtkPoints object. frameNumber is ignored if dynamic is false.""" fin = NetCDFFile(self.source, 'r') points = vtkPoints() if dynamic is True: N_vert = fin.variables[quantityName].shape[1] else: N_vert = len(fin.variables[quantityName]) x = num.ravel(num.array(fin.variables['x'], num.float)) y = num.ravel(num.array(fin.variables['y'], num.float)) if dynamic is True: q = num.array(fin.variables[quantityName][frameNumber], num.float) else: q = num.ravel(num.array(fin.variables[quantityName], num.float)) q *= self.height_zScales[quantityName] q += self.height_offset[quantityName] for v in range(N_vert): points.InsertNextPoint(x[v], y[v], q[v]) if self.xmin == None or self.xmin > x[v]: self.xmin = x[v] if self.xmax == None or self.xmax < x[v]: self.xmax = x[v] if self.ymin == None or self.ymin > y[v]: self.ymin = y[v] if self.ymax == None or self.ymax < y[v]: self.ymax = y[v] if self.zmin == None or self.zmin > q[v]: self.zmin = q[v] if self.zmax == None or self.zmax < q[v]: self.zmax = q[v] fin.close() return points def precache_height_quantities(self): """Precache any height-based quantities. Call before rendering beigns.""" for q in self.height_quantities: if self.height_dynamic[q] is True: print 'Precaching %s' % q for i in range(self.maxFrameNumber + 1): # maxFrameNumber is zero-indexed print ' - Frame %d of %d' % (i, self.maxFrameNumber) self.vtk_heightQuantityCache[i][q]\ = self.read_height_quantity(q, True, i) def build_quantity_dict(self): quantities = {} fin = NetCDFFile(self.source, 'r') for q in filter(lambda n:n != 'x' and n != 'y' and n != 'z' and n != 'time' and n != 'volumes', fin.variables.keys()): if len(fin.variables[q].shape) == 1: # Not a time-varying quantity quantities[q] = num.ravel(num.array(fin.variables[q], num.float)) else: # Time-varying, get the current timestep data quantities[q] = num.array(fin.variables[q][self.frameNumber], num.float) fin.close() return quantities def setup_gui(self): Visualiser.setup_gui(self) self.tk_quit.grid(row=0, column=0, sticky=W+E) self.tk_movie_toggle = Button(self.tk_controlFrame, text="Movie off", command=self.movie_toggle) self.tk_movie_toggle.grid(row=0, column=6, sticky=W+E) self.tk_restart = Button(self.tk_controlFrame, text="<<<", command=self.restart, width=5) self.tk_restart.grid(row=1, column=0, sticky=W+E) self.tk_back10 = Button(self.tk_controlFrame, text="<<", command=self.back10, width=5) self.tk_back10.grid(row=1, column=1, sticky=W+E) self.tk_back = Button(self.tk_controlFrame, text="<", command=self.back, width=5) self.tk_back.grid(row=1, column=2, sticky=W+E) self.tk_pauseResume = Button(self.tk_controlFrame, text="Pause", command=self.pauseResume, width=15) self.tk_pauseResume.grid(row=1, column=3, sticky=W+E) self.tk_forward = Button(self.tk_controlFrame, text=">", command=self.forward, width=5) self.tk_forward.grid(row=1, column=4, sticky=W+E) self.tk_forward10 = Button(self.tk_controlFrame, text=">>", command=self.forward10, width=5) self.tk_forward10.grid(row=1, column=5, sticky=W+E) self.tk_forwardEnd = Button(self.tk_controlFrame, text=">>>", command=self.forwardEnd, width=5) self.tk_forwardEnd.grid(row=1, column=6, sticky=W+E) self.tk_frameNumber = Label(self.tk_controlFrame, text='Frame') self.tk_frameNumber.grid(row=2, column=0, sticky=W+E) self.tk_gotoFrame = Scale(self.tk_controlFrame, from_=0, to=self.maxFrameNumber, orient=HORIZONTAL) self.tk_gotoFrame.grid(row=2, column=1, columnspan=2, sticky=W+E) self.tk_stepLabel = Label(self.tk_controlFrame, text='Step') self.tk_stepLabel.grid(row=2, column=4, sticky=W+E) self.tk_frameStep = Scale(self.tk_controlFrame, from_=0, to=self.maxFrameNumber, orient=HORIZONTAL) self.tk_frameStep.grid(row=2, column=5, columnspan=2, sticky=W+E) # Make the buttons stretch to fill all available space for i in range(7): self.tk_controlFrame.grid_columnconfigure(i, weight=1) def run(self): self.alter_tkroot(Tk.after, (self.frameDelay, self.animateForward)) Visualiser.run(self) def restart(self): self.frameNumber = 0 self.redraw_quantities() self.update_labels() self.pause() if self.movie: self.save_image() def forwardEnd(self): self.frameNumber = self.maxFrameNumber self.redraw_quantities() self.update_labels() self.pause() def movie_toggle(self): if self.movie == True: self.movie = False self.tk_movie_toggle.config(text='Movie off') else: self.movie = True self.tk_movie_toggle.config(text='Movie on ') def save_image(self): from vtk import vtkJPEGWriter, vtkJPEGWriter, vtkPNGWriter from vtk import vtkPNMWriter, vtkWindowToImageFilter from os import path sourcebase, _ = path.splitext(self.source) fname = sourcebase+'%05g.png' % self.frameNumber #print fname extmap = {'.jpg' : vtkJPEGWriter, '.jpeg' : vtkJPEGWriter, '.png' : vtkPNGWriter, '.pnm' : vtkPNMWriter, } basename, ext = path.splitext(fname) try: Writer = extmap[ext.lower()] except KeyError: error_msg("Don't know how to handle %s files" % ext, parent=self) return renWin = self.vtk_renderer.GetRenderWindow() w2i = vtkWindowToImageFilter() writer = Writer() w2i.SetInput(renWin) w2i.Update() writer.SetInput(w2i.GetOutput()) writer.SetFileName(fname) renWin.Render() writer.Write() def back10(self): if self.frameNumber - 10 >= 0: self.frameNumber -= 10 else: self.frameNumber = 0 self.redraw_quantities() self.update_labels() self.pause() def back(self): if self.frameNumber > 0: self.frameNumber -= 1 self.redraw_quantities() self.update_labels() self.pause() def pauseResume(self): if self.paused is True: self.resume() else: self.pause() def pause(self): self.paused = True self.tk_pauseResume.config(text="Resume") def resume(self): self.paused = False self.tk_pauseResume.config(text="Pause") self.frameNumber = self.tk_gotoFrame.get() self.frameStep = self.tk_frameStep.get() self.tk_root.after(self.frameDelay, self.animateForward) def forward(self): if self.frameNumber < self.maxFrameNumber: self.frameNumber += 1 self.redraw_quantities() self.update_labels() self.pause() def forward_step(self): if self.frameNumber + self.frameStep <= self.maxFrameNumber: self.frameNumber += self.frameStep self.redraw_quantities() self.update_labels() else: self.frameNumber = self.maxFrameNumber self.redraw_quantities() self.update_labels() self.pause() if self.movie: self.save_image() def forward10(self): if self.frameNumber + 10 <= self.maxFrameNumber: self.frameNumber += 10 else: self.frameNumber = self.maxFrameNumber self.redraw_quantities() self.update_labels() self.pause() def animateForward(self): if self.paused is not True: self.forward_step() self.tk_root.after(self.frameDelay, self.animateForward) def update_labels(self): #self.tk_frameNumber.config(text='%05g of %05g'%(self.frameNumber,self.maxFrameNumber)) self.tk_gotoFrame.set(self.frameNumber) self.tk_frameStep.set(self.frameStep) def shutdown(self): #self.pause() self.tk_root.withdraw() self.tk_root.destroy()
class FrameScale(object): # TODO Bugs: When the scale bar is dragged to overlap the other the values does not update correctly # TODO The histogram should be updated when scale is changed def __init__(self, a, frame, view): self.view = view amin = np.amin(a) amax = np.amax(a) # Image min and max values self.min_var = IntVar(value=amin) self.max_var = IntVar(value=amax) self.min_label = Label(frame, text="Min.") self.max_label = Label(frame, text="Max.") self.min_entry = Entry(frame, textvariable=self.min_var) self.max_entry = Entry(frame, textvariable=self.max_var) self.min_entry.bind( "<Return>", lambda event: self.min_entry_change( self.min_scale, self.min_var.get())) self.max_entry.bind( "<Return>", lambda event: self.max_entry_change( self.max_scale, self.max_var.get())) self.min_scale = Scale(frame, orient=HORIZONTAL, from_=amin, to=amax - 1, length=150, command=self.min_change) self.max_scale = Scale(frame, orient=HORIZONTAL, from_=amin + 1, to=amax, length=150, command=self.max_change) self.min_scale.set(amin) self.max_scale.set(amax) self.min_label.grid(row=0, column=0, sticky='w') self.max_label.grid(row=1, column=0, sticky='w') self.min_entry.grid(row=0, column=1, ipady=2) self.max_entry.grid(row=1, column=1, ipady=2) self.min_scale.grid(row=0, column=2, sticky='e', padx=10) self.max_scale.grid(row=1, column=2, sticky='e', padx=10) #Scale bar def min_change(self, val): val = int(val) temp = self.max_var.get() if (val < temp): self.min_var.set(val) self.view.change_clim(val, temp) else: self.min_var.set(temp - 1) self.view.change_clim(temp - 1, temp) #Scale bar def max_change(self, val): val = int(val) temp = self.min_var.get() if (val > temp): self.max_var.set(val) self.view.change_clim(temp, val) else: self.min_var.set(temp + 1) self.view.change_clim(temp, temp + 1) #Entry #TODO: only allow numbers as input. Use nbr_check from FrameFormat. def min_entry_change(self, scale, value): if (value < int(self.max_var.get())): scale.configure(from_=value - 20, to=value + 20) scale.set(value) else: scale.configure(from_=value - 20, to=value + 20) scale.set(int(self.max_var.get()) - 1) #Entry #TODO: only allow numbers as input. Use nbr_check from FrameFormat. def max_entry_change(self, scale, value): if (value > int(self.min_var.get())): scale.configure(from_=value - 20, to=value + 20) scale.set(value) else: scale.configure(from_=value - 20, to=value + 20) scale.set(int(self.min_var.get()) + 1) def update_limit(self, a): self.min_change(np.amin(a)) self.max_change(np.amax(a)) self.min_entry_change(self.min_scale, np.amin(a)) self.max_entry_change(self.max_scale, np.amax(a))
class Cockpit(ttkFrame): ''' Remote device GUI ''' #TODO: 20160415 DPM - Set these values from configuration file #--- config THROTTLE_BY_USER = True THROTTLE_RESOLUTION = 0.1 # Joystick enabled or not, if any JOYSTICK_ENABLED = True DEFAULT_DRONE_IP = "192.168.1.130" DEFAULT_DRONE_PORT = 2121 #--- end config KEY_ANG_SPEED = "ang-speed" KEY_ANGLES = "angles" KEY_ACCEL = "accel" PID_KEYS = ["P", "I", "D"] DIR_NONE = 0 DIR_VERTICAL = 1 DIR_HORIZONTAL = 2 def __init__(self, parent, isDummy = False, droneIp = DEFAULT_DRONE_IP, dronePort = DEFAULT_DRONE_PORT): ''' Constructor ''' ttkFrame.__init__(self, parent) self._target = [0.0] * 4 self._selectedPidConstats = "--" self._pidConstants = { Cockpit.KEY_ANG_SPEED:{ "X":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Y":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Z":{ "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ANGLES: { "X":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Y":{ "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ACCEL:{ "X":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Y":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Z":{ "P": 0.0, "I": 0.0, "D": 0.0 } } } self.parent = parent self.initUI() self._controlKeysLocked = False if not isDummy: self._link = INetLink(droneIp, dronePort) else: self._link = ConsoleLink() self._link.open() self._updateInfoThread = Thread(target=self._updateInfo) self._updateInfoThreadRunning = False self._readingState = False self._start() def initUI(self): self.parent.title("Drone control") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) self.parent.bind_all("<Key>", self._keyDown) self.parent.bind_all("<KeyRelease>", self._keyUp) if system() == "Linux": self.parent.bind_all("<Button-4>", self._onMouseWheelUp) self.parent.bind_all("<Button-5>", self._onMouseWheelDown) else: #case of Windows self.parent.bind_all("<MouseWheel>", self._onMouseWheel) #Commands commandsFrame = tkFrame(self) commandsFrame.grid(column=0, row=0, sticky="WE") self._started = IntVar() self._startedCB = Checkbutton(commandsFrame, text="On", variable=self._started, command=self._startedCBChanged) self._startedCB.pack(side=LEFT, padx=4) # self._integralsCB = Checkbutton(commandsFrame, text="Int.", variable=self._integralsEnabled, \ # command=self._integralsCBChanged, state=DISABLED) # self._integralsCB.pack(side=LEFT, padx=4) self._quitButton = Button(commandsFrame, text="Quit", command=self.exit) self._quitButton.pack(side=LEFT, padx=2, pady=2) # self._angleLbl = Label(commandsFrame, text="Angle") # self._angleLbl.pack(side=LEFT, padx=4) # # self._angleEntry = Entry(commandsFrame, state=DISABLED) # self._angleEntry.pack(side=LEFT) #Info infoFrame = tkFrame(self) infoFrame.grid(column=1, row=1, sticky="NE", padx=4) #Throttle Label(infoFrame, text="Throttle").grid(column=0, row=0, sticky="WE") self._throttleTexts = [StringVar(),StringVar(),StringVar(),StringVar()] Entry(infoFrame, textvariable=self._throttleTexts[3], state=DISABLED, width=5).grid(column=0, row=1) Entry(infoFrame, textvariable=self._throttleTexts[0], state=DISABLED, width=5).grid(column=1, row=1) Entry(infoFrame, textvariable=self._throttleTexts[2], state=DISABLED, width=5).grid(column=0, row=2) Entry(infoFrame, textvariable=self._throttleTexts[1], state=DISABLED, width=5).grid(column=1, row=2) #Angles Label(infoFrame, text="Angles").grid(column=0, row=3, sticky="WE") self._angleTexts = [StringVar(),StringVar(),StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._angleTexts[index], state=DISABLED, width=5).grid(column=index, row=4) #Accels Label(infoFrame, text="Accels").grid(column=0, row=5, sticky="WE") self._accelTexts = [StringVar(),StringVar(),StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._accelTexts[index], state=DISABLED, width=5).grid(column=index, row=6) #Speeds Label(infoFrame, text="Speeds").grid(column=0, row=7, sticky="WE") self._speedTexts = [StringVar(),StringVar(),StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._speedTexts[index], state=DISABLED, width=5).grid(column=index, row=8) #Height Label(infoFrame, text="Height").grid(column=0, row=9, sticky="E") self._heightText = StringVar() Entry(infoFrame, textvariable=self._heightText, state=DISABLED, width=5).grid(column=1, row=9) #Loop rate Label(infoFrame, text="Loop @").grid(column=0, row=10, sticky="E") self._loopRateText = StringVar() Entry(infoFrame, textvariable=self._loopRateText, state=DISABLED, width=5).grid(column=1, row=10) Label(infoFrame, text="Hz").grid(column=2, row=10, sticky="W") #control controlFrame = tkFrame(self) controlFrame.grid(column=0, row=1, sticky="W") self._throttle = DoubleVar() if Cockpit.THROTTLE_BY_USER: self._thrustScale = Scale(controlFrame, orient=VERTICAL, from_=100.0, to=0.0, \ tickinterval=0, variable=self._throttle, resolution=Cockpit.THROTTLE_RESOLUTION, \ length=200, showvalue=1, \ state=DISABLED, command=self._onThrustScaleChanged) else: self._thrustScale = Scale(controlFrame, orient=VERTICAL, from_=100.0, to=-100.0, \ tickinterval=0, variable=self._throttle, \ length=200, showvalue=1, \ state=DISABLED, command=self._onThrustScaleChanged) self._thrustScale.bind("<Double-Button-1>", self._onThrustScaleDoubleButton1, "+") self._thrustScale.grid(column=0) self._shiftCanvas = Canvas(controlFrame, bg="white", height=400, width=400, \ relief=SUNKEN) self._shiftCanvas.bind("<Button-1>", self._onMouseButton1) #self._shiftCanvas.bind("<ButtonRelease-1>", self._onMouseButtonRelease1) self._shiftCanvas.bind("<B1-Motion>", self._onMouseButton1Motion) self._shiftCanvas.bind("<Double-Button-1>", self._onMouseDoubleButton1) self._shiftCanvas.bind("<Button-3>", self._onMouseButton3) #self._shiftCanvas.bind("<ButtonRelease-3>", self._onMouseButtonRelease3) self._shiftCanvas.bind("<B3-Motion>", self._onMouseButton3Motion) self._shiftCanvas.grid(row=0,column=1, padx=2, pady=2) self._shiftCanvas.create_oval(1, 1, 400, 400, outline="#ff0000") self._shiftCanvas.create_line(200, 2, 200, 400, fill="#ff0000") self._shiftCanvas.create_line(2, 200, 400, 200, fill="#ff0000") self._shiftMarker = self._shiftCanvas.create_oval(196, 196, 204, 204, outline="#0000ff", fill="#0000ff") self._yaw = DoubleVar() self._yawScale = Scale(controlFrame, orient=HORIZONTAL, from_=-100.0, to=100.0, \ tickinterval=0, variable=self._yaw, \ length=200, showvalue=1, \ command=self._onYawScaleChanged) self._yawScale.bind("<Double-Button-1>", self._onYawScaleDoubleButton1, "+") self._yawScale.grid(row=1, column=1) self._controlKeyActive = False #PID calibration pidCalibrationFrame = tkFrame(self) pidCalibrationFrame.grid(column=0, row=2, sticky="WE"); self._pidSelected = StringVar() self._pidSelected.set("--") self._pidListBox = OptionMenu(pidCalibrationFrame, self._pidSelected, "--", \ Cockpit.KEY_ANG_SPEED, Cockpit.KEY_ANGLES, Cockpit.KEY_ACCEL, \ command=self._onPidListBoxChanged) self._pidListBox.pack(side=LEFT, padx=2) self._pidListBox.config(width=10) self._axisSelected = StringVar() self._axisSelected.set("--") self._axisListBox = OptionMenu(pidCalibrationFrame, self._axisSelected, "--", "X", "Y", "Z", \ command=self._onAxisListBoxChanged) self._axisListBox.pack(side=LEFT, padx=2) self._axisListBox.config(state=DISABLED) Label(pidCalibrationFrame, text="P").pack(side=LEFT, padx=(14, 2)) self._pidPString = StringVar() self._pidPString.set("0.00") self._pidPSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=10000.0, increment=0.01, state=DISABLED, \ textvariable=self._pidPString, command=self._onPidSpinboxChanged) self._pidPSpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="I").pack(side=LEFT, padx=(14, 2)) self._pidIString = StringVar() self._pidIString.set("0.00") self._pidISpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=10000.0, increment=0.01, state=DISABLED, \ textvariable=self._pidIString, command=self._onPidSpinboxChanged) self._pidISpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="D").pack(side=LEFT, padx=(14, 2)) self._pidDString = StringVar() self._pidDString.set("0.00") self._pidDSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=10000.0, increment=0.01, state=DISABLED, \ textvariable=self._pidDString, command=self._onPidSpinboxChanged) self._pidDSpinbox.pack(side=LEFT, padx=2) #debug debugFrame = tkFrame(self) debugFrame.grid(column=0, row=3, sticky="WE") self._debugMsg = Message(debugFrame, anchor="nw", justify=LEFT, relief=SUNKEN, width=300) self._debugMsg.pack(fill=BOTH, expand=1) def _start(self): self._readDroneConfig() if Cockpit.JOYSTICK_ENABLED: self._joystickManager = JoystickManager.getInstance() self._joystickManager.start() joysticks = self._joystickManager.getJoysticks() if len(joysticks) != 0: self._joystick = joysticks[0] self._joystick.onAxisChanged += self._onJoystickAxisChanged self._joystick.onButtonPressed += self._onJoystickButtonPressed else: self._joystick = None def _onJoystickAxisChanged(self, sender, index): if self._started.get() and sender == self._joystick: axisValue = self._joystick.getAxisValue(index) if index == 0: self._yaw.set(axisValue) self._updateTarget() elif index == 1 and not Cockpit.THROTTLE_BY_USER: thrust = -axisValue self._throttle.set(thrust) self._updateTarget() elif index == 2 and Cockpit.THROTTLE_BY_USER: rowThrottle = (axisValue + 100.0)/2.0 if rowThrottle < 10.0: throttle = rowThrottle * 6.0 elif rowThrottle < 90.0: throttle = 60.0 + ((rowThrottle - 10.0) / 8.0) else: throttle = 70.0 + (rowThrottle - 90.0) * 3.0 self._throttle.set(throttle) self._sendThrottle() elif index == 3: x = 196 + axisValue * 2 lastCoords = self._shiftCanvas.coords(self._shiftMarker) coords = (x, lastCoords[1]) self._plotShiftCanvasMarker(coords) elif index == 4: y = 196 + axisValue * 2 lastCoords = self._shiftCanvas.coords(self._shiftMarker) coords = (lastCoords[0], y) self._plotShiftCanvasMarker(coords) def _onJoystickButtonPressed(self, sender, index): if sender == self._joystick and index == 7: if self._started.get() == 0: self._startedCB.select() else: self._startedCB.deselect() # Tkinter's widgets seem not to be calling the event-handler # when they are changed programmatically. Therefore, the # even-handler is called explicitly here. self._startedCBChanged() def exit(self): self._link.send({"key": "close", "data": None}) self._stopUpdateInfoThread() self._link.close() if Cockpit.JOYSTICK_ENABLED: self._joystickManager.stop() self.quit() def _updateTarget(self): markerCoords = self._shiftCanvas.coords(self._shiftMarker) coords = ((markerCoords[0] + markerCoords[2]) / 2, (markerCoords[1] + markerCoords[3]) / 2) self._target[0] = float(coords[1] - 200) / 2.0 # X-axis angle / X-axis acceleration self._target[1] = float(coords[0] - 200) / 2.0 # Y-axis angle / Y-axis acceleration #Remote control uses clockwise angle, but the drone's referece system uses counter-clockwise angle self._target[2] = -self._yaw.get() # Z-axis angular speed # Z-axis acceleration (thrust). Only when the motor throttle is not controlled by user directly if Cockpit.THROTTLE_BY_USER: self._target[3] = 0.0 else: self._target[3] = self._throttle.get() self._sendTarget() def _keyDown(self, event): if event.keysym == "Escape": self._throttle.set(0) self._started.set(0) self._thrustScale.config(state=DISABLED) self._stopUpdateInfoThread() self._sendIsStarted() elif event.keysym.startswith("Control"): self._controlKeyActive = True elif not self._controlKeysLocked and self._controlKeyActive: if event.keysym == "Up": self._thrustScaleUp() elif event.keysym == "Down": self._thrustScaleDown() elif event.keysym == "Left": self._yawLeft() elif event.keysym == "Right": self._yawRight() elif event.keysym == "space": self._yawReset() if not Cockpit.THROTTLE_BY_USER: self._thrustReset() elif not self._controlKeysLocked and not self._controlKeyActive: if event.keysym == "Up": self._moveShiftCanvasMarker((0,-5)) elif event.keysym == "Down": self._moveShiftCanvasMarker((0,5)) elif event.keysym == "Left": self._moveShiftCanvasMarker((-5,0)) elif event.keysym == "Right": self._moveShiftCanvasMarker((5,0)) elif event.keysym == "space": self._resetShiftCanvasMarker() def _keyUp(self, eventArgs): if eventArgs.keysym.startswith("Control"): self._controlKeyActive = False def _onMouseButton1(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseButtonRelease1(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 196, 196, 204, 204) def _limitCoordsToSize(self, coords, size, width): maxSize = size-(width/2.0) minSize = -(width/2.0) if coords[0] > maxSize: x = maxSize elif coords[0] < minSize: x = minSize else: x = coords[0] if coords[1] > maxSize: y = maxSize elif coords[1] < minSize: y = minSize else: y = coords[1] return (x,y) def _plotShiftCanvasMarker(self, coords): coords = self._limitCoordsToSize(coords, 400, 8) self._shiftCanvas.coords(self._shiftMarker, coords[0], coords[1], coords[0] + 8, coords[1] + 8) self._updateTarget() def _moveShiftCanvasMarker(self, shift): lastCoords = self._shiftCanvas.coords(self._shiftMarker) newCoords = (lastCoords[0] + shift[0], lastCoords[1] + shift[1]) self._plotShiftCanvasMarker(newCoords) def _resetShiftCanvasMarker(self): self._shiftCanvas.coords(self._shiftMarker, 196, 196, 204, 204) self._updateTarget() def _onMouseButton1Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseDoubleButton1(self, eventArgs): self._resetShiftCanvasMarker() def _onMouseButton3(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) self._mouseDirection = Cockpit.DIR_NONE def _onMouseButtonRelease3(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 196, 196, 204, 204) def _onMouseButton3Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) if self._mouseDirection == Cockpit.DIR_NONE: if abs(deltaCoords[0]) > abs(deltaCoords[1]): self._mouseDirection = Cockpit.DIR_HORIZONTAL else: self._mouseDirection = Cockpit.DIR_VERTICAL if self._mouseDirection == Cockpit.DIR_HORIZONTAL: deltaCoords = (deltaCoords[0], 0) else: deltaCoords = (0, deltaCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _thrustScaleUp(self): #TODO: 20160526 DPM: El valor de incremento de aceleración (1.0) puede ser muy alto if self._started.get(): newValue = self._thrustScale.get() \ + (Cockpit.THROTTLE_RESOLUTION if Cockpit.THROTTLE_BY_USER else 1.0) self._thrustScale.set(newValue) self._updateTarget() def _thrustScaleDown(self): #TODO: 20160526 DPM: El valor de decremento de aceleración (1.0) puede ser muy alto if self._started.get(): newValue = self._thrustScale.get() \ - (Cockpit.THROTTLE_RESOLUTION if Cockpit.THROTTLE_BY_USER else 1.0) self._thrustScale.set(newValue) self._updateTarget() def _thrustReset(self): if self._started.get(): self._thrustScale.set(0.0) self._updateTarget() def _onThrustScaleDoubleButton1(self, eventArgs): self._thrustReset() return "break" def _yawRight(self): newValue = self._yaw.get() + 1 self._yaw.set(newValue) self._updateTarget() def _yawLeft(self): newValue = self._yaw.get() - 1 self._yaw.set(newValue) self._updateTarget() def _yawReset(self): self._yaw.set(0) self._updateTarget() def _onMouseWheelUp(self, eventArgs): if not self._controlKeyActive: self._thrustScaleUp() else: self._yawRight() def _onMouseWheelDown(self, eventArgs): if not self._controlKeyActive: self._thrustScaleDown() else: self._yawLeft() def _onMouseWheel(self, eventArgs): factor = eventArgs.delta/(1200.0 if Cockpit.THROTTLE_BY_USER and not self._controlKeyActive else 120.0) if not self._controlKeyActive: if self._started.get(): newValue = self._thrustScale.get() + factor self._thrustScale.set(newValue) self._updateTarget() else: newValue = self._yaw.get() + factor self._yaw.set(newValue) self._updateTarget() def _onYawScaleChanged(self, eventArgs): self._updateTarget() def _onYawScaleDoubleButton1(self, eventArgs): self._yawReset() return "break" def _startedCBChanged(self): if not self._started.get(): self._throttle.set(0) self._thrustScale.config(state=DISABLED) #self._integralsCB.config(state=DISABLED) self._stopUpdateInfoThread() else: self._thrustScale.config(state="normal") #self._integralsCB.config(state="normal") self._startUpdateInfoThread() self._sendIsStarted() # def _integralsCBChanged(self): # # self._link.send({"key": "integrals", "data":self._integralsEnabled.get() != 0}) # def _onThrustScaleChanged(self, eventArgs): if Cockpit.THROTTLE_BY_USER: self._sendThrottle() else: self._updateTarget() def _sendThrottle(self): self._link.send({"key": "throttle", "data": self._throttle.get()}) def _sendTarget(self): self._link.send({"key": "target", "data": self._target}) def _sendIsStarted(self): isStarted = self._started.get() != 0 self._link.send({"key": "is-started", "data": isStarted}) def _sendPidCalibrationData(self): if self._pidSelected.get() != "--" and self._axisSelected.get() != "--": pidData = { "pid": self._pidSelected.get(), "axis": self._axisSelected.get(), "p": float(self._pidPSpinbox.get()), "i": float(self._pidISpinbox.get()), "d": float(self._pidDSpinbox.get())} self._link.send({"key": "pid-calibration", "data": pidData}) def _updatePidCalibrationData(self): pid = self._pidSelected.get() axis = self._axisSelected.get() if pid != "--" and axis != "--": self._pidConstants[pid][axis]["P"] = float(self._pidPSpinbox.get()) self._pidConstants[pid][axis]["I"] = float(self._pidISpinbox.get()) self._pidConstants[pid][axis]["D"] = float(self._pidDSpinbox.get()) def _readDroneConfig(self): self._link.send({"key": "read-drone-config", "data": None}, self._onDroneConfigRead) def _readDroneState(self): if not self._readingState: self._readingState = True self._link.send({"key": "read-drone-state", "data": None}, self._onDroneStateRead) def _readPidConfigItem(self, message, cockpitKey, axises, configKeys): for i in range(len(axises)): for j in range(len(Cockpit.PID_KEYS)): self._pidConstants[cockpitKey][axises[i]][Cockpit.PID_KEYS[j]] = message[configKeys[j]][i] def _onDroneConfigRead(self, message): #TODO Show current configuration within the GUI (at least relevant settings) if message: #Angle-speeds self._readPidConfigItem(message, Cockpit.KEY_ANG_SPEED, ["X", "Y", "Z"], \ [Configuration.PID_ANGLES_SPEED_KP, \ Configuration.PID_ANGLES_SPEED_KI, \ Configuration.PID_ANGLES_SPEED_KD]) #Angles self._readPidConfigItem(message, Cockpit.KEY_ANGLES, ["X", "Y"], \ [Configuration.PID_ANGLES_KP, \ Configuration.PID_ANGLES_KI, \ Configuration.PID_ANGLES_KD]) #Accels self._readPidConfigItem(message, Cockpit.KEY_ACCEL, ["X", "Y", "Z"], \ [Configuration.PID_ACCEL_KP, \ Configuration.PID_ACCEL_KI, \ Configuration.PID_ACCEL_KD]) def _onDroneStateRead(self, state): if state: for index in range(4): self._throttleTexts[index].set("{0:.3f}".format(state["_throttles"][index])) for index in range(3): self._accelTexts[index].set("{0:.3f}".format(state["_accels"][index])) self._angleTexts[index].set("{0:.3f}".format(state["_angles"][index])) currentPeriod = state["_currentPeriod"] if currentPeriod > 0.0: freq = 1.0/currentPeriod self._loopRateText.set("{0:.3f}".format(freq)) else: self._loopRateText.set("--") else: self._stopUpdateInfoThread() self._readingState = False def _onPidSpinboxChanged(self): self._updatePidCalibrationData() self._sendPidCalibrationData() def _onPidListBoxChanged(self, pid): self._axisSelected.set("--") self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._selectedPidConstats = pid if pid == "--": self._axisListBox.config(state=DISABLED) self._controlKeysLocked = False else: self._axisListBox.config(state="normal") self._controlKeysLocked = True def _onAxisListBoxChanged(self, axis): if axis == "--" or (self._selectedPidConstats == Cockpit.KEY_ANGLES and axis == "Z"): self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._controlKeysLocked = axis != "--" else: self._pidPString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["P"])) self._pidIString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["I"])) self._pidDString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["D"])) self._pidPSpinbox.config(state="normal") self._pidISpinbox.config(state="normal") self._pidDSpinbox.config(state="normal") self._controlKeysLocked = True def _updateInfo(self): while self._updateInfoThreadRunning: self._readDroneState() time.sleep(1.0) def _startUpdateInfoThread(self): self._updateInfoThreadRunning = True if not self._updateInfoThread.isAlive(): self._updateInfoThread.start() def _stopUpdateInfoThread(self): self._updateInfoThreadRunning = False if self._updateInfoThread.isAlive(): self._updateInfoThread.join()
class Cockpit(ttkFrame): ''' Remote controller GUI ''' KEY_ANG_SPEED = "ang-speed" KEY_ANGLES = "angles" KEY_ACCEL = "accel" PID_KEYS = ["P", "I", "D"] DEFAULT_DRONE_IP = "192.168.1.130" DEFAULT_DRONE_PORT = 2121 DIR_NONE = 0 DIR_VERTICAL = 1 DIR_HORIZONTAL = 2 MAX_ACCEL = 10.0 #TODO angles. Replace by m/s² MAX_ACCEL_Z = 0.1 #m/s² MAX_ANGLE_SPEED = 50.0 #º/s def __init__(self, parent, isDummy = False, droneIp = DEFAULT_DRONE_IP, dronePort = DEFAULT_DRONE_PORT): ''' Constructor ''' ttkFrame.__init__(self, parent) self._started = IntVar() self._integralsEnabled = IntVar() self._target = [0.0] * 4 self._selectedPidConstats = "--" self._pidConstants = { Cockpit.KEY_ANG_SPEED:{ "X":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Y":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Z":{ "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ANGLES: { "X":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Y":{ "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ACCEL:{ "X":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Y":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Z":{ "P": 0.0, "I": 0.0, "D": 0.0 } } } self.parent = parent self.initUI() self._controlKeysLocked = False if not isDummy: self._link = INetLink(droneIp, dronePort) else: self._link = ConsoleLink() self._link.open() self._updateInfoThread = Thread(target=self._updateInfo) self._updateInfoThreadRunning = False self._readingState = False self._start() def initUI(self): self.parent.title("Drone control") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) self.parent.bind_all("<Key>", self._keyDown) self.parent.bind_all("<KeyRelease>", self._keyUp) if system() == "Linux": self.parent.bind_all("<Button-4>", self._onMouseWheelUp) self.parent.bind_all("<Button-5>", self._onMouseWheelDown) else: #case of Windows self.parent.bind_all("<MouseWheel>", self._onMouseWheel) #Commands commandsFrame = tkFrame(self) commandsFrame.grid(column=0, row=0, sticky="WE") self._startedCB = Checkbutton(commandsFrame, text="On", variable=self._started, command=self._startedCBChanged) self._startedCB.pack(side=LEFT, padx=4) self._integralsCB = Checkbutton(commandsFrame, text="Int.", variable=self._integralsEnabled, \ command=self._integralsCBChanged, state=DISABLED) self._integralsCB.pack(side=LEFT, padx=4) self._quitButton = Button(commandsFrame, text="Quit", command=self.exit) self._quitButton.pack(side=LEFT, padx=2, pady=2) # self._angleLbl = Label(commandsFrame, text="Angle") # self._angleLbl.pack(side=LEFT, padx=4) # # self._angleEntry = Entry(commandsFrame, state=DISABLED) # self._angleEntry.pack(side=LEFT) #Info infoFrame = tkFrame(self) infoFrame.grid(column=1, row=1, sticky="E", padx=4) #Throttle Label(infoFrame, text="Throttle").grid(column=0, row=0, sticky="WE") self._throttleTexts = [StringVar(),StringVar(),StringVar(),StringVar()] Entry(infoFrame, textvariable=self._throttleTexts[3], state=DISABLED, width=5).grid(column=0, row=1) Entry(infoFrame, textvariable=self._throttleTexts[0], state=DISABLED, width=5).grid(column=1, row=1) Entry(infoFrame, textvariable=self._throttleTexts[2], state=DISABLED, width=5).grid(column=0, row=2) Entry(infoFrame, textvariable=self._throttleTexts[1], state=DISABLED, width=5).grid(column=1, row=2) #Angles Label(infoFrame, text="Angles").grid(column=0, row=3, sticky="WE") self._angleTexts = [StringVar(),StringVar(),StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._angleTexts[index], state=DISABLED, width=5).grid(column=index, row=4) #Accels Label(infoFrame, text="Accels").grid(column=0, row=5, sticky="WE") self._accelTexts = [StringVar(),StringVar(),StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._accelTexts[index], state=DISABLED, width=5).grid(column=index, row=6) #Speeds Label(infoFrame, text="Speeds").grid(column=0, row=7, sticky="WE") self._speedTexts = [StringVar(),StringVar(),StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._speedTexts[index], state=DISABLED, width=5).grid(column=index, row=8) #Height Label(infoFrame, text="Height").grid(column=0, row=9, sticky="W") self._heightText = StringVar() Entry(infoFrame, state=DISABLED, width=5).grid(column=1, row=9) #control controlFrame = tkFrame(self) controlFrame.grid(column=0, row=1, sticky="W") self._throttle = DoubleVar() self._thrustScale = Scale(controlFrame, orient=VERTICAL, from_=100.0, to=-100.0, \ tickinterval=0, variable=self._throttle, \ length=200, showvalue=1, \ state=DISABLED, command=self._onThrustScaleChanged) self._thrustScale.bind("<Double-Button-1>", self._onThrustScaleDoubleButton1, "+") self._thrustScale.grid(column=0) self._shiftCanvas = Canvas(controlFrame, bg="white", height=400, width=400, \ relief=SUNKEN) self._shiftCanvas.bind("<Button-1>", self._onMouseButton1) #self._shiftCanvas.bind("<ButtonRelease-1>", self._onMouseButtonRelease1) self._shiftCanvas.bind("<B1-Motion>", self._onMouseButton1Motion) self._shiftCanvas.bind("<Double-Button-1>", self._onMouseDoubleButton1) self._shiftCanvas.bind("<Button-3>", self._onMouseButton3) #self._shiftCanvas.bind("<ButtonRelease-3>", self._onMouseButtonRelease3) self._shiftCanvas.bind("<B3-Motion>", self._onMouseButton3Motion) self._shiftCanvas.grid(row=0,column=1, padx=2, pady=2) self._shiftCanvas.create_oval(2, 2, 400, 400, outline="#ff0000") self._shiftCanvas.create_line(201, 2, 201, 400, fill="#ff0000") self._shiftCanvas.create_line(2, 201, 400, 201, fill="#ff0000") self._shiftMarker = self._shiftCanvas.create_oval(197, 197, 205, 205, outline="#0000ff", fill="#0000ff") self._yaw = DoubleVar() self._yawScale = Scale(controlFrame, orient=HORIZONTAL, from_=-100.0, to=100.0, \ tickinterval=0, variable=self._yaw, \ length=200, showvalue=1, \ command=self._onYawScaleChanged) self._yawScale.bind("<Double-Button-1>", self._onYawScaleDoubleButton1, "+") self._yawScale.grid(row=1, column=1) self._controlKeyActive = False #PID calibration pidCalibrationFrame = tkFrame(self) pidCalibrationFrame.grid(column=0, row=2, sticky="WE"); self._pidSelected = StringVar() self._pidSelected.set("--") self._pidListBox = OptionMenu(pidCalibrationFrame, self._pidSelected, "--", \ Cockpit.KEY_ANG_SPEED, Cockpit.KEY_ANGLES, Cockpit.KEY_ACCEL, \ command=self._onPidListBoxChanged) self._pidListBox.pack(side=LEFT, padx=2) self._pidListBox.config(width=10) self._axisSelected = StringVar() self._axisSelected.set("--") self._axisListBox = OptionMenu(pidCalibrationFrame, self._axisSelected, "--", "X", "Y", "Z", \ command=self._onAxisListBoxChanged) self._axisListBox.pack(side=LEFT, padx=2) self._axisListBox.config(state=DISABLED) Label(pidCalibrationFrame, text="P").pack(side=LEFT, padx=(14, 2)) self._pidPString = StringVar() self._pidPString.set("0.00") self._pidPSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=100.0, increment=0.01, state=DISABLED, \ textvariable=self._pidPString, command=self._onPidSpinboxChanged) self._pidPSpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="I").pack(side=LEFT, padx=(14, 2)) self._pidIString = StringVar() self._pidIString.set("0.00") self._pidISpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=100.0, increment=0.01, state=DISABLED, \ textvariable=self._pidIString, command=self._onPidSpinboxChanged) self._pidISpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="D").pack(side=LEFT, padx=(14, 2)) self._pidDString = StringVar() self._pidDString.set("0.00") self._pidDSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=100.0, increment=0.01, state=DISABLED, \ textvariable=self._pidDString, command=self._onPidSpinboxChanged) self._pidDSpinbox.pack(side=LEFT, padx=2) #debug debugFrame = tkFrame(self) debugFrame.grid(column=0, row=3, sticky="WE") self._debugMsg = Message(debugFrame, anchor="nw", justify=LEFT, relief=SUNKEN, width=300) self._debugMsg.pack(fill=BOTH, expand=1) def _start(self): self._readDroneConfig() def exit(self): self._link.send({"key": "close", "data": None}) self._stopUpdateInfoThread() self._link.close() self.quit() def _updateTarget(self): markerCoords = self._shiftCanvas.coords(self._shiftMarker) coords = ((markerCoords[0] + markerCoords[2]) / 2, (markerCoords[1] + markerCoords[3]) / 2) self._target[1] = float(coords[0] - 201) * Cockpit.MAX_ACCEL / 200.0 self._target[0] = float(coords[1] - 201) * Cockpit.MAX_ACCEL / 200.0 #Remote control uses clockwise angle, but the drone's referece system uses counter-clockwise angle self._target[2] = -self._yaw.get() * Cockpit.MAX_ANGLE_SPEED / 100.0 self._target[3] = self._throttle.get() * Cockpit.MAX_ACCEL_Z / 100.0 self._sendTarget() def _keyDown(self, event): if event.keysym == "Escape": self._throttle.set(0) self._started.set(0) self._thrustScale.config(state=DISABLED) self._stopUpdateInfoThread() self._sendIsStarted() elif event.keysym.startswith("Control"): self._controlKeyActive = True elif not self._controlKeysLocked and self._controlKeyActive: if event.keysym == "Up": self._thrustScaleUp() elif event.keysym == "Down": self._thrustScaleDown() elif event.keysym == "Left": self._yawLeft() elif event.keysym == "Right": self._yawRight() elif event.keysym == "space": self._yawReset() self._thrustReset() elif not self._controlKeysLocked and not self._controlKeyActive: if event.keysym == "Up": self._moveShiftCanvasMarker((0,-5)) elif event.keysym == "Down": self._moveShiftCanvasMarker((0,5)) elif event.keysym == "Left": self._moveShiftCanvasMarker((-5,0)) elif event.keysym == "Right": self._moveShiftCanvasMarker((5,0)) elif event.keysym == "space": self._resetShiftCanvasMarker() def _keyUp(self, eventArgs): if eventArgs.keysym.startswith("Control"): self._controlKeyActive = False def _onMouseButton1(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseButtonRelease1(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 197, 197, 205, 205) def _limitCoordsToSize(self, coords, size): if coords[0] > size: x = size elif coords[0] < 0: x = 0 else: x = coords[0] if coords[1] > size: y = size elif coords[1] < 0: y = 0 else: y = coords[1] return (x,y) def _moveShiftCanvasMarker(self, shift): lastCoords = self._shiftCanvas.coords(self._shiftMarker) newCoords = (lastCoords[0] + shift[0], lastCoords[1] + shift[1]) newCoords = self._limitCoordsToSize(newCoords, 400) self._shiftCanvas.coords(self._shiftMarker, newCoords[0], newCoords[1], newCoords[0] + 8, newCoords[1] + 8) self._updateTarget() def _resetShiftCanvasMarker(self): self._shiftCanvas.coords(self._shiftMarker, 197, 197, 205, 205) self._updateTarget() def _onMouseButton1Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseDoubleButton1(self, eventArgs): self._resetShiftCanvasMarker() def _onMouseButton3(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) self._mouseDirection = Cockpit.DIR_NONE def _onMouseButtonRelease3(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 197, 197, 205, 205) def _onMouseButton3Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) if self._mouseDirection == Cockpit.DIR_NONE: if abs(deltaCoords[0]) > abs(deltaCoords[1]): self._mouseDirection = Cockpit.DIR_HORIZONTAL else: self._mouseDirection = Cockpit.DIR_VERTICAL if self._mouseDirection == Cockpit.DIR_HORIZONTAL: deltaCoords = (deltaCoords[0], 0) else: deltaCoords = (0, deltaCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _thrustScaleUp(self): if self._started.get(): newValue = self._thrustScale.get() + 1 self._thrustScale.set(newValue) self._updateTarget() def _thrustScaleDown(self): if self._started.get(): newValue = self._thrustScale.get() - 1 self._thrustScale.set(newValue) self._updateTarget() def _thrustReset(self): if self._started.get(): self._thrustScale.set(0.0) self._updateTarget() def _onThrustScaleDoubleButton1(self, eventArgs): self._thrustReset() return "break" def _yawRight(self): newValue = self._yaw.get() + 1 self._yaw.set(newValue) self._updateTarget() def _yawLeft(self): newValue = self._yaw.get() - 1 self._yaw.set(newValue) self._updateTarget() def _yawReset(self): self._yaw.set(0) self._updateTarget() def _onMouseWheelUp(self, eventArgs): if not self._controlKeyActive: self._thrustScaleUp() else: self._yawRight() def _onMouseWheelDown(self, eventArgs): if not self._controlKeyActive: self._thrustScaleDown() else: self._yawLeft() def _onMouseWheel(self, eventArgs): factor = int(eventArgs.delta/120) if not self._controlKeyActive: if self._started.get(): newValue = self._thrustScale.get() + factor self._thrustScale.set(newValue) self._updateTarget() else: newValue = self._yaw.get() + factor self._yaw.set(newValue) self._updateTarget() def _onYawScaleChanged(self, eventArgs): self._updateTarget() def _onYawScaleDoubleButton1(self, eventArgs): self._yawReset() return "break" def _startedCBChanged(self): if not self._started.get(): self._throttle.set(0) self._thrustScale.config(state=DISABLED) self._integralsCB.config(state=DISABLED) self._stopUpdateInfoThread() else: self._thrustScale.config(state="normal") self._integralsCB.config(state="normal") self._startUpdateInfoThread() self._sendIsStarted() def _integralsCBChanged(self): self._link.send({"key": "integrals", "data":self._integralsEnabled.get() != 0}) def _onThrustScaleChanged(self, eventArgs): self._updateTarget() def _sendTarget(self): self._link.send({"key": "target", "data": self._target}) def _sendIsStarted(self): isStarted = self._started.get() != 0 self._link.send({"key": "is-started", "data": isStarted}) def _sendPidCalibrationData(self): if self._pidSelected.get() != "--" and self._axisSelected.get() != "--": pidData = { "pid": self._pidSelected.get(), "axis": self._axisSelected.get(), "p": float(self._pidPSpinbox.get()), "i": float(self._pidISpinbox.get()), "d": float(self._pidDSpinbox.get())} self._link.send({"key": "pid-calibration", "data": pidData}) def _updatePidCalibrationData(self): pid = self._pidSelected.get() axis = self._axisSelected.get() if pid != "--" and axis != "--": self._pidConstants[pid][axis]["P"] = float(self._pidPSpinbox.get()) self._pidConstants[pid][axis]["I"] = float(self._pidISpinbox.get()) self._pidConstants[pid][axis]["D"] = float(self._pidDSpinbox.get()) def _readDroneConfig(self): self._link.send({"key": "read-drone-config", "data": None}, self._onDroneConfigRead) def _readDroneState(self): if not self._readingState: self._readingState = True self._link.send({"key": "read-drone-state", "data": None}, self._onDroneStateRead) def _readPidConfigItem(self, message, cockpitKey, axises, configKeys): for i in range(len(axises)): for j in range(len(Cockpit.PID_KEYS)): self._pidConstants[cockpitKey][axises[i]][Cockpit.PID_KEYS[j]] = message[configKeys[j]][i] def _onDroneConfigRead(self, message): #TODO Show current configuration within the GUI (at least relevant settings) if message: #Angle-speeds self._readPidConfigItem(message, Cockpit.KEY_ANG_SPEED, ["X", "Y", "Z"], \ [Configuration.PID_ANGLES_SPEED_KP, \ Configuration.PID_ANGLES_SPEED_KI, \ Configuration.PID_ANGLES_SPEED_KD]) #Angles self._readPidConfigItem(message, Cockpit.KEY_ANGLES, ["X", "Y"], \ [Configuration.PID_ANGLES_KP, \ Configuration.PID_ANGLES_KI, \ Configuration.PID_ANGLES_KD]) #Accels self._readPidConfigItem(message, Cockpit.KEY_ACCEL, ["X", "Y", "Z"], \ [Configuration.PID_ACCEL_KP, \ Configuration.PID_ACCEL_KI, \ Configuration.PID_ACCEL_KD]) def _onDroneStateRead(self, state): if state: for index in range(4): self._throttleTexts[index].set("{0:.3f}".format(state["_throttles"][index])) for index in range(3): self._accelTexts[index].set("{0:.3f}".format(state["_accels"][index])) self._angleTexts[index].set("{0:.3f}".format(state["_angles"][index])) else: self._stopUpdateInfoThread() self._readingState = False def _onPidSpinboxChanged(self): self._updatePidCalibrationData() self._sendPidCalibrationData() def _onPidListBoxChanged(self, pid): self._axisSelected.set("--") self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._selectedPidConstats = pid if pid == "--": self._axisListBox.config(state=DISABLED) self._controlKeysLocked = False else: self._axisListBox.config(state="normal") self._controlKeysLocked = True def _onAxisListBoxChanged(self, axis): if axis == "--" or (self._selectedPidConstats == Cockpit.KEY_ANGLES and axis == "Z"): self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._controlKeysLocked = axis != "--" else: self._pidPString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["P"])) self._pidIString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["I"])) self._pidDString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["D"])) self._pidPSpinbox.config(state="normal") self._pidISpinbox.config(state="normal") self._pidDSpinbox.config(state="normal") self._controlKeysLocked = True def _updateInfo(self): while self._updateInfoThreadRunning: self._readDroneState() time.sleep(1.0) def _startUpdateInfoThread(self): self._updateInfoThreadRunning = True if not self._updateInfoThread.isAlive(): self._updateInfoThread.start() def _stopUpdateInfoThread(self): self._updateInfoThreadRunning = False if self._updateInfoThread.isAlive(): self._updateInfoThread.join()
class Cockpit(ttkFrame): ''' Remote device GUI ''' #TODO: 20160415 DPM - Set these values from configuration file #--- config THROTTLE_BY_USER = True THROTTLE_RESOLUTION = 0.1 # Joystick enabled or not, if any JOYSTICK_ENABLED = True DEFAULT_DRONE_IP = "192.168.1.130" DEFAULT_DRONE_PORT = 2121 #--- end config KEY_ANG_SPEED = "ang-speed" KEY_ANGLES = "angles" KEY_ACCEL = "accel" PID_KEYS = ["P", "I", "D"] DIR_NONE = 0 DIR_VERTICAL = 1 DIR_HORIZONTAL = 2 def __init__(self, parent, isDummy=False, droneIp=DEFAULT_DRONE_IP, dronePort=DEFAULT_DRONE_PORT): ''' Constructor ''' ttkFrame.__init__(self, parent) self._target = [0.0] * 4 self._selectedPidConstats = "--" self._pidConstants = { Cockpit.KEY_ANG_SPEED: { "X": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Y": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Z": { "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ANGLES: { "X": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Y": { "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ACCEL: { "X": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Y": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Z": { "P": 0.0, "I": 0.0, "D": 0.0 } } } self.parent = parent self.initUI() self._controlKeysLocked = False if not isDummy: self._link = INetLink(droneIp, dronePort) else: self._link = ConsoleLink() self._link.open() self._updateInfoThread = Thread(target=self._updateInfo) self._updateInfoThreadRunning = False self._readingState = False self._start() def initUI(self): self.parent.title("Drone control") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) self.parent.bind_all("<Key>", self._keyDown) self.parent.bind_all("<KeyRelease>", self._keyUp) if system() == "Linux": self.parent.bind_all("<Button-4>", self._onMouseWheelUp) self.parent.bind_all("<Button-5>", self._onMouseWheelDown) else: #case of Windows self.parent.bind_all("<MouseWheel>", self._onMouseWheel) #Commands commandsFrame = tkFrame(self) commandsFrame.grid(column=0, row=0, sticky="WE") self._started = IntVar() self._startedCB = Checkbutton(commandsFrame, text="On", variable=self._started, command=self._startedCBChanged) self._startedCB.pack(side=LEFT, padx=4) # self._integralsCB = Checkbutton(commandsFrame, text="Int.", variable=self._integralsEnabled, \ # command=self._integralsCBChanged, state=DISABLED) # self._integralsCB.pack(side=LEFT, padx=4) self._quitButton = Button(commandsFrame, text="Quit", command=self.exit) self._quitButton.pack(side=LEFT, padx=2, pady=2) # self._angleLbl = Label(commandsFrame, text="Angle") # self._angleLbl.pack(side=LEFT, padx=4) # # self._angleEntry = Entry(commandsFrame, state=DISABLED) # self._angleEntry.pack(side=LEFT) #Info infoFrame = tkFrame(self) infoFrame.grid(column=1, row=1, sticky="NE", padx=4) #Throttle Label(infoFrame, text="Throttle").grid(column=0, row=0, sticky="WE") self._throttleTexts = [ StringVar(), StringVar(), StringVar(), StringVar() ] Entry(infoFrame, textvariable=self._throttleTexts[3], state=DISABLED, width=5).grid(column=0, row=1) Entry(infoFrame, textvariable=self._throttleTexts[0], state=DISABLED, width=5).grid(column=1, row=1) Entry(infoFrame, textvariable=self._throttleTexts[2], state=DISABLED, width=5).grid(column=0, row=2) Entry(infoFrame, textvariable=self._throttleTexts[1], state=DISABLED, width=5).grid(column=1, row=2) #Angles Label(infoFrame, text="Angles").grid(column=0, row=3, sticky="WE") self._angleTexts = [StringVar(), StringVar(), StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._angleTexts[index], state=DISABLED, width=5).grid(column=index, row=4) #Accels Label(infoFrame, text="Accels").grid(column=0, row=5, sticky="WE") self._accelTexts = [StringVar(), StringVar(), StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._accelTexts[index], state=DISABLED, width=5).grid(column=index, row=6) #Speeds Label(infoFrame, text="Speeds").grid(column=0, row=7, sticky="WE") self._speedTexts = [StringVar(), StringVar(), StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._speedTexts[index], state=DISABLED, width=5).grid(column=index, row=8) #Height Label(infoFrame, text="Height").grid(column=0, row=9, sticky="E") self._heightText = StringVar() Entry(infoFrame, textvariable=self._heightText, state=DISABLED, width=5).grid(column=1, row=9) #Loop rate Label(infoFrame, text="Loop @").grid(column=0, row=10, sticky="E") self._loopRateText = StringVar() Entry(infoFrame, textvariable=self._loopRateText, state=DISABLED, width=5).grid(column=1, row=10) Label(infoFrame, text="Hz").grid(column=2, row=10, sticky="W") #control controlFrame = tkFrame(self) controlFrame.grid(column=0, row=1, sticky="W") self._throttle = DoubleVar() if Cockpit.THROTTLE_BY_USER: self._thrustScale = Scale(controlFrame, orient=VERTICAL, from_=100.0, to=0.0, \ tickinterval=0, variable=self._throttle, resolution=Cockpit.THROTTLE_RESOLUTION, \ length=200, showvalue=1, \ state=DISABLED, command=self._onThrustScaleChanged) else: self._thrustScale = Scale(controlFrame, orient=VERTICAL, from_=100.0, to=-100.0, \ tickinterval=0, variable=self._throttle, \ length=200, showvalue=1, \ state=DISABLED, command=self._onThrustScaleChanged) self._thrustScale.bind("<Double-Button-1>", self._onThrustScaleDoubleButton1, "+") self._thrustScale.grid(column=0) self._shiftCanvas = Canvas(controlFrame, bg="white", height=400, width=400, \ relief=SUNKEN) self._shiftCanvas.bind("<Button-1>", self._onMouseButton1) #self._shiftCanvas.bind("<ButtonRelease-1>", self._onMouseButtonRelease1) self._shiftCanvas.bind("<B1-Motion>", self._onMouseButton1Motion) self._shiftCanvas.bind("<Double-Button-1>", self._onMouseDoubleButton1) self._shiftCanvas.bind("<Button-3>", self._onMouseButton3) #self._shiftCanvas.bind("<ButtonRelease-3>", self._onMouseButtonRelease3) self._shiftCanvas.bind("<B3-Motion>", self._onMouseButton3Motion) self._shiftCanvas.grid(row=0, column=1, padx=2, pady=2) self._shiftCanvas.create_oval(1, 1, 400, 400, outline="#ff0000") self._shiftCanvas.create_line(200, 2, 200, 400, fill="#ff0000") self._shiftCanvas.create_line(2, 200, 400, 200, fill="#ff0000") self._shiftMarker = self._shiftCanvas.create_oval(196, 196, 204, 204, outline="#0000ff", fill="#0000ff") self._yaw = DoubleVar() self._yawScale = Scale(controlFrame, orient=HORIZONTAL, from_=-100.0, to=100.0, \ tickinterval=0, variable=self._yaw, \ length=200, showvalue=1, \ command=self._onYawScaleChanged) self._yawScale.bind("<Double-Button-1>", self._onYawScaleDoubleButton1, "+") self._yawScale.grid(row=1, column=1) self._controlKeyActive = False #PID calibration pidCalibrationFrame = tkFrame(self) pidCalibrationFrame.grid(column=0, row=2, sticky="WE") self._pidSelected = StringVar() self._pidSelected.set("--") self._pidListBox = OptionMenu(pidCalibrationFrame, self._pidSelected, "--", \ Cockpit.KEY_ANG_SPEED, Cockpit.KEY_ANGLES, Cockpit.KEY_ACCEL, \ command=self._onPidListBoxChanged) self._pidListBox.pack(side=LEFT, padx=2) self._pidListBox.config(width=10) self._axisSelected = StringVar() self._axisSelected.set("--") self._axisListBox = OptionMenu(pidCalibrationFrame, self._axisSelected, "--", "X", "Y", "Z", \ command=self._onAxisListBoxChanged) self._axisListBox.pack(side=LEFT, padx=2) self._axisListBox.config(state=DISABLED) Label(pidCalibrationFrame, text="P").pack(side=LEFT, padx=(14, 2)) self._pidPString = StringVar() self._pidPString.set("0.00") self._pidPSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=10000.0, increment=0.01, state=DISABLED, \ textvariable=self._pidPString, command=self._onPidSpinboxChanged) self._pidPSpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="I").pack(side=LEFT, padx=(14, 2)) self._pidIString = StringVar() self._pidIString.set("0.00") self._pidISpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=10000.0, increment=0.01, state=DISABLED, \ textvariable=self._pidIString, command=self._onPidSpinboxChanged) self._pidISpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="D").pack(side=LEFT, padx=(14, 2)) self._pidDString = StringVar() self._pidDString.set("0.00") self._pidDSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=10000.0, increment=0.01, state=DISABLED, \ textvariable=self._pidDString, command=self._onPidSpinboxChanged) self._pidDSpinbox.pack(side=LEFT, padx=2) #debug debugFrame = tkFrame(self) debugFrame.grid(column=0, row=3, sticky="WE") self._debugMsg = Message(debugFrame, anchor="nw", justify=LEFT, relief=SUNKEN, width=300) self._debugMsg.pack(fill=BOTH, expand=1) def _start(self): self._readDroneConfig() if Cockpit.JOYSTICK_ENABLED: self._joystickManager = JoystickManager.getInstance() self._joystickManager.start() joysticks = self._joystickManager.getJoysticks() if len(joysticks) != 0: self._joystick = joysticks[0] self._joystick.onAxisChanged += self._onJoystickAxisChanged self._joystick.onButtonPressed += self._onJoystickButtonPressed else: self._joystick = None def _onJoystickAxisChanged(self, sender, index): if self._started.get() and sender == self._joystick: axisValue = self._joystick.getAxisValue(index) if index == 0: self._yaw.set(axisValue) self._updateTarget() elif index == 1 and not Cockpit.THROTTLE_BY_USER: thrust = -axisValue self._throttle.set(thrust) self._updateTarget() elif index == 2 and Cockpit.THROTTLE_BY_USER: rowThrottle = (axisValue + 100.0) / 2.0 if rowThrottle < 10.0: throttle = rowThrottle * 6.0 elif rowThrottle < 90.0: throttle = 60.0 + ((rowThrottle - 10.0) / 8.0) else: throttle = 70.0 + (rowThrottle - 90.0) * 3.0 self._throttle.set(throttle) self._sendThrottle() elif index == 3: x = 196 + axisValue * 2 lastCoords = self._shiftCanvas.coords(self._shiftMarker) coords = (x, lastCoords[1]) self._plotShiftCanvasMarker(coords) elif index == 4: y = 196 + axisValue * 2 lastCoords = self._shiftCanvas.coords(self._shiftMarker) coords = (lastCoords[0], y) self._plotShiftCanvasMarker(coords) def _onJoystickButtonPressed(self, sender, index): if sender == self._joystick and index == 7: if self._started.get() == 0: self._startedCB.select() else: self._startedCB.deselect() # Tkinter's widgets seem not to be calling the event-handler # when they are changed programmatically. Therefore, the # even-handler is called explicitly here. self._startedCBChanged() def exit(self): self._link.send({"key": "close", "data": None}) self._stopUpdateInfoThread() self._link.close() if Cockpit.JOYSTICK_ENABLED: self._joystickManager.stop() self.quit() def _updateTarget(self): markerCoords = self._shiftCanvas.coords(self._shiftMarker) coords = ((markerCoords[0] + markerCoords[2]) / 2, (markerCoords[1] + markerCoords[3]) / 2) self._target[0] = float( coords[1] - 200) / 2.0 # X-axis angle / X-axis acceleration self._target[1] = float( coords[0] - 200) / 2.0 # Y-axis angle / Y-axis acceleration #Remote control uses clockwise angle, but the drone's referece system uses counter-clockwise angle self._target[2] = -self._yaw.get() # Z-axis angular speed # Z-axis acceleration (thrust). Only when the motor throttle is not controlled by user directly if Cockpit.THROTTLE_BY_USER: self._target[3] = 0.0 else: self._target[3] = self._throttle.get() self._sendTarget() def _keyDown(self, event): if event.keysym == "Escape": self._throttle.set(0) self._started.set(0) self._thrustScale.config(state=DISABLED) self._stopUpdateInfoThread() self._sendIsStarted() elif event.keysym.startswith("Control"): self._controlKeyActive = True elif not self._controlKeysLocked and self._controlKeyActive: if event.keysym == "Up": self._thrustScaleUp() elif event.keysym == "Down": self._thrustScaleDown() elif event.keysym == "Left": self._yawLeft() elif event.keysym == "Right": self._yawRight() elif event.keysym == "space": self._yawReset() if not Cockpit.THROTTLE_BY_USER: self._thrustReset() elif not self._controlKeysLocked and not self._controlKeyActive: if event.keysym == "Up": self._moveShiftCanvasMarker((0, -5)) elif event.keysym == "Down": self._moveShiftCanvasMarker((0, 5)) elif event.keysym == "Left": self._moveShiftCanvasMarker((-5, 0)) elif event.keysym == "Right": self._moveShiftCanvasMarker((5, 0)) elif event.keysym == "space": self._resetShiftCanvasMarker() def _keyUp(self, eventArgs): if eventArgs.keysym.startswith("Control"): self._controlKeyActive = False def _onMouseButton1(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseButtonRelease1(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 196, 196, 204, 204) def _limitCoordsToSize(self, coords, size, width): maxSize = size - (width / 2.0) minSize = -(width / 2.0) if coords[0] > maxSize: x = maxSize elif coords[0] < minSize: x = minSize else: x = coords[0] if coords[1] > maxSize: y = maxSize elif coords[1] < minSize: y = minSize else: y = coords[1] return (x, y) def _plotShiftCanvasMarker(self, coords): coords = self._limitCoordsToSize(coords, 400, 8) self._shiftCanvas.coords(self._shiftMarker, coords[0], coords[1], coords[0] + 8, coords[1] + 8) self._updateTarget() def _moveShiftCanvasMarker(self, shift): lastCoords = self._shiftCanvas.coords(self._shiftMarker) newCoords = (lastCoords[0] + shift[0], lastCoords[1] + shift[1]) self._plotShiftCanvasMarker(newCoords) def _resetShiftCanvasMarker(self): self._shiftCanvas.coords(self._shiftMarker, 196, 196, 204, 204) self._updateTarget() def _onMouseButton1Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseDoubleButton1(self, eventArgs): self._resetShiftCanvasMarker() def _onMouseButton3(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) self._mouseDirection = Cockpit.DIR_NONE def _onMouseButtonRelease3(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 196, 196, 204, 204) def _onMouseButton3Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) if self._mouseDirection == Cockpit.DIR_NONE: if abs(deltaCoords[0]) > abs(deltaCoords[1]): self._mouseDirection = Cockpit.DIR_HORIZONTAL else: self._mouseDirection = Cockpit.DIR_VERTICAL if self._mouseDirection == Cockpit.DIR_HORIZONTAL: deltaCoords = (deltaCoords[0], 0) else: deltaCoords = (0, deltaCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _thrustScaleUp(self): #TODO: 20160526 DPM: El valor de incremento de aceleración (1.0) puede ser muy alto if self._started.get(): newValue = self._thrustScale.get() \ + (Cockpit.THROTTLE_RESOLUTION if Cockpit.THROTTLE_BY_USER else 1.0) self._thrustScale.set(newValue) self._updateTarget() def _thrustScaleDown(self): #TODO: 20160526 DPM: El valor de decremento de aceleración (1.0) puede ser muy alto if self._started.get(): newValue = self._thrustScale.get() \ - (Cockpit.THROTTLE_RESOLUTION if Cockpit.THROTTLE_BY_USER else 1.0) self._thrustScale.set(newValue) self._updateTarget() def _thrustReset(self): if self._started.get(): self._thrustScale.set(0.0) self._updateTarget() def _onThrustScaleDoubleButton1(self, eventArgs): self._thrustReset() return "break" def _yawRight(self): newValue = self._yaw.get() + 1 self._yaw.set(newValue) self._updateTarget() def _yawLeft(self): newValue = self._yaw.get() - 1 self._yaw.set(newValue) self._updateTarget() def _yawReset(self): self._yaw.set(0) self._updateTarget() def _onMouseWheelUp(self, eventArgs): if not self._controlKeyActive: self._thrustScaleUp() else: self._yawRight() def _onMouseWheelDown(self, eventArgs): if not self._controlKeyActive: self._thrustScaleDown() else: self._yawLeft() def _onMouseWheel(self, eventArgs): factor = eventArgs.delta / (1200.0 if Cockpit.THROTTLE_BY_USER and not self._controlKeyActive else 120.0) if not self._controlKeyActive: if self._started.get(): newValue = self._thrustScale.get() + factor self._thrustScale.set(newValue) self._updateTarget() else: newValue = self._yaw.get() + factor self._yaw.set(newValue) self._updateTarget() def _onYawScaleChanged(self, eventArgs): self._updateTarget() def _onYawScaleDoubleButton1(self, eventArgs): self._yawReset() return "break" def _startedCBChanged(self): if not self._started.get(): self._throttle.set(0) self._thrustScale.config(state=DISABLED) #self._integralsCB.config(state=DISABLED) self._stopUpdateInfoThread() else: self._thrustScale.config(state="normal") #self._integralsCB.config(state="normal") self._startUpdateInfoThread() self._sendIsStarted() # def _integralsCBChanged(self): # # self._link.send({"key": "integrals", "data":self._integralsEnabled.get() != 0}) # def _onThrustScaleChanged(self, eventArgs): if Cockpit.THROTTLE_BY_USER: self._sendThrottle() else: self._updateTarget() def _sendThrottle(self): self._link.send({"key": "throttle", "data": self._throttle.get()}) def _sendTarget(self): self._link.send({"key": "target", "data": self._target}) def _sendIsStarted(self): isStarted = self._started.get() != 0 self._link.send({"key": "is-started", "data": isStarted}) def _sendPidCalibrationData(self): if self._pidSelected.get() != "--" and self._axisSelected.get( ) != "--": pidData = { "pid": self._pidSelected.get(), "axis": self._axisSelected.get(), "p": float(self._pidPSpinbox.get()), "i": float(self._pidISpinbox.get()), "d": float(self._pidDSpinbox.get()) } self._link.send({"key": "pid-calibration", "data": pidData}) def _updatePidCalibrationData(self): pid = self._pidSelected.get() axis = self._axisSelected.get() if pid != "--" and axis != "--": self._pidConstants[pid][axis]["P"] = float(self._pidPSpinbox.get()) self._pidConstants[pid][axis]["I"] = float(self._pidISpinbox.get()) self._pidConstants[pid][axis]["D"] = float(self._pidDSpinbox.get()) def _readDroneConfig(self): self._link.send({ "key": "read-drone-config", "data": None }, self._onDroneConfigRead) def _readDroneState(self): if not self._readingState: self._readingState = True self._link.send({ "key": "read-drone-state", "data": None }, self._onDroneStateRead) def _readPidConfigItem(self, message, cockpitKey, axises, configKeys): for i in range(len(axises)): for j in range(len(Cockpit.PID_KEYS)): self._pidConstants[cockpitKey][axises[i]][ Cockpit.PID_KEYS[j]] = message[configKeys[j]][i] def _onDroneConfigRead(self, message): #TODO Show current configuration within the GUI (at least relevant settings) if message: #Angle-speeds self._readPidConfigItem(message, Cockpit.KEY_ANG_SPEED, ["X", "Y", "Z"], \ [Configuration.PID_ANGLES_SPEED_KP, \ Configuration.PID_ANGLES_SPEED_KI, \ Configuration.PID_ANGLES_SPEED_KD]) #Angles self._readPidConfigItem(message, Cockpit.KEY_ANGLES, ["X", "Y"], \ [Configuration.PID_ANGLES_KP, \ Configuration.PID_ANGLES_KI, \ Configuration.PID_ANGLES_KD]) #Accels self._readPidConfigItem(message, Cockpit.KEY_ACCEL, ["X", "Y", "Z"], \ [Configuration.PID_ACCEL_KP, \ Configuration.PID_ACCEL_KI, \ Configuration.PID_ACCEL_KD]) def _onDroneStateRead(self, state): if state: for index in range(4): self._throttleTexts[index].set("{0:.3f}".format( state["_throttles"][index])) for index in range(3): self._accelTexts[index].set("{0:.3f}".format( state["_accels"][index])) self._angleTexts[index].set("{0:.3f}".format( state["_angles"][index])) currentPeriod = state["_currentPeriod"] if currentPeriod > 0.0: freq = 1.0 / currentPeriod self._loopRateText.set("{0:.3f}".format(freq)) else: self._loopRateText.set("--") else: self._stopUpdateInfoThread() self._readingState = False def _onPidSpinboxChanged(self): self._updatePidCalibrationData() self._sendPidCalibrationData() def _onPidListBoxChanged(self, pid): self._axisSelected.set("--") self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._selectedPidConstats = pid if pid == "--": self._axisListBox.config(state=DISABLED) self._controlKeysLocked = False else: self._axisListBox.config(state="normal") self._controlKeysLocked = True def _onAxisListBoxChanged(self, axis): if axis == "--" or (self._selectedPidConstats == Cockpit.KEY_ANGLES and axis == "Z"): self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._controlKeysLocked = axis != "--" else: self._pidPString.set("{:.2f}".format( self._pidConstants[self._selectedPidConstats][axis]["P"])) self._pidIString.set("{:.2f}".format( self._pidConstants[self._selectedPidConstats][axis]["I"])) self._pidDString.set("{:.2f}".format( self._pidConstants[self._selectedPidConstats][axis]["D"])) self._pidPSpinbox.config(state="normal") self._pidISpinbox.config(state="normal") self._pidDSpinbox.config(state="normal") self._controlKeysLocked = True def _updateInfo(self): while self._updateInfoThreadRunning: self._readDroneState() time.sleep(1.0) def _startUpdateInfoThread(self): self._updateInfoThreadRunning = True if not self._updateInfoThread.isAlive(): self._updateInfoThread.start() def _stopUpdateInfoThread(self): self._updateInfoThreadRunning = False if self._updateInfoThread.isAlive(): self._updateInfoThread.join()
class EnviroDialog(Dialog): """ Dialog for editing the simulation environment details """ def __init__(self, parent, populator=None, manager=None): """ Construct the dialog :param parent: The tk element that is the parent of the dialog :return: An instance of EnviroDialog """ self._entries = { "floor_texture": None, "wall_height": None, "edge_width": None, "sky_texture": None, "start_node": None } Dialog.__init__(self, parent=parent, title="EnvironmentConfiguration", populator=populator, manager=manager) def body(self, parent): """ Overridden method defining the body of the dialog :param parent: :return: """ self._floorSel = ImagePicker(parent, "Floor Texture:", default=self._entries["floor_texture"], auto_move=True, move_fold="Data") self._floorSel.grid(row=0, columnspan=4) self._skySel = ImagePicker(parent, "Sky Texture:", default=self._entries["sky_texture"], auto_move=True, move_fold="Data") self._skySel.grid(row=1, columnspan=4) Label(parent, text="Wall Height:", width=10, anchor=W).grid(row=2, column=0, sticky=W) Label(parent, text="Edge Width:", width=10, anchor=W).grid(row=3, column=0, sticky=W) self._wallScale = Scale(parent, from_=10, to=1000, orient=HORIZONTAL) if self._entries["wall_height"] is not None: self._wallScale.set(self._entries["wall_height"]) self._wallScale.grid(row=2, column=1, columnspan=2, sticky=W) self._edgeScale = Scale(parent, from_=10, to=1000, orient=HORIZONTAL) if self._entries["edge_width"] is not None: self._edgeScale.set(self._entries["edge_width"]) self._edgeScale.grid(row=3, column=1, columnspan=2, sticky=W) Label(parent, text="Starting Node:", anchor=W).grid(row=4, column=0, sticky=W) self._start_node = Label(parent, text=self._entries["start_node"], anchor=W) self._start_node.grid(row=4, column=1, sticky=W) def populate(self, manager): self._entries["floor_texture"] = manager.floor_texture self._entries["edge_width"] = manager.edge_width self._entries["sky_texture"] = manager.sky_texture self._entries["start_node"] = manager.start_node self._entries["edge_width"] = manager.edge_width self._entries["wall_height"] = manager.wall_height def validate(self): (result, message) = DataValidator.validate(DataStore.EVENT.ENVIRONMENT_EDIT, { "floor_texture" : self._floorSel.get(), "sky_texture" : self._skySel.get() }) if result is not True: tkMessageBox.showerror("Input Error", message) return result def apply(self): self._entries["floor_texture"] = self._floorSel.get() self._entries["edge_width"] = self._edgeScale.get() self._entries["sky_texture"] = self._skySel.get() self._entries["wall_height"] = self._wallScale.get() self._manager.inform(DataStore.EVENT.ENVIRONMENT_EDIT, self._entries) def auto_populate(self): self._floorSel.set(Defaults.Environment.FLOOR_TEXTURE) self._skySel.set(Defaults.Environment.SKY_TEXTURE) self._wallScale.set(Defaults.Environment.WALL_HEIGHT) self._edgeScale.set(Defaults.Environment.EDGE_WIDTH) self._start_node.config(text=Defaults.Environment.START_NODE)
class SettingWindow(Toplevel): def __init__(self, master, max_num_features, num_frames, mser_image): Toplevel.__init__(self, master) self.protocol('WM_DELETE_WINDOW', self.withdraw) self.notebook = ttk.Notebook(self) frame_feats = ttk.Frame(self.notebook) frame_forest = ttk.Frame(self.notebook) frame_mser = ttk.Frame(self.notebook) frame_other = ttk.Frame(self.notebook) self.notebook.add(frame_feats, text="Features ") self.notebook.add(frame_forest, text=" Forest ") self.notebook.add(frame_mser, text=" MSER ") self.notebook.add(frame_other, text=" Other ") self.max_num_feats = max_num_features self.selection = None self.mser_image = mser_image rand_row = random.randint(1, 512 - 200) rand_col = random.randint(1, 512 - 110) self.mser_area = mser_image[rand_row:rand_row + 180, rand_col:rand_col + 100] # read images from icons folder self.hf0_img = PhotoImage(file="./icons/hf0.gif") self.hf1_img = PhotoImage(file="./icons/hf1.gif") self.hf2_img = PhotoImage(file="./icons/hf2.gif") self.hf3_img = PhotoImage(file="./icons/hf3.gif") self.hf4_img = PhotoImage(file="./icons/hf4.gif") self.hf5_img = PhotoImage(file="./icons/hf5.gif") self.features_vars = list() for i in range(max_num_features): self.features_vars.append(IntVar()) Label(frame_feats, text="Patch size (" + u"\N{GREEK SMALL LETTER PI}" + "):").grid( row=0, column=0, pady=5) self.patch_size_spinbox = Spinbox(frame_feats, from_=3, to=30, width=3) self.patch_size_spinbox.delete(0, END) self.patch_size_spinbox.insert(END, 10) self.patch_size_spinbox.grid(row=0, column=1, padx=5) f1 = ttk.Labelframe(frame_feats, text='Mean filter') f1.grid(row=1, columnspan=2) Label(f1, text=u"\N{GREEK SMALL LETTER PI}").grid(row=0, column=0) Checkbutton(f1, text="R", variable=self.features_vars[0]).grid(row=0, column=1) Checkbutton(f1, text="G", variable=self.features_vars[1]).grid(row=0, column=2) Checkbutton(f1, text="B", variable=self.features_vars[2]).grid(row=0, column=3) Label(f1, text=u"\N{GREEK SMALL LETTER PI}" + "/2").grid(row=1, column=0) Checkbutton(f1, text="R", variable=self.features_vars[3]).grid(row=1, column=1) Checkbutton(f1, text="G", variable=self.features_vars[4]).grid(row=1, column=2) Checkbutton(f1, text="B", variable=self.features_vars[5]).grid(row=1, column=3) f2 = ttk.Labelframe(frame_feats, text="Gaussian filter") f2.grid(row=2, columnspan=2) Label(f2, text=str(1.0)).grid(row=0, column=0) Checkbutton(f2, text="R", variable=self.features_vars[6]).grid(row=0, column=1) Checkbutton(f2, text="G", variable=self.features_vars[7]).grid(row=0, column=2) Checkbutton(f2, text="B", variable=self.features_vars[8]).grid(row=0, column=3) Label(f2, text=str(3.5)).grid(row=1, column=0) Checkbutton(f2, text="R", variable=self.features_vars[9]).grid(row=1, column=1) Checkbutton(f2, text="G", variable=self.features_vars[10]).grid(row=1, column=2) Checkbutton(f2, text="B", variable=self.features_vars[11]).grid(row=1, column=3) f3 = ttk.Labelframe(frame_feats, text="Laplacian of gaussian") f3.grid(row=3, columnspan=2) Label(f3, text=str(2.0)).grid(row=0, column=0) Checkbutton(f3, text="R", variable=self.features_vars[12]).grid(row=0, column=1) Checkbutton(f3, text="G", variable=self.features_vars[13]).grid(row=0, column=2) Checkbutton(f3, text="B", variable=self.features_vars[14]).grid(row=0, column=3) Label(f3, text=str(3.5)).grid(row=1, column=0) Checkbutton(f3, text="R", variable=self.features_vars[15]).grid(row=1, column=1) Checkbutton(f3, text="G", variable=self.features_vars[16]).grid(row=1, column=2) Checkbutton(f3, text="B", variable=self.features_vars[17]).grid(row=1, column=3) f4 = ttk.Labelframe(frame_feats, text="Haar-like features") f4.grid(row=1, rowspan=2, column=3, padx=5) Checkbutton(f4, image=self.hf0_img, variable=self.features_vars[18]).grid(row=0, column=0) Checkbutton(f4, image=self.hf1_img, variable=self.features_vars[19]).grid(row=0, column=1) Checkbutton(f4, image=self.hf2_img, variable=self.features_vars[20]).grid(row=1, column=0) Checkbutton(f4, image=self.hf3_img, variable=self.features_vars[21]).grid(row=1, column=1) Checkbutton(f4, image=self.hf4_img, variable=self.features_vars[22]).grid(row=2, column=0) Checkbutton(f4, image=self.hf5_img, variable=self.features_vars[23]).grid(row=2, column=1) buttons_paned_window = PanedWindow(frame_feats, orient=VERTICAL) buttons_paned_window.grid(row=3, column=3) self.select_all_button = Button(buttons_paned_window, text="Select all", command=self._select_all) buttons_paned_window.add(self.select_all_button) self.clear_selection_button = Button(buttons_paned_window, text="Clear selection", command=self._clear_selection) buttons_paned_window.add(self.clear_selection_button) # default values for j in [0, 1, 3, 6, 7, 9, 15, 21, 23]: self.features_vars[j].set(1) # FOREST FRAMES # number of trees f5 = ttk.Labelframe(frame_forest, text="Number of trees") f5.grid(row=0, columnspan=2, pady=5, padx=5) Label(f5, text="N").grid(row=1, column=0) self.num_trees_scale = Scale(f5, from_=5, to=500, resolution=5, orient=HORIZONTAL) self.num_trees_scale.set(300) self.num_trees_scale.grid(row=0, column=1, rowspan=2) # depth single tree f6 = ttk.Labelframe(frame_forest, text="Depth single tree") f6.grid(row=1, columnspan=2, pady=5, padx=5) Label(f6, text="d").grid(row=1, column=0) self.depth_tree_scale = Scale(f6, from_=2, to=20, orient=HORIZONTAL) self.depth_tree_scale.set(3) self.depth_tree_scale.grid(row=0, column=1, rowspan=2) # percentage number of features f7 = ttk.Labelframe(frame_forest, text="% subset of features") f7.grid(row=2, columnspan=2, pady=5, padx=5) Label(f7, text="m").grid(row=1, column=0) self.percentage_feats_scale = Scale(f7, from_=0.0, to=1, resolution=0.05, orient=HORIZONTAL) self.percentage_feats_scale.set(0.5) self.percentage_feats_scale.grid(row=0, column=1, rowspan=2) # mser frame # delta f8 = ttk.Labelframe(frame_mser, text="Delta") f8.grid(row=0, columnspan=2, pady=5, padx=5) Label(f8, text=u"\N{GREEK SMALL LETTER DELTA}").grid(row=1, column=0) self.delta_scale = Scale(f8, from_=1, to=10, resolution=1, orient=HORIZONTAL) self.delta_scale.set(2) self.delta_scale.grid(row=0, column=1, rowspan=2) # min area f9 = ttk.Labelframe(frame_mser, text="Minimum area") f9.grid(row=1, columnspan=2, pady=5, padx=5) Label(f9, text="m").grid(row=1, column=0) self.min_area_scale = Scale(f9, from_=2, to=200, orient=HORIZONTAL) self.min_area_scale.set(10) self.min_area_scale.grid(row=0, column=1, rowspan=2) # percentage number of features f10 = ttk.Labelframe(frame_mser, text="Maximum area") f10.grid(row=2, columnspan=2, pady=5, padx=5) Label(f10, text="M").grid(row=1, column=0) self.max_area_scale = Scale(f10, from_=50, to=1000, resolution=5, orient=HORIZONTAL) self.max_area_scale.set(350) self.max_area_scale.grid(row=0, column=1, rowspan=2) # mser image f11 = ttk.Labelframe(frame_mser) f11.grid(row=0, rowspan=3, column=2, padx=5) self.mser_img_array = Image.fromarray(self.mser_area, "RGB") self.mser_img = ImageTk.PhotoImage(self.mser_img_array) img_label = Label(f11, image=self.mser_img) img_label.grid(row=0, column=0) buttons_p_w_mser = PanedWindow(f11, orient=HORIZONTAL) try_button = Button(f11, text="Try", command=self.try_mser) buttons_p_w_mser.add(try_button) change_button = Button(f11, text="New img", command=self.change_mser) buttons_p_w_mser.add(change_button) buttons_p_w_mser.grid(row=1, column=0) # other frame f12 = ttk.Labelframe(frame_other, text="Refinement") f12.grid(row=0, columnspan=2, pady=5, padx=5) Label(f12, text=u"\N{GREEK CAPITAL LETTER PHI}_l").grid(row=1, column=0) self.low_thresh_scale = Scale(f12, from_=0, to=1, resolution=0.05, orient=HORIZONTAL, length=90) self.low_thresh_scale.set(0.45) self.low_thresh_scale.grid(row=0, column=1, rowspan=2) Label(f12, text=u"\N{GREEK CAPITAL LETTER PHI}_h").grid(row=3, column=0) self.high_thresh_scale = Scale(f12, from_=0, to=1, resolution=0.05, orient=HORIZONTAL, length=90) self.high_thresh_scale.set(0.65) self.high_thresh_scale.grid(row=2, column=1, rowspan=2) f13 = ttk.Labelframe(frame_other, text="Dots distance") f13.grid(row=1, columnspan=2, pady=5, padx=5) Label(f13, text=u" \N{GREEK SMALL LETTER SIGMA}").grid(row=1, column=0) self.dots_distance_scale = Scale(f13, from_=1, to=20, resolution=1, orient=HORIZONTAL, length=90) self.dots_distance_scale.set(6) self.dots_distance_scale.grid(row=0, column=1, rowspan=2) f14 = ttk.Labelframe(frame_other, text="Tracks") f14.grid(row=0, column=3, pady=5, padx=5) Label(f14, text="N").grid(row=1, column=0) self.num_frames_tracks_spinbox = Spinbox(f14, from_=2, to=num_frames, width=10) self.num_frames_tracks_spinbox.delete(0, END) self.num_frames_tracks_spinbox.insert(END, num_frames) self.num_frames_tracks_spinbox.grid(row=0, column=1, rowspan=2) Label(f14, text=u"\N{GREEK SMALL LETTER TAU}").grid(row=3, column=0) self.gaps_scale = Scale(f14, from_=1, to=10, resolution=1, orient=HORIZONTAL, length=90) self.gaps_scale.set(2) self.gaps_scale.grid(row=2, column=1, rowspan=2) self.notebook.pack(padx=1, pady=1) save_button = Button(self, text=" Save and Close window ", command=self.withdraw) save_button.pack(pady=2) def _select_all(self): for i, var in enumerate(self.features_vars): var.set(1) def _clear_selection(self): for i, var in enumerate(self.features_vars): var.set(0) def change_mser(self): rand_row = random.randint(1, 512 - 200) rand_col = random.randint(1, 512 - 110) self.mser_area = self.mser_image[rand_row:rand_row + 180, rand_col:rand_col + 100] self.update_mser_image(self.mser_area) def try_mser(self): delta = self.delta_scale.get() min_area = self.min_area_scale.get() max_area = self.max_area_scale.get() image = self.mser_area red_c = image[:, :, 0] red_c = cv2.equalizeHist(red_c) det_img = image.copy() mser = cv2.MSER(delta, _min_area=min_area, _max_area=max_area) regions = mser.detect(red_c) cp = list() new_c = np.zeros(self.mser_area.shape, dtype=np.uint8) for r in regions: for point in r: cp.append(point) det_img[point[1], point[0], 0] = 0 det_img[point[1], point[0], 1] = 0 det_img[point[1], point[0], 2] = 204 #new_c[point[1], point[0]] = 255 self.update_mser_image(det_img) def update_mser_image(self, new_image): self.mser_img_array = Image.fromarray(new_image) self.mser_img.paste(self.mser_img_array) def get_patch_size(self): patch_size = self.patch_size_spinbox.get() return int(patch_size) def get_num_frames_tracks(self): num_frames_tracks = self.num_frames_tracks_spinbox.get() return int(num_frames_tracks) def get_mser_opts(self): return [ self.delta_scale.get(), self.min_area_scale.get(), self.max_area_scale.get() ] def get_forest_opts(self): return [ self.num_trees_scale.get(), self.depth_tree_scale.get(), self.percentage_feats_scale.get() ] def get_low_thresh(self): return self.low_thresh_scale.get() def get_high_thresh(self): return self.high_thresh_scale.get() def get_dots_distance(self): return int(self.dots_distance_scale.get()) def get_selection_mask(self): if self.selection is not None: return self.selection selection_mask = np.zeros((self.max_num_feats, ), dtype='bool') for i, var in enumerate(self.features_vars): selection_mask[i] = var.get() self.selection = selection_mask return selection_mask
class Logplayer(Frame): def __init__(self, root, logfn, viewer, analyzator=None, logdir=None, **kwargs): self.root=root self.logdir = logdir self.analyzator = analyzator if not self.logdir: self.logdir=os.path.join(os.path.dirname(os.path.dirname(__file__)),'log') Frame.__init__(self, **kwargs) self.view=viewer(self, x=800, y=600, onLeft=self.clickL, onRight=self.clickR) self.createWidgets() self.bind_all("<Left>", self.prev) self.bind_all("<Right>", self.next) self.view.redraw() self.root.update() if not logfn: logfn = self.logSelectDialog() self.dirname, self.filename = os.path.split(logfn) self.startRun(logfn,0) self.view.redraw() self.root.after(10,self.view.zoomExtens) self.running=False def logSelectDialog(self): self.root.update() timestamps, dirs, fns = zip(*sorted(listdir(self.logdir), reverse=True)) o=SelcectDialog(self.root, "select log", timestamps) self.root.wait_window(o.top) index=timestamps.index(o.option) return os.path.join(dirs[index], fns[index]) def createWidgets(self): self.controlFrame=Frame(self) self.posScale = Scale(self.controlFrame, orient='horizontal', length=210, from_=0, tickinterval=0, command=self.pos_callback, showvalue=0,sliderlength=10, resolution=1) #self.posScale.bind('<Button-1>',self.pos_start_drag) #self.posScale.bind('<ButtonRelease-1>', self.pos_set) self.playB=Button(self.controlFrame, text="play", command=self.playToggle) self.nextB=Button(self.controlFrame, text="next", command=self.nextRun) self.prevB=Button(self.controlFrame, text="prev", command=self.prevRun) self.camera=tkimg(self, width=640, height=512) self.controlFrame.grid(row=0,column=1, sticky="nw") self.view.grid(row=1, column=1, sticky="nsew") self.grid_columnconfigure(1,weight=1) self.grid_rowconfigure(1,weight=1) self.camera.grid(row = 1, column = 2, sticky = 'ne') self.playB.grid(row=0, column=1) self.prevB.grid(row=0, column=2) self.nextB.grid(row=0, column=3) self.posScale.grid(row=0, column=0) def playToggle(self, *args): if self.running: self.root.after_cancel(self.cycleId) else: self.cycleId=self.root.after(20, self.showrun) self.running=not self.running def nextRun(self, *args): self.changeRun(1) def prevRun(self, *args): self.changeRun(-1) def changeRun(self, step): if self.running: self.root.after_cancel(self.cycleId) self.running=False run=self.loggenerator.run+step if run >= len(self.loggenerator.runconfig) or run<0: timestamps, dirs, fns = zip(*sorted(listdir(self.logdir))) index=fns.index(self.filename)+step if 0<=index<len(fns): run = 0 if step==1 else -1 self.startRun(os.path.join(dirs[index], fns[index]), run) else: self.startRun(os.path.join(self.dirname, self.filename), run) def restart(self, keeppostion=True, startposition=None): self.loggenerator.startrun(self.loggenerator.run, startposition=startposition) self.data=list(self.loggenerator) if not keeppostion: self.position=0 self.posScale.set(self.position) self.showData(self.position) def startRun(self, fn, run, keeppostion=False): self.dirname, self.filename = os.path.split(fn) print("starting log %s run %i"%(fn,run)) self.loggenerator=EduroMaxiReader(fn) self.loggenerator.startrun(run) self.root.title("%s %s run %i"%(self.dirname,self.filename, self.loggenerator.run+1)) self.data=list(self.loggenerator) if not keeppostion: self.position=0 self.posScale.set(self.position) self.showData(self.position) self.posScale.configure(to_= len(self.data)-1) self.view.reset() self.camera.clear() def showData(self,position): self.position=position self.posScale.set(position) time, pose, msgs = self.data[position] self.view.MsgListener(time, pose, msgs) #if self.analyzator: # self.analyzator(time, pose, msgs) self.view.robot.update(*pose) self.view.redraw_lasers() self.view.robot.redraw(self.view) if msgs['CAMERALOG']: _, shot = msgs['CAMERALOG'] if shot: shot = os.path.join(self.dirname, shot.split('/')[-1]) self.camera.showImg(shot) def clickL(self, x, y): #self.loggenerator.correction=Position(self.loggenerator.correction.x+x-self.view.robot.x, self.loggenerator.correction.y+y-self.view.robot.y, self.loggenerator.correction.a) #print(self.loggenerator.correction) A=self.data[self.position][1] S=self.data[0][1] #self.restart(keeppostion=True, startposition=Position(S.x+x-A.x, S.y+y-A.y, S.a)) print("left", x,y) def clickR(self, x, y): #try to modify start position so that actual position heading is in direction of this click #actual position A=self.data[self.position][1] #start position S=self.data[0][1] #polar vector from actual to start dist=((A.x-S.x)**2+(A.y-S.y)**2)**0.5 if S.x==A.x:#when x distance is 0 then angle +-90 (when y dist is 0 then it doesnt matter beta=-pi/2 if S.y>=A.y else pi/2 else: beta=atan((A.y-S.y)/(A.x-S.x)) #angle difference from actual position to click #TODO solve +-90 same as above delta=A.a-atan((A.y-y)/(A.x-x)) newStart = Position(A.x+cos(beta+delta)*dist, A.y+sin(beta+delta)*dist, S.a-delta) #self.restart(keeppostion=True, startposition=newStart) print("right", x, y) def showrun(self): pos=self.position+1 if pos<len(self.data): self.showData(pos) self.cycleId=self.root.after(20, self.showrun) else: self.running=False def next(self, event): pos=self.position+1 if pos<len(self.data): self.posScale.set(pos) def prev(self, event): pos=self.position-1 if pos>=0: self.posScale.set(pos) def pos_callback(self, pos, *args): if not self.running: self.showData(int(pos))
def gui(): from Tkinter import Tk, Label, Entry,Button, Scale, Checkbutton,W,HORIZONTAL, Frame, StringVar, IntVar, DoubleVar, Radiobutton, BooleanVar, E global root root = Tk() root.wm_title("Compute R_complete") line = 0 global insFile, hklFile, nHKL, nParams, nHKLLabel, fracFree, status, nParamsLabel, nCPU, rCompleteLabel, cycles, lsType, cleanup,nFree, nRunsLabel, mergeCheck, compileMap insFile = StringVar() hklFile = StringVar() nHKL = IntVar() nParams = IntVar() nFree = IntVar() fracFree = DoubleVar() fracFree.set(5.0) nCPU = IntVar() nCPU.set(maxCPU) cycles = IntVar() cycles.set(10) lsType = IntVar() lsType.set(1) cleanup = BooleanVar() cleanup.set(True) mergeCheck = BooleanVar() mergeCheck.set(True) compileMap = BooleanVar() compileMap.set(True) Label(root, text='Instruction File:').grid(row=line, column=0, sticky=E) Entry(root, textvariable=insFile).grid(row=line, column=1) Button(root, text='Browse', command=browseINS).grid(row=line, column=2) line += 1 Label(root, text='Reflection File:').grid(row=line, column=0, sticky=E) Entry(root, textvariable=hklFile).grid(row=line, column=1) Button(root, text='Browse', command=browseHKL).grid(row=line, column=2) line += 1 Checkbutton(root, var=mergeCheck, text='Merge Reflections').grid(row=line, column=1, sticky=W) line += 1 Button(root, text='Load', command=load).grid(row=line, columnspan=3) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='# of reflections:').grid(row=line, sticky=E) nHKLLabel = Label(root, text='???') nHKLLabel.grid(row=line, column=1, sticky=W) line += 1 Label(root, text='# of atoms:').grid(row=line, sticky=E) nParamsLabel = Label(root, text='???') nParamsLabel.grid(row=line, column=1, sticky=W) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='Select Parameters').grid(row=line, column=1) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='# of free reflections:').grid(row=line, sticky=E) nFreeEntry = Entry(root, width=5, textvariable=nFree) nFreeEntry.grid(row=line, column=1, sticky=W) nFreeEntry.bind('<Return>', setScale) nRunsLabel = Label(root, text='# runs') nRunsLabel.grid(row=line, column=2) line += 1 Label(root, text='% of free reflections:').grid(row=line, column=0, sticky=E) w = Scale(root, from_=0.1, to=10.0, resolution=0.1, orient=HORIZONTAL, length=200, var=fracFree, command=percentScale) w.grid(row=line, column=1, columnspan=2, sticky=W) line += 1 Label(root, text='stable <-------------------------------> fast').grid(row=line, column=1, columnspan=2, sticky=W) line += 1 Frame(root, height=10).grid(row=line) line += 1 Label(root, text='Refinement cycles:').grid(row=line, column=0, sticky=E) ls = Scale(root, from_=0, to=50, resolution=1, orient=HORIZONTAL, length=200, var=cycles) ls.grid(row=line, column=1, columnspan=2, sticky=W) line += 1 Label(root, text='fast <--------------------> less model bias').grid(row=line, column=1, columnspan=2, sticky=W) line += 1 Frame(root, height=10).grid(row=line) line += 1 Label(root, text='# of CPUs:').grid(row=line, column=0, sticky=E) ww = Scale(root, from_=1, to=maxCPU, orient=HORIZONTAL, length=200, var=nCPU) ww.grid(row=line, column=1, columnspan=2, sticky=W) line += 1 Label(root, text='Refinement Type:').grid(row=line, column=0, sticky=E) Radiobutton(root, text='CGLS', var=lsType, value=1).grid(row=line, column=1, sticky=W) Radiobutton(root, text='L.S.', var=lsType, value=2).grid(row=line, column=2, sticky=W) line += 1 Frame(root, height=10).grid(row=line) line += 1 Label(root, text='Compile map:').grid(row=line, column=0, sticky=E) Checkbutton(root, var=compileMap).grid(row=line, column=1, sticky=W) line += 1 Label(root, text='Cleanup:').grid(row=line, column=0, sticky=E) Checkbutton(root, var=cleanup).grid(row=line, column=1, sticky=W) line += 1 Button(root, text='RUN', command=run, width=25).grid(row=line, columnspan=3) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='R_complete:').grid(row=line, column=0, sticky=E) rCompleteLabel = Label(root, text='???') rCompleteLabel.grid(row=line, column=1, sticky=W) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='Status:').grid(row=line, column=0, sticky=E) status = Label(root, text='Idle... Please load files.') status.grid(row=line, column=1, columnspan=2, sticky=W) global IDLE IDLE = True root.mainloop()
class SettingWindow(Toplevel): def __init__(self, master, max_num_features, num_frames, mser_image): Toplevel.__init__(self, master) self.protocol('WM_DELETE_WINDOW', self.withdraw) self.notebook = ttk.Notebook(self) frame_feats = ttk.Frame(self.notebook) frame_forest = ttk.Frame(self.notebook) frame_mser = ttk.Frame(self.notebook) frame_other = ttk.Frame(self.notebook) self.notebook.add(frame_feats, text="Features ") self.notebook.add(frame_forest, text=" Forest ") self.notebook.add(frame_mser, text=" MSER ") self.notebook.add(frame_other, text=" Other ") self.max_num_feats = max_num_features self.selection = None self.mser_image = mser_image rand_row = random.randint(1, 512-200) rand_col = random.randint(1, 512-110) self.mser_area = mser_image[rand_row:rand_row+180, rand_col:rand_col+100] # read images from icons folder self.hf0_img = PhotoImage(file="./icons/hf0.gif") self.hf1_img = PhotoImage(file="./icons/hf1.gif") self.hf2_img = PhotoImage(file="./icons/hf2.gif") self.hf3_img = PhotoImage(file="./icons/hf3.gif") self.hf4_img = PhotoImage(file="./icons/hf4.gif") self.hf5_img = PhotoImage(file="./icons/hf5.gif") self.features_vars = list() for i in range(max_num_features): self.features_vars.append(IntVar()) Label(frame_feats, text="Patch size (" + u"\N{GREEK SMALL LETTER PI}" + "):").grid(row=0, column=0, pady=5) self.patch_size_spinbox = Spinbox(frame_feats, from_=3, to=30, width=3) self.patch_size_spinbox.delete(0, END) self.patch_size_spinbox.insert(END, 10) self.patch_size_spinbox.grid(row=0, column=1, padx=5) f1 = ttk.Labelframe(frame_feats, text='Mean filter') f1.grid(row=1, columnspan=2) Label(f1, text=u"\N{GREEK SMALL LETTER PI}").grid(row=0, column=0) Checkbutton(f1, text="R", variable=self.features_vars[0]).grid(row=0, column=1) Checkbutton(f1, text="G", variable=self.features_vars[1]).grid(row=0, column=2) Checkbutton(f1, text="B", variable=self.features_vars[2]).grid(row=0, column=3) Label(f1, text=u"\N{GREEK SMALL LETTER PI}" + "/2").grid(row=1, column=0) Checkbutton(f1, text="R", variable=self.features_vars[3]).grid(row=1, column=1) Checkbutton(f1, text="G", variable=self.features_vars[4]).grid(row=1, column=2) Checkbutton(f1, text="B", variable=self.features_vars[5]).grid(row=1, column=3) f2 = ttk.Labelframe(frame_feats, text="Gaussian filter") f2.grid(row=2, columnspan=2) Label(f2, text=str(1.0)).grid(row=0, column=0) Checkbutton(f2, text="R", variable=self.features_vars[6]).grid(row=0, column=1) Checkbutton(f2, text="G", variable=self.features_vars[7]).grid(row=0, column=2) Checkbutton(f2, text="B", variable=self.features_vars[8]).grid(row=0, column=3) Label(f2, text=str(3.5)).grid(row=1, column=0) Checkbutton(f2, text="R", variable=self.features_vars[9]).grid(row=1, column=1) Checkbutton(f2, text="G", variable=self.features_vars[10]).grid(row=1, column=2) Checkbutton(f2, text="B", variable=self.features_vars[11]).grid(row=1, column=3) f3 = ttk.Labelframe(frame_feats, text="Laplacian of gaussian") f3.grid(row=3, columnspan=2) Label(f3, text=str(2.0)).grid(row=0, column=0) Checkbutton(f3, text="R", variable=self.features_vars[12]).grid(row=0, column=1) Checkbutton(f3, text="G", variable=self.features_vars[13]).grid(row=0, column=2) Checkbutton(f3, text="B", variable=self.features_vars[14]).grid(row=0, column=3) Label(f3, text=str(3.5)).grid(row=1, column=0) Checkbutton(f3, text="R", variable=self.features_vars[15]).grid(row=1, column=1) Checkbutton(f3, text="G", variable=self.features_vars[16]).grid(row=1, column=2) Checkbutton(f3, text="B", variable=self.features_vars[17]).grid(row=1, column=3) f4 = ttk.Labelframe(frame_feats, text="Haar-like features") f4.grid(row=1, rowspan=2, column=3, padx=5) Checkbutton(f4, image=self.hf0_img, variable=self.features_vars[18]).grid(row=0, column=0) Checkbutton(f4, image=self.hf1_img, variable=self.features_vars[19]).grid(row=0, column=1) Checkbutton(f4, image=self.hf2_img, variable=self.features_vars[20]).grid(row=1, column=0) Checkbutton(f4, image=self.hf3_img, variable=self.features_vars[21]).grid(row=1, column=1) Checkbutton(f4, image=self.hf4_img, variable=self.features_vars[22]).grid(row=2, column=0) Checkbutton(f4, image=self.hf5_img, variable=self.features_vars[23]).grid(row=2, column=1) buttons_paned_window = PanedWindow(frame_feats, orient=VERTICAL) buttons_paned_window.grid(row=3, column=3) self.select_all_button = Button(buttons_paned_window, text="Select all", command=self._select_all) buttons_paned_window.add(self.select_all_button) self.clear_selection_button = Button(buttons_paned_window, text="Clear selection", command=self._clear_selection) buttons_paned_window.add(self.clear_selection_button) # default values for j in [0, 1, 3, 6, 7, 9, 15, 21, 23]: self.features_vars[j].set(1) # FOREST FRAMES # number of trees f5 = ttk.Labelframe(frame_forest, text="Number of trees") f5.grid(row=0, columnspan=2, pady=5, padx=5) Label(f5, text="N").grid(row=1, column=0) self.num_trees_scale = Scale(f5, from_=5, to=500, resolution=5, orient=HORIZONTAL) self.num_trees_scale.set(300) self.num_trees_scale.grid(row=0, column=1, rowspan=2) # depth single tree f6 = ttk.Labelframe(frame_forest, text="Depth single tree") f6.grid(row=1, columnspan=2, pady=5, padx=5) Label(f6, text="d").grid(row=1, column=0) self.depth_tree_scale = Scale(f6, from_=2, to=20, orient=HORIZONTAL) self.depth_tree_scale.set(3) self.depth_tree_scale.grid(row=0, column=1, rowspan=2) # percentage number of features f7 = ttk.Labelframe(frame_forest, text="% subset of features") f7.grid(row=2, columnspan=2, pady=5, padx=5) Label(f7, text="m").grid(row=1, column=0) self.percentage_feats_scale = Scale(f7, from_=0.0, to=1, resolution=0.05, orient=HORIZONTAL) self.percentage_feats_scale.set(0.5) self.percentage_feats_scale.grid(row=0, column=1, rowspan=2) # mser frame # delta f8 = ttk.Labelframe(frame_mser, text="Delta") f8.grid(row=0, columnspan=2, pady=5, padx=5) Label(f8, text=u"\N{GREEK SMALL LETTER DELTA}").grid(row=1, column=0) self.delta_scale = Scale(f8, from_=1, to=10, resolution=1, orient=HORIZONTAL) self.delta_scale.set(2) self.delta_scale.grid(row=0, column=1, rowspan=2) # min area f9 = ttk.Labelframe(frame_mser, text="Minimum area") f9.grid(row=1, columnspan=2, pady=5, padx=5) Label(f9, text="m").grid(row=1, column=0) self.min_area_scale = Scale(f9, from_=2, to=200, orient=HORIZONTAL) self.min_area_scale.set(10) self.min_area_scale.grid(row=0, column=1, rowspan=2) # percentage number of features f10 = ttk.Labelframe(frame_mser, text="Maximum area") f10.grid(row=2, columnspan=2, pady=5, padx=5) Label(f10, text="M").grid(row=1, column=0) self.max_area_scale = Scale(f10, from_=50, to=1000, resolution=5, orient=HORIZONTAL) self.max_area_scale.set(350) self.max_area_scale.grid(row=0, column=1, rowspan=2) # mser image f11 = ttk.Labelframe(frame_mser) f11.grid(row=0, rowspan=3, column=2, padx=5) self.mser_img_array = Image.fromarray(self.mser_area, "RGB") self.mser_img = ImageTk.PhotoImage(self.mser_img_array) img_label = Label(f11, image=self.mser_img) img_label.grid(row=0, column=0) buttons_p_w_mser = PanedWindow(f11, orient=HORIZONTAL) try_button = Button(f11, text="Try", command=self.try_mser) buttons_p_w_mser.add(try_button) change_button = Button(f11, text="New img", command=self.change_mser) buttons_p_w_mser.add(change_button) buttons_p_w_mser.grid(row=1, column=0) # other frame f12 = ttk.Labelframe(frame_other, text="Refinement") f12.grid(row=0, columnspan=2, pady=5, padx=5) Label(f12, text=u"\N{GREEK CAPITAL LETTER PHI}_l").grid(row=1, column=0) self.low_thresh_scale = Scale(f12, from_=0, to=1, resolution=0.05, orient=HORIZONTAL, length=90) self.low_thresh_scale.set(0.45) self.low_thresh_scale.grid(row=0, column=1, rowspan=2) Label(f12, text=u"\N{GREEK CAPITAL LETTER PHI}_h").grid(row=3, column=0) self.high_thresh_scale = Scale(f12, from_=0, to=1, resolution=0.05, orient=HORIZONTAL, length=90) self.high_thresh_scale.set(0.65) self.high_thresh_scale.grid(row=2, column=1, rowspan=2) f13 = ttk.Labelframe(frame_other, text="Dots distance") f13.grid(row=1, columnspan=2, pady=5, padx=5) Label(f13, text=u" \N{GREEK SMALL LETTER SIGMA}").grid(row=1, column=0) self.dots_distance_scale = Scale(f13, from_=1, to=20, resolution=1, orient=HORIZONTAL, length=90) self.dots_distance_scale.set(6) self.dots_distance_scale.grid(row=0, column=1, rowspan=2) f14 = ttk.Labelframe(frame_other, text="Tracks") f14.grid(row=0, column=3, pady=5, padx=5) Label(f14, text="N").grid(row=1, column=0) self.num_frames_tracks_spinbox = Spinbox(f14, from_=2, to=num_frames, width=10) self.num_frames_tracks_spinbox.delete(0, END) self.num_frames_tracks_spinbox.insert(END, num_frames) self.num_frames_tracks_spinbox.grid(row=0, column=1, rowspan=2) Label(f14, text=u"\N{GREEK SMALL LETTER TAU}").grid(row=3, column=0) self.gaps_scale = Scale(f14, from_=1, to=10, resolution=1, orient=HORIZONTAL, length=90) self.gaps_scale.set(2) self.gaps_scale.grid(row=2, column=1, rowspan=2) self.notebook.pack(padx=1, pady=1) save_button = Button(self, text=" Save and Close window ", command=self.withdraw) save_button.pack(pady=2) def _select_all(self): for i, var in enumerate(self.features_vars): var.set(1) def _clear_selection(self): for i, var in enumerate(self.features_vars): var.set(0) def change_mser(self): rand_row = random.randint(1, 512-200) rand_col = random.randint(1, 512-110) self.mser_area = self.mser_image[rand_row:rand_row+180, rand_col:rand_col+100] self.update_mser_image(self.mser_area) def try_mser(self): delta = self.delta_scale.get() min_area = self.min_area_scale.get() max_area = self.max_area_scale.get() image = self.mser_area red_c = image[:,:,0] red_c = cv2.equalizeHist(red_c) det_img = image.copy() mser = cv2.MSER(delta, _min_area=min_area, _max_area=max_area) regions = mser.detect(red_c) cp = list() new_c = np.zeros(self.mser_area.shape, dtype=np.uint8) for r in regions: for point in r: cp.append(point) det_img[point[1], point[0], 0] = 0 det_img[point[1], point[0], 1] = 0 det_img[point[1], point[0], 2] = 204 #new_c[point[1], point[0]] = 255 self.update_mser_image(det_img) def update_mser_image(self, new_image): self.mser_img_array = Image.fromarray(new_image) self.mser_img.paste(self.mser_img_array) def get_patch_size(self): patch_size = self.patch_size_spinbox.get() return int(patch_size) def get_num_frames_tracks(self): num_frames_tracks = self.num_frames_tracks_spinbox.get() return int(num_frames_tracks) def get_mser_opts(self): return [self.delta_scale.get(), self.min_area_scale.get(), self.max_area_scale.get()] def get_forest_opts(self): return [self.num_trees_scale.get(), self.depth_tree_scale.get(), self.percentage_feats_scale.get()] def get_low_thresh(self): return self.low_thresh_scale.get() def get_high_thresh(self): return self.high_thresh_scale.get() def get_dots_distance(self): return int(self.dots_distance_scale.get()) def get_selection_mask(self): if self.selection is not None: return self.selection selection_mask = np.zeros((self.max_num_feats, ), dtype='bool') for i, var in enumerate(self.features_vars): selection_mask[i] = var.get() self.selection = selection_mask return selection_mask
class Equilibrium_GUI(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.manager = Equilibrium_manager() # Slider setting the animation pace, in ms/frame self.slider = None self.diameter = None # Variables making sure data is properly set before rendering # associated with checkboxes to give user feedback self.configured = IntVar() self.configured.set(0) self.configured_box = None self.loaded = IntVar() self.configured.set(0) self.loaded_box = None self.init_UI(self.parent) self.test_auto_conf() #self.plot_figure(self.parent) def init_UI(self, frame): frame.title("Pegasus system") b_import = Button(master=frame, text='Importer', command=self.open_data_file) b_import.grid(row=1, column=0) b_export = Button(master=frame, text='Exporter', command=self.export_data) b_export.grid(row=1, column=1) b_play = Button(master=frame, text='Play', command=self.render) b_play.grid(row=1, column=2) b_display = Button(master=frame, text='Afficher', command=self.display) b_display.grid(row=1, column=3) b_import = Button(master=frame, text='Séparer pas/trot', command=self.open_split_file_popup) b_import.grid(row=1, column=5) b_config = Button(master=frame, text='Configurer', command=self.open_conf_file) b_config.grid(row=1, column=6) b_quit = Button(master=frame, text='Quitter', command=self._quit) b_quit.grid(row=1, column=7) slider_label_fast = Label(master=frame, text="Rapide") slider_label_fast.grid(row=2, column=2) slider_label_slow = Label(master=frame, text="Lent") slider_label_slow.grid(row=2, column=0) self.slider = Scale(master=frame, from_=150, to=15, orient=HORIZONTAL, length=100) self.slider.set(25) self.slider.grid(row=2, column=1) slider_label_large = Label(master=frame, text="Large") slider_label_large.grid(row=2, column=6) slider_label_tiny = Label(master=frame, text="Réduit") slider_label_tiny.grid(row=2, column=4) self.diameter = Scale(master=frame, from_=1, to=35, resolution=0.1, orient=HORIZONTAL, length=100) self.diameter.set(35) self.diameter.grid(row=2, column=5) self.configured_box = Checkbutton(master=frame, text="Configuré", variable=self.configured, state=DISABLED) self.configured_box.grid(row=3, column=0) self.loaded_box = Checkbutton(master=frame, text="Données chargées", variable=self.loaded, state=DISABLED) self.loaded_box.grid(row=3, column=1) def test_auto_conf(self): onlyfiles = [f for f in listdir(".") if isfile(join(".", f))] if ("equi_librium.conf" in onlyfiles): self.manager.read_config_file("equi_librium.conf") self.configured.set(1) def open_data_file(self): if (self.configured.get() == 1): ftypes = [('All files', '*')] dlg = tkFileDialog.Open(self, filetypes=ftypes) fl = dlg.show() if fl != '': self.manager.read_data_file(fl) self.loaded.set(1) else: tkMessageBox.showwarning( "Configuration non effectuee", "Il faut configurer avant d'importer les données") def open_split_file_popup(self): w = popupWindow(self.master, self.manager) self.master.wait_window(w.top) def open_conf_file(self): ftypes = [('conf files', '*.conf'), ('All files', '*')] dlg = tkFileDialog.Open(self, filetypes=ftypes) fl = dlg.show() if fl != '': self.manager.read_config_file(fl) self.configured.set(1) def export_data(self): ftypes = [('All files', '*')] dlg = tkFileDialog.SaveAs(self, filetypes=ftypes) fl = dlg.show() if fl != '': self.manager.set_diameter(self.diameter.get()) self.manager.export_chart(fl + ".csv") def display(self): self.manager.set_diameter(self.diameter.get()) self.manager.display_data() def render(self): self.manager.render_animation(self.slider.get()) #self.plot_figure(self.parent) def plot_figure(self, frame): f = Figure(figsize=(5, 4), dpi=100) #f = self.manager.render_animation() #a = f.add_subplot(111) #t = arange(0.0,3.0,0.01) #s = sin(2*pi*t) #a.plot(t,s) canvas = FigureCanvasTkAgg(f, master=frame) canvas.show() canvas.get_tk_widget().grid(row=0, columnspan=5) # canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) # toolbar = NavigationToolbar2TkAgg( canvas, root ) # toolbar.update() # canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) canvas._tkcanvas.grid(row=0, columnspan=5) def on_key_event(event): print('you pressed %s' % event.key) key_press_handler(event, canvas) #, toolbar) canvas.mpl_connect('key_press_event', on_key_event) def _quit(self): self.quit() # stops mainloop self.destroy() # this is necessary on Windows to prevent
class OfflineVisualiser(Visualiser): """A VTK-powered offline visualiser which runs in its own thread. In addition to the functions provided by the standard visualiser, the following additional functions are provided: precache_height_quantities() - Precache all the vtkpoints structures for any dynamic height based quantities to render. """ def __init__(self, source, frameDelay=100, frameStep=1): """The source parameter is assumed to be a NetCDF sww file. The frameDelay parameter is the number of milliseconds waited between frames. """ Visualiser.__init__(self, source) self.frameNumber = 0 fin = NetCDFFile(self.source, 'r') self.maxFrameNumber = fin.variables['time'].shape[0] - 1 fin.close() #self.frameNumberTkVariable = StringVar() #self.frameNumberTkVariable.set('Frame - %05g'%self.framNumber) self.frameDelay = frameDelay self.xmin = None self.xmax = None self.ymin = None self.ymax = None self.zmin = None self.zmax = None self.frameStep = frameStep self.vtk_heightQuantityCache = [] for i in range(self.maxFrameNumber + 1): # maxFrameNumber is zero indexed. self.vtk_heightQuantityCache.append({}) self.paused = False self.movie = False def setup_grid(self): fin = NetCDFFile(self.source, 'r') self.vtk_cells = vtkCellArray() N_tri = fin.variables['volumes'].shape[0] for v in range(N_tri): self.vtk_cells.InsertNextCell(3) for i in range(3): self.vtk_cells.InsertCellPoint(fin.variables['volumes'][v][i]) fin.close() def update_height_quantity(self, quantityName, dynamic=True): polydata = self.vtk_polyData[quantityName] = vtkPolyData() if dynamic is True: #print ' - Frame',self.frameNumber,'of',self.maxFrameNumber if not self.vtk_heightQuantityCache[self.frameNumber].has_key( quantityName): self.vtk_heightQuantityCache[self.frameNumber][quantityName]\ = self.read_height_quantity(quantityName, True, self.frameNumber) polydata.SetPoints( self.vtk_heightQuantityCache[self.frameNumber][quantityName]) else: polydata.SetPoints(self.read_height_quantity(quantityName, False)) polydata.SetPolys(self.vtk_cells) def get_3d_bounds(self): return [ self.xmin, self.xmax, self.ymin, self.ymax, self.zmin, self.zmax ] def read_height_quantity(self, quantityName, dynamic=True, frameNumber=0): """Read in a height based quantity from the NetCDF source file and return a vtkPoints object. frameNumber is ignored if dynamic is false.""" fin = NetCDFFile(self.source, 'r') points = vtkPoints() if dynamic is True: N_vert = fin.variables[quantityName].shape[1] else: N_vert = len(fin.variables[quantityName]) x = num.ravel(num.array(fin.variables['x'], num.float)) y = num.ravel(num.array(fin.variables['y'], num.float)) if dynamic is True: q = num.array(fin.variables[quantityName][frameNumber], num.float) else: q = num.ravel(num.array(fin.variables[quantityName], num.float)) q *= self.height_zScales[quantityName] q += self.height_offset[quantityName] for v in range(N_vert): points.InsertNextPoint(x[v], y[v], q[v]) if self.xmin is None or self.xmin > x[v]: self.xmin = x[v] if self.xmax is None or self.xmax < x[v]: self.xmax = x[v] if self.ymin is None or self.ymin > y[v]: self.ymin = y[v] if self.ymax is None or self.ymax < y[v]: self.ymax = y[v] if self.zmin is None or self.zmin > q[v]: self.zmin = q[v] if self.zmax is None or self.zmax < q[v]: self.zmax = q[v] fin.close() return points def precache_height_quantities(self): """Precache any height-based quantities. Call before rendering beigns.""" for q in self.height_quantities: if self.height_dynamic[q] is True: print 'Precaching %s' % q for i in range(self.maxFrameNumber + 1): # maxFrameNumber is zero-indexed print ' - Frame %d of %d' % (i, self.maxFrameNumber) self.vtk_heightQuantityCache[i][q]\ = self.read_height_quantity(q, True, i) def build_quantity_dict(self): quantities = {} fin = NetCDFFile(self.source, 'r') for q in filter( lambda n: n != 'x' and n != 'y' and n != 'z' and n != 'time' and n != 'volumes', fin.variables.keys()): if len(fin.variables[q].shape) == 1: # Not a time-varying quantity quantities[q] = num.ravel( num.array(fin.variables[q], num.float)) else: # Time-varying, get the current timestep data quantities[q] = num.array(fin.variables[q][self.frameNumber], num.float) fin.close() return quantities def setup_gui(self): Visualiser.setup_gui(self) self.tk_quit.grid(row=0, column=0, sticky=W + E) self.tk_movie_toggle = Button(self.tk_controlFrame, text="Movie off", command=self.movie_toggle) self.tk_movie_toggle.grid(row=0, column=6, sticky=W + E) self.tk_restart = Button(self.tk_controlFrame, text="<<<", command=self.restart, width=5) self.tk_restart.grid(row=1, column=0, sticky=W + E) self.tk_back10 = Button(self.tk_controlFrame, text="<<", command=self.back10, width=5) self.tk_back10.grid(row=1, column=1, sticky=W + E) self.tk_back = Button(self.tk_controlFrame, text="<", command=self.back, width=5) self.tk_back.grid(row=1, column=2, sticky=W + E) self.tk_pauseResume = Button(self.tk_controlFrame, text="Pause", command=self.pauseResume, width=15) self.tk_pauseResume.grid(row=1, column=3, sticky=W + E) self.tk_forward = Button(self.tk_controlFrame, text=">", command=self.forward, width=5) self.tk_forward.grid(row=1, column=4, sticky=W + E) self.tk_forward10 = Button(self.tk_controlFrame, text=">>", command=self.forward10, width=5) self.tk_forward10.grid(row=1, column=5, sticky=W + E) self.tk_forwardEnd = Button(self.tk_controlFrame, text=">>>", command=self.forwardEnd, width=5) self.tk_forwardEnd.grid(row=1, column=6, sticky=W + E) self.tk_frameNumber = Label(self.tk_controlFrame, text='Frame') self.tk_frameNumber.grid(row=2, column=0, sticky=W + E) self.tk_gotoFrame = Scale(self.tk_controlFrame, from_=0, to=self.maxFrameNumber, orient=HORIZONTAL) self.tk_gotoFrame.grid(row=2, column=1, columnspan=2, sticky=W + E) self.tk_stepLabel = Label(self.tk_controlFrame, text='Step') self.tk_stepLabel.grid(row=2, column=4, sticky=W + E) self.tk_frameStep = Scale(self.tk_controlFrame, from_=0, to=self.maxFrameNumber, orient=HORIZONTAL) self.tk_frameStep.grid(row=2, column=5, columnspan=2, sticky=W + E) # Make the buttons stretch to fill all available space for i in range(7): self.tk_controlFrame.grid_columnconfigure(i, weight=1) def run(self): self.alter_tkroot(Tk.after, (self.frameDelay, self.animateForward)) Visualiser.run(self) def restart(self): self.frameNumber = 0 self.redraw_quantities() self.update_labels() self.pause() if self.movie: self.save_image() def forwardEnd(self): self.frameNumber = self.maxFrameNumber self.redraw_quantities() self.update_labels() self.pause() def movie_toggle(self): if self.movie == True: self.movie = False self.tk_movie_toggle.config(text='Movie off') else: self.movie = True self.tk_movie_toggle.config(text='Movie on ') def save_image(self): from vtk import vtkJPEGWriter, vtkJPEGWriter, vtkPNGWriter from vtk import vtkPNMWriter, vtkWindowToImageFilter from os import path sourcebase, _ = path.splitext(self.source) fname = sourcebase + '%05g.png' % self.frameNumber #print fname extmap = { '.jpg': vtkJPEGWriter, '.jpeg': vtkJPEGWriter, '.png': vtkPNGWriter, '.pnm': vtkPNMWriter, } basename, ext = path.splitext(fname) try: Writer = extmap[ext.lower()] except KeyError: error_msg("Don't know how to handle %s files" % ext, parent=self) return renWin = self.vtk_renderer.GetRenderWindow() w2i = vtkWindowToImageFilter() writer = Writer() w2i.SetInput(renWin) w2i.Update() writer.SetInput(w2i.GetOutput()) writer.SetFileName(fname) renWin.Render() writer.Write() def back10(self): if self.frameNumber - 10 >= 0: self.frameNumber -= 10 else: self.frameNumber = 0 self.redraw_quantities() self.update_labels() self.pause() def back(self): if self.frameNumber > 0: self.frameNumber -= 1 self.redraw_quantities() self.update_labels() self.pause() def pauseResume(self): if self.paused is True: self.resume() else: self.pause() def pause(self): self.paused = True self.tk_pauseResume.config(text="Resume") def resume(self): self.paused = False self.tk_pauseResume.config(text="Pause") self.frameNumber = self.tk_gotoFrame.get() self.frameStep = self.tk_frameStep.get() self.tk_root.after(self.frameDelay, self.animateForward) def forward(self): if self.frameNumber < self.maxFrameNumber: self.frameNumber += 1 self.redraw_quantities() self.update_labels() self.pause() def forward_step(self): if self.frameNumber + self.frameStep <= self.maxFrameNumber: self.frameNumber += self.frameStep self.redraw_quantities() self.update_labels() else: self.frameNumber = self.maxFrameNumber self.redraw_quantities() self.update_labels() self.pause() if self.movie: self.save_image() def forward10(self): if self.frameNumber + 10 <= self.maxFrameNumber: self.frameNumber += 10 else: self.frameNumber = self.maxFrameNumber self.redraw_quantities() self.update_labels() self.pause() def animateForward(self): if self.paused is not True: self.forward_step() self.tk_root.after(self.frameDelay, self.animateForward) def update_labels(self): #self.tk_frameNumber.config(text='%05g of %05g'%(self.frameNumber,self.maxFrameNumber)) self.tk_gotoFrame.set(self.frameNumber) self.tk_frameStep.set(self.frameStep) def shutdown(self): #self.pause() self.tk_root.withdraw() self.tk_root.destroy()
class FourBarGUI(object): """ GUI to model a 4-bar mechanism. """ def __init__(self, wdw, r, c): """ Determines layout of the canvas, number of rows and colums is r and c. """ wdw.title('a 4-bar mechanism') self.fbr = FourBar() self.rows = r self.cols = c self.ox = c/3 self.oy = 3*r/4 # print "A =" , (self.ox, self.oy) self.togo = False # the canvas and start, stop, and clear buttons self.c = Canvas(wdw, width=self.cols, height=self.rows, bg='green') self.c.grid(row=1, column=2, columnspan=2) self.startbut = Button(wdw, text='start', command = self.start) self.startbut.grid(row=3, column=2, sticky=W+E) self.stopbut = Button(wdw, text='stop', command = self.stop) self.stopbut.grid(row=3, column=3, sticky=W+E) self.clearbut = Button(wdw, text='clear', command = self.clear) self.clearbut.grid(row=3, column=4, columnspan=3, sticky=W+E) # the length of the crank self.crank_lbl = Label(wdw, text='crank', justify=LEFT) self.crank_lbl.grid(row=0, column=0) self.crank_bar = IntVar() self.L = Scale(wdw, orient='vertical', from_=0, to=self.rows/2, \ tickinterval=20, resolution=1, length=self.rows, \ variable=self.crank_bar, command=self.draw_mechanism) self.L.set(self.fbr.crank) self.L.grid(row=1, column=0) # the angle that drives the crank self.angle_lbl = Label(wdw, text='angle', justify=LEFT) self.angle_lbl.grid(row=0, column=1) self.angle = DoubleVar() self.t = Scale(wdw, orient='vertical', from_=0, to=6.30, \ tickinterval=0.30, resolution=0.01, length=self.rows, \ variable=self.angle, command=self.draw_mechanism) self.t.grid(row=1, column=1) self.angle.set(self.fbr.angle) # the bar at the right self.right_bar_lbl = Label(wdw, text='right bar', justify=LEFT) self.right_bar_lbl.grid(row=0, column=4) self.right_bar = IntVar() self.r = Scale(wdw, orient='vertical', from_=0, to=self.rows/2, \ tickinterval=20, resolution=1, length=self.rows, \ variable=self.right_bar, command=self.draw_mechanism) self.r.grid(row=1, column=4) self.right_bar.set(self.fbr.right) # the top bar attached to the crank self.top_bar_lbl = Label(wdw, text='top bar', justify=LEFT) self.top_bar_lbl.grid(row=0, column=5) self.r_top_bar = IntVar() self.R = Scale(wdw, orient='vertical', from_=0, to=self.rows/2, \ tickinterval=20, resolution=1, length=self.rows, \ variable=self.r_top_bar, command=self.draw_mechanism) self.R.grid(row=1, column=5) self.r_top_bar.set(self.fbr.top) # the scale for the coupler bar self.coupler_bar_lbl = Label(wdw, text='coupler', justify=LEFT) self.coupler_bar_lbl.grid(row=0, column=6) self.coupler_bar = IntVar() self.cpl = Scale(wdw, orient='vertical', from_=0, to=self.rows/2, \ tickinterval=20, resolution=1, length=self.rows, \ variable=self.coupler_bar, command=self.draw_mechanism) self.cpl.grid(row=1, column=6) self.coupler_bar.set(self.fbr.coupler) # the horizontal bottom bar self.flat_lbl = Label(wdw, text='right joint', justify=RIGHT) self.flat_lbl.grid(row=2, column=1) self.flat = IntVar() self.f = Scale(wdw, orient='horizontal', from_=0, to=self.rows/2, \ tickinterval=50, resolution=1, length=self.cols, \ variable=self.flat, command=self.draw_mechanism) self.f.grid(row=2, column=2, columnspan=2) self.flat.set(self.fbr.flat) # coordinates of the coupler point appear on top self.ex = Entry(wdw) # for x value self.ex.grid(row=0, column=2) self.ex.insert(INSERT, "x = ") self.ey = Entry(wdw) # for y value self.ey.grid(row=0, column=3) self.ey.insert(INSERT,"y = ") # check button for drawing of coupler curve self.curve = IntVar() self.cb = Checkbutton(wdw, text='coupler', \ variable=self.curve, onvalue=1, offvalue=0) self.curve.set(1) self.cb.grid(row=3, column=0) # draw the mechanism on canvas self.draw_mechanism(0) def update_values(self): """ Takes all values of the scales and updates the data attributes of self.fbr. """ self.fbr.flat = self.flat.get() self.fbr.crank = self.crank_bar.get() self.fbr.top = self.r_top_bar.get() self.fbr.right = self.right_bar.get() self.fbr.coupler = self.coupler_bar.get() self.fbr.angle = self.angle.get() #self.fbr.print_joints() def draw_coupler_point(self, p): """ Draws coupler point with coordinates in p if the curve checkbox is on. Note that the previous values for the coordinates of the coupler point are stored in the entry fields. """ if self.curve.get() == 1: px = self.ox + p[0] py = self.oy - p[1] eqx = self.ex.get() Lx = eqx.split('=') if Lx[1] == ' ': qx = 0.0 else: qx = float(Lx[1]) eqy = self.ey.get() Ly = eqy.split('=') if Ly[1] == ' ': qy = 0.0 else: qy = float(Ly[1]) if (qx != 0.0) and (qy != 0.0): qx = self.ox + qx qy = self.oy - qy self.c.create_line(qx, qy, px, py, width=1) def fill_entries(self, p): """ Fills the entry fields with the coordinates of the coupler point in p. """ sx = 'x = %f' % p[0] sy = 'y = %f' % p[1] self.ex.delete(0, END) self.ex.insert(INSERT, sx) self.ey.delete(0, END) self.ey.insert(INSERT, sy) def draw_link(self, p, q, s): """ Draws the link from point with coordinates in p to the point with coordinates in q, using s as tag. """ self.c.delete(s) px = self.ox + p[0] py = self.oy - p[1] qx = self.ox + q[0] qy = self.oy - q[1] self.c.create_line(px, py, qx, qy, width=2, tags=s) def draw_mechanism(self, v): """ Fills the canvas with the current model of the planar 4-bar mechanism. Because this command is called by the sliders, the argument v is needed but not used. """ self.update_values() L = self.fbr.joints() for i in range(0, len(L)): p = L[i] px = self.ox + p[0] py = self.oy - p[1] sj = 'joint%d' % i self.c.delete(sj) self.c.create_oval(px-6, py-6, px+6, py+6, width=1, \ outline='black', fill='red', tags=sj) self.draw_link(L[0], L[2], 'link0') self.draw_link(L[1], L[3], 'link1') self.draw_link(L[2], L[3], 'link2') self.draw_link(L[2], L[4], 'link3') self.draw_coupler_point(L[4]) self.fill_entries(L[4]) def start(self): """ Starts the animation, adding 0.01 to angle. """ self.togo = True while self.togo: theta = self.angle.get() theta = theta + 0.01 if theta > 6.28: theta = 0 self.angle.set(theta) self.draw_mechanism(0) self.c.update() def stop(self): """ Stops the animation. """ self.togo = False def clear(self): """ Clears the canvas. """ self.c.delete(ALL)
class DPSinterface: """ DSPinterface is a Tk graphical interface to drive a DPS power supplier. """ def __init__(self, root): """ Create a DSP interface instance. :param root: is the Tk() interface where the DPS will be drawedstring with the prot name i.e. /dev/ttyUSB0 or COM5 for Windows :returns: a new instance of DPS graphical interface """ self.root = root root.title("DPS power supplier interface") root.protocol("WM_DELETE_WINDOW", self.wnwcmdclose) self.dps = None self.poller = None self.waver = None self.strtme = time() self.dpsfwave = None self.maxoutv = 5 self.maxoutc = 5 menubar = Menu(root) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Exit", command=self.wnwcmdclose) menubar.add_cascade(label="File", menu=filemenu) scopemenu = Menu(menubar, tearoff=0) scopemenu.add_command(label="Load sampled points...", command=self.mnucmdloadsmppts) scopemenu.add_command(label="Save sampled points as...", command=self.mnucmdsavesmppts) menubar.add_cascade(label="Scope", menu=scopemenu) wavemenu = Menu(menubar, tearoff=0) wavemenu.add_command(label="New wave", command=self.mnucmdnewwve) wavemenu.add_command(label="Load wave...", command=self.mnucmdloadwve) wavemenu.add_command(label="Edit wave...", command=self.mnucmdedtwve) wavemenu.add_command(label="Save wave as...", command=self.mnucmdsavewve) menubar.add_cascade(label="Wave", menu=wavemenu) memmenu = Menu(menubar, tearoff=0) memmenu.add_command(label="Edit memories...", command=self.mnucmdedtmem) menubar.add_cascade(label="Memory", menu=memmenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label="Help...", command=self.mnucmdhelp) helpmenu.add_command(label="About...", command=self.mnucmdabout) menubar.add_cascade(label="Help", menu=helpmenu) root.config(menu=menubar) row = 0 col = 0 rowspan = 1 colspan = 1 insertlabelrow(root, row, col, ("Serial: ", None, "Addr,Baud: "), E) col += colspan self.svardpsport = StringVar() self.svardpsport.set('/dev/ttyUSB0') self.entryserport = Entry(root, textvariable=self.svardpsport, width=ENTRYWIDTH, justify='right') self.entryserport.grid(row=row, column=col, sticky=W) col += colspan col += colspan self.svardpsaddbrt = StringVar() self.svardpsaddbrt.set('1, 9600') self.entrydpsadd = Entry(root, textvariable=self.svardpsaddbrt, width=ENTRYWIDTH, justify='right') self.entrydpsadd.grid(row=row, column=col, sticky=W) col += colspan colspan = 2 self.ivarconctd = IntVar() self.ivarconctd.set(0) Checkbutton(root, variable=self.ivarconctd, text='Connect', command=self.butcmdconnect).grid(row=row, column=col, columnspan=colspan, sticky=E + W) row += rowspan col = 0 colspan = 1 Separator(root, orient='horizontal').grid(row=row, columnspan=8, sticky=E + W, pady=8) row += rowspan rowspan = 1 colspan = 2 col = 0 self.ivarbrghtnes = IntVar() s = Scale(root, label='Brightness', variable=self.ivarbrghtnes, from_=0, to=5, resolution=1, orient="horizontal") s.bind("<ButtonRelease-1>", self.sclbndbrghtnss) s.grid(row=row, column=col, columnspan=colspan, sticky=E + W) col += colspan colspan = 1 Label(root, text="Model: ").grid(row=row, column=col, sticky=E) col += colspan self.ivarmodel = IntVar() Entry(root, textvariable=self.ivarmodel, state="readonly", width=ENTRYWIDTH, justify='right').grid(row=row, column=col, sticky=W) col += colspan colspan = 2 self.ivarsetmem = IntVar() s = Scale(root, label='Mem Recall', variable=self.ivarsetmem, from_=1, to=9, resolution=1, orient="horizontal") s.bind("<ButtonRelease-1>", self.sclbndmemory) s.grid(row=row, column=col, columnspan=colspan, sticky=E + W) row += rowspan colspan = 1 col = 0 insertlabelrow( root, row, col, (("Vinp [V]: ", VCOL), None, "Out Mode: ", None, "Protection: "), E) self.dvarvinp = DoubleVar() self.svarwrmde = StringVar() self.setworkmode(0) self.svarprot = StringVar() self.setprotection(0) insertentryrow( root, row, col, (None, self.dvarvinp, None, self.svarwrmde, None, self.svarprot), 'right', W, 'readonly') colspan = 1 row += rowspan col = 0 insertlabelrow(root, row, col, (("Vmax [V]: ", VCOL), None, ("Cmax [A]: ", CCOL), None, ("Pmax [W]: ", PCOL)), E) self.dvarvmaxm0 = DoubleVar() self.dvarcmaxm0 = DoubleVar() self.dvarpmaxm0 = DoubleVar() entries = insertentryrow(root, row, col, (None, self.dvarvmaxm0, None, self.dvarcmaxm0, None, self.dvarpmaxm0), 'right', W) for e, f in zip(entries, (self.entbndvmax, self.entbndcmax, self.entbndpmax)): e.bind('<FocusOut>', f) e.bind('<Return>', f) row += rowspan col = 0 insertlabelrow(root, row, col, (("Vout [V]: ", VCOL), None, ("Cout [A]: ", CCOL), None, ("Pout [W]: ", PCOL)), E) self.dvarvout = DoubleVar() self.dvarcout = DoubleVar() self.dvarpout = DoubleVar() insertentryrow( root, row, col, (None, self.dvarvout, None, self.dvarcout, None, self.dvarpout), 'right', W, 'readonly') row += rowspan col = 0 self.scope = Scope(root, [], row, col) row += 9 col = 4 Label(root, text="Rte[s/Sa]: ").grid(row=row, column=col, sticky=E) col += colspan self.dvarsecsmp = DoubleVar() self.dvarsecsmp.set(self.scope.sampletime()) e = Entry(root, textvariable=self.dvarsecsmp, width=ENTRYWIDTH, justify='right').grid(row=row, column=col, sticky=W) row += rowspan col = 0 colspan = 2 self.ivaracquire = IntVar() self.ivaracquire.set(0) Checkbutton(root, variable=self.ivaracquire, text='Run Acquisition', command=self.butcmdacquire).grid(row=row, column=col, columnspan=2, sticky=E + W) col += colspan self.ivarkeylock = IntVar() self.ivarkeylock.set(0) Checkbutton(root, variable=self.ivarkeylock, text="Key Lock", command=self.butcmdkeylock).grid(row=row, column=col, sticky=E + W, columnspan=colspan) col += colspan self.ivaroutenab = IntVar() self.ivaroutenab.set(0) Checkbutton(root, variable=self.ivaroutenab, text="Output Enable", command=self.butcmdoutenable).grid(row=row, column=col, sticky=E + W, columnspan=colspan) row += rowspan col = 0 rowspan = 1 colspan = 3 self.dvarvscale = DoubleVar() self.voltscale = Scale(root, label='Vset [V]', foreground=VCOL, variable=self.dvarvscale, from_=0, to=self.maxoutv, resolution=1, orient="horizontal") #, label='Vset[V]' self.voltscale.bind("<ButtonRelease-1>", self.sclbndvolt) self.voltscale.grid(row=row, column=col, columnspan=colspan, sticky=E + W) col += colspan self.dvarcscale = DoubleVar() self.curntscale = Scale(root, label='Cset[A]', foreground=CCOL, variable=self.dvarcscale, from_=0, to=self.maxoutc, resolution=1, orient="horizontal") #,label='Cset[A]' self.curntscale.bind("<ButtonRelease-1>", self.sclbndcrnt) self.curntscale.grid(row=row, column=col, columnspan=colspan, sticky=E + W) row += rowspan col = 0 self.dvarvscalef = DoubleVar() sc = Scale(root, foreground=VCOL, variable=self.dvarvscalef, from_=0, to=0.99, resolution=0.01, orient="horizontal") sc.bind("<ButtonRelease-1>", self.sclbndvolt) sc.grid(row=row, column=col, columnspan=colspan, sticky=E + W) col += colspan self.dvarcscalef = DoubleVar() sc = Scale(root, foreground=CCOL, variable=self.dvarcscalef, from_=0, to=0.99, resolution=0.01, orient="horizontal") sc.bind("<ButtonRelease-1>", self.sclbndcrnt) sc.grid(row=row, column=col, columnspan=colspan, sticky=E + W) row += rowspan col = 0 colspan = 1 Separator(root, orient='horizontal').grid(row=row, columnspan=6, sticky=E + W, pady=8) row += rowspan colspan = 1 col = 0 Label(root, text="Waveform: ").grid(row=row, column=col, sticky=E) col += colspan colspan = 2 self.svarwave = StringVar() Entry(root, textvariable=self.svarwave, width=ENTRYWIDTH, justify='right', state='readonly').grid(row=row, column=col, columnspan=colspan, sticky=E + W) col += colspan colspan = 1 self.ivarplaywv = IntVar() self.ivarplaywv.set(0) Checkbutton(root, variable=self.ivarplaywv, text='Play', command=self.butcmdplaywave).grid(row=row, column=col, sticky=E + W) col += colspan self.ivarpausewv = IntVar() self.ivarpausewv.set(0) Checkbutton(root, variable=self.ivarpausewv, text='Pause', command=self.butcmdpausewave).grid(row=row, column=col, sticky=E + W) col += colspan self.ivarloopwv = IntVar() self.ivarloopwv.set(0) Checkbutton(root, variable=self.ivarloopwv, text='Loop').grid(row=row, column=col, sticky=E + W) self.scope.update() self.scope.redraw() def sclbndvolt(self, event): """ Voltage scale bind command to set the voltage on the DSP. :param event: the event describing what changed """ if self.isconnected(): self.dps.set(['vset'], [self.getvscale()]) def sclbndcrnt(self, event): """ Current scale bind command to set the current on the DSP. :param event: the event describing what changed """ if self.isconnected(): self.dps.set(['cset'], [self.getcscale()]) def sclbndmemory(self, event): """ Memory-set bind command to set the memory on the DSP. :param event: the event describing what changed """ if self.isconnected(): m = self.ivarsetmem.get() self.dps.set(['mset'], [m]) self.updatefields(True) def sclbndbrghtnss(self, event): """ Brightness bind command to set the brightness on the DSP. :param event: the event describing what changed """ if self.isconnected(): b = self.ivarbrghtnes.get() self.dps.set(['brght'], [b]) def mnucmdnewwve(self): """ New wave menu command to initialize a new wave. """ self.dpsfwave = Dpsfile() self.svarwave.set('unnamed') def mnucmdloadwve(self): """ Load wave menu command to load a wave file. """ fname = tkFileDialog.askopenfilename(initialdir=".", title="Select wave file to load", filetypes=(("dps files", "*.dps"), ("all files", "*.*"))) if fname: self.svarwave.set(os.path.basename(fname)) self.dpsfwave = Dpsfile() self.dpsfwave.load(fname) def mnucmdedtwve(self): """ Edit wave menu command to open the edit wave window. """ if self.dpsfwave is not None: Wveinterface(self.root, self.dpsfwave.getpoints()) else: tkMessageBox.showinfo('No wave loaded', 'Load or create a new wave file to modify') def mnucmdsavewve(self): """ Save wave menu command to save the current wave in memory. """ if self.dpsfwave is not None: fname = tkFileDialog.asksaveasfilename( initialdir=".", title="Select wave file to save", filetypes=(("dps files", "*.dps"), ("all files", "*.*"))) if fname: self.dpsfwave.save(fname) self.svarwave.set(os.path.basename(fname)) else: tkMessageBox.showinfo('No wave in memory', 'Load or create a wave file to modify') def mnucmdloadsmppts(self): """ Load sampled points menu command to load in the graphical view sampled before. """ fname = tkFileDialog.askopenfilename(initialdir=".", title="Select data file to load", filetypes=(("dps files", "*.dps"), ("all files", "*.*"))) if fname: self.scope.load(fname) def mnucmdsavesmppts(self): """ Save sampled points menu command to save the last points sampled showed in the graphical view. """ fname = tkFileDialog.asksaveasfilename( initialdir=".", title="Select data file to save", filetypes=(("dps files", "*.dps"), ("all files", "*.*"))) if fname: self.scope.save(fname) def mnucmdedtmem(self): """ Memory menu command to edit the values of preset memories on DSP. """ if self.isconnected(): Meminterface(self.root, self.dps, self.updatefields) def mnucmdabout(self): """ About menu command to show the window with program information. """ Txtinterface( self.root, 'About', """DPS interface is designed by Simone Pernice That project was born as a textual driver to interface any DPS device. After the driver I made also a graphical interface to manage DPS devices. This project was born because I was not able to find any Linux application to manage DPS devices via USB. For question email to me: [email protected] Version {} relesed on {} First release on 3rd February 2019 Turin Italy DPS interface is under licence {} If you like this program please make a donation with PayPal to [email protected]""" .format(__version__, __date__, __license__)) def mnucmdhelp(self): """ Help menu command to show basic help on usage. """ Txtinterface( self.root, 'Help', """This is an interface to remote controll a power supplier of DPS series. The white fields can be edited, the gray are read only. To connect to DPS power supplier first link it to the PC through an USB cable. The data required to connect is on the first row of the graphical interface. Write the serial address on the first field (COMxx for Windows or /dev/ttyUSBx for Linux). Address and baudrate do not require update because they are the default for DPS power supplier. Turn on DPS with up key pressed to change those values. Press 'Connect' check button and if the device is present it is linked. Press again the same check button to disconnect the DPS. Once the link to DPS is in place all the data on the interface are updated and on the DPS the keylock is set. The second block of graphical interface contains all data about DPS. The brightness set which can be changed through the scale regulation. The model number. The memory from which recall the preset parameters. The input voltage, the output mode cv (constant voltage) or cc (constant current). The protection mode: none (no protection triggered), ovp (over voltage protection), ocp (over current protection), opp (over power protection). The maximum voltage, current and power to provide before triggering the self protection. The next row contains output voltage, current and power in textual form. A time diagram of the DPS output voltage, current and power is avaiable. It is possible to play with the mouse on that screen: - wheel press to fit in the screen all the enabled drawings - wheel to zoom in time - shift+wheel to zoom on Y for the highlighted curves - ctrl+wheel to change the enabled curves - left button drag to move the highlighted curve The same mouse functions are available in the fields below the diagram: - voltage per division, current per division and watt per division - zero position for voltage, current and power - check button to view voltage, current and power - time: second per divisions and zero position for time The sample time is used for the acquisition. DPS is quite slot, the minimum read time is around 1 second. The suggested rate is to have a sample for displayed pixel. The next buttons are: - Run acquisition: starts a thread that read the DPS status, update the interface fields as well as the time diagram. The acquisition points can be saved and loaded to be showed lated with menu commands on DPS scope load/save. They can be also edited through the wave edit window and played. - Key lock: set or reset the DPS key lock. It should be on in order to have faster communication. If key lock is on less fields of DPS are read since user can change them only through the PC interface. - Output enable to enable the DPS output Eventually there are the voltage and current scale. Thery are split in two: - the first is for coarse (1 unit/step) adjustment the unit of voltage/current - the second is for fine (0.01 unit/step) adjustament of voltage/current On the last block of interface there is a waveform field showing the wave loaded. Wave is a set of required output voltage and current at give timings. It is possible play and pause it through the respective commands of the interface. If loop is set when the wave play is completed it restarts. The acquisition may slow the wave player, use low acquisition sample time to avoid delays.""") def butcmdconnect(self): """ Connect check button command to connect to the DSP. It reads: serial port address, DPS address and serial speed from other interface fields. If it is capable to link to the DPS: - the maximum voltage and current are read and scale maximums set accordingly - the DPS current data are read and set accordingly in the interface - the localDPS interface is locked so that the user cannot change them but has to go through the graphical interface if the DPS is locked the polling is faster because less data needs to be read from DPS - the input fields are disabled """ if self.ivarconctd.get(): try: flds = self.svardpsaddbrt.get().split(',') if len(flds) > 0: ch = int(flds[0]) else: ch = 1 if len(flds) > 1: br = int(flds[1]) else: br = 9600 self.dps = DPSdriver(self.svardpsport.get(), ch, br) except Exception as e: tkMessageBox.showerror('Error', 'Cannot connect: ' + str(e)) self.ivarconctd.set(0) self.dps = None return m = self.dps.get(['model']) m = m[0] self.ivarmodel.set(m) to = m / 100 self.voltscale.config(to=to) self.maxoutv = to to = m % 100 self.curntscale.config(to=to) self.maxoutv = to self.scope.resetpoints() self.ivarkeylock.set(1) self.butcmdkeylock() self.updatefields(True) self.entryserport.config(state='readonly') self.entrydpsadd.config(state='readonly') else: # Stop polling self.ivaracquire.set(0) if self.poller: self.poller.wake() time.sleep(1.) # Wait to be sure the thread exits # Stop waveform generation self.ivarplaywv.set(0) if self.waver: self.waver.wake() time.sleep(1.) # Wait to be sure the thread exits self.dps = None self.entryserport.config(state=NORMAL) self.entrydpsadd.config(state=NORMAL) def butcmdacquire(self): """ Acquire check button command to manage the acquisition thread to read the DSP data. If the button is not selected the thread is lunched. If the button is selected the thread is stopped. """ if self.ivaracquire.get(): if not self.isconnected(): self.ivaracquire.set(0) return self.scope.resetpoints() self.strtme = time() self.poller = Poller(self.ivaracquire, self.dvarsecsmp, self.updatefields) else: self.poller.wake() def butcmdkeylock(self): """ Key lock button command to enable or disable the key lock on DPS remote interface. """ if self.isconnected(): self.dps.set(['lock'], [self.ivarkeylock.get()]) else: self.ivarkeylock.set(0) def butcmdoutenable(self): """ DPS output button command to enable or disable the DPS output power. """ if self.isconnected(): self.dps.set(['onoff'], [self.ivaroutenab.get()]) else: self.ivaroutenab.set(0) def butcmdplaywave(self): """ Wave generator check button command to manage the wave generation thread to make a waveform on the DSP. If the button is not selected the thread is lunched. If the button is selected the thread is stopped. """ if self.ivarplaywv.get(): if not self.isconnected(): self.ivarplaywv.set(0) return if not self.dpsfwave: tkMessageBox.showinfo('No wave in memory', 'Load or create a wave file to modify') self.ivarplaywv.set(0) return if not self.ivaroutenab.get(): self.ivaroutenab.set(1) self.butcmdoutenable() self.waver = Waver(self.setvcdps, self.ivarplaywv, self.ivarpausewv, self.ivarloopwv, self.dpsfwave.getpoints()) else: self.waver.wake() def butcmdpausewave(self): """ Wave generator pause check button command to temporary pause the wave generations. """ self.waver.wake() def wnwcmdclose(self): """ DPS main window close. Before exiting the supplier is disconnected the external supplier. """ if self.ivarconctd.get(): self.ivarconctd.set(0) self.butcmdconnect() self.root.destroy() def entbndvmax(self, event): """ Maximum voltage entry bind to set the protection maximum ouput voltage of the DSP. :param event: the event describing what changed """ if self.isconnected(): if self.dvarvmaxm0.get() > self.maxoutv * PROTEXCEED: self.dvarvmaxm0.set(self.maxoutv * PROTEXCEED) elif self.dvarvmaxm0.get() < 0.: self.dvarvmaxm0.set(0.) self.dps.set(['m0ovp'], [self.dvarvmaxm0.get()]) def entbndcmax(self, event): """ Maximum current entry bind to set the protection maximum output curret of the DSP. :param event: the event describing what changed """ if self.isconnected(): if self.dvarcmaxm0.get() > self.maxoutc * PROTEXCEED: self.dvarcmaxm0.set(self.maxoutc * PROTEXCEED) elif self.dvarcmaxm0.get() < 0.: self.dvarcmaxm0.set(0.) self.dps.set(['m0ocp'], [self.dvarcmaxm0.get()]) def entbndpmax(self, event): """ Maximum power entry bind to set the protection maximum output power of the DSP. :param event: the event describing what changed """ if self.isconnected(): if self.dvarpmaxm0.get( ) > self.maxoutv * self.maxoutc * PROTEXCEED * PROTEXCEED: self.dvarpmaxm0.set(self.maxoutv * self.maxoutc * PROTEXCEED * PROTEXCEED) elif self.dvarpmaxm0.get() < 0.: self.dvarcmaxm0.set(0.) self.dps.set(['m0opp'], [self.dvarpmaxm0.get()]) def setvcdps(self, v, c): """ Set the DPS output voltage and current moving their scales accordingly. :param v: the required voltage, if negative it is not changed :param c: the required current, if negative it is not changed """ if v >= 0: if c >= 0: self.setvscale(v) self.setcscale(c) self.dps.set(['vset', 'cset'], [v, c]) else: self.setvscale(v) self.dps.set(['vset'], [v]) elif c >= 0: self.setcscale(c) self.dps.set(['cset'], [c]) def isconnected(self): """ Check if the DPS is connected, if not display a message. :returns: True if connected, False if not """ if self.dps is None: tkMessageBox.showinfo('Not connected', 'Enstablish a connection before') return False return True def setvscale(self, v): """ Set the voltage scale, nothing is changed on the DPS. :param v: the voltage to set """ if v > self.maxoutv: v = self.maxoutv elif v < 0: v = 0 self.dvarvscale.set(int(v)) self.dvarvscalef.set(round(v - int(v), 2)) def getvscale(self): """ Get the voltage scale set value. :returns: the voltage set """ return self.dvarvscale.get() + self.dvarvscalef.get() def setcscale(self, c): """ Set the current scale, nothing is changed on the DPS. :param c: the current to set """ if c > self.maxoutc: c = self.maxoutc elif c < 0: c = 0 self.dvarcscale.set(int(c)) self.dvarcscalef.set(round(c - int(c), 2)) def getcscale(self): """ Get the current scale set value. :returns: the current set """ return self.dvarcscale.get() + self.dvarcscalef.get() def updatefields(self, forcereadall=False): """ Reads data stored in DPS and updates the interface fields accordingly. In order to be as fast as possible, if keylock is enabled, reads only the fields that can change without uses access. If keylock is disabled all the fields are read because user may have changed something from the interface. :param forcereadall: if True read and update all the DPS fields regardless of the keylock status :returns: the point read. A point is made by (time, voltage, current, power) """ if not forcereadall and self.ivarkeylock.get( ): # If user keep locked fewer data are read, otherwise all data = self.dps.get( ['vout', 'cout', 'pout', 'vinp', 'lock', 'prot', 'cvcc']) self.dvarvout.set(data[0]) self.dvarcout.set(data[1]) self.dvarpout.set(data[2]) self.dvarvinp.set(data[3]) self.ivarkeylock.set(data[4]) self.setprotection(data[5]) self.setworkmode(data[6]) vcp = data[0:3] else: # All data is read data = self.dps.get([ 'vset', 'cset', 'vout', 'cout', 'pout', 'vinp', 'lock', 'prot', 'cvcc', 'onoff', 'brght', 'mset', 'm0ovp', 'm0ocp', 'm0opp' ]) self.setvscale(data[0]) self.setcscale(data[1]) self.dvarvout.set(data[2]) self.dvarcout.set(data[3]) self.dvarpout.set(data[4]) self.dvarvinp.set(data[5]) self.ivarkeylock.set(data[6]) self.setprotection(data[7]) self.setworkmode(data[8]) self.ivaroutenab.set(data[9]) self.ivarbrghtnes.set(data[10]) self.ivarsetmem.set(data[11]) self.dvarvmaxm0.set(data[12]) self.dvarcmaxm0.set(data[13]) self.dvarpmaxm0.set(data[14]) vcp = data[2:5] vcp.insert(TPOS, time() - self.strtme) self.scope.addpoint(vcp) return vcp def setprotection(self, p): """ Set the protection field with an user readable string explaining the DPS protection status. :param p: the protection statu returned by the DPS """ self.svarprot.set({0: 'none', 1: 'ovp', 2: 'ocp', 3: 'opp'}[p]) def setworkmode(self, wm): """ Set the workmode field with an user readable string explaining the DPS work mode. :param wm: the working mode returned by the DPS """ self.svarwrmde.set({0: 'cv', 1: 'cc'}[wm])
class Controls: def __init__(self, profile): self.profile = profile self.show_rgb_mask = False self.show_hsv_mask = False def start(self, master): self.master = master master.title("Controls") self.rgb_red_min = Scale(master, from_=0, to=255, background="red") self.rgb_red_max = Scale(master, from_=0, to=255, background="red") self.hue_label = Label(master, text="hue", background="white") self.sat_label = Label(master, text="sat", background="white") self.val_label = Label(master, text="val", background="white") self.rgb_green_min = Scale(master, from_=0, to=255, background="green") self.rgb_green_max = Scale(master, from_=0, to=255, background="green") self.rgb_blue_min = Scale(master, from_=0, to=255, background="blue") self.rgb_blue_max = Scale(master, from_=0, to=255, background="blue") self.hsv_hue_min = Scale(master, from_=0, to=255, background="white") self.hsv_hue_max = Scale(master, from_=0, to=255, background="white") self.hsv_sat_min = Scale(master, from_=0, to=255, background="white") self.hsv_sat_max = Scale(master, from_=0, to=255, background="white") self.hsv_val_min = Scale(master, from_=0, to=255, background="white") self.hsv_val_max = Scale(master, from_=0, to=255, background="white") # set default values self.rgb_red_min.set(self.profile.red[0]) self.rgb_red_max.set(self.profile.red[1]) self.rgb_green_min.set(self.profile.green[0]) self.rgb_green_max.set(self.profile.green[1]) self.rgb_blue_min.set(self.profile.blue[0]) self.rgb_blue_max.set(self.profile.blue[1]) self.hsv_hue_min.set(self.profile.hsv_hue[0]) self.hsv_hue_max.set(self.profile.hsv_hue[1]) self.hsv_sat_min.set(self.profile.hsv_sat[0]) self.hsv_sat_max.set(self.profile.hsv_sat[1]) self.hsv_val_min.set(self.profile.hsv_val[0]) self.hsv_val_max.set(self.profile.hsv_val[1]) self.show_rgb_mask_var = IntVar() self.show_rgb_mask_check = Checkbutton(master, text="RGB Mask", variable=self.show_rgb_mask_var) self.show_hsv_mask_var = IntVar() self.show_hsv_mask_check = Checkbutton(master, text="HSV Mask", variable=self.show_hsv_mask_var) # LAYOUT self.rgb_red_min.grid(row=0, column=0) self.rgb_red_max.grid(row=0, column=1) self.hue_label.grid(row=1, column=0, columnspan=2) self.sat_label.grid(row=1, column=2, columnspan=2) self.val_label.grid(row=1, column=4, columnspan=2) self.rgb_green_min.grid(row=0, column=2) self.rgb_green_max.grid(row=0, column=3) self.rgb_blue_min.grid(row=0, column=4) self.rgb_blue_max.grid(row=0, column=5) self.hsv_hue_min.grid(row=2, column=0) self.hsv_hue_max.grid(row=2, column=1) self.hsv_sat_min.grid(row=2, column=2) self.hsv_sat_max.grid(row=2, column=3) self.hsv_val_min.grid(row=2, column=4) self.hsv_val_max.grid(row=2, column=5) self.show_rgb_mask_check.grid(row=3, column=0) self.show_hsv_mask_check.grid(row=3, column=1) def update_profile(self): self.rgb_red_min.set( min(self.rgb_red_min.get(), self.rgb_red_max.get())) self.rgb_red_max.set( max(self.rgb_red_min.get(), self.rgb_red_max.get())) self.profile.red = [self.rgb_red_min.get(), self.rgb_red_max.get()] self.profile.green = [ self.rgb_green_min.get(), self.rgb_green_max.get() ] self.profile.blue = [self.rgb_blue_min.get(), self.rgb_blue_max.get()] self.profile.hsv_hue = [self.hsv_hue_min.get(), self.hsv_hue_max.get()] self.profile.hsv_sat = [self.hsv_sat_min.get(), self.hsv_sat_max.get()] self.profile.hsv_val = [self.hsv_val_min.get(), self.hsv_val_max.get()] self.show_rgb_mask = self.show_rgb_mask_var.get() == 1 self.show_hsv_mask = self.show_hsv_mask_var.get() == 1
class SteeringFrame(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.initialize() def initialize(self): self.grid() self.entryVariable = StringVar() self.entry = Entry(self, textvariable=self.entryVariable) self.entry.grid(column=0,row=0,sticky='EW') self.entry.bind("<Return>", self.OnPressEnter) button = Button(self,text="Set Steering", command=self.OnButtonClick) button.grid(column=1,row=0) self.labelVariable = StringVar() label = Label(self,textvariable=self.labelVariable, anchor="w",fg="white",bg="green") label.grid(column=0,row=1,columnspan=1,sticky='EW') self.labelVariable.set("Start..") left = Button(self,text="Left", command=self.Left) left.grid(column=0,row=2) right = Button(self,text="Right", command=self.Right) right.grid(column=1,row=2) centre = Button(self,text="Centre", command=self.Centre) centre.grid(column=3,row=2) self.steerVariable = StringVar() steerState = Label(self,textvariable=self.steerVariable, anchor="w",fg="white",bg="green") steerState.grid(column=0,row=3,columnspan=1,sticky='EW') self.steerVariable.set("Start..") self.turn_angle = steering.GetTurnAngle(); self.slider = Scale(self, from_=-self.turn_angle, to=self.turn_angle, orient=HORIZONTAL, command=self.setSteerState) self.slider.grid(column=0, row=4, columnspan=3, sticky='EW') self.grid_columnconfigure(0,weight=1) self.update() #self.geometry(self.geometry()) # caused busy wait? self.entry.focus_set() #self.entry.selection_range(0, Tkinter.END) # caused busy wait? def OnButtonClick(self): steerAngle = int(self.entryVariable.get()) steering.SetAngle(steerAngle) self.slider.set(steerAngle) self.labelVariable.set("Steering set: " + str(steerAngle) ) self.entry.focus_set() self.entry.selection_range(0, END) def OnPressEnter(self,event): steerAngle = int(self.entryVariable.get()) steering.SetAngle(steerAngle) self.slider.set(steerAngle) self.labelVariable.set("Steering set: " + str(steerAngle) ) self.entry.focus_set() self.entry.selection_range(0, END) def Left(self): self.steerVariable.set("LEFT") steering.TurnLeft() self.slider.set(-self.turn_angle) def Right(self): self.steerVariable.set("RIGHT") steering.TurnRight() self.slider.set(self.turn_angle) def Centre(self): self.steerVariable.set("CENTRE") steering.TurnStraight() self.slider.set(0) def setSteerState(self, val): val = self.slider.get() steering.SetAngle(val) steerAngle = val; self.steerVariable.set("Steering: " + str(val) )
def gui(): from Tkinter import Tk, Label, Entry, Button, Scale, Checkbutton, W, HORIZONTAL, Frame, StringVar, IntVar, DoubleVar, Radiobutton, BooleanVar, E global root root = Tk() root.wm_title("Compute R_complete") line = 0 global insFile, hklFile, nHKL, nParams, nHKLLabel, fracFree, status, nParamsLabel, nCPU, rCompleteLabel, cycles, lsType, cleanup, nFree, nRunsLabel, mergeCheck, compileMap insFile = StringVar() hklFile = StringVar() nHKL = IntVar() nParams = IntVar() nFree = IntVar() fracFree = DoubleVar() fracFree.set(5.0) nCPU = IntVar() nCPU.set(maxCPU) cycles = IntVar() cycles.set(10) lsType = IntVar() lsType.set(1) cleanup = BooleanVar() cleanup.set(True) mergeCheck = BooleanVar() mergeCheck.set(True) compileMap = BooleanVar() compileMap.set(True) Label(root, text='Instruction File:').grid(row=line, column=0, sticky=E) Entry(root, textvariable=insFile).grid(row=line, column=1) Button(root, text='Browse', command=browseINS).grid(row=line, column=2) line += 1 Label(root, text='Reflection File:').grid(row=line, column=0, sticky=E) Entry(root, textvariable=hklFile).grid(row=line, column=1) Button(root, text='Browse', command=browseHKL).grid(row=line, column=2) line += 1 Checkbutton(root, var=mergeCheck, text='Merge Reflections').grid(row=line, column=1, sticky=W) line += 1 Button(root, text='Load', command=load).grid(row=line, columnspan=3) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='# of reflections:').grid(row=line, sticky=E) nHKLLabel = Label(root, text='???') nHKLLabel.grid(row=line, column=1, sticky=W) line += 1 Label(root, text='# of atoms:').grid(row=line, sticky=E) nParamsLabel = Label(root, text='???') nParamsLabel.grid(row=line, column=1, sticky=W) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='Select Parameters').grid(row=line, column=1) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='# of free reflections:').grid(row=line, sticky=E) nFreeEntry = Entry(root, width=5, textvariable=nFree) nFreeEntry.grid(row=line, column=1, sticky=W) nFreeEntry.bind('<Return>', setScale) nRunsLabel = Label(root, text='# runs') nRunsLabel.grid(row=line, column=2) line += 1 Label(root, text='% of free reflections:').grid(row=line, column=0, sticky=E) w = Scale(root, from_=0.1, to=10.0, resolution=0.1, orient=HORIZONTAL, length=200, var=fracFree, command=percentScale) w.grid(row=line, column=1, columnspan=2, sticky=W) line += 1 Label(root, text='stable <-------------------------------> fast').grid( row=line, column=1, columnspan=2, sticky=W) line += 1 Frame(root, height=10).grid(row=line) line += 1 Label(root, text='Refinement cycles:').grid(row=line, column=0, sticky=E) ls = Scale(root, from_=0, to=50, resolution=1, orient=HORIZONTAL, length=200, var=cycles) ls.grid(row=line, column=1, columnspan=2, sticky=W) line += 1 Label(root, text='fast <--------------------> less model bias').grid( row=line, column=1, columnspan=2, sticky=W) line += 1 Frame(root, height=10).grid(row=line) line += 1 Label(root, text='# of CPUs:').grid(row=line, column=0, sticky=E) ww = Scale(root, from_=1, to=maxCPU, orient=HORIZONTAL, length=200, var=nCPU) ww.grid(row=line, column=1, columnspan=2, sticky=W) line += 1 Label(root, text='Refinement Type:').grid(row=line, column=0, sticky=E) Radiobutton(root, text='CGLS', var=lsType, value=1).grid(row=line, column=1, sticky=W) Radiobutton(root, text='L.S.', var=lsType, value=2).grid(row=line, column=2, sticky=W) line += 1 Frame(root, height=10).grid(row=line) line += 1 Label(root, text='Compile map:').grid(row=line, column=0, sticky=E) Checkbutton(root, var=compileMap).grid(row=line, column=1, sticky=W) line += 1 Label(root, text='Cleanup:').grid(row=line, column=0, sticky=E) Checkbutton(root, var=cleanup).grid(row=line, column=1, sticky=W) line += 1 Button(root, text='RUN', command=run, width=25).grid(row=line, columnspan=3) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='R_complete:').grid(row=line, column=0, sticky=E) rCompleteLabel = Label(root, text='???') rCompleteLabel.grid(row=line, column=1, sticky=W) line += 1 Frame(root, height=20).grid(row=line) line += 1 Label(root, text='Status:').grid(row=line, column=0, sticky=E) status = Label(root, text='Idle... Please load files.') status.grid(row=line, column=1, columnspan=2, sticky=W) global IDLE IDLE = True root.mainloop()
#Percentage of data to analize: percentg_val = DoubleVar() percentg_val.set(0) #Initialize a value #Define and show slider PercentgEntry = Scale(frame1, label="Percentage of data to analize:", bg="LightCyan2", from_=0, to=100, orient=HORIZONTAL, length=300, resolution=0.5, variable=percentg_val) PercentgEntry.grid(row=17, column=0, columnspan=2, sticky=W) #Button to open root browser latestThread = None #analysis thread b = None class browser_thread(threading.Thread): """thread for opening a TBrowser""" def __init__(self): self.exit = threading.Event() threading.Thread.__init__(self) def run(self): global b b = ROOT.TBrowser()
class VRConfigDialog(Dialog): """ Defines a custom dialog for editing the Virtual Reality params """ def __init__(self, parent, populator=None, manager=None): """ Construct the dialog """ self._entries = { "frame_angle" : None, "distortion" : False, "windowed" : False, "eye_height" : None, "minimum_dist_to_wall" : None } self._win_var = IntVar(0) self._distortion_var = IntVar(0) Dialog.__init__(self, parent=parent, title="VRConfiguration", populator=populator, manager=manager) def body(self, parent): """ Overridden method defining the body of the dialog :param parent: :return: """ # Define all of the labels for our options Label(parent, text="Frame Angle:", padx=3, anchor=SW, height=2).grid(row=0, column=0, sticky=W) Label(parent, text="Eye Height:", padx=3, anchor=SW, height=2).grid(row=1, column=0, pady=2, sticky=W ) Label(parent, text="MinDistToWall:", padx=3, anchor=SW, height=2).grid(row=2, column=0, pady=2, sticky=W) Label(parent, text="Distortion:", padx=3).grid(row=3, column=0, pady=2, sticky=W) Label(parent, text="Windowed:", padx=3).grid(row=4, column=0, pady=2, sticky=W) # Define the sub-widgets that the labels are referring to self._frameAngle = Scale(parent, from_=-20, to=20, orient=HORIZONTAL) if self._entries["frame_angle"] is not None: self._frameAngle.set(self._entries["frame_angle"]) else: self._frameAngle.set(-5) self._frameAngle.grid(row=0, column=1, padx=3) self._eyeHeight = Scale(parent, from_=0, to=500, orient=HORIZONTAL) if self._entries["eye_height"] is not None: self._eyeHeight.set( self._entries["eye_height"] ) else: self._eyeHeight.set(50) self._eyeHeight.grid(row=1, column=1, padx=3) self._minDistToWall = Scale(parent, from_=1, to=300, orient=HORIZONTAL) if self._entries["minimum_dist_to_wall"] is not None: self._minDistToWall.set( self._entries["minimum_dist_to_wall"] ) else: self._minDistToWall.set(20) self._minDistToWall.grid(row=2, column=1, padx=3) self._distortion = Checkbutton(parent, variable=self._distortion_var, offvalue=0, onvalue=1, text="Enable", command=self._toggle_distortion) self._distortion.grid(row=3, column=1, padx=3) self._windowed = Checkbutton(parent, variable=self._win_var, offvalue=0, onvalue=1, text="Enable", command=self._toggle_windowed) self._windowed.grid(row=4, column=1, padx=3) def _toggle_distortion(self): """ Toggle the distortion flag :return: """ self._distortion_var.set(0 if self._distortion_var.get() == 1 else 1) val = self._entries["distortion"] self._entries["distortion"] = not val Debug.printi("Distortion toggled to " + (str(not val)), Debug.Level.INFO) self._distortion.toggle() def _toggle_windowed(self): """ Toggle the windowed flag :return: """ self._win_var.set(0 if self._win_var.get() == 1 else 1) val = self._entries["windowed"] self._entries["windowed"] = not val Debug.printi("Windowing toggled to " + (str(not val)), Debug.Level.INFO) self._windowed.toggle() def populate(self, manager): self._entries["frame_angle"] = manager.frame_angle self._entries["distortion"] = manager.distortion self._entries["windowed"] = manager.windowed self._entries["eye_height"] = manager.eye_height self._entries["minimum_dist_to_wall"] = manager.minimum_dist_to_wall self._win_var.set( 0 if manager.windowed is False else 1 ) self._distortion_var.set( 0 if manager.distortion is False else 1 ) def validate(self): (result, message) = DataValidator.validate(DataStore.EVENT.VR_EDIT, self._entries) if result is not True: tkMessageBox.showerror("Input Error", message) return result def apply(self): self._entries["frame_angle"] = self._frameAngle.get() self._entries["eye_height"] = self._eyeHeight.get() self._entries["minimum_dist_to_wall"] = self._minDistToWall.get() self._manager.inform(DataStore.EVENT.VR_EDIT, self._entries) def auto_populate(self): self._frameAngle.set(Defaults.VR.FRAME_ANGLE) self._eyeHeight.set(Defaults.VR.EYE_HEIGHT) self._minDistToWall.set(Defaults.VR.MIN_DIST_TO_WALL) self._distortion_var.set(Defaults.VR.DISTORTION) self._win_var.set(Defaults.VR.WINDOWED)
class SPIFrame(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.initialize() def initialize(self): self.grid() self.entryVariable = StringVar() self.entry = Entry(self, textvariable=self.entryVariable) self.entry.grid(column=0,row=0,sticky='EW') self.entry.bind("<Return>", self.OnPressEnter) button = Button(self,text="SPI send", command=self.OnButtonClick) button.grid(column=1,row=0) #ramp = Button(self,text="RAMP", command=self.setlabvar) #ramp.grid(column=1,row=1) self.labelVariable = StringVar() label = Label(self,textvariable=self.labelVariable, anchor="w",fg="white",bg="blue") label.grid(column=0,row=1,columnspan=1,sticky='EW') self.labelVariable.set("Start..") self.slider = Scale(self, from_=0, to=80, orient=HORIZONTAL, command=self.setlabvar) self.slider.grid(column=0, row=2, columnspan=3, sticky='EW') self.PID = PIDTune(self) self.PID.grid(column=0, row=3, columnspan=3, sticky='EW') self.grid_columnconfigure(0,weight=1) self.update() #self.geometry(self.geometry()) # caused busy wait? self.entry.focus_set() #self.entry.selection_range(0, Tkinter.END) # caused busy wait? def setlabvar(self, val): val = self.slider.get() speed.SetAcceleration(val) accel = val; self.labelVariable.set("Duty set: " + str(val) ) def OnButtonClick(self): accel = int(self.entryVariable.get()) self.slider.set(accel) speed.SetAcceleration(accel) self.labelVariable.set("Duty set: " + str(accel) ) self.entry.focus_set() self.entry.selection_range(0, END) def OnPressEnter(self,event): accel = int(self.entryVariable.get()) self.slider.set(accel) speed.SetAcceleration(accel) self.labelVariable.set("Duty set: " + str(accel) ) self.entry.focus_set() self.entry.selection_range(0, END)
class Application(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind((host, port)) self.grid() self.columnconfigure(0, minsize=100) self.columnconfigure(1, minsize=200) self.columnconfigure(2, minsize=200) self.columnconfigure(3, minsize=150) self.columnconfigure(4, minsize=150) self.columnconfigure(5, minsize=150) self.columnconfigure(6, minsize=150) self.create_widgets() self.settables = self.assemble_settables() self.gui_logger = logging.getLogger('gui') self.request_update() def create_widgets(self): self.create_monitor() self.create_check_buttons() self.create_ranges() self.create_scales() self.create_radio_buttons() self.create_voices() self.quitButton = Button(self, text='Quit', command=self.quit) self.quitButton.grid(columnspan=7, sticky=E + W) def assemble_settables(self): settables = self.winfo_children() for w in settables: settables += w.winfo_children() return filter(lambda w: w.__class__.__name__ in ['Scale', 'Checkbutton'], settables) def create_radio_buttons(self): # Scale related entries = ['DIATONIC', 'HARMONIC', 'MELODIC', 'PENTATONIC', 'PENTA_MINOR'] self.scale = StringVar() self.scale.set('DIATONIC') self.rb_frame = Frame(self) for e in entries: rb = Radiobutton(self.rb_frame, value=e, text=e, anchor=W, command=self.send_scale, variable=self.scale) rb.grid(row=len(self.rb_frame.winfo_children()), sticky=W) self.rb_frame.grid(column=1, row=len(self.grid_slaves(column=1)), rowspan=3) def create_monitor(self): self.monitor_frame = LabelFrame(self, text="Monitor and Transport") this_cycle = Scale(self.monitor_frame, label='cycle_pos', orient=HORIZONTAL, from_=1, to=16, resolution=1) this_cycle.disable, this_cycle.enable = (None, None) this_cycle.ref = 'cycle_pos' this_cycle.grid(column=0, row=0, sticky=E + W) self.updateButton = Button(self.monitor_frame, text='Reload all Settings', command=self.request_update) self.updateButton.grid(row=1, sticky=E + W) self.ForceCaesuraButton = Button(self.monitor_frame, text='Force Caesura', command=self.force_caesura) self.ForceCaesuraButton.grid(row=2, sticky=E + W) self.saveBehaviourButton = Button(self.monitor_frame, text='Save current behaviour', command=self.request_saving_behaviour) self.saveBehaviourButton.grid(row=3, sticky=E + W) self.saveBehaviourNameEntry = Entry(self.monitor_frame) self.saveBehaviourNameEntry.grid(row=4, sticky=E + W) self.saveBehaviourNameEntry.bind('<KeyRelease>', self.request_saving_behaviour) self.selected_behaviour = StringVar() self.selected_behaviour.trace('w', self.new_behaviour_chosen) self.savedBehavioursMenu = OptionMenu(self.monitor_frame, self.selected_behaviour, None,) self.savedBehavioursMenu.grid(row=5, sticky=E + W) self.monitor_frame.grid(column=0, row=10, sticky=E + W) def request_update(self): self.send({'sys': 'update'}) def request_saving_behaviour(self, event=None): """callback for save behaviour button and textentry""" if event and event.widget == self.saveBehaviourNameEntry: if event.keysym == 'Return': name = self.saveBehaviourNameEntry.get() self.saveBehaviourNameEntry.delete(0, len(name)) else: return else: # button was pressed name = self.saveBehaviourNameEntry.get() if name: self.send({'sys': ['save_behaviour', name]}) def force_caesura(self): self.send({'force_caesura': True}) def create_voices(self): voice_ids = ['1', '2', '3', '4'] SCALES = OrderedDict([ ('pan_pos', {'min': -1, 'max': 1, 'start': 0.5, 'res': 0.001}), ('volume', {'min': 0, 'max': 1, 'start': 0.666, 'res': 0.001}), ('slide_duration_msecs', {'min': 0, 'max': 2000, 'start': 60, 'res': 1}), ('slide_duration_prop', {'min': 0, 'max': 2, 'start': 0.666, 'res': 0.001}), ('binaural_diff', {'min': 0, 'max': 66, 'start': 0.2, 'res': 0.01}) ]) for vid in voice_ids: counter = 0 for sca in SCALES: name = 'voice_' + vid + '_' + sca setattr(self, 'min_' + name, SCALES[sca]['min']) setattr(self, 'max_' + name, SCALES[sca]['max']) this_sca = Scale(self, label=sca, orient=HORIZONTAL, from_=getattr(self, 'min_' + name), to=getattr(self, 'max_' + name), resolution=SCALES[sca]['res']) this_sca.enable = ('enable' in SCALES[sca].keys() and SCALES[sca]['enable'] or None) this_sca.disable = ('disable' in SCALES[sca].keys() and SCALES[sca]['disable'] or None) this_sca.grid(column=int(2 + int(vid)), row=counter, sticky=E + W) this_sca.bind("<ButtonRelease>", self.scale_handler) this_sca.ref = name counter += 1 CHECK_BUTTONS = OrderedDict( [('mute', False), ('automate_binaural_diffs', True), ('automate_note_duration_prop', True), ('use_proportional_slide_duration', {'val': True, 'label': 'proportional slide'}), ('automate_pan', True), ('automate_wavetables', True)]) for vid in voice_ids: counter = 0 cb_frame = LabelFrame(self, text="Voice {0} - Automation".format(vid)) setattr(self, 'voice_' + vid + '_cb_frame', cb_frame) for cb in CHECK_BUTTONS: options = CHECK_BUTTONS[cb] name = 'voice_' + vid + '_' + cb label = (options['label'] if isinstance(options, dict) and 'label' in options.keys() else (cb[9:] if cb[:9] == 'automate_' else cb)) setattr(self, name, IntVar(value=type(options) == dict and options['val'] or options)) self.this_cb = Checkbutton(cb_frame, text=label, variable=getattr(self, name)) self.this_cb.bind('<Button-1>', self.check_boxes_handler) self.this_cb.disable = None self.this_cb.grid(sticky=W, column=0, row=counter) self.this_cb.ref = name counter += 1 # add trigger wavetable-button trigWavetableButton = Button(cb_frame, text='Next Wavetable') trigWavetableButton.bind('<Button-1>', self.trigger_waveform_handler) trigWavetableButton.ref = 'voice_' + vid + "_trigger_wavetable" trigWavetableButton.grid(row=counter) cb_frame.grid(column=int(vid) + 2, row=5, sticky=E + W + N, rowspan=8) for vid in voice_ids: generation_types = ["random", "random_harmonic", "harmonic"] partial_pools = ["even", "odd", "all"] prefix = 'voice_' + vid + '_' types_name = prefix + 'wavetable_generation_type' pools_name = prefix + 'partial_pool' setattr(self, types_name, StringVar()) getattr(self, types_name).set("random") setattr(self, pools_name, StringVar()) getattr(self, pools_name).set("all") target_frame = getattr(self, 'voice_' + vid + '_cb_frame') gen_typ_frame = LabelFrame(target_frame, text="type") gen_typ_frame.grid(row=len(target_frame.winfo_children()), sticky=W) for gen_t in generation_types: gen_t_entry = Radiobutton(gen_typ_frame, value=gen_t, text=gen_t, anchor=W, variable=getattr(self, types_name)) gen_t_entry.bind('<ButtonRelease-1>', self.wt_handler) gen_t_entry.ref = types_name gen_t_entry.grid(row=len(gen_typ_frame.winfo_children()), sticky=W) pp_frame = LabelFrame(target_frame, text="harmonics") for pp in partial_pools: pp_entry = Radiobutton(pp_frame, value=pp, text=pp, anchor=W, variable=getattr(self, pools_name)) pp_entry.bind('<ButtonRelease-1>', self.wt_handler) pp_entry.ref = pools_name pp_entry.grid(row=len(pp_frame.winfo_children()), sticky=E + W) this_num_partials = Scale(pp_frame, label='number of harmonics', orient=HORIZONTAL, from_=1, to=24, resolution=1) this_num_partials.ref = prefix + 'num_partials' this_num_partials.grid(column=0, row=len(pp_frame.winfo_children()), sticky=E + W) this_num_partials.bind("<ButtonRelease>", self.scale_handler) pp_frame.grid(row=len(target_frame.winfo_children()), sticky=E + W) def wt_handler(self, event): print event.widget.tk ref = event.widget.ref self.send({ref: getattr(self, ref).get()}) def create_check_buttons(self): self.cb_frame = LabelFrame(self, text="Global Settings") for cb in CHECK_BUTTONS: label = cb target_parent = self.cb_frame if isinstance(CHECK_BUTTONS[cb], dict) and 'sub_frame' in CHECK_BUTTONS[cb].keys(): target_parent = getattr(self, CHECK_BUTTONS[cb]['sub_frame']) setattr(self, cb, IntVar(value=type(CHECK_BUTTONS[cb]) == dict and CHECK_BUTTONS[cb]['val'] or CHECK_BUTTONS[cb])) self.this_cb = Checkbutton(target_parent, text=label, variable=getattr(self, cb)) self.this_cb.bind('<Button-1>', self.check_boxes_handler) self.this_cb.disable = (type(CHECK_BUTTONS[cb]) == dict and 'disable' in CHECK_BUTTONS[cb].keys()) self.this_cb.grid(sticky=W, column=0, row=len(target_parent.winfo_children())) self.this_cb.ref = cb for but in GLOBAL_BUTTONS: label = but ele = GLOBAL_BUTTONS[but] this_but = Button(self.cb_frame, text=but) this_but.bind('<ButtonRelease-1>', getattr(self, ele['handler'])) this_but.ref = but this_but.grid(sticky=W, column=0, row=len(self.cb_frame.winfo_children())) self.cb_frame.grid(column=0, row=0, rowspan=10, sticky=N) def new_behaviour_chosen(self, a, b, c): self.send({'sys': ['change_behaviour', self.selected_behaviour.get()]}) def set_value(self, name, val): '''sets a widget to the specified value various different widget types need custom setting functionality''' direct = ['scale', 'wavetable_generation_type', 'partial_pool'] if filter(lambda x: match("(voice_\d_|)" + x, name), direct): self.gui_logger.info("setting: '{0}' to '{1}' in GUI".format(name, val)) getattr(self, name).set(val) return if name == 'saved_behaviours' and len(val): self.savedBehavioursMenu.destroy() self.savedBehavioursMenu = OptionMenu(self.monitor_frame, self.selected_behaviour, *sorted(val)) self.savedBehavioursMenu.grid(row=5, sticky=E + W) return for w in self.settables: typ = w.__class__.__name__ if w.ref == name: #print "setting '{0}' of type: '{1}' to: {2}".format(name, typ, val) if typ == 'Scale': w.set(val) elif typ == "Checkbutton": w.select() if val else w.deselect() def check_boxes_handler(self, event): '''handles checkbox events. shows and hides gui elements according to their enable/disable fields''' #print event.__dict__ #print event.widget.__dict__ ref = event.widget.ref val = not getattr(self, ref).get() # because is read before the var is changed self.send({ref: val}) #print ref, val # handle gui elements # enable/disable functionality temporarily(?) commented on: # Wed Aug 17 09:39:54 CEST 2011 # if event.widget.disable: # for w in self.children.values(): # # # this try clause is for debugging, remove when stable # try: # w.ref # #print w.ref # except: # pass # if (w.__class__.__name__ == 'Scale' and # (w.disable or w.enable)): # if w.disable == ref: # if val: # w.grid() # else: # w.grid_remove() # elif w.enable == ref: # if val: # w.grid_remove() # else: # w.grid() # #print w.disable, w.enable def create_scales(self): counter = 0 for sca in SCALES: label = SCALES[sca]['label'] if 'label' in SCALES[sca].keys() else sca setattr(self, 'min_' + sca, SCALES[sca]['min']) setattr(self, 'max_' + sca, SCALES[sca]['max']) self.this_scale = Scale(self, label=label, orient=HORIZONTAL, from_=getattr(self, 'min_' + sca), to=getattr(self, 'max_' + sca), resolution=SCALES[sca]['res']) self.this_scale.set(SCALES[sca]['start']) self.this_scale.enable = ('enable' in SCALES[sca].keys() and SCALES[sca]['enable'] or None) self.this_scale.disable = ('disable' in SCALES[sca].keys() and SCALES[sca]['disable'] or None) if 'pos' in SCALES[sca].keys(): pos = SCALES[sca]['pos'] col = pos['c'] row = pos['r'] else: row = counter col = 1 counter += 1 self.this_scale.grid(column=col, row=row, sticky=E + W) self.this_scale.ref = sca self.this_scale.bind("<ButtonRelease>", self.scale_handler) def scale_handler(self, event): self.send({event.widget.ref: event.widget.get()}) self.gui_logger.info("handling scale: {0}, with new value: {1}".format( event.widget.ref, event.widget.get())) def trigger_waveform_handler(self, event): self.send({event.widget.ref: True}) #print event.widget.ref, "- triggering wavetable" def send_scale(self): do = {'scale': self.scale.get()} self.send(do) #print do def send(self, msg): self.gui_logger.info("sending: {0}".format(msg)) self.send_sock.sendto(json.dumps(msg), (remote_host, send_port)) def create_ranges(self): counter = 0 for ran in RANGES: setattr(self, 'min_' + ran, RANGES[ran]['min']) setattr(self, 'max_' + ran, RANGES[ran]['max']) self.this_min_scale = Scale(self, label='min ' + ran, orient=HORIZONTAL, from_=getattr(self, 'min_' + ran), to=getattr(self, 'max_' + ran), resolution=RANGES[ran]['res']) self.this_max_scale = Scale(self, label='max ' + ran, orient=HORIZONTAL, from_=getattr(self, 'min_' + ran), to=getattr(self, 'max_' + ran), resolution=RANGES[ran]['res']) self.this_min_scale.set(RANGES[ran]['min_start']) self.this_max_scale.set(RANGES[ran]['max_start']) self.this_min_scale.enable = ('enable' in RANGES[ran].keys() and RANGES[ran]['enable'] or None) self.this_min_scale.disable = ('disable' in RANGES[ran].keys() and RANGES[ran]['disable'] or None) self.this_max_scale.enable = ('enable' in RANGES[ran].keys() and RANGES[ran]['enable'] or None) self.this_max_scale.disable = ('disable' in RANGES[ran].keys() and RANGES[ran]['disable'] or None) self.this_min_scale.grid(column=2, row=counter, sticky=E + W) self.this_max_scale.grid(column=2, row=counter + 1, sticky=E + W) self.this_min_scale.ref = 'min_' + ran self.this_max_scale.ref = 'max_' + ran self.this_min_scale.bind("<ButtonRelease>", self.scale_handler) self.this_max_scale.bind("<ButtonRelease>", self.scale_handler) counter += 2 def socket_read_handler(self, file, mask): data_object = json.loads(file.recv(1024)) do = data_object.items()[0] #print "do:", do self.set_value(do[0], do[1])
class Visualiser(object): ''' Generic Offline Visualiser. Subclasses need to be used that specify how to handle a data source. ''' ### Public functions ### def __init__(self, title="Visualisation", width=400, height=400, recording=False, recordPattern=None, paused=False, source=None): ''' Constructor. Params: title: string - Title of the visualisation window width: int - Width of the visualisation window height: int - Height of the visualisation window recording: boolean - Start with recording enabled? recordPattern: string - Pattern for recorded images, e.g., cylinders%05g.png paused: boolean - Start with playback paused? source:- The data source to read. What is required here varies by visualiser. ''' # Visualisation options self.vis_features = [] self.vis_frame = 0 self.vis_frameStep = 1 self.vis_jumping = False self.vis_recording = recording self.vis_recordPattern = recordPattern self.vis_paused = paused self.vis_source = source # VTK structures self.vtk_cells = vtkCellArray() self.vtk_renderer = vtkRenderer() # Tk structures self.tk_root = Tk() self.tk_root.title(title) self.tk_root.grid_rowconfigure(0, weight=1) self.tk_root.grid_columnconfigure(0, weight=3) self.tk_root.bind('<Destroy>', self.destroyed) if not self.vis_paused: self.tk_root.after(100, self.animate) self.tk_renderWidget = vtkTkRenderWidget(self.tk_root, width=width, height=height) self.tk_renderWidget.grid(row=0, column=0, sticky=N+S+E+W) self.tk_renderWidget.GetRenderWindow().AddRenderer(self.vtk_renderer) self.tk_featureFrame = Frame(self.tk_root) self.tk_featureFrame.grid(row=0, column=1, rowspan=2) Label(self.tk_featureFrame, text='Features:').grid(row=0, column=0) self.tk_controlFrame = Frame(self.tk_root) self.tk_controlFrame.grid(row=1, column=0) self.tk_quit = Button(self.tk_controlFrame, text="Quit", command=self.shutdown) self.tk_quit.grid(row=0, column=0, columnspan=2) def pause(): if self.vis_paused: self.tk_pause.config(text='Pause') self.tk_root.after(100, self.animate) else: self.tk_pause.config(text='Resume') self.vis_paused ^= True self.tk_pause = Button(self.tk_controlFrame, text="Pause", command=pause) self.tk_pause.grid(row=0, column=2, columnspan=2) if self.vis_recordPattern is not None: def record(): if self.vis_recording: self.tk_record.config(text='Start Recording') else: self.tk_record.config(text='Stop Recording') self.vis_recording ^= True self.tk_record = Button(self.tk_controlFrame, text="Start Recording", command=record) self.tk_record.grid(row=0, column=4, columnspan=2) if self.vis_recording: self.tk_record.config(text="Stop Recording") def make_seek_button(label, column, frame): def jump(): self.jumpTo(frame) b = Button(self.tk_controlFrame, text=label, command=jump) b.grid(row=1, column=column, sticky=W+E) return b self.tk_seek_start = make_seek_button("|<", 0, 0) self.tk_seek_back10 = make_seek_button("<<", 1, lambda: self.vis_frame - 10) self.tk_seek_back1 = make_seek_button("<", 2, lambda: self.vis_frame - 1) self.tk_seek_forward1 = make_seek_button(">", 3, lambda: self.vis_frame + 1) self.tk_seek_forward10 = make_seek_button(">>", 4, lambda: self.vis_frame + 10) self.tk_seek_end = make_seek_button(">|", 5, self.getMaxFrameNumber) Label(self.tk_controlFrame, text='Frame').grid(row=2, column=0, sticky=W+E) def changeFrame(frame): if not self.vis_jumping: self.vis_jumping = True self.jumpTo(self.tk_frame.get()) self.vis_jumping = False self.tk_frame = Scale(self.tk_controlFrame, command=changeFrame, from_=0, to=0, orient=HORIZONTAL) self.tk_frame.grid(row=2, column=1, columnspan=2, sticky=W+E) Label(self.tk_controlFrame, text='Step').grid(row=2, column=3, sticky=W+E) def changeFrameStep(step): self.vis_frameStep = int(step) self.tk_frameStep = Scale(self.tk_controlFrame, command=changeFrameStep, from_=1, to=1, orient=HORIZONTAL) self.tk_frameStep.grid(row=2, column=4, columnspan=2, sticky=W+E) self.setupGrid() def add_feature(self, feature): '''Add a feature to this visualiser''' self.vis_features.append(feature) feature.button(self.tk_featureFrame).grid(row=len(self.vis_features), column=0, sticky=W+E) feature.visualiser = self def run(self): '''Start the visualiser''' self.redraw() self.tk_root.mainloop() ### Private funcitions ### def resetSliders(self): ''' Recalculate the upper bound on the frame and frameStep sliders. ''' maxFrame = self.getMaxFrameNumber() self.tk_frame.config(to=maxFrame) self.tk_frameStep.config(to=maxFrame) def jumpTo(self, frame): ''' Jump to a given frame. If frame is a function, jump to the return value of frame(). ''' oldFrame = self.vis_frame if hasattr(frame, '__call__'): self.vis_frame = frame() else: self.vis_frame = frame maxFrame = self.getMaxFrameNumber() if self.vis_frame < 0: self.vis_frame = 0 elif self.vis_frame > maxFrame: self.vis_frame = maxFrame self.vis_paused = True self.tk_pause.config(text='Resume') self.tk_frame.set(self.vis_frame) self.redraw(oldFrame != self.vis_frame) def redraw(self, update=False): self.resetSliders() for feature in [ f for f in self.vis_features if not f.dynamic ]: feature.draw(self.vtk_renderer) if update: for feature in [ f for f in self.vis_features if f.dynamic]: f.redraw(self.vtk_renderer) self.tk_renderWidget.GetRenderWindow().Render() self.tk_root.update_idletasks() ### Gui events ### def destroyed(self, event): if event.widget == self.tk_root: self.shutdown() def shutdown(self): self.tk_root.withdraw() self.tk_root.destroy() def animate(self): if not self.vis_paused: self.jumpTo(self.vis_frame + self.vis_frameStep) if self.vis_recording and self.vis_recordPattern is not None: self.save_image() self.tk_root.after(100, self.animate) def save_image(self): extmap = {'.jpg' : vtkJPEGWriter, '.jpeg' : vtkJPEGWriter, '.png' : vtkPNGWriter, '.pnm' : vtkPNMWriter} _, ext = splitext(self.vis_recordPattern) try: writer = extmap[ext.lower()]() except KeyError: print 'ERROR: Can\'t handle %s extension. Recording disabled.' % ext self.vis_recordPattern = None return win = self.vtk_renderer.GetRenderWindow() w2i = vtkWindowToImageFilter() w2i.SetInput(win) w2i.Update() writer.SetInput(w2i.GetOutput()) writer.SetFileName(self.vis_recordPattern % self.vis_frame) win.Render() writer.Write() ### Things subclasses need to override ### def setupGrid(self): ''' Populate the vtkCellArray instance at self.vtk_cells. Subclasses are required to override this function to read from their source as appropriate. ''' raise NotImplementedError('Subclass needs to override Visualiser::setupGrid!') def getMaxFrameNumber(self): ''' Return the maximum frame number. This will need to be defined by a subclass. ''' raise NotImplementedError('Subclass needs to override Visualiser::getMaxFrameNumber!') def getQuantityPoints(self, quantityName, dynamic=True, frameNumber=0): ''' Return the points of a quantity at a given frame as a list [float]. Subclasses need to override this. ''' raise NotImplementedError('Subclass needs to override Visualiser::getQuantityPoints!') def getQuantityDict(self): ''' Return the values of all quantities at a given time as a dictionary. Sublclasses need to override this. ''' raise NotImplementedError('Subclass needs to override Visualiser::getQuantityDict!')
class Scraper(): def __init__(self, img_filename='snap.png'): self.canvas = Canvas() self.scales = [] self.canvas.pack() self._current_image = None self._original_image = None self.root = self._load_gui() self.open_image(img_filename) self.repaint() self.root.mainloop() @property def image(self): return self._current_image @image.setter def image(self, value): self._current_image = value @property def original_image(self): return self._original_image @image.setter def image(self, value): self._original_image = value def open_image(self, filename): self.original_image = Image.open(filename).copy() self.image = self.original_image def repaint(self): rgb_min = (self.scale_red_min.get(), self.scale_green_min.get(), self.scale_blue_min.get()) rgb_max = (self.scale_red_max.get(), self.scale_green_max.get(), self.scale_blue_max.get()) print "repainting", rgb_min, rgb_max self.image = strip_colors(self.original_image, rgb_min, rgb_max) self._paint(self.image) def _paint(self, image): if image is not None: # check if we need to resize the canvas, based on the size of the image being painted image_width, image_height = image.size if self.canvas.winfo_width() != image_width or self.canvas.winfo_height() != image_height: self.canvas.config(width=image_width, height=image_height) # paint the image to the canvas self._img_tk = ImageTk.PhotoImage(image) self.canvas.create_image(0, 0, image=self._img_tk, anchor=NW) else: print 'there is no image to paint' return -1 def _load_gui(self): root = Tk() root.title("Scraper") main_frame = Frame(root) main_frame.grid(column=0, row=0, sticky=(N, W, E, S)) main_frame.columnconfigure(0, weight=1) main_frame.rowconfigure(0, weight=1) main_frame.pack() self.scale_red_min = Scale(main_frame, from_=0, to=255, orient=HORIZONTAL, command=self._update_red_min) self.scale_red_min.grid(column=1, row=1) self.scale_red_max = Scale(main_frame, from_=0, to=255, orient=HORIZONTAL, command=self._update_red_max) self.scale_red_max.grid(column=2, row=1) self.scale_red_max.set(255) self.scale_green_min = Scale(main_frame, from_=0, to=255, orient=HORIZONTAL, command=self._update_green_min) self.scale_green_min.grid(column=1, row=2) self.scale_green_max = Scale(main_frame, from_=0, to=255, orient=HORIZONTAL, command=self._update_green_max) self.scale_green_max.grid(column=2, row=2) self.scale_green_max.set(255) self.scale_blue_min = Scale(main_frame, from_=0, to=255, orient=HORIZONTAL, command=self._update_blue_min) self.scale_blue_min.grid(column=1, row=3) self.scale_blue_max = Scale(main_frame, from_=0, to=255, orient=HORIZONTAL, command=self._update_blue_max) self.scale_blue_max.grid(column=2, row=3) self.scale_blue_max.set(255) return root def _update_red_min(self, value): print 'updating red min to %s' % value if self.scale_red_min.get() > self.scale_red_max.get(): self.scale_red_max.set(self.scale_red_min.get()) self.repaint() def _update_red_max(self, value): print 'updating red max to %s' % value if self.scale_red_max.get() < self.scale_red_min.get(): self.scale_red_min.set(self.scale_red_max.get()) self.repaint() def _update_green_min(self, value): print 'updating green min to %s' % value if self.scale_green_min.get() > self.scale_green_max.get(): self.scale_green_max.set(self.scale_green_min.get()) self.repaint() def _update_green_max(self, value): print 'updating green max to %s' % value if self.scale_green_max.get() < self.scale_green_min.get(): self.scale_green_min.set(self.scale_green_max.get()) self.repaint() def _update_blue_min(self, value): print 'updating blue min to %s' % value if self.scale_blue_min.get() > self.scale_blue_max.get(): self.scale_blue_max.set(self.scale_blue_min.get()) self.repaint() def _update_blue_max(self, value): print 'updating blue max to %s' % value if self.scale_blue_max.get() < self.scale_blue_min.get(): self.scale_blue_min.set(self.scale_blue_max.get()) self.repaint()
def sunfounder_client(ip, key): #!/usr/bin/env python # -*- coding: utf-8 -*- from Tkinter import Tk, Button, Label, Scale, HORIZONTAL print "Connecting to IP", ip ctrl_cmd = ['forward', 'backward', 'left', 'right', 'stop', 'read cpu_temp', 'home', 'distance', 'x+', 'x-', 'y+', 'y-', 'xy_home'] top = Tk() # Create a top window top.title('Sunfounder Raspberry Pi Smart Video Car') HOST = ip # Server(Raspberry Pi) IP address PORT = 21567 BUFSIZ = 1024 # buffer size ADDR = (HOST, PORT) tcpCliSock = socket(AF_INET, SOCK_STREAM) # Create a socket tcpCliSock.connect(ADDR) # Connect with the server # ============================================================================= # The function is to send the command forward to the server, so as to make the # car move forward. # ============================================================================= def forward_fun(event): print "\nUser command = forward" print "Encrypted command = ",crypto.AES_encrypt('forward',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('forward',key))) def backward_fun(event): print '\nUser command = backward' print "Encrypted command = ",crypto.AES_encrypt('backward',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('backward',key))) def left_fun(event): print '\nUser command = left' print "Encrypted command = ",crypto.AES_encrypt('left',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('left',key))) def right_fun(event): print '\nUser command = right' print "Encrypted command = ",crypto.AES_encrypt('right',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('right',key))) def stop_fun(event): print '\nUser command = stop' print "Encrypted command = ",crypto.AES_encrypt('stop',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('stop',key))) def home_fun(event): print '\nUser command = home' print "Encrypted command = ",crypto.AES_encrypt('home',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('home',key))) def x_increase(event): print '\nUser command = x+' print "Encrypted command = ",crypto.AES_encrypt('x+',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('x+',key))) def x_decrease(event): print '\nUser command = x-' print "Encrypted command = ",crypto.AES_encrypt('x-',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('x-',key))) def y_increase(event): print '\nUser command = y+' print "Encrypted command = ",crypto.AES_encrypt('y+',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('y+',key))) def y_decrease(event): print '\nUser command = y-' print "Encrypted command = ",crypto.AES_encrypt('y-',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('y-',key))) def xy_home(event): print '\nUser command = xy_home' print "Encrypted command = ",crypto.AES_encrypt('xy_home',key)[2] tcpCliSock.send(str(crypto.AES_encrypt('xy_home',key))) # ============================================================================= # Exit the GUI program and close the network connection between the client # and server. # ============================================================================= def quit_fun(event): print "\nShutting down program..." top.quit() tcpCliSock.send(str(crypto.AES_encrypt('stop',key))) tcpCliSock.close() # ============================================================================= # Create buttons # ============================================================================= Btn0 = Button(top, width=5, text='Forward') Btn1 = Button(top, width=5, text='Backward') Btn2 = Button(top, width=5, text='Left') Btn3 = Button(top, width=5, text='Right') Btn4 = Button(top, width=5, text='Quit') Btn5 = Button(top, width=5, height=2, text='Home') # ============================================================================= # Buttons layout # ============================================================================= Btn0.grid(row=0,column=1) Btn1.grid(row=2,column=1) Btn2.grid(row=1,column=0) Btn3.grid(row=1,column=2) Btn4.grid(row=3,column=2) Btn5.grid(row=1,column=1) # ============================================================================= # Bind the buttons with the corresponding callback function. # ============================================================================= Btn0.bind('<ButtonPress-1>', forward_fun) # When button0 is pressed down, call the function forward_fun(). Btn1.bind('<ButtonPress-1>', backward_fun) Btn2.bind('<ButtonPress-1>', left_fun) Btn3.bind('<ButtonPress-1>', right_fun) Btn0.bind('<ButtonRelease-1>', stop_fun) # When button0 is released, call the function stop_fun(). Btn1.bind('<ButtonRelease-1>', stop_fun) Btn2.bind('<ButtonRelease-1>', stop_fun) Btn3.bind('<ButtonRelease-1>', stop_fun) Btn4.bind('<ButtonRelease-1>', quit_fun) Btn5.bind('<ButtonRelease-1>', home_fun) # ============================================================================= # Create buttons # ============================================================================= Btn07 = Button(top, width=5, text='X+', bg='red') Btn08 = Button(top, width=5, text='X-', bg='red') Btn09 = Button(top, width=5, text='Y-', bg='red') Btn10 = Button(top, width=5, text='Y+', bg='red') Btn11 = Button(top, width=5, height=2, text='HOME', bg='red') # ============================================================================= # Buttons layout # ============================================================================= Btn07.grid(row=1,column=5) Btn08.grid(row=1,column=3) Btn09.grid(row=2,column=4) Btn10.grid(row=0,column=4) Btn11.grid(row=1,column=4) # ============================================================================= # Bind button events # ============================================================================= Btn07.bind('<ButtonPress-1>', x_increase) Btn08.bind('<ButtonPress-1>', x_decrease) Btn09.bind('<ButtonPress-1>', y_decrease) Btn10.bind('<ButtonPress-1>', y_increase) Btn11.bind('<ButtonPress-1>', xy_home) #Btn07.bind('<ButtonRelease-1>', home_fun) #Btn08.bind('<ButtonRelease-1>', home_fun) #Btn09.bind('<ButtonRelease-1>', home_fun) #Btn10.bind('<ButtonRelease-1>', home_fun) #Btn11.bind('<ButtonRelease-1>', home_fun) # ============================================================================= # Bind buttons on the keyboard with the corresponding callback function to # control the car remotely with the keyboard. # ============================================================================= top.bind('<KeyPress-a>', left_fun) # Press down key 'A' on the keyboard and the car will turn left. top.bind('<KeyPress-d>', right_fun) top.bind('<KeyPress-s>', backward_fun) top.bind('<KeyPress-w>', forward_fun) top.bind('<KeyPress-h>', home_fun) top.bind('<KeyRelease-a>', home_fun) # Release key 'A' and the car will turn back. top.bind('<KeyRelease-d>', home_fun) top.bind('<KeyRelease-s>', stop_fun) top.bind('<KeyRelease-w>', stop_fun) spd = 50 def changeSpeed(ev=None): tmp = 'speed' global spd spd = speed.get() data = tmp + str(spd) # Change the integers into strings and combine them with the string 'speed'. #print 'sendData = %s' % data print '\nUser command = ', data print "Encrypted command = ",crypto.AES_encrypt(data,key)[2] tcpCliSock.send(str(crypto.AES_encrypt(data,key))) # Send the speed data to the server(Raspberry Pi) label = Label(top, text='Speed:', fg='red') # Create a label label.grid(row=6, column=0) # Label layout speed = Scale(top, from_=0, to=100, orient=HORIZONTAL, command=changeSpeed) # Create a scale speed.set(50) speed.grid(row=6, column=1) def main(): top.mainloop() if __name__ == '__main__': main()
class Tool_Path_Generator: def __init__(self, top=None): '''This class configures and populates the toplevel window. top is the toplevel containing window.''' _bgcolor = '#e6e6e6' # X11 color: 'gray85' _fgcolor = '#000000' # X11 color: 'black' font11 = "-size 15 -weight normal -slant roman " \ "-underline 0 -overstrike 0" self.axial_length = DoubleVar() self.printbed_diameter = DoubleVar() self.final_diameter = DoubleVar() self.filament_width_og = DoubleVar() self.helix_angle = DoubleVar() self.smear_factor = DoubleVar() self.flow_rate = DoubleVar() self.uv_offset = DoubleVar() self.use_strong_pattern = BooleanVar() self.axial_length.set(200.0) self.printbed_diameter.set(10.0) self.final_diameter.set(15.0) self.filament_width_og.set(0.41) self.helix_angle.set(45.0) self.smear_factor.set(100.0) self.flow_rate.set(0.0015) self.uv_offset.set(32.5) self.use_strong_pattern.set(True) top.geometry("700x550") top.title("SkelePrint Tool Path Generator") top.configure(background="#e6e6e6") top.configure(highlightbackground="#e6e6e6") top.configure(highlightcolor="black") self.Label7 = Label(top) self.Label7.grid(row=0, column=0, sticky=W) self.Label7.configure(background="#e6e6e6") self.Label7.configure(font=font11) self.Label7.configure(foreground="#000000") self.Label7.configure(text='''SkelePrint Tool Path Generator''') self.Labelframe1 = LabelFrame(top) self.Labelframe1.grid(row=1, column=0, sticky=N+S) self.Labelframe1.configure(relief=GROOVE) self.Labelframe1.configure(foreground="black") self.Labelframe1.configure(text='''Dimensions''') self.Labelframe1.configure(background="#e6e6e6") self.Labelframe1.configure(highlightbackground="#e6e6e6") self.Labelframe1.configure(highlightcolor="black") self.axial_length_entry = Entry(self.Labelframe1) self.axial_length_entry.grid(row=0, column=1) self.axial_length_entry.configure(background="white") self.axial_length_entry.configure(font="TkFixedFont") self.axial_length_entry.configure(foreground="#000000") self.axial_length_entry.configure(highlightbackground="#e6e6e6") self.axial_length_entry.configure(highlightcolor="black") self.axial_length_entry.configure(insertbackground="black") self.axial_length_entry.configure(selectbackground="#c4c4c4") self.axial_length_entry.configure(selectforeground="black") self.axial_length_entry.configure(textvariable=self.axial_length) self.Label1 = Label(self.Labelframe1) self.Label1.grid(row=0, column=0, sticky=E) self.Label1.configure(activebackground="#e6e6e6") self.Label1.configure(activeforeground="black") self.Label1.configure(background="#e6e6e6") self.Label1.configure(foreground="#000000") self.Label1.configure(highlightbackground="#e6e6e6") self.Label1.configure(highlightcolor="black") self.Label1.configure(text='''Axial Length''') self.Label2 = Label(self.Labelframe1) self.Label2.grid(row=0, column=2, sticky=W) self.Label2.configure(activebackground="#e6e6e6") self.Label2.configure(activeforeground="black") self.Label2.configure(background="#e6e6e6") self.Label2.configure(disabledforeground="#e6e6e6") self.Label2.configure(foreground="#000000") self.Label2.configure(highlightbackground="#e6e6e6") self.Label2.configure(highlightcolor="black") self.Label2.configure(text='''mm''') self.Label3 = Label(self.Labelframe1) self.Label3.grid(row=1, column=0, sticky=E) self.Label3.configure(activebackground="#e6e6e6") self.Label3.configure(activeforeground="black") self.Label3.configure(background="#e6e6e6") self.Label3.configure(foreground="#000000") self.Label3.configure(highlightbackground="#e6e6e6") self.Label3.configure(highlightcolor="black") self.Label3.configure(text='''Printbed Diameter''') self.Entry2 = Entry(self.Labelframe1) self.Entry2.grid(row=1, column=1) self.Entry2.configure(background="white") self.Entry2.configure(font="TkFixedFont") self.Entry2.configure(foreground="#000000") self.Entry2.configure(highlightbackground="#e6e6e6") self.Entry2.configure(highlightcolor="black") self.Entry2.configure(insertbackground="black") self.Entry2.configure(selectbackground="#c4c4c4") self.Entry2.configure(selectforeground="black") self.Entry2.configure(textvariable=self.printbed_diameter) self.Label4 = Label(self.Labelframe1) self.Label4.grid(row=1, column=2, sticky=W) self.Label4.configure(activebackground="#e6e6e6") self.Label4.configure(activeforeground="black") self.Label4.configure(background="#e6e6e6") self.Label4.configure(foreground="#000000") self.Label4.configure(highlightbackground="#e6e6e6") self.Label4.configure(highlightcolor="black") self.Label4.configure(text='''mm''') self.Label5 = Label(self.Labelframe1) self.Label5.grid(row=2, column=0, sticky=E) self.Label5.configure(activebackground="#e6e6e6") self.Label5.configure(activeforeground="black") self.Label5.configure(background="#e6e6e6") self.Label5.configure(foreground="#000000") self.Label5.configure(highlightbackground="#e6e6e6") self.Label5.configure(highlightcolor="black") self.Label5.configure(text='''Final Print Diameter''') self.final_diameter_entry = Entry(self.Labelframe1) self.final_diameter_entry.grid(row=2, column=1) self.final_diameter_entry.configure(background="white") self.final_diameter_entry.configure(font="TkFixedFont") self.final_diameter_entry.configure(foreground="#000000") self.final_diameter_entry.configure(highlightbackground="#e6e6e6") self.final_diameter_entry.configure(highlightcolor="black") self.final_diameter_entry.configure(insertbackground="black") self.final_diameter_entry.configure(selectbackground="#c4c4c4") self.final_diameter_entry.configure(selectforeground="black") self.final_diameter_entry.configure(textvariable=self.final_diameter) self.Label6 = Label(self.Labelframe1) self.Label6.grid(row=2, column=2, sticky=W) self.Label6.configure(activebackground="#e6e6e6") self.Label6.configure(activeforeground="black") self.Label6.configure(background="#e6e6e6") self.Label6.configure(foreground="#000000") self.Label6.configure(highlightbackground="#e6e6e6") self.Label6.configure(highlightcolor="black") self.Label6.configure(text='''mm''') self.Entry4 = Entry(self.Labelframe1) self.Entry4.grid(row=3, column=1) self.Entry4.configure(background="white") self.Entry4.configure(font="TkFixedFont") self.Entry4.configure(foreground="#000000") self.Entry4.configure(highlightbackground="#e6e6e6") self.Entry4.configure(highlightcolor="black") self.Entry4.configure(insertbackground="black") self.Entry4.configure(selectbackground="#c4c4c4") self.Entry4.configure(selectforeground="black") self.Entry4.configure(textvariable=self.filament_width_og) self.Label7 = Label(self.Labelframe1) self.Label7.grid(row=3, column=2, sticky=W) self.Label7.configure(activebackground="#e6e6e6") self.Label7.configure(activeforeground="black") self.Label7.configure(background="#e6e6e6") self.Label7.configure(foreground="#000000") self.Label7.configure(highlightbackground="#e6e6e6") self.Label7.configure(highlightcolor="black") self.Label7.configure(text='''mm''') self.Label8 = Label(self.Labelframe1) self.Label8.grid(row=3, column=0, sticky=E) self.Label8.configure(activebackground="#e6e6e6") self.Label8.configure(activeforeground="black") self.Label8.configure(background="#e6e6e6") self.Label8.configure(foreground="#000000") self.Label8.configure(highlightbackground="#e6e6e6") self.Label8.configure(highlightcolor="black") self.Label8.configure(text='''Filament Width''') self.tip = Label(self.Labelframe1, width=300, height=300) __location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) img = Image.open(os.path.join(__location__, 'dimensions.png')) one = ImageTk.PhotoImage(img) self.tip = Label(self.Labelframe1, image=one) self.tip.image = one self.tip.configure(background="#e6e6e6") self.tip.grid(row=4, columnspan=3) self.Labelframe2 = LabelFrame(top) self.Labelframe2.grid(row=1, column=1, sticky=N+S) self.Labelframe2.configure(relief=GROOVE) self.Labelframe2.configure(foreground="black") self.Labelframe2.configure(text='''Print Properties''') self.Labelframe2.configure(background="#e6e6e6") self.Labelframe2.configure(highlightbackground="#e6e6e6") self.Labelframe2.configure(highlightcolor="black") self.Label9 = Label(self.Labelframe2) self.Label9.grid(row=0, column=0, sticky=E) self.Label9.configure(activebackground="#e6e6e6") self.Label9.configure(activeforeground="black") self.Label9.configure(background="#e6e6e6") self.Label9.configure(foreground="#000000") self.Label9.configure(highlightbackground="#e6e6e6") self.Label9.configure(highlightcolor="black") self.Label9.configure(text='''Helix Angle''') self.Entry5 = Entry(self.Labelframe2) self.Entry5.grid(row=0, column=1) self.Entry5.configure(background="white") self.Entry5.configure(font="TkFixedFont") self.Entry5.configure(foreground="#000000") self.Entry5.configure(highlightbackground="#e6e6e6") self.Entry5.configure(highlightcolor="black") self.Entry5.configure(insertbackground="black") self.Entry5.configure(selectbackground="#c4c4c4") self.Entry5.configure(selectforeground="black") self.Entry5.configure(textvariable=self.helix_angle) self.Label10 = Label(self.Labelframe2) self.Label10.grid(row=0, column=2, sticky=W) self.Label10.configure(activebackground="#e6e6e6") self.Label10.configure(activeforeground="black") self.Label10.configure(background="#e6e6e6") self.Label10.configure(foreground="#000000") self.Label10.configure(highlightbackground="#e6e6e6") self.Label10.configure(highlightcolor="black") self.Label10.configure(text='''degrees [0 - 90]''') self.strong_targeter_button = Radiobutton(self.Labelframe2) self.strong_targeter_button.grid(row=1, column=0, sticky=E) self.strong_targeter_button.configure(variable=self.use_strong_pattern) self.strong_targeter_button.configure(value=True) self.strong_targeter_button.configure(activebackground="#e6e6e6") self.strong_targeter_button.configure(activeforeground="black") self.strong_targeter_button.configure(background="#e6e6e6") self.strong_targeter_button.configure(foreground="#000000") self.strong_targeter_button.configure(highlightbackground="#e6e6e6") self.strong_targeter_button.configure(highlightcolor="black") self.strong_targeter_label = Label(self.Labelframe2) self.strong_targeter_label.grid(row=1, column=1, sticky=W) self.strong_targeter_label.configure(activebackground="#e6e6e6") self.strong_targeter_label.configure(activeforeground="black") self.strong_targeter_label.configure(background="#e6e6e6") self.strong_targeter_label.configure(foreground="#000000") self.strong_targeter_label.configure(highlightbackground="#e6e6e6") self.strong_targeter_label.configure(highlightcolor="black") self.strong_targeter_label.configure(text="Strong angle pattern") self.default_targeter_button = Radiobutton(self.Labelframe2) self.default_targeter_button.grid(row=2, column=0, sticky=E) self.default_targeter_button.configure(activebackground="#e6e6e6") self.default_targeter_button.configure(activeforeground="black") self.default_targeter_button.configure(background="#e6e6e6") self.default_targeter_button.configure(foreground="#000000") self.default_targeter_button.configure(highlightbackground="#e6e6e6") self.default_targeter_button.configure(highlightcolor="black") self.default_targeter_button.configure( variable=self.use_strong_pattern) self.default_targeter_button.configure(value=False) self.default_targeter_label = Label(self.Labelframe2) self.default_targeter_label.grid(row=2, column=1, sticky=W) self.default_targeter_label.configure(activebackground="#e6e6e6") self.default_targeter_label.configure(activeforeground="black") self.default_targeter_label.configure(background="#e6e6e6") self.default_targeter_label.configure(foreground="#000000") self.default_targeter_label.configure(highlightbackground="#e6e6e6") self.default_targeter_label.configure(highlightcolor="black") self.default_targeter_label.configure(text="Default angle pattern") self.Scale1 = Scale(self.Labelframe2) self.Scale1.grid(row=5, column=1, columnspan=2, sticky=S+W) self.Scale1.configure(activebackground="#e6e6e6") self.Scale1.configure(background="#e6e6e6") self.Scale1.configure(font="TkTextFont") self.Scale1.configure(foreground="#000000") self.Scale1.configure(from_="5.0") self.Scale1.configure(highlightbackground="#d9d9d9") self.Scale1.configure(highlightcolor="black") self.Scale1.configure(length="150") self.Scale1.configure(orient="horizontal") self.Scale1.configure(resolution="5.0") self.Scale1.configure(troughcolor="#d9d9d9") self.Scale1.configure(variable=self.smear_factor) self.Label8 = Label(self.Labelframe2) self.Label8.grid(row=3, column=0, sticky=E) self.Label8.configure(background="#e6e6e6") self.Label8.configure(foreground="#000000") self.Label8.configure(text='''Flow rate''') self.Entry6 = Entry(self.Labelframe2) self.Entry6.grid(row=3, column=1) self.Entry6.configure(background="white") self.Entry6.configure(font="TkFixedFont") self.Entry6.configure(foreground="#000000") self.Entry6.configure(highlightbackground="#e6e6e6") self.Entry6.configure(highlightcolor="black") self.Entry6.configure(insertbackground="black") self.Entry6.configure(selectbackground="#c4c4c4") self.Entry6.configure(selectforeground="black") self.Entry6.configure(textvariable=self.flow_rate) self.Label12 = Label(self.Labelframe2) self.Label12.grid(row=3, column=2, sticky=W) self.Label12.configure(activebackground="#e6e6e6") self.Label12.configure(activeforeground="black") self.Label12.configure(background="#e6e6e6") self.Label12.configure(foreground="#000000") self.Label12.configure(highlightbackground="#d9d9d9") self.Label12.configure(highlightcolor="black") self.Label12.configure(text='''cm^3 / s''') self.uv_label = Label(self.Labelframe2) self.uv_label.grid(row=4, column=0, sticky=E) self.uv_label.configure(activebackground="#e6e6e6") self.uv_label.configure(activeforeground="black") self.uv_label.configure(background="#e6e6e6") self.uv_label.configure(foreground="#000000") self.uv_label.configure(highlightbackground="#d9d9d9") self.uv_label.configure(highlightcolor="black") self.uv_label.configure(text="UV Distance") self.uv_entry = Entry(self.Labelframe2) self.uv_entry.grid(row=4, column=1) self.uv_entry.configure(background="white") self.uv_entry.configure(font="TkFixedFont") self.uv_entry.configure(foreground="#000000") self.uv_entry.configure(highlightbackground="#e6e6e6") self.uv_entry.configure(highlightcolor="black") self.uv_entry.configure(insertbackground="black") self.uv_entry.configure(selectbackground="#c4c4c4") self.uv_entry.configure(selectforeground="black") self.uv_entry.configure(textvariable=self.uv_offset) self.uv_label_2 = Label(self.Labelframe2) self.uv_label_2.grid(row=4, column=2, sticky=W) self.uv_label_2.configure(activebackground="#e6e6e6") self.uv_label_2.configure(activeforeground="black") self.uv_label_2.configure(background="#e6e6e6") self.uv_label_2.configure(foreground="#000000") self.uv_label_2.configure(highlightbackground="#d9d9d9") self.uv_label_2.configure(highlightcolor="black") self.uv_label_2.configure(text='''mm''') self.Label11 = Label(self.Labelframe2) self.Label11.grid(row=5, column=0, sticky=S+E) self.Label11.configure(activebackground="#e6e6e6") self.Label11.configure(activeforeground="black") self.Label11.configure(background="#e6e6e6") self.Label11.configure(foreground="#000000") self.Label11.configure(highlightbackground="#d9d9d9") self.Label11.configure(highlightcolor="black") self.Label11.configure(text='''Layer Height %''') self.Label13 = Label(self.Labelframe2) self.Label13.grid(row=6, columnspan=3) self.Label13.configure(activebackground="#f9f9f9") self.Label13.configure(activeforeground="black") self.Label13.configure(background="#e6e6e6") self.Label13.configure(foreground="#000000") self.Label13.configure(highlightbackground="#d9d9d9") self.Label13.configure(highlightcolor="black") self.Label13.configure(text='''caution: layer height % is experimental default = 100% (ie. layer height = filament width)''') self.Message1 = Message(self.Labelframe2) self.Message1.grid(row=8, columnspan=3) self.Message1.configure(anchor=N) self.Message1.configure(background="#e6e6e6") self.Message1.configure(foreground="#000000") self.Message1.configure(highlightbackground="#e6e6e6") self.Message1.configure(highlightcolor="black") self.Message1.configure(text='''Helix Angle Conditions: If the angle is > 90, it will be set to 90 degrees If angle is < 0, it will be set to 0 degrees If angle = 0, the layer will consist of a single helix printed as close \ together as possible If angle = 90, the layer will consist of many straight lines''') self.tip2 = Label(self.Labelframe2, width=300, height=91) img2 = Image.open(os.path.join(__location__, 'theta.jpg')) two = ImageTk.PhotoImage(img2) self.tip2 = Label(self.Labelframe2, image=two) self.tip2.image = two self.tip2.configure(background="#e6e6e6") self.tip2.grid(row=7, columnspan=3) self.Label8 = Label(top) self.Label8.grid(row=5, columnspan=2) self.Label8.configure(background="#e6e6e6") self.Label8.configure(foreground="#000000") self.Label8.configure(text='''G Code file will be saved on your Desktop under: "gcode/timestamp_skeleprint_gcode.gcode"''') self.Button1 = Button(top) self.Button1.grid(row=2, columnspan=2) self.Button1.configure(activebackground="#e6e6e6") self.Button1.configure(activeforeground="#e6e6e6") self.Button1.configure(background="#e6e6e6") self.Button1.configure(command=lambda: tpg_gui_support.tpg( self.axial_length.get(), self.filament_width_og.get(), self.printbed_diameter.get(), self.final_diameter.get(), self.helix_angle.get(), self.smear_factor.get(), self.flow_rate.get(), self.uv_offset.get(), self.use_strong_pattern.get())) self.Button1.configure(foreground="#000000") self.Button1.configure(highlightbackground="#e6e6e6") self.Button1.configure(highlightcolor="black") self.Button1.configure(relief=RAISED) self.Button1.configure(text='''Generate G Code''') self.menubar = Menu(top, font="TkMenuFont", bg=_bgcolor, fg=_fgcolor) top.configure(menu=self.menubar)