def showLoading(self): self.attemptedMarkov = True popup = Toplevel() text = Message(popup, text="The Markov Generator is still loading!\n\nText will show up when loaded!") text.pack() closePop = Button(popup, text="Okay!", command=popup.destroy) closePop.pack()
def concolefooter(self): footerframe = Frame(self.master) footerframe.config(padx=5, pady=5, bg=Styles.colours["darkGrey"]) title = Message(footerframe, text="Console:", justify=CENTER, bg=Styles.colours["darkGrey"], foreground=Styles.colours["yellow"], width=100, font=Styles.fonts["entry"]) consoletext = Text(footerframe, height=5, width=80, bg=Styles.colours["darkGrey"], foreground=Styles.colours["grey"], state=NORMAL, relief=FLAT, font=Styles.fonts["console"]) consoletext.insert(END, "Welcome to Project Bi") consoletext.config(state=DISABLED) self.console.setconsolefield(consoletext) scroll = Scrollbar(footerframe, command=consoletext.yview, relief=FLAT) consoletext.configure(yscrollcommand=scroll.set) self.boptimize = yellowbutton(footerframe, "Optimize", 20, lambda e: self.observerstage()) deactivatebutton(self.boptimize) self.boptimize.pack(side=RIGHT, fill=BOTH, padx=5, pady=5) self.boptimize.configure(font=Styles.fonts["h1Button"]) title.pack(side=LEFT, fill=BOTH) scroll.pack(side=LEFT, fill=BOTH) consoletext.pack(side=LEFT, fill=BOTH) footerframe.grid(row=2, column=0, sticky=W + E + N + S, columnspan=2) return footerframe
def OnButtonClick(): e2.focus_set() e2.selection_range(0, END) e3.focus_set() e3.selection_range(0, END) e4.focus_set() e4.selection_range(0, END) win = Toplevel() win.title("Program Answer") if len(oracle_old_connection(e2.get(), e3.get())) > 0: cur = oracle_connection() alter_string = 'alter user %s identified by %s' % (e2.get(), e4.get()) cur.execute(alter_string) message = "Password Successfully Updated" msg = Message(win, text=message) msg.pack() button = Button(win, text='OK', command=win.destroy) button.pack() else: message = "Error!!!!" msg = Message(win, text=message) msg.pack() button = Button(win, text='OK', command=win.destroy) button.pack()
def about(): help_window = tk.Toplevel(master) help_window.geometry("1000x1000") help_window.title("About the Bunimovich Stadia Evolution Viewer") text = Message(help_window, text = TEXT, padx = 100) text.pack()
class Data_Cell(Cell): def __init__(self, master, variable, anchor=W, bordercolor=None, borderwidth=1, padx=0, pady=0, background=None, foreground=None, font=None): Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=borderwidth, bd=0) self._message_widget = Message(self, textvariable=variable, font=font, background=background, foreground=foreground) self._message_widget.pack(expand=True, padx=padx, pady=pady, anchor=anchor)
def Msg(name): master = tk() master.title('Error message') msg = Message(master, text='unable to load ' + name + ' from file') # msg.config() msg.pack() # close_button = Button(master, text='OK', command=master.destroy) mainloop()
def About(): top = Toplevel() top.title("About this application...") msg = Message(top, text="Turing Machine simulator\nWritten on python 2.7 \ using Tkinter\n2016 Kyiv, IASA\nby Illya Barziy") msg.pack() button = Button(top, text="OK", command=top.destroy) button.pack()
def message_box_notification(self, txt): #crate the top level for message box mess_box = Toplevel() mess_box.title("Message") #create the message text msg = Message(mess_box, text = txt) msg.pack() #create top level close button mess_box_close = Button(mess_box, text = "OK", command = mess_box.destroy) mess_box_close.pack()
def info(self): #Infoseite zeigen top = Toplevel() top.title(u"Über dieses Programm...") msg = Message(top, text=u'Dieses Programm dient zur Auswertung von Messungen für die Bestimmung des Weiterreißwiderstands nach DIN ISO 6133:2004-05\n\nZur Detektion der Maxima dient ein Algorithmus aus MATLAB (http://billauer.co.il/peakdet.html) verwendet, welcher nach Python übersetzt wurden.\n\nDas Programm entscheidet je nach Anzahl der Maxima selbst, welche Vorgabe für die Auswertung zu verwenden ist.\n\n\n\nErstellt von Lukas Scheffler') msg.pack() button = Button(top, text="Verbergen", command=top.destroy) button.pack()
def onSetParams(self): top = Toplevel(self.parent) top.title("Model Setup") msg = Message(top, text="") msg.pack() applyb = Button(top, text="Apply", command=top.destroy) closeb = Button(top, text="Close", command=top.destroy) applyb.pack() closeb.pack()
class Data_Cell(Cell): def __init__(self, master, variable, anchor=W, bordercolor=None, borderwidth=1, padx=0, pady=0, background=None, foreground=None, font=None): Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=borderwidth, bd= 0) self._message_widget = Message(self, textvariable=variable, font=font, background=background, foreground=foreground) self._message_widget.pack(expand=True, padx=padx, pady=pady, anchor=anchor) self.bind("<Configure>", self._on_configure) def _on_configure(self, event): self._message_widget.configure(width=event.width)
def make_error_dialog(player_dlg): error_dlg = Toplevel(master=player_dlg); error_dlg.title( "Error" ); error_dlg.grab_set(); error_msg = Message(error_dlg, aspect=300, text="Player count must be between %d and %d" % (MIN_PLAYER_COUNT, MAX_PLAYER_COUNT) ) error_msg.pack(); error_button = Button(error_dlg, text="OK", \ command=error_dlg.destroy); error_button.pack(); error_dlg.update(); return
class Data_Cell(Cell): """ A Class used as a data cell for a table widget Methods ------- _on_configure() Description | Message widget configuration """ def __init__(self, master, variable, anchor=CENTER, bordercolor=None, borderwidth=1, padx=0, pady=0, background=None, foreground=None, font=None): """ Parameters ---------- :param master: the master of the data cell :param variable: data variable :param anchor: anchor :param bordercolor: the color of the border :param borderwidth: the width of the data cell border :param padx: x coordinate padding :param pady: y coordinate padding :param background: background color :param foreground: foreground color :param font: text font """ Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=borderwidth, bd=0) self._message_widget = Message(self, textvariable=variable, font=font, background=background, foreground=foreground) self._message_widget.pack(padx=padx, pady=pady, anchor=anchor) self._message_widget.configure(width=110, pady=1.2)
def help_f(self): top = Toplevel() top.title("HELP") msg = Message(top, width= 500, text="Noise Threshold (NT) - Noise Based Main Threshold \ (Sigmas)\n Threshold 1 - Main Threshold (LSBs) \n Threshold 2 - End of Pulse Error \n \ Threshold 3 - End of Tail Error \n When Thr1 = Thr3 = 0 their values are defined as: \n \ (THR1 = NT (LSBs) / THR3 = NT*NOISE_ADC / 5)") msg.pack() button = Button(top, text="Close", command=top.destroy) button.pack()
def help_f(self): top = Toplevel() top.title("HELP") msg = Message(top, width=500, text="Noise Threshold (NT) - Noise Based Main Threshold \ (Sigmas)\n Threshold 1 - Main Threshold (LSBs) \n Threshold 2 - End of Pulse Error \n \ Threshold 3 - End of Tail Error \n When Thr1 = Thr3 = 0 their values are defined as: \n \ (THR1 = NT (LSBs) / THR3 = NT*NOISE_ADC / 5)") msg.pack() button = Button(top, text="Close", command=top.destroy) button.pack()
def create_path(self): width_msg = 100 width_path = 400 path_frame = Frame(self.root) path_frame.pack(fill=X) # read read_frame = Frame(path_frame) read_frame.pack(fill=X) read_msg = Message(read_frame, width=width_msg, text='Video path:') read_msg.pack(side=LEFT) read_path = Message(read_frame, width=width_path, text='file1') read_path.pack(side=LEFT) read_btn = Button(read_frame, text="Choose file", command=self.click_read) read_btn.pack(side=RIGHT) # save save_frame = Frame(path_frame) save_frame.pack(fill=X) save_msg = Message(save_frame, width=width_msg, text='Save to:') save_msg.pack(side=LEFT) save_path = Message(save_frame, width=width_path, text='file2') save_path.pack(side=LEFT) save_btn = Button(save_frame, text="Choose file", command=self.click_save) save_btn.pack(side=RIGHT) self.read_path = read_path self.save_path = save_path
class Panle: def __init__(self, master): self.frame = Frame(master) self.frame.pack(side=TOP) self.label = Tkinter.Label(self.frame, text='wordEcho') self.label.pack() self.input = Entry(self.frame, width=45) self.input.pack(side=LEFT) self.button = Button(self.frame, text='翻译') self.button.pack(side=RIGHT) self.frame2 = Scrollbar(master) self.frame2.pack(side=TOP) self.ms = Message(self.frame2, anchor='w', width=150) self.ms.pack()
class AlreadyExistsDialog(Dialog): def __init__(self, master_frame, new, old, variable, class_=None, relx=0.5, rely=0.3): Dialog.__init__(self, master_frame, 'Warning!', class_, relx, rely) self.new = new self.old = old self.variable = variable self.text = "Warning: Customer Found with the same name.\n" + \ "\nNew record: " + new[1] + " " + new[2] + " " + new[0] + \ " (" + new[3] + ") - " + new[4].strftime("%m/%d/%Y") + \ "\nOld record: " + old[1] + " " + old[2] + " " + old[0] + \ " (" + old[3] + ") - " + old[4].strftime("%m/%d/%Y") + \ "\n\n Cancel to edit entry, Override to replace old entry." def show(self): self.setup() self.msg = Message(self.root, text=self.text, aspect=400) self.msg.pack(expand=True, fill=BOTH) self.frame = Frame(self.root) b1 = Button(self.frame, text="Cancel", command=self.cancel) b1.pack(side=LEFT, fill=BOTH, expand=True, padx=10) b2 = Button(self.frame, text="Override", command=self.replace) b2.pack(side=LEFT, fill=BOTH, expand=True, padx=10) #b3 = Button(self.frame, text="Add Duplicate", command=self.add_duplicate) #b3.pack(side=LEFT, fill=BOTH, expand=True) self.frame.pack(padx=10, pady=10) self.root.bind('<Return>', self.cancel) self.enable() def cancel(self, event=None): self.variable.set(0) self.root.quit() def replace(self): self.variable.set(1) self.root.quit() def add_duplicate(self): self.variable.set(2) self.wm_delete_window()
def help_f(self): top = Toplevel() top.title("HELP") msg = Message(top, width= 500, text="COEFF Calibration Procedure: \n \ Input Start Point and Length of the pulse \n \ Input an initial guess of the pulse amplitude \n \ Use a ROI with at least 1000 samples of baseline \n \ and 1000 samples after pulse end \n \ Adjust loop range and step until a graph error \n \ with a minimum is observed \n \ Refine the search to increase precision") msg.pack() button = Button(top, text="Close", command=top.destroy) button.pack()
def help_f(self): top = Toplevel() top.title("HELP") msg = Message(top, width=500, text="COEFF Calibration Procedure: \n \ Input Start Point and Length of the pulse \n \ Input an initial guess of the pulse amplitude \n \ Use a ROI with at least 1000 samples of baseline \n \ and 1000 samples after pulse end \n \ Adjust loop range and step until a graph error \n \ with a minimum is observed \n \ Refine the search to increase precision") msg.pack() button = Button(top, text="Close", command=top.destroy) button.pack()
class mainheader(): def __init__(self, master, console,params,maingui): self.frame = Frame(master) self.frame.config(padx=5, pady=5, bg=Styles.colours["darkGrey"]) self.frame.grid(row=0, column=0, sticky=W + E + N + S,columnspan=2) self.title = Message(self.frame, text="ProjectB: Selection", justify=CENTER, bg=Styles.colours["darkGrey"], foreground=Styles.colours["yellow"], width=300, font=Styles.fonts["h1"]) def importsettings(event): f = tkFileDialog.askopenfilename(parent=master, title='Choose a file') parsemodifycustomvar(params,parsein(f,parseintosimple(params),console)) maingui.ready("model") maingui.ready("bayes") def exportfile(event): f = tkFileDialog.asksaveasfilename(parent=master, title='Choose a file') parseout(f,parseintosimple(params),console) self.bimport = camobutton(self.frame, "Import Settings", 15,importsettings) self.bexport = camobutton(self.frame, "Export Settings", 15,exportfile) self.badvset = yellowbutton(self.frame, "Advanced Settings", 20) self.badvset.bind("<Button-1>", lambda a: AdvancedSettings(console,params)) self.qb = qbutton(self.frame) self.title.pack(side=LEFT, fill=BOTH, padx=5, pady=5) self.qb.pack(side=RIGHT, fill=BOTH, padx=5, pady=5) self.badvset.pack(side=RIGHT, fill=BOTH, padx=5, pady=5) self.bexport.pack(side=RIGHT, fill=BOTH, padx=5, pady=5) self.bimport.pack(side=RIGHT, fill=BOTH, padx=5, pady=5) def destroy(self): self.frame.destroy() def observationstage(self): self.bimport.destroy() self.bexport.destroy() self.badvset.destroy() self.title.config(text="ProjectB: Observation") def evaluationstage(self): self.title.config(text="ProjectB: Evaluation")
def help(self): #Hilfeseite zeigen top = Toplevel() top.title("Hilfe") label1 = ttk.Label(top, text = u"Projekt öffnen/erstellen", font=self.normal_font) label1.pack() msg1 = Message(top, text=u'Über Datei -> Neu muss zu Beginn eine .txt-Datei erstellt werden. In dieser werden die Ergebnisse der Auswertung gespeichert.\n\nAlternativ kann über Datei -> Öffnen... ein bereits existierendes Projekt mit den neuen Ergebnissen erweitert werden. \n\n') msg1.pack() label2 = ttk.Label(top, text = u"Messung importieren und auswerten", font=self.normal_font) label2.pack() msg2 = Message(top, text=u'Zunächst muss über Datei -> Messung importieren die gewünschte Messung importiert werden.\n\nAnschließend werden Delta X und Delta Y so eingestellt, dass nur die gewünschten Maxima (rote Punkte im Graphen) vom Algorithmus erkannt werden.\n\nZur Berechnung des Weiterreißwiderstandes wird die Probendicke benötigt. Diese muss im entsprechenden Fenster eingetragen werden (Trennung durch . nicht durch , Bsp: 1.75).\n\nÜber die Schaltfläche Berechnen werden die gewünschten Werte berechnet.\n\nNachdem der Probenname und optional ein Kommentar zur Messung in die entsprechenden Fenster eingetragen wurden, lässt sich die Auswertung im zuvor gewählten Projekt abspeichern.') msg2.pack() button = Button(top, text="Verbergen", command=top.destroy) button.pack()
def drawWindow(self): #cache the images to display self.redditraw = Image.open("reddit.png").resize((40,40), Image.ANTIALIAS) self.redditimg = ImageTk.PhotoImage(self.redditraw) self.twitterraw = Image.open("twitter.png").resize((40,40), Image.ANTIALIAS) self.twitterimg = ImageTk.PhotoImage(self.twitterraw) for post in self.posts: frame = Frame(self) frame.pack(side=TOP, fill=X) #this was for using web images #file = cStringIO.StringIO(urllib.urlopen("location").read()) #draw if post.type == "RedditPost": reddit = Label(frame, image=self.redditimg) reddit.image = self.redditimg #reddit.bind("<1>", lambda event, url=post[1]: web.open_new(url)) reddit.pack(side=LEFT) else: twitter = Label(frame, image=self.twitterimg) twitter.image = self.twitterimg #twitter.bind("<1>", lambda event, url=post[1]: web.open_new(url)) twitter.pack(side=LEFT) body = Frame(frame) body.pack(fill=BOTH) #the actual post body title = Message(body, text=post.title, foreground="#0000dd", justify=LEFT, width=550) #title.bind("<1>", lambda event, url=post[4]: web.open_new(url)) title.pack(anchor=W) #date and author details = Label(body, text=post.date+" by "+post.user) details.pack(anchor=W)
def vis_thread(): global txt, color, txt2, color2 def update(): global txt, color, txt2, color2 msg.config(text=txt, background=color) msg2.config(text=txt2, background=color2) root.after(250, update) root = Tk() root.geometry("900x600") msg = Message(root, text=txt, background=color) msg.config(font=('times', 200, 'italic bold')) msg.pack() msg2 = Message(root, text=txt2, background=color2) msg2.config(font=('times', 70, 'italic bold')) msg2.pack() root.after(250, update) root.mainloop()
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
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 GetCfgSectionNameDialog(Toplevel): def __init__(self, parent, title, message, used_names, _htest=False): """ message - string, informational message to display used_names - string collection, names already in use for validity check _htest - bool, change box location when running htest """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) self.resizable(height=FALSE, width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Cancel) self.parent = parent self.message = message self.used_names = used_names self.create_widgets() self.withdraw() #hide while setting geometry self.update_idletasks() #needs to be done here so that the winfo_reqwidth is valid self.messageInfo.config(width=self.frameMain.winfo_reqwidth()) self.geometry("+%d+%d" % (parent.winfo_rootx() + (parent.winfo_width() / 2 - self.winfo_reqwidth() / 2), parent.winfo_rooty() + ((parent.winfo_height() / 2 - self.winfo_reqheight() / 2) if not _htest else 100)) ) #centre dialog over parent (or below htest box) self.deiconify() #geometry set, unhide self.wait_window() def create_widgets(self): self.name = StringVar(self.parent) self.fontSize = StringVar(self.parent) self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN) self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT, padx=5, pady=5, text=self.message) #,aspect=200) entryName = Entry(self.frameMain, textvariable=self.name, width=30) entryName.focus_set() self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH) entryName.pack(padx=5, pady=5) frameButtons = Frame(self, pady=2) frameButtons.pack(side=BOTTOM) self.buttonOk = Button(frameButtons, text='Ok', width=8, command=self.Ok) self.buttonOk.pack(side=LEFT, padx=5) self.buttonCancel = Button(frameButtons, text='Cancel', width=8, command=self.Cancel) self.buttonCancel.pack(side=RIGHT, padx=5) def name_ok(self): ''' After stripping entered name, check that it is a sensible ConfigParser file section name. Return it if it is, '' if not. ''' name = self.name.get().strip() if not name: #no name specified tkMessageBox.showerror(title='Name Error', message='No name specified.', parent=self) elif len(name) > 30: #name too long tkMessageBox.showerror( title='Name Error', message='Name too long. It should be no more than ' + '30 characters.', parent=self) name = '' elif name in self.used_names: tkMessageBox.showerror(title='Name Error', message='This name is already in use.', parent=self) name = '' return name def Ok(self, event=None): name = self.name_ok() if name: self.result = name self.destroy() def Cancel(self, event=None): self.result = '' self.destroy()
class GetCfgSectionNameDialog(Toplevel): def __init__(self, parent, title, message, used_names, _htest=False): """ message - string, informational message to display used_names - string collection, names already in use for validity check _htest - bool, change box location when running htest """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) self.resizable(height=FALSE, width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Cancel) self.parent = parent self.message = message self.used_names = used_names self.create_widgets() self.withdraw() #hide while setting geometry self.update_idletasks() #needs to be done here so that the winfo_reqwidth is valid self.messageInfo.config(width=self.frameMain.winfo_reqwidth()) self.geometry( "+%d+%d" % ( parent.winfo_rootx() + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), parent.winfo_rooty() + ((parent.winfo_height()/2 - self.winfo_reqheight()/2) if not _htest else 100) ) ) #centre dialog over parent (or below htest box) self.deiconify() #geometry set, unhide self.wait_window() def create_widgets(self): self.name = StringVar(self.parent) self.fontSize = StringVar(self.parent) self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN) self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT, padx=5, pady=5, text=self.message) #,aspect=200) entryName = Entry(self.frameMain, textvariable=self.name, width=30) entryName.focus_set() self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH) entryName.pack(padx=5, pady=5) frameButtons = Frame(self, pady=2) frameButtons.pack(side=BOTTOM) self.buttonOk = Button(frameButtons, text='Ok', width=8, command=self.Ok) self.buttonOk.pack(side=LEFT, padx=5) self.buttonCancel = Button(frameButtons, text='Cancel', width=8, command=self.Cancel) self.buttonCancel.pack(side=RIGHT, padx=5) def name_ok(self): ''' After stripping entered name, check that it is a sensible ConfigParser file section name. Return it if it is, '' if not. ''' name = self.name.get().strip() if not name: #no name specified tkMessageBox.showerror(title='Name Error', message='No name specified.', parent=self) elif len(name)>30: #name too long tkMessageBox.showerror(title='Name Error', message='Name too long. It should be no more than '+ '30 characters.', parent=self) name = '' elif name in self.used_names: tkMessageBox.showerror(title='Name Error', message='This name is already in use.', parent=self) name = '' return name def Ok(self, event=None): name = self.name_ok() if name: self.result = name self.destroy() def Cancel(self, event=None): self.result = '' self.destroy()
init_main_window(root, ethan_eyes); app = Ethan(root, player_num, ethan_eyes); root.mainloop(); return #start program with dialog box, which will lead to either just terminating #or making a game box if __name__ == "__main__": player_dlg = Tk(); player_dlg.geometry("%dx%d+%d+%d" % (INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT, X_POS, Y_POS)) # TODO: make dialog box show up in the middle of the screen based on # screen resolution player_dlg.title("How many players?") msg = Message(player_dlg, text="How many players will be playing this time?"); msg.pack() player_count = Entry(player_dlg) player_count.pack() ethan_eyes_var = IntVar() ethan_eyes_check = Checkbutton(player_dlg, \ text="enable Ethan Eyes", \ var=ethan_eyes_var) ethan_eyes_check.pack() confirm_button = Button(player_dlg, text="OK", \ command=lambda: generate_main(player_dlg, \ player_count, \ ethan_eyes_var) ); confirm_button.pack(); cancle_button = Button(player_dlg, text="Cancel", \ command=lambda: player_dlg.quit() ); cancle_button.pack();
def generate(): ## Get the values of all variables ## cvar = constvar.get() # Which variable is the constant one? sams = int(samples.get()) # Number of samples avg = float(var.get()) # Average value of sampled variable eps = eRange.get() # Range of sampled variable para = float(const.get()) # Value of constant variable its = int(iterations.get()) # Number of iterations star = int(start.get()) # First iteration to display to user styp = sampleType.get() # Type of sampling method ## Set the view variables according to the check boxes make their default ## values an empty string. c,s,b = "","","" if c_check.get() == '1': c = "c" # Indicates if cylinder view is checked. if s_check.get() == '1': s = "s" # Indicates if spherical view is checked. if b_check.get() == '1': b = "b" # Indicates if stadia view is checked. # get the eps value ready to pass to the plotter if eps == "pi/4": epsi = pi/4 elif eps == "pi/16": epsi = pi/16 elif eps == "pi/64": epsi = pi/64 elif eps == "pi/256": epsi = pi/256 elif eps == "pi/1024": epsi = pi/1024 else: print "Invalid input from drop down menu" ## Make sure that the number of iterations and the first iteration to ## ## display are consistent ------------------------------------------- ## if its <= star: iteration_error_window = tk.Toplevel(master) iteration_error_window.geometry("300x150") iteration_error_window.title("ILLEGAL INPUT DETECTED") text = Message(iteration_error_window, text = ITERMESSAGE) text.pack() return False ## Make sure that theta is in the proper range ## if cvar == "theta" and (para < 0 or para >= 2*pi): # para is a theta value and its outside of the [0,2pi) range. # Mod it into the [0,2pi) range. para = mod2pi(para) ## Make sure that phi is in the proper range ## if cvar == "phi" and (para <= -pi/2 or para >= pi/2): phi_error_window = tk.Toplevel(master) phi_error_window.geometry("400x300") phi_error_window.title("ILLEGAL INPUT DETECTED") text = Message(phi_error_window, text = PHIMESSAGE1) text.pack() return False ## Make sure that phi is in the proper range ## if cvar == "theta" and (avg - epsi <= -pi/2 or avg + epsi >= pi/2): phi_error_window = tk.Toplevel(master) phi_error_window.geometry("400x200") phi_error_window.title("ILLEGAL INPUT DETECTED") text = Message(phi_error_window, text = PHIMESSAGE2) text.pack() return False if plotter(cvar, sams, avg - epsi, avg + epsi, para, its+1, star, 2, \ styp, c + s + b): confirm_window = tk.Toplevel(master) confirm_window.geometry("200x100") confirm_window.title("Success!") msg = """ Pdf(s) of the desired images were created in the folder containing this application. """ text = Message(confirm_window, text = msg, padx = 10) text.pack()
"------------------------------------- Label Update Method ------------------------------------**" def update_the_label(newText): sleep(1) packable_update.configure(text=newText) root1.update_idletasks() "------------------------------------- UI Packing/Execution ------------------------------------**" #Setup Button to Trigger Main Method when Pressed goButton = tk.Button(root1, text="Start", width=25, command=mainMethod, relief=tk.RAISED) #Pack Elements into Root Window panel.pack(side=tk.TOP, fill="both", expand="yes") welcome1.pack() welcome2.pack() label.pack() entry.pack() goButton.pack(padx=5, pady=5) packable_update.pack() #Run the Code with UI root1.mainloop()
def do_a_turn(self): # roll die die1_val = random.randint(1,6) die2_val = random.randint(1,6) # TODO: potentially simulate die motion like in matlab version # change die values on screen self.dice1_strvar.set(str(die1_val)) self.dice2_strvar.set(str(die2_val)) # check dice conditions ethan_val = int(self.ethan_strvar.get()) player_val = int(self.player_strvars[self.player_turn].get()) if (die1_val+die2_val == 4): # player gets ethan's stuff player_val = player_val + ethan_val ethan_val = 0 elif (die1_val+die2_val == 2) and self.ethan_eyes: # player loses it all to ethan ethan_val = player_val + ethan_val player_val = 0 else: # player loses one chip to ethan ethan_val = ethan_val + 1 player_val = player_val - 1 self.ethan_strvar.set(str(ethan_val)) self.player_strvars[self.player_turn].set(str(player_val)) # check for the lose condition total_chips = 0 num_players_positive = 0 for x in self.player_strvars: if int(x.get()) > 0: num_players_positive = num_players_positive + 1 total_chips = total_chips + int(x.get()) if total_chips == 0: # everybody loses error_dlg = Toplevel(master=self.main_window) error_dlg.geometry("%dx%d+%d+%d" % (INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT, X_POS, Y_POS)) error_dlg.title( "LOSERS" ); error_dlg.grab_set(); error_msg = Message(error_dlg, aspect=300, text="EVERYBODY LOSES, AE2015") error_msg.pack(); error_button = Button(error_dlg, text="OK", \ command=lambda: self.destroy_all(error_dlg)); error_button.pack(); error_dlg.update(); elif (num_players_positive == 1) and (player_val > ethan_val): #win condition, only for player who just moved player_name = self.player_name_list[self.player_turn].get(); error_dlg = Toplevel(master=self.main_window) error_dlg.geometry("%dx%d+%d+%d" % (INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT, X_POS, Y_POS)) error_dlg.title( "WINNER" ); error_dlg.grab_set(); error_msg = Message(error_dlg, aspect=300, text="GOOD JOB %s, YOU BEAT ETHAN" % (player_name) ) error_msg.pack(); error_button = Button(error_dlg, text="OK", \ command=error_dlg.destroy); error_button.pack(); error_dlg.update(); else: # game isn't over, increment turn to next positive player self.player_frame_list[self.player_turn].config(relief='flat', \ borderwidth=0) num_players = len(self.player_strvars) found_next = 0 self.current_turn = self.current_turn + 1 while (found_next == 0): self.player_turn = (self.player_turn + 1) % num_players if int(self.player_strvars[self.player_turn].get()) > 0: found_next = 1 self.player_frame_list[self.player_turn].config(relief='raised', \ borderwidth=2) return
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()
if tkMessageBox.askyesno('Steam Library Exporter v0.9', 'Thanks for using Steam Library Exporter! \nWant to run it again?'): print "Running again!" entry.delete(0,tk.END) update_the_label("Ready (again)!") else: root1.destroy() "------------------------------------- Label Update Method ------------------------------------**" def update_the_label(newText): sleep(1) packable_update.configure(text = newText) root1.update_idletasks() "------------------------------------- UI Packing/Execution ------------------------------------**" #Setup Button to Trigger Main Method when Pressed goButton = tk.Button(root1, text="Start", width=25, command=mainMethod, relief=tk.RAISED) #Pack Elements into Root Window panel.pack(side=tk.TOP, fill = "both", expand = "yes") welcome1.pack() welcome2.pack() label.pack() entry.pack() goButton.pack(padx=5,pady=5) packable_update.pack() #Run the Code with UI root1.mainloop()
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 Application(Frame): """Main application class""" json_file_name = "functions.json" # should be none after tests json_data = None def __init__(self, master=None): Frame.__init__(self, master) self.pack() self.create_widgets() master.title("Regula falsi") master.minsize(width=100, height=100) master.config(menu=self.menubar) def calculate(self): """Does all the work""" item_number = self.fields.curselection() polynomial = self.json_data["functions"][int(item_number[0])] result_floating = None result_interval = None if self.json_data["method"] == "regula_falsi": try: result_floating = regula_falsi(polynomial["a"], polynomial["b"], function_from_list, polynomial["coeff"] ) except MyError as error: result_floating = error.value except ZeroDivisionError: result_floating = "Unfortunately, division by 0 happened" try: result_interval = regula_falsi_interval(polynomial["a"], polynomial["b"], function_from_list, polynomial["coeff"] ) result_interval = result_interval.format('%.20E')[9:] result_interval = result_interval[:-1] except MyError as error: result_interval = error.value elif self.json_data["method"] == "newton": try: result_floating = newton(polynomial["x"], polynomial["epsilon"], polynomial["max_iterations"], function_from_list, polynomial["coeff"], polynomial["derivative_coeff"] ) except ZeroDivisionError: result_floating = "Unfortunately, division by zero happened" except MyError as error: result_floating = error.value try: result_interval = newton_interval(polynomial["x"], polynomial["epsilon"], polynomial["max_iterations"], function_from_list, polynomial["coeff"], polynomial["derivative_coeff"] ) result_interval = result_interval.format('%.20E')[9:] result_interval = result_interval[:-1] except MyError as error: result_interval = error.value else: result_interval = "You propably" result_floating = "gave me wrong JSON" self.show_result_floating.config(text=result_interval) self.show_result_interval.config(text=result_floating) def read_json_file(self): """Reads function coefficients from json files""" self.fields.delete(0, END) self.json_file_name = load_file() with open(self.json_file_name) as json_file: self.json_data = json.load(json_file) for item in self.json_data["functions"]: self.fields.insert(END, item["coeff"]) def create_widgets(self): """Creates buttons on the window and binds actions to them""" #listbox for the equations self.fields = Listbox(self, selectmode=SINGLE) self.fields.config(width=100) self.fields.pack() #button for calculating self.calculate_button = Button(self) self.calculate_button["text"] = "Calculate" self.calculate_button["command"] = self.calculate self.calculate_button.pack({"side": "left"}) #to the end self.show_result_floating = Message(self, width=250) self.show_result_floating.pack() #second Message self.show_result_interval = Message(self, width=250) self.show_result_interval.pack() #adding menu buttons to the window self.menubar = Menu(self) self.menubar.add_command(label="Load file", command=self.read_json_file) self.menubar.add_command(label="Quit", command=self.quit)
class Data_Cell(Cell): def __init__(self, master, variable, anchor=W, bordercolor=None, borderwidth=1, padx=0, pady=0, background=None, foreground=None, font=None, row_num=0, col_num=0, table=None, width=100, text_to_img=None): Cell.__init__(self, master, background=background, highlightbackground=bordercolor, highlightcolor=bordercolor, highlightthickness=borderwidth, bd=0) self.background = background self.text_to_img = text_to_img self.variable = variable self.row_num = row_num self.table = table def change_var(*args): try: if self.text_to_img[self.variable.get()]: #print(self.variable.get()) bg = background fg = foreground if self._message_widget: bg = self._message_widget.cget('background') fg = self._message_widget.cget('foreground') self._message_widget.pack_forget() self._message_widget = Label( self, width=width, background=bg, foreground=fg, image=self.text_to_img[self.variable.get()]) self._message_widget.pack(expand=True, padx=padx, pady=pady, anchor=anchor) self._message_widget.bind( '<Button-1>', lambda event, row=self.row_num, table=self.table: cell_selected(event, table, row)) except KeyError as e: pass return if text_to_img: variable.trace('w', change_var) self._message_widget = Message(self, width=width, textvariable=variable, font=font, background=background, foreground=foreground) self._message_widget.pack(expand=True, padx=padx, pady=pady, anchor=anchor) self.bind('<Button-1>', lambda event, row=row_num, table=table: cell_selected( event, table, row)) self._message_widget.bind('<Button-1>', lambda event, row=row_num, table=table: cell_selected(event, table, row))
# De message widget is hetzelfde als het Label maar biedt meer flexibiliteit voor text from Tkinter import Tk, Message master = Tk() whatever_you_do = "Whatever you do will be insignificant, but it is very important that you do it.\n(Mahatma Gandhi)" msg = Message(master, text=whatever_you_do) msg.config(bg='lightgreen', font=('times', 24, 'italic')) msg.pack() master.mainloop() # Alle magelijke opties voor de config van een message widget, kunnen hier teruggevonden worden: # https://www.python-course.eu/tkinter_message_widget.php