def initUI(self): self.parent.title("Buttons") self.style = Style() self.style.theme_use("default") frame = Frame(self, relief=GROOVE, borderwidth=5) frame.pack(fill=BOTH, expand=1) self.pack(fill = BOTH, expand = 1) self.imageLabel = Label(frame, image = "") self.imageLabel.pack(fill=BOTH, expand=1) closeButton = Button(self, text="Close") closeButton.pack(side=RIGHT) okButton = Button(self, text="OK") okButton.pack(side=RIGHT) options = [item for item in dir(cv2.cv) if item.startswith("CV_CAP_PROP")] option = OptionMenu(self, self.key, *options) self.key.set(options[0]) option.pack(side="left") spin = Spinbox(self, from_=0, to=1, increment=0.05) self.val = spin.get() spin.pack(side="left")
class Application(Frame): MAXWORDLEN = 20 DEFAULTWORDLEN = 3 MANDATORY1stCHAR=0 def __init__(self, master=None): Frame.__init__(self, master, padx=3, pady=3) self.dictionaryfile = None self.menubar = Menu() self.__createWidgets() self.menubar.add_cascade(label='File', menu=self.__File) self.menubar.add_cascade(label='Options', menu=self.__Options) self.menubar.add_command(label='About', command=self.__about) self.__params.grid(row=0, column=0, sticky=W) self.__res_pane = Frame() self.__res_pane.grid(row=2, column=0, sticky=E + W) self.__status = Label(anchor=W, relief=SUNKEN) self.__status.grid(row=3, column=0, sticky=E + W) self.osDictFile() self.matchobj = None if self.dictionaryfile is None: self.status('No dictionary defined!') master.config(menu=self.menubar) def __createWidgets(self): self.__params = Frame(padx=5, pady=5) Label(text='Letters: ', anchor=E).grid(row=0, column=0, sticky=E, in_=self.__params) self.__char_entry = Entry(width=10) self.__chk1st = Checkbutton(variable=self.MANDATORY1stCHAR, command=self.__CB) Label(text='First letter appears in every result ', anchor=W).grid( row=0, column=4, sticky=E, in_=self.__params) self.__char_entry.grid(row=0, column=1, columnspan=2, sticky=W, in_=self.__params) self.__chk1st.grid(row=0, column=3, sticky=W, in_=self.__params) Label(text='Minimum length of result words: ', anchor=E).grid( row=1, column=0, sticky=E, in_=self.__params) self.__word_length_ctrl = Spinbox(from_=1, to=Application.MAXWORDLEN, width=2) self.__word_length_ctrl.delete(0, END) self.__word_length_ctrl.insert(0, Application. DEFAULTWORDLEN) self.__word_length_ctrl.grid(row=1, column=1, in_=self.__params, sticky=W) self.__go_button = Button(text='Go', command=self.__findWords) self.__go_button.grid(row=1, column=2, sticky=E, in_=self.__params) self.__Options = Menu() self.__Options.add_command(label='Choose dictionary', command=self.__choosedict) self.__File = Menu() self.__File.add_command(label='Export as ODT (Open document text)', command=self.__export) self.__char_entry.focus_set() self.__char_entry.bind("<Return>", self.__keyPressEnter) def __CB(self): self.MANDATORY1stCHAR = not self.MANDATORY1stCHAR def __choosedict(self): try: self.dictionaryfile = tkFileDialog.askopenfile(mode='r').name self.status('') except AttributeError: pass def osDictFile(self): if 'linux' in sys.platform: self.dictionaryfile = '/usr/share/dict/words' def __about(self): AboutDialog(self) def status(self, text): self.__status.config(text=text) self.__status.update_idletasks() def __findWords(self): self.__res_pane.grid_forget() chars = self.__char_entry.get() minlen = int(self.__word_length_ctrl.get()) if len(chars) < minlen: tkMessageBox.showerror(title='Not enough letters', message='''Not enough letters given\n You must give at least as many letters as the minimum required word length''') return res = self.__getres(minlen, chars) self.__res_pane = ResultPane(res) self.__res_pane.grid(row=2, column=0, sticky=E + W) def __getres(self, minlen, chars): firstpass = True while True: try: self.matchobj = None if firstpass and self.dictionaryfile is None: self.matchobj = Match(minlen=minlen, chars=chars, statushandler=self.status, mand1st=self.MANDATORY1stCHAR) firstpass = False else: self.matchobj = Match(minlen=minlen, chars=chars, dict=self.dictionaryfile, statushandler=self.status, mand1st=self.MANDATORY1stCHAR) res = self.matchobj.wordMatch() return res except IOError: ans = tkMessageBox.askyesno(title='No Dictionary', message='''No dictionary file was found, would you like to choose a dictionary file? (No) aborts the application''') if ans: self.__choosedict() else: sys.exit() def __keyPressEnter(self, event): self.__findWords() def __export(self): options= {} options['defaultextension'] = '.odt' options['filetypes'] = [('all files', '.*'), ('Open Document Text', '.odt')] options['initialdir'] = expanduser('~') options['initialfile'] = self.__char_entry.get() f = asksaveasfilename(**options) outfile = Doc(self.matchobj) outfile.write(unicode(f, "utf-8"))
class Extruder(LabelFrame): def __init__(self, root, prtr, settings, log, *arg): LabelFrame.__init__(self, root, *arg, text="Extruder") self.app = root self.printer = prtr self.settings = settings self.log = log self.bExtrude = Button(self, text="Extrude", width=10, command=self.doExtrude) self.bExtrude.grid(row=1, column=1, padx=2) self.spDistance = Spinbox(self, from_=1, to=MAXDIST, width=6, justify=RIGHT) self.spDistance.grid(row=1, column=2) self.spDistance.delete(0, END) self.spDistance.insert(0, STARTDIST) self.spDistance.bind("<FocusOut>", self.valDistance) self.spDistance.bind("<Leave>", self.valDistance) l = Label(self, text="mm", justify=LEFT) l.grid(row=1, column=3, sticky=W) self.bReverse = Button(self, text="Reverse", width=10, command=self.doReverse) self.bReverse.grid(row=2, column=1, padx=2) self.spSpeed = Spinbox(self, from_=1, to=MAXFEED, width=6, justify=RIGHT) self.spSpeed.grid(row=2, column=2) self.spSpeed.delete(0, END) self.spSpeed.insert(0, self.settings.efeed) self.spSpeed.bind("<FocusOut>", self.valSpeed) self.spSpeed.bind("<Leave>", self.valSpeed) l = Label(self, text="mm/min", justify=LEFT) l.grid(row=2, column=3, sticky=W) def valDistance(self, *arg): invalid = False try: y = float(self.spDistance.get()) except: self.log.logMsg("Value for distance not a valid number") invalid = True else: if y <=0 or y >MAXDIST: self.log.logMsg("Value for Distance out of range (0-%d)" % MAXDIST) invalid = True if invalid: self.spDistance.delete(0, END) self.spDistance.insert(0, STARTDIST) return True def valSpeed(self, *arg): invalid = False try: x = int(self.spSpeed.get()) except: self.log.logMsg("Value for E feed rate not a valid integer") invalid = True else: if x <=0 or x >MAXFEED: self.log.logMsg("Value for E feed rate out of range(0-%d)" % MAXFEED) invalid = True if invalid: self.spSpeed.delete(0, END) self.spSpeed.insert(0, "%d" % self.settings.efeed) else: if self.settings.efeed != x: self.settings.efeed = x self.settings.setModified() return True def doExtrude(self): if self.app.printerAvailable(cmd="G91"): dist = self.spDistance.get() self.printer.send_now("G91") self.printer.send_now("G1 E%s F%s" % (dist, self.settings.efeed)) self.printer.send_now("G90") def doReverse(self): if self.app.printerAvailable(cmd="G91"): dist = self.spDistance.get() self.printer.send_now("G91") self.printer.send_now("G1 E-%s F%s" % (dist, self.settings.efeed)) self.printer.send_now("G90")
class RelyGUI(object): """ GUI for driving storage reliability simulations """ # # enumerated values for menus and spin boxes # disk_type = None diskTypes = [ # menu of disk drive types "Enterprise", "Consumer", "Real" ] disk_nre = None nre_rates = [ # list of likely NRE rates ... correspond to above "1.0E-19", "1.0E-18", "1.0E-17", "1.0E-16", "1.0E-15", "1.0E-14", "1.0E-13" ] # default FIT rates for various classes of drives fit_map = { 'Enterprise': "826", 'Consumer': "1320", 'Real': "7800" } # default NRE rates for various classes of drives nre_map = { 'Enterprise': "1.0E-16", 'Consumer': "1.0E-15", 'Real': "1.0E-14" } raid_type = None raidTypes = [ # menu of RAID types "RAID-0", "RAID-1", "RAID-5", "RAID-6" ] raid_vols = None # default volume counts for various RAID configurations vol_map = { 'RAID-0': 1, 'RAID-1': 2, 'RAID-5': 3, 'RAID-6': 6, } nre_model = None nreTypes = [ # ways of modeling NREs "ignore", "error", "fail", "error+fail/2" ] raid_rplc = None replace_times = [ # list of likely drive replacement times (hours) 0, 1, 2, 4, 6, 8, 10, 12, 18, 24 ] rados_down = None markout_times = [ # list of likely OSD mark-down times (minutes) 0, 1, 2, 3, 4, 5, 10, 15, 20, 30, 45, 60 ] raid_speed = None rados_speed = None rebuild_speeds = [ # list of likely rebuild speeds (MB/s) 1, 2, 5, 10, 15, 20, 25, 40, 50, 60, 80, 100, 120, 140, 160 ] remote_latency = None async_latencies = [ # list of likely asynchronous replication latencies 0, 1, 5, 10, 30, 60, 300, 600, 900, 1800, 3600, 2 * 3600, 6 * 3600, 12 * 3600, 18 * 3600, 24 * 3600 ] rados_fullness = None fullness = [ # list of likely volume fullness percentages 50, 75, 80, 85, 90, 95, 100 ] site_num = None site_count = [ # list of likely remote site numbers 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] remote_speed = None remote_speeds = [ # list of likely remote recovery speeds (MB/s) 1, 2, 5, 10, 20, 25, 40, 50, 60, 80, 100, 150, 200, 300, 400, 500, 600, 800, 1000 ] remote_fail = None site_destroy = [ # force majeure event frequency 10, 100, 1000, 10000, 100000, "never" ] remote_rplc = None site_recover = [ # number of days to recover a destroyed facility 1, 2, 4, 8, 12, 16, 20, 30, 40, 50, 60, 80, 100, 150, 200, 250, 300, 365, "never" ] verbosities = [ # display verbosity "all", "parameters", "headings", "data only" ] verbosity = None period = None disk_size = None rados_pgs = None rados_cpys = None stripe_length = None # these we generate dynamically obj_size = None object_sizes = [] min_obj_size = 1 * 1024 * 1024 max_obj_size = 1 * 1024 * 1024 * 1024 * 1024 step_obj_size = 4 # GUI widget field widths ROWS = 20 BORDER = 5 short_wid = 2 med_wid = 4 long_wid = 6 short_fmt = "%2d" med_fmt = "%4d" long_fmt = "%6d" # references to the input widget fields (I know ...) def do_disk(self): """ calculate disk reliability """ self.getCfgInfo() self.doit(self.cfg, "disk") def do_raid(self): """ calculate raid reliability """ self.getCfgInfo() self.doit(self.cfg, "raid") def do_rados(self): """ calculate RADOS reliability """ self.getCfgInfo() self.doit(self.cfg, "rados") def do_sites(self): """ calculate Multi-Site RADOS reliability """ self.getCfgInfo() self.doit(self.cfg, "multi") def diskchoice(self, value): """ change default FIT and NRE rates to match disk selection """ self.disk_nre.delete(0, END) self.disk_nre.insert(0, self.nre_map[value]) self.disk_fit.delete(0, END) self.disk_fit.insert(0, self.fit_map[value]) self.disk_fit2.delete(0, END) self.disk_fit2.insert(0, self.fit_map[value]) def raidchoice(self, value): """ change default # of volumes to match RAID levels """ self.raid_vols.delete(0, END) self.raid_vols.insert(0, self.vol_map[value]) def __init__(self, cfg, doit): """ create the GUI panel widgets cfg -- parameter values (input and output) doit -- method to call to run simulations """ # gather the basic parameters self.cfg = cfg self.doit = doit self.root = Tk() self.root.title('Data Reliability Model') t = Frame(self.root, bd=2 * self.BORDER) # w.iconbitmap(default='inktank.ico') # ? windows only ? # left stack (DISK) f = Frame(t, bd=self.BORDER, relief=RIDGE) r = 1 Label(f, text="Disk Type").grid(row=r) self.disk_type = StringVar(f) self.disk_type.set(self.diskTypes[0]) OptionMenu(f, self.disk_type, *self.diskTypes, command=self.diskchoice).grid(row=r + 1) Label(f).grid(row=r + 2) r += 3 Label(f, text="Size (GiB)").grid(row=r) self.disk_size = Entry(f, width=self.long_wid) self.disk_size.delete(0, END) self.disk_size.insert(0, self.long_fmt % (cfg.disk_size / GiB)) self.disk_size.grid(row=r + 1) Label(f).grid(row=r + 2) r += 3 Label(f, text="Primary FITs").grid(row=r) self.disk_fit = Entry(f, width=self.long_wid) self.disk_fit.grid(row=r + 1) Label(f).grid(row=r + 2) r += 3 Label(f, text="Secondary FITs").grid(row=r) self.disk_fit2 = Entry(f, width=self.long_wid) self.disk_fit2.grid(row=r + 1) Label(f).grid(row=r + 2) r += 3 Label(f, text="NRE rate").grid(row=r) self.disk_nre = Spinbox(f, width=self.long_wid, values=self.nre_rates) self.disk_nre.grid(row=r + 1) Label(f).grid(row=r + 2) r += 3 while r < self.ROWS: Label(f).grid(row=r) r += 1 Button(f, text="RELIABILITY", command=self.do_disk).grid(row=r) f.grid(column=1, row=1) self.diskchoice(self.diskTypes[0]) # set default disk type # second stack (RAID) f = Frame(t, bd=self.BORDER, relief=RIDGE) r = 1 Label(f, text="RAID Type").grid(row=r) self.raid_type = StringVar(f) self.raid_type.set("RAID-1") OptionMenu(f, self.raid_type, *self.raidTypes, command=self.raidchoice).grid(row=r + 1) Label(f).grid(row=r + 2) r += 3 Label(f, text="Replace (hours)").grid(row=r) self.raid_rplc = Spinbox(f, width=self.short_wid, values=self.replace_times) self.raid_rplc.grid(row=r + 1) self.raid_rplc.delete(0, END) self.raid_rplc.insert(0, "%d" % cfg.raid_replace) Label(f).grid(row=r + 2) r += 3 Label(f, text="Rebuild (MiB/s)").grid(row=r) self.raid_speed = Spinbox(f, width=self.med_wid, values=self.rebuild_speeds) self.raid_speed.grid(row=r + 1) self.raid_speed.delete(0, END) self.raid_speed.insert(0, "%d" % (cfg.raid_recover / MiB)) Label(f).grid(row=r + 2) r += 3 Label(f, text="Volumes").grid(row=r) self.raid_vols = Spinbox(f, from_=1, to=10, width=self.short_wid) self.raid_vols.grid(row=r + 1) Label(f).grid(row=r + 2) self.raidchoice("RAID-1") # set default number of volumes r += 3 while r < self.ROWS: Label(f).grid(row=r) r += 1 Button(f, text="RELIABILITY", command=self.do_raid).grid(row=r) f.grid(column=2, row=1) # third stack (RADOS) f = Frame(t, bd=self.BORDER, relief=RIDGE) r = 1 Label(f, text="RADOS copies").grid(row=r) self.rados_cpys = Spinbox(f, values=(1, 2, 3, 4, 5, 6), width=self.short_wid) self.rados_cpys.grid(row=r + 1) self.rados_cpys.delete(0, END) self.rados_cpys.insert(0, "%d" % cfg.rados_copies) Label(f).grid(row=r + 2) r += 3 Label(f, text="Mark-out (min)").grid(row=r) self.rados_down = Spinbox(f, values=self.markout_times, width=self.short_wid) self.rados_down.grid(row=r + 1) self.rados_down.delete(0, END) self.rados_down.insert(0, "%d" % (cfg.rados_markout * 60)) Label(f).grid(row=r + 2) r += 3 Label(f, text="Recovery (MiB/s)").grid(row=r) self.rados_speed = Spinbox(f, width=self.med_wid, values=self.rebuild_speeds) self.rados_speed.grid(row=r + 1) self.rados_speed.delete(0, END) self.rados_speed.insert(0, "%d" % (cfg.rados_recover / MiB)) Label(f).grid(row=r + 2) r += 3 Label(f, text="Space Usage (%)").grid(row=r) self.rados_fullness = Spinbox(f, values=self.fullness, width=self.med_wid) self.rados_fullness.grid(row=r + 1) self.rados_fullness.delete(0, END) self.rados_fullness.insert(0, "%d" % (cfg.rados_fullness * 100)) Label(f).grid(row=r + 2) r += 3 Label(f, text="Declustering (pg)").grid(row=r) self.rados_pgs = Entry(f, width=self.med_wid) self.rados_pgs.delete(0, END) self.rados_pgs.insert(0, self.med_fmt % cfg.rados_decluster) self.rados_pgs.grid(row=r + 1) Label(f).grid(row=r + 2) r += 3 Label(f, text="Stripe Length").grid(row=r) self.stripe_length = Entry(f, width=self.med_wid) self.stripe_length.delete(0, END) self.stripe_length.insert(0, self.med_fmt % cfg.stripe_length) self.stripe_length.grid(row=r + 1) Label(f).grid(row=r + 2) r += 3 while r < self.ROWS: Label(f).grid(row=r) r += 1 Button(f, text="RELIABILITY", command=self.do_rados).grid(row=r) f.grid(column=3, row=1) # fourth stack (remote site) r = 1 f = Frame(t, bd=self.BORDER, relief=RIDGE) Label(f, text="RADOS Sites").grid(row=r) self.site_num = Spinbox(f, values=self.site_count, width=self.short_wid) self.site_num.grid(row=r + 1) self.site_num.delete(0, END) self.site_num.insert(0, "%d" % cfg.remote_sites) Label(f).grid(row=r + 2) r += 3 Label(f, text="Rep Latency (s)").grid(row=r) self.remote_latency = Spinbox(f, values=self.async_latencies, width=self.long_wid) self.remote_latency.grid(row=r + 1) self.remote_latency.delete(0, END) self.remote_latency.insert(0, "%d" % (cfg.remote_latency * 60 * 60)) Label(f).grid(row=r + 2) r += 3 Label(f, text="Recovery (MiB/s)").grid(row=r) self.remote_speed = Spinbox(f, values=self.remote_speeds, width=self.med_wid) self.remote_speed.grid(row=r + 1) self.remote_speed.delete(0, END) self.remote_speed.insert(0, "%d" % (cfg.remote_recover / MiB)) Label(f).grid(row=r + 2) r += 3 Label(f, text="Disaster (years)").grid(row=r) self.remote_fail = Spinbox(f, values=self.site_destroy, width=self.long_wid) self.remote_fail.grid(row=r + 1) self.remote_fail.delete(0, END) self.remote_fail.insert(0, "1000") # FIX - get this from cfg ... but translate from FITS Label(f).grid(column=2, row=r + 2) r += 3 Label(f, text="Site Recover (days)").grid(row=r) self.remote_avail = Spinbox(f, values=self.site_recover, width=self.long_wid) self.remote_avail.grid(row=r + 1) self.remote_avail.delete(0, END) self.remote_avail.insert(0, "30") # FIX - get this from cfg ... but translate from FITS Label(f).grid(row=r + 2) r += 3 while r < self.ROWS: Label(f).grid(row=r) r += 1 Button(f, text="RELIABILITY", command=self.do_sites).grid(row=r) f.grid(column=4, row=1) # and the control panel r = 2 c = 1 Label(t).grid(column=c, row=r) Label(t, text="NRE model").grid(column=c, row=r + 1) self.nre_model = StringVar(t) self.nre_model.set(self.cfg.nre_model) OptionMenu(t, self.nre_model, *self.nreTypes).grid(column=c, row=r + 2) c = 2 Label(t).grid(column=c, row=r) Label(t, text="Period (years)").grid(column=c, row=r + 1) self.period = Spinbox(t, from_=1, to=10, width=self.short_wid) self.period.grid(column=c, row=r + 2) c = 3 Label(t).grid(column=c, row=r) Label(t, text="Object size").grid(column=c, row=r + 1) # generate object sizes dynamically from parameters os = self.min_obj_size while os <= self.max_obj_size: if os < MB: s = "%dKB" % (os / KB) elif os < GB: s = "%dMB" % (os / MB) elif os < TB: s = "%dGB" % (os / GB) else: s = "%dTB" % (os / TB) self.object_sizes.append(s) os *= self.step_obj_size self.obj_size = Spinbox(t, values=self.object_sizes, width=self.long_wid) self.obj_size.grid(column=c, row=r + 2) self.obj_size.delete(0, END) self.obj_size.insert(0, self.object_sizes[0]) c = 4 Label(t).grid(column=c, row=r) Label(t, text="Verbosity").grid(column=c, row=r + 1) self.verbosity = StringVar(t) self.verbosity.set(cfg.verbose) OptionMenu(t, self.verbosity, *self.verbosities).grid(column=c, row=r + 2) # and then finalize everything t.grid() def getCfgInfo(self): """ scrape configuration information out of the widgets """ self.cfg.period = 365.25 * 24 * int(self.period.get()) self.cfg.disk_type = self.disk_type.get() self.cfg.disk_size = int(self.disk_size.get()) * GiB self.cfg.disk_nre = float(self.disk_nre.get()) self.cfg.disk_fit = int(self.disk_fit.get()) self.cfg.disk_fit2 = int(self.disk_fit2.get()) self.cfg.raid_vols = int(self.raid_vols.get()) self.cfg.raid_type = self.raid_type.get() self.cfg.raid_replace = int(self.raid_rplc.get()) self.cfg.raid_recover = int(self.raid_speed.get()) * MiB self.cfg.nre_model = self.nre_model.get() self.cfg.rados_copies = int(self.rados_cpys.get()) self.cfg.rados_markout = float(self.rados_down.get()) / 60 self.cfg.rados_recover = int(self.rados_speed.get()) * MiB self.cfg.rados_decluster = int(self.rados_pgs.get()) self.cfg.stripe_length = int(self.stripe_length.get()) self.cfg.rados_fullness = float(self.rados_fullness.get()) / 100 self.cfg.remote_latency = float(self.remote_latency.get()) / (60 * 60) self.cfg.remote_sites = int(self.site_num.get()) self.cfg.remote_recover = int(self.remote_speed.get()) * MiB self.cfg.verbose = self.verbosity.get() # these parameters can also have the value "never" v = self.remote_fail.get() self.cfg.majeure = 0 if v == "never" else \ 1000000000 / (float(self.remote_fail.get()) * 365.25 * 24) v = self.remote_avail.get() self.cfg.site_recover = 0 if v == "never" else \ float(self.remote_avail.get()) * 24 # a more complex process for the dynamically generated lists self.cfg.obj_size = self.min_obj_size i = 0 while i < len(self.object_sizes) and \ self.cfg.obj_size < self.max_obj_size: if self.obj_size.get() == self.object_sizes[i]: break self.cfg.obj_size *= self.step_obj_size i += 1 # sanity checking on arguments with limits if self.cfg.stripe_length < 1: self.cfg.stripe_length = 1 if self.cfg.stripe_length > self.cfg.rados_decluster: print("\nIGNORING stripe width (%d) > decluster (%d)\n" % (self.cfg.stripe_length, self.cfg.rados_decluster)) self.cfg.stripe_length = self.cfg.rados_decluster def mainloop(self): self.root.mainloop()
class ChargenColor: def __init__(self, parent): self.colorNegative = [] self.colorNeutral = [] self.colorPositive = [] self.scaleNegative = 0.0 self.scalePositive = 0.0 self.cNegative = '#ff0000' self.cNeutral = '#000000' self.cPositive = '#0000ff' self.minNegativeLbl = Label(rootC, text='Min Found: -') self.maxPositiveLbl = Label(rootC, text='Max Found: -') self.scaleLbl = Label(rootC, text='Scale: - to -') self.sclSelectPositive = Spinbox(rootC) self.sclSelectPositive.insert(0, 0.0) self.sclSelectNegative = Spinbox(rootC) self.sclSelectNegative.insert(0, 0.0) self.buttonNegativeCharge = Button( rootC, text='Negative Charge Color', command=self.chooseNegativeCharge) self.buttonNeutralCharge = Button( rootC, text='Neutral Charge Color', command=self.chooseNeutralCharge) self.buttonPositiveCharge = Button( rootC, text='Positive Charge Color', command=self.choosePositiveCharge) self.buttonBG = Button( rootC, text='Background Color', command=self.chooseBackground) self.buttonUpdateColor = Button( rootC, text='Update', command=self.updateColor) self.title = Label(rootC, text="Select your colors") self.buttonClose = Button(rootC, text="Close", command=rootC.destroy) self.buttonBG.pack() self.title.pack() self.buttonNegativeCharge.pack() self.buttonNeutralCharge.pack() self.buttonPositiveCharge.pack() self.minNegativeLbl.pack() self.maxPositiveLbl.pack() self.scaleLbl.pack() self.sclSelectNegative.pack() self.sclSelectPositive.pack() self.buttonUpdateColor.pack() self.buttonClose.pack() def chooseNegativeCharge(self): self.colorNegative = askcolor( color=self.cNegative, title="Negative Charge Color") self.buttonNegativeCharge.config(fg=self.colorNegative[1]) self.cNegative = self.colorNegative[1] def chooseNeutralCharge(self): self.colorNeutral = askcolor( color=self.cNeutral, title="Neutral Charge Color") self.buttonNeutralCharge.config(fg=self.colorNeutral[1]) self.cNeutral = self.colorNeutral[1] def choosePositiveCharge(self): self.colorPositive = askcolor( color=self.cPositive, title="Positive Charge Color") self.buttonPositiveCharge.config(fg=self.colorPositive[1]) self.cPositive = self.colorPositive[1] def chooseBackground(self): bgcolor = askcolor( color=self.cPositive, title="Positive Charge Color") cmd.set_color("bg_chargy_color", bgcolor[0]) cmd.bg_color("bg_chargy_color") def updateColor(self): selection = 'all' stored.atoms_charge = [] stored.atoms_colors = [] cmd.map_new('chargyc_map', selection="(all)") if not self.colorNeutral: tkMessageBox.showerror("Error", "Set Neutral Color, Please") return if not self.colorNegative: tkMessageBox.showerror("Error", "Set Negative Color, Please") return if not self.colorPositive: tkMessageBox.showerror("Error", "Set Positive Color, Please") return cmd.iterate_state(1, '(' + selection + ')', 'stored.atoms_charge.append(partial_charge)') _i = 0 minValue = None maxValue = None while _i < len(stored.atoms_charge): color = [] if _i == 0: maxValue = stored.atoms_charge[_i] minValue = stored.atoms_charge[_i] if(stored.atoms_charge[_i] > maxValue): maxValue = stored.atoms_charge[_i] if stored.atoms_charge[_i] < minValue: minValue = stored.atoms_charge[_i] _i += 1 self.minNegativeLbl["text"] = 'Min Found: ' + str(round(minValue, 3)) self.maxPositiveLbl["text"] = 'Max Found: ' + str(round(maxValue, 3)) if(self.scaleNegative == 0.0 and self.scalePositive == 0.0): self.scaleNegative = round(minValue, 3) self.scalePositive = round(maxValue, 3) self.sclSelectNegative.delete(0, "end") self.sclSelectPositive.delete(0, "end") self.sclSelectNegative.insert(0, round(minValue, 3)) self.sclSelectPositive.insert(0, round(maxValue, 3)) else: self.scaleNegative = float(self.sclSelectNegative.get()) self.scalePositive = float(self.sclSelectPositive.get()) minValue = float(self.sclSelectNegative.get()) maxValue = float(self.sclSelectPositive.get()) self.scaleLbl["text"] = 'Scale: ' + str( self.scaleNegative) + ' to ' + str(self.scalePositive) middleValue = 0 if(maxValue < 0): maxValue = 0 if(minValue > 0): minValue = 0 _i = 0 while _i < len(stored.atoms_charge): color = [] cmd.set_color("neutral_color", self.colorNeutral[0]) cmd.set_color("positive_color", self.colorPositive[0]) cmd.set_color("negative_color", self.colorNegative[0]) if(stored.atoms_charge[_i] >= middleValue): if(stored.atoms_charge[_i] == middleValue): cmd.set_color(str(_i) + "_color", self.colorNeutral[0]) else: cmd.set_color(str(_i) + "_color", self.getColor( self.colorNeutral[0], self.colorPositive[0], maxValue, stored.atoms_charge[_i] if stored.atoms_charge[_i] < maxValue else maxValue)) else: cmd.set_color(str(_i) + "_color", self.getColor( self.colorNeutral[0], self.colorNegative[0], abs(minValue), abs(stored.atoms_charge[_i]) if abs(stored.atoms_charge[_i]) < abs(minValue) else abs(minValue))) index = cmd.get_color_index(str(_i) + "_color") stored.atoms_colors.append(index) _i += 1 cmd.alter_state(1, '(' + selection + ')', "color=stored.atoms_colors.pop(0)") cmd.ramp_new('chargy_ramp', 'chargyc_map', range=[self.scaleNegative, ((self.scaleNegative+self.scalePositive)/2.0), self.scalePositive], color=['negative_color', 'neutral_color', 'positive_color']) def getColor(self, color, colorMax, step, index): colorStep = [0, 0, 0] colorStep[0] = ((colorMax[0]-color[0])/step) colorStep[1] = ((colorMax[1]-color[1])/step) colorStep[2] = ((colorMax[2]-color[2])/step) return [ 1.0 if (color[0] + (colorStep[0]*index)) / 255.0 >= 1 else (color[0] + (colorStep[0]*index))/255.0, 1.0 if (color[1] + (colorStep[1]*index)) / 255.0 >= 1 else (color[1] + (colorStep[1]*index))/255.0, 1.0 if (color[2] + (colorStep[2]*index)) / 255.0 >= 1 else (color[2] + (colorStep[2]*index))/255.0 ]
class MoveControl(LabelFrame): def __init__(self, root, prtr, settings, log, *arg): fn = os.path.join(settings.cmdFolder, "images", "control_xyz.png") self.image = Image.open(fn) self.photo = ImageTk.PhotoImage(self.image) LabelFrame.__init__(self, root, *arg, text="Movement") self.app = root self.hilite = None self.hilitemask = None self.settings = settings self.printer = prtr self.log = log l = Label(self, text="mm/min") l.grid(row=1, column=2) l = Label(self, text="X/Y Feed Rate:") l.grid(row=2, column=1, sticky=E) self.xyfeed = Spinbox(self, width=10, justify=RIGHT, from_=0, to=MAXFEED) self.xyfeed.grid(row=2, column=2) self.xyfeed.delete(0, END) self.xyfeed.insert(0, settings.xyfeed) self.xyfeed.bind("<FocusOut>", self.valxyFeed) self.xyfeed.bind("<Leave>", self.valxyFeed) l = Label(self, text="Z Feed Rate:") l.grid(row=3, column=1, sticky=E) self.zfeed = Spinbox(self, width=10, justify=RIGHT, from_=0, to=MAXFEED) self.zfeed.grid(row=3, column=2) self.zfeed.delete(0, END) self.zfeed.insert(0, settings.zfeed) self.zfeed.bind("<FocusOut>", self.valzFeed) self.zfeed.bind("<Leave>", self.valzFeed) self.canvas = Canvas(self, width=self.image.size[0], height=self.image.size[1], *arg) self.canvas.create_image((0, 0), image=self.photo, anchor=N+W) self.canvas.grid(row=4, column=1, columnspan=2) for m in imageMasks: self.canvas.create_oval((m[0][0]-mask_rad, m[0][1]-mask_rad, m[0][0]+mask_rad, m[0][1]+mask_rad), outline="#FF0000", width=4, tags=m[1], state=HIDDEN) self.canvas.bind("<Button-1>", self.OnLeftDown) self.canvas.bind("<Motion>", self.OnMotion) self.canvas.bind("<Enter>", self.OnEnter) self.canvas.bind("<Leave>", self.OnLeave) self.bAllOff = Button(self, text="Release Motors", command=self.allMotorsOff) self.bAllOff.grid(row=5, column=1, columnspan=2) def valxyFeed(self, *arg): x = self.validFeed(self.xyfeed.get(), 'XY') if x == None: self.xyfeed.delete(0, END) self.xyfeed.insert(0, "%d" % self.settings.xyfeed) return True if self.settings.xyfeed != x: self.settings.xyfeed = x self.settings.setModified() return True def valzFeed(self, *arg): x = self.validFeed(self.zfeed.get(), 'Z') if x == None: self.zfeed.delete(0, END) self.zfeed.insert(0, "%d" % self.settings.zfeed) return True if self.settings.zfeed != x: self.settings.zfeed = x self.settings.setModified() return True def validFeed(self, fv, axis): try: x = int(fv) except: self.log.logMsg("Value for %s feed rate not a valid integer" % axis) return None if x <=0 or x >MAXFEED: self.log.logMsg("Value for %s feed rate out of range(0-5000)" % axis) return None return x def allMotorsOff(self): if self.app.printerAvailable(cmd="M84"): self.printer.send_now("M84") def OnMotion(self, e): for i in range(len(imageGeometry)): if boundBy((e.x, e.y), imageGeometry[i][0]): self.setHilite(i) return if self.hilite != None: self.clearHilite() def OnEnter(self, e): self.clearHilite() def OnLeave(self, e): self.clearHilite() def setHilite(self, i): if i != self.hilite: self.canvas.delete("HILITE") self.canvas.create_rectangle(imageGeometry[i][0], outline="#C85E5D", width=3, fill="gray", stipple="gray50", tags="HILITE") self.hilite = i if self.hilitemask: self.canvas.itemconfig(self.hilitemask, state=HIDDEN) if imageGeometry[i][2]: self.hilitemask = imageGeometry[i][2] self.canvas.itemconfig(self.hilitemask, state=NORMAL) def clearHilite(self): self.canvas.delete("HILITE") self.hilite = None self.hilitemask = None for m in imageMasks: self.canvas.itemconfig(m[1], state=HIDDEN) def OnLeftDown(self, e): if self.app.printerAvailable(cmd="G1"): for g in imageGeometry: if boundBy((e.x, e.y), g[0]): if "G1" in g[1]: if "X" in g[1]: feed = self.settings.xyfeed elif "Y" in g[1]: feed = self.settings.xyfeed elif "Z" in g[1]: feed = self.settings.zfeed else: feed = 100 self.printer.send_now("G91") self.printer.send_now(g[1] + " F" + str(feed)) self.printer.send_now("G90") else: self.printer.send_now(g[1]) break
class PeakFinder: def __init__(self, master): self.master = master master.title(u"Weiterreißwiderstand") self.big_font = tkFont.Font(family='Helvetica', size=36, weight='bold') self.normal_font = tkFont.Font(family='Helvetica', size=20, weight='normal') self.X = None self.Y = None self.maxima = None self.maxima_x = None self.number_max = 0 self.number_max_string = StringVar() self.max_max = 0.0 self.max_max_string = StringVar() self.min_max = 0.0 self.min_max_string = StringVar() self.median = 0.0 self.median_string = StringVar() self.w_string = StringVar() self.distance_string = StringVar() self.method_string = StringVar() self.sample_file = '' self.project_file = '' self.w = 0.0 self.distance = 0.0 ######################################################################### ''' Optionen für Dateidialoge ''' self.file_opt = options = {} options['defaultextension'] = '.txt' options['filetypes'] = [('text files', '.txt')] options['initialfile'] = '' options['parent'] = master options['title'] = 'Messung importieren' self.file_opt2 = options = {} options['defaultextension'] = '.txt' options['filetypes'] = [('text files', '.txt')] options['initialfile'] = '' options['parent'] = master options['title'] = 'Neues Projekt erstellen.' self.file_opt3 = options = {} options['defaultextension'] = '.txt' options['filetypes'] = [('text files', '.txt')] options['initialfile'] = '' options['parent'] = master options['title'] = 'Vorhandenes Projekt öffnen.' ##################################################################################################### ''' GUI ''' ''' MenueLeiste ''' ############################################################################################### self.menubar = Menu(master) # create a pulldown menu, and add it to the menu bar self.filemenu = Menu(self.menubar, tearoff=0) self.filemenu.add_command(label="Neu", command=self.new_file, font = self.normal_font) self.filemenu.add_command(label=u"Öffnen...", command=self.open_file, font = self.normal_font) self.filemenu.add_separator() self.filemenu.add_command(label="Messung importieren", command=self.get_filepath, font = self.normal_font) self.filemenu.add_separator() self.filemenu.add_command(label="Beenden", command=root.quit, font = self.normal_font) self.menubar.add_cascade(label="Datei", menu=self.filemenu, font = self.normal_font) self.helpmenu = Menu(self.menubar, tearoff=0) self.helpmenu.add_command(label="Hilfe", command=self.help, font = self.normal_font) self.helpmenu.add_command(label=u"Über", command=self.info, font = self.normal_font) self.menubar.add_cascade(label="Hilfe", menu=self.helpmenu, font = self.normal_font) master.config(menu=self.menubar) ############################################################################################## ''' Parameter ''' self.option_label = ttk.Label(master, text = "Parameter", font = self.big_font) self.option_label.grid(row = 0, rowspan = 2, columnspan = 4, sticky=W) self.delta_x_label = ttk.Label(master, text = "Delta X", font = self.normal_font) self.delta_x_label.grid(row = 3, sticky=W) self.delta_x_spinbox = Spinbox(master, from_=10, to=500, increment = 10, font = self.normal_font, width = 4, command = self.plot) self.delta_x_spinbox.grid(row = 3, column = 1) self.delta_y_label = ttk.Label(master, text = "Delta Y", font = self.normal_font) self.delta_y_label.grid(row = 4, column = 0, sticky=W) self.delta_y_spinbox = Spinbox(master, from_=0, to=2, increment = 0.05, font = self.normal_font, width = 4, command = self.plot) self.delta_y_spinbox.grid(row = 4, column = 1) # self.plot_button = Button(master, text = "Plotten", font = self.normal_font, command = self.plot, width = 10) # self.plot_button.grid(row = 3, column = 2, columnspan = 1) self.sample_thickness_label = ttk.Label(master, text = "Probendicke [mm]", font = self.normal_font) self.sample_thickness_label.grid(row = 5, column = 0, sticky=W) self.sample_thickness_entry = ttk.Entry(master, font = self.normal_font, width = 5) self.sample_thickness_entry.grid(row = 5, column = 1) self.calculate_button = Button(master, text = "Berechnen", font = self.normal_font, command = self.calculate, width = 10) self.calculate_button.grid(row = 6, column = 1, columnspan = 1) ########################################################################################################## ''' Speichern ''' self.save_label = ttk.Label(master, text = "Auswertung Speichern", font = self.big_font) self.save_label.grid(row = 7, rowspan = 2, columnspan = 4, sticky=W) self.sample_name_label = ttk.Label(master, text = "Probenname", font = self.normal_font) self.sample_name_label.grid(row = 9, sticky=W) self.sample_name_entry = ttk.Entry(master, font = self.normal_font) self.sample_name_entry.grid(row = 9, column = 1, columnspan = 3) self.comment_label = ttk.Label(master, text = "Kommentar", font = self.normal_font) self.comment_label.grid(row = 10, sticky=W) self.comment_entry = ttk.Entry(master, font = self.normal_font) self.comment_entry.grid(row = 10, column = 1, columnspan = 3) self.save_button = Button(master, text = "Speichern", font = self.normal_font, command = self.save, width = 10) self.save_button.grid(row = 12, column = 1, columnspan = 2, sticky=W) ############################################################################################################## ''' Analyse ''' self.number_max_label = ttk.Label(master, text = "Anzahl Maxima:", font = self.normal_font) self.number_max_label.grid(row = 9, column = 6, sticky=W) self.number_max_int_label = ttk.Label(master, textvariable = self.number_max_string, font = self.normal_font) self.number_max_int_label.grid(row = 9, column = 7) self.max_max_label = ttk.Label(master, text = "Median [N]:", font = self.normal_font) self.max_max_label.grid(row = 10, column = 6, sticky=W) self.max_max_int_label = ttk.Label(master, textvariable = self.median_string, font = self.normal_font) self.max_max_int_label.grid(row = 10, column = 7) self.min_max_label = ttk.Label(master, text = u"Weiterreißwiderstand [N/mm]:", font = self.normal_font) self.min_max_label.grid(row = 11, column = 6, sticky=W) self.min_max_int_label = ttk.Label(master, textvariable = self.w_string, font = self.normal_font) self.min_max_int_label.grid(row = 11, column = 7) self.min_max_label = ttk.Label(master, text = u"Spannweite [mm]:", font = self.normal_font) self.min_max_label.grid(row = 12, column = 6, sticky=W) self.min_max_int_label = ttk.Label(master, textvariable = self.distance_string, font = self.normal_font) self.min_max_int_label.grid(row = 12, column = 7) self.method_label = ttk.Label(master, text="Methode:", font = self.normal_font) self.method_label.grid(row = 13, column = 6, sticky=W) self.method_method_label = ttk.Label(master, textvariable = self.method_string, font = self.normal_font) self.method_method_label.grid(row = 13, column = 7) ########################################################################################################## ''' Canvas ''' # Create a canvas self.w, self.h = 800, 500 self.canvas = Canvas(master, width=self.w, height=self.h) self.canvas.grid(row = 0, column = 5, columnspan = 5, rowspan = 9) ''' Funktionen ''' def plot(self): try: #Maxima finden self._max, self._min = self.peakdetect(self.Y, self.X, float(self.delta_x_spinbox.get()), float(self.delta_y_spinbox.get())) #Maxima in Array schreiben self.xm = [p[0] for p in self._max] self.ym = [p[1] for p in self._max] #Maxima verarbeiten self.maxima = self.ym self.maxima_x = self.xm self.number_max = len(self._max) self.number_max_string.set(str(self.number_max)) self.max_max = max(self.ym) self.max_max_string.set(str(self.max_max)) self.min_max = min(self.ym) self.min_max_string.set(str(self.min_max)) #Graph Plotten self.fig = plt.Figure(figsize=(8, 5), dpi=100) self.ax = self.fig.add_subplot(111) self.ax.plot(self.X, self.Y) self.ax.plot(self.xm, self.ym, 'ro', markersize = 10) #self.ax.axvline(x=10, ymin = 0, ymax = 1, linewidth = 2, color = 'g') self.ax.axis('auto') self.ax.set_xlabel('Weg [mm]') self.ax.set_ylabel('Kraft [N]') self.fig_photo = self.draw_figure(self.canvas, self.fig, loc=(0, 0)) #Methode Anzeigen if 1 < self.number_max <= 5 : self.method_string.set('Median') elif self.number_max < 2: self.method_string.set('Maximum') else: self.method_string.set('80% Median') except: tkMessageBox.showwarning('Fehler bei der Berechnung!', 'Bitte Eingaben prüfen.') def calculate(self): if self.sample_thickness_entry.get() == '': tkMessageBox.showwarning('Keine Probendicke eingetragen!', 'Bitte Probendicke zur Berechnung eintragen.') else: if self.number_max <= 5: self.median_calculation() else: self.percent_calculation() def percent_calculation(self): #Berechnung 80 Prozent von #Maxima n = int(round(self.number_max*0.8)) delta = self.number_max - n #Entfernen der ersten und zweiten 10 Prozent del self.maxima[0:int(round(delta/2))] del self.maxima[len(self.maxima)-int(round(delta-int(round(delta/2)))):len(self.maxima)] del self.maxima_x[0:int(round(delta/2))] del self.maxima_x[len(self.maxima_x)-int(round(delta-int(round(delta/2)))):len(self.maxima_x)] #Berechnung des Weiterreisswiderstands try: d = float(self.sample_thickness_entry.get()) #Auslesen der Probendicke self.median = np.median(self.maxima) #Berechnung des Medians self.w = self.median/d self.median_string.set(str(self.median)) self.w_string.set(str(self.w)) #Berechnung der Spannweite self.distance = self.maxima_x[len(self.maxima_x)-1]-self.maxima_x[0] self.distance_string.set(str(self.distance)) except: tkMessageBox.showwarning(u'Probendicke hat falsche Formatierung!', u'Bitte Probendicke in der Form Z.ZZ eingeben (Z=Zahl).') def median_calculation(self): #Berechnung des Weiterreisswiderstands try: d = float(self.sample_thickness_entry.get()) #Auslesen der Probendicke self.median = np.median(self.maxima) #Berechnung des Medians self.w = self.median/d self.median_string.set(str(self.median)) self.w_string.set(str(self.w)) #Berechnung der Spannweite self.distance = self.maxima_x[len(self.maxima_x)-1]-self.maxima_x[0] self.distance_string.set(str(self.distance)) except: tkMessageBox.showwarning(u'Probendicke hat falsche Formatierung!', u'Bitte Probendicke in der Form Z.ZZ eingeben (Z=Zahl).') def save(self): #Speichern der Auswertung im Projekt if self.project_file != '' and self.sample_name_entry.get() != '': maxima_string = '' for maximum in self.maxima: maxima_string += str(maximum)+'\t' print(maxima_string) self.project_file_write = open(self.project_file, 'a') self.project_file_write.write('\n'+self.sample_name_entry.get()+'\t'+str(self.number_max)+'\t'+str(self.median)+'\t'+self.sample_thickness_entry.get()+'\t'+str(self.w)+'\t'+str(self.distance)+'\t'+self.method_string.get()+'\t'+self.comment_entry.get()+'\t'+maxima_string) self.project_file_write.close() elif self.project_file == '': tkMessageBox.showwarning(u'Keine Datei zum Speichern geöffnet!', u'Bitte Datei zum Speichern öffnen oder neue Datei erstellen.') elif self.sample_name_entry.get() == '': tkMessageBox.showwarning(u'Keine Probenname eingetragen!', u'Bitte Probenname eintragen.') def new_file(self): #Neues Projekt erstellen self.project_file = tkFileDialog.asksaveasfilename(**self.file_opt2) self.project_file_write = open(self.project_file, 'a') self.project_file_write.write('Probenname\tNMax\tMedian\tProbendicke\tWeiterreisswiderstand\tSpannweite\tMethode\tKommentar\tMaxima(80%)') self.project_file_write.close() def open_file(self): #Bestehendes Projekt öffnen self.project_file = tkFileDialog.askopenfilename(**self.file_opt3) def get_filepath(self): #Dateipfad von Messung erfragen self.sample_file = tkFileDialog.askopenfilename(**self.file_opt) self.import_data(self.sample_file) def import_data(self, loadfile): #Messung importieren self.X, self.Y = np.loadtxt(loadfile, usecols = (1,0), unpack = True) self.plot() 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 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 draw_figure(self, canvas, figure, loc=(0, 0)): ''' Draw a matplotlib figure onto a Tk canvas loc: location of top-left corner of figure on canvas in pixels. Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py ''' figure_canvas_agg = FigureCanvasAgg(figure) figure_canvas_agg.draw() figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds figure_w, figure_h = int(figure_w), int(figure_h) photo = PhotoImage(master=canvas, width=figure_w, height=figure_h) # Position: convert from top-left anchor to center anchor canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo) # Unfortunatly, there's no accessor for the pointer to the native renderer tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2) # Return a handle which contains a reference to the photo object # which must be kept live or else the picture disappears return photo def _datacheck_peakdetect(self, x_axis, y_axis): if x_axis is None: x_axis = range(len(y_axis)) if len(y_axis) != len(x_axis): raise (ValueError, 'Input vectors y_axis and x_axis must have same length') #needs to be a numpy array y_axis = np.array(y_axis) x_axis = np.array(x_axis) return x_axis, y_axis def peakdetect(self, y_axis, x_axis = None, lookahead = 300, delta=0): """ Converted from/based on a MATLAB script at: http://billauer.co.il/peakdet.html function for detecting local maximas and minmias in a signal. Discovers peaks by searching for values which are surrounded by lower or larger values for maximas and minimas respectively keyword arguments: y_axis -- A list containg the signal over which to find peaks x_axis -- (optional) A x-axis whose values correspond to the y_axis list and is used in the return to specify the postion of the peaks. If omitted an index of the y_axis is used. (default: None) lookahead -- (optional) distance to look ahead from a peak candidate to determine if it is the actual peak (default: 200) '(sample / period) / f' where '4 >= f >= 1.25' might be a good value delta -- (optional) this specifies a minimum difference between a peak and the following points, before a peak may be considered a peak. Useful to hinder the function from picking up false peaks towards to end of the signal. To work well delta should be set to delta >= RMSnoise * 5. (default: 0) delta function causes a 20% decrease in speed, when omitted Correctly used it can double the speed of the function return -- two lists [max_peaks, min_peaks] containing the positive and negative peaks respectively. Each cell of the lists contains a tupple of: (position, peak_value) to get the average peak value do: np.mean(max_peaks, 0)[1] on the results to unpack one of the lists into x, y coordinates do: x, y = zip(*tab) """ max_peaks = [] min_peaks = [] dump = [] #Used to pop the first hit which almost always is false # check input data x_axis, y_axis = self._datacheck_peakdetect(x_axis, y_axis) # store data length for later use length = len(y_axis) #perform some checks if lookahead < 1: raise ValueError, "Lookahead must be '1' or above in value" if not (np.isscalar(delta) and delta >= 0): raise ValueError, "delta must be a positive number" #maxima and minima candidates are temporarily stored in #mx and mn respectively mn, mx = np.Inf, -np.Inf #Only detect peak if there is 'lookahead' amount of points after it for index, (x, y) in enumerate(zip(x_axis[:-lookahead], y_axis[:-lookahead])): if y > mx: mx = y mxpos = x if y < mn: mn = y mnpos = x ####look for max#### if y < mx-delta and mx != np.Inf: #Maxima peak candidate found #look ahead in signal to ensure that this is a peak and not jitter if y_axis[index:index+lookahead].max() < mx: max_peaks.append([mxpos, mx]) dump.append(True) #set algorithm to only find minima now mx = np.Inf mn = np.Inf if index+lookahead >= length: #end is within lookahead no more peaks can be found break continue #else: #slows shit down this does # mx = ahead # mxpos = x_axis[np.where(y_axis[index:index+lookahead]==mx)] ####look for min#### if y > mn+delta and mn != -np.Inf: #Minima peak candidate found #look ahead in signal to ensure that this is a peak and not jitter if y_axis[index:index+lookahead].min() > mn: min_peaks.append([mnpos, mn]) dump.append(False) #set algorithm to only find maxima now mn = -np.Inf mx = -np.Inf if index+lookahead >= length: #end is within lookahead no more peaks can be found break #else: #slows shit down this does # mn = ahead # mnpos = x_axis[np.where(y_axis[index:index+lookahead]==mn)] #Remove the false hit on the first value of the y_axis try: if dump[0]: max_peaks.pop(0) else: min_peaks.pop(0) del dump except IndexError: #no peaks were found, should the function return empty lists? pass return [max_peaks, min_peaks]
class TkPictureFrame(Frame): def __init__(self, x, y, master=None): Frame.__init__(self, master) self.photo = None self.resolution = (x, y) #The center of the Canvas is 0, 0. Find the center so #we can draw the image properly. self.center = ( x/2, y/2) #Setup the canvas self.picture = Canvas(self, width=x, height=y) #Place the canvas in the Grid. self.picture.grid(row=0,column=0,columnspan=2) #Camera check button control. self.checkButton = Checkbutton(self, text='Camera?',\ command=self.toggleCamera) #Place it on the grid. self.checkButton.grid(row=1,column=0) #Spinbox to set FPS self.fpsSpin = Spinbox(self, text="FPS", from_=2, to=30,\ command=self.fpsSpinCallback) self.fpsSpin.grid(row=1, column=1) #Set framerate self.fpsSpinCallback() #To determine if the camera is running self.capturing = False def fpsSpinCallback(self): self.fps = int(self.fpsSpin.get()) def changePic(self, photo): #Make a reference to the old photo for removal self.oldphoto = self.photo #Setup the new photo self.photo = photo #Draw the new photo over the old photo self.picture.create_image(self.center,image=self.photo) #Remove the old photo self.picture.delete(self.oldphoto) #Enable the checkbox self.checkButton.config(state="normal") def timedDisable(self, widget): #Disable a widget for 2 seconds. widget.config(state="disabled") time.sleep(2) widget.config(state="normal") def threadTimeDisable(self, widget): #Run the timed disable in a thread to avoid lockups. thread.start_new_thread(self.timedDisable, (widget,)) def startCamera(self): #Disable the checkbox and fps spinner. self.checkButton.config(state="disabled") self.fpsSpin.config(state="disabled") #Start the camera thread.start_new_thread(self.setupCamera, self.resolution) self.capturing = True def stopCamera(self): #Disable the checkbox for a duration. self.threadTimeDisable(self.checkButton) #Enable the spinner. self.fpsSpin.config(state="normal") #Clear the canvas. self.capturing = False self.picture.delete("all") def toggleCamera(self): if self.capturing: self.stopCamera() else: self.startCamera() def setupCamera(self, x, y): with picamera.PiCamera() as camera: camera.resolution = (x, y) camera.framerate = self.fps stream = io.BytesIO() for each in camera.capture_continuous(stream, format='jpeg'): # Truncate the stream to the current position (in case # prior iterations output a longer image) each.truncate() #Rewind the stream each.seek(0) #Open the image stream image = Image.open(each) photo = ImageTk.PhotoImage(image) #Break out of the loop if not capturing if not self.capturing: break #Update the canvas self.changePic(photo) #Reset playback to the beginning for the next image. each.seek(0)
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 TTTUI(object): default_ply = 6 neither_color = '#%02x%02x%02x' % (255, 255, 255) # white player_color = '#%02x%02x%02x' % (62, 188, 0) # green computer_color = '#%02x%02x%02x' % (192, 35, 3) # red win_color = '#%02x%02x%02x' % (25, 111, 254) # blue def __init__(self): # TTT related self.ttt = Board(ply=9) self.human_first = True # UI related self.root = Tk() self.root.resizable(0, 0) self.root.title("3D TTT") # TTT frames self.ttt_frames = [Frame(self.root) for x in range(3)] for i in range(3): self.ttt_frames[i].grid(row=0, column=i) self.button_pos = dict() self._init_board() # control frame self.control_frame = Frame(self.root, padx=5, pady=5) self.control_frame.grid(row=1, column=1) self.new_game_btn = Button(self.control_frame, text='New Game', \ command=lambda: self.reset()) self.new_game_btn.pack(side=LEFT, fill=BOTH, expand=True) self.toggle_human_first_btn = Button(self.control_frame, \ text='Human First', command=lambda: self.toggle_human_first()) self.toggle_human_first_btn.pack(side=RIGHT, fill=BOTH, expand=True) self.ply_box = Spinbox(self.control_frame, from_=1, to=20, \ textvariable=self.ttt.difficulty, command=lambda: self.reset()) self.ply_box.pack(side=RIGHT, fill=BOTH, expand=True) # start UI self.update_pieces() self.start() self.root.mainloop() def toggle_human_first(self): self.human_first = not self.human_first self.toggle_human_first_btn.config(text='Human First' if \ self.human_first else 'Computer First') self.reset() def _find_button(self, frame, r, c): for child in frame.children.values(): info = child.grid_info() if info['row'] == r and info['column'] == c: return child return None def update_pieces(self): player_pieces = self.ttt.get_moves(self.ttt.human) computer_pieces = self.ttt.get_moves(self.ttt.ai) cnt = 0 for b, board in enumerate(self.ttt.board): for r, row in enumerate(board): for c, col in enumerate(row): color = self.neither_color text = '-' occupied = False if cnt in player_pieces: color = self.player_color text = self.ttt.human if cnt in computer_pieces: color = self.computer_color text = self.ttt.ai if self.ttt.complete and cnt in self.ttt.winning_combo: color = self.win_color btn = self.button_pos[cnt] btn.config(text=text, bg=color, state=DISABLED if \ occupied else NORMAL) cnt += 1 def place_human(self, position): if position in self.ttt.allowed_moves and not self.ttt.complete: self.ttt.move(position, self.ttt.human) self.ttt.human_turn = False self.update_pieces() self.place_computer() def place_computer(self): if not self.ttt.complete: self.ttt.computers_move() self.update_pieces() def reset(self): self.ttt.reset() self.ttt.difficulty = self.default_ply if not \ self.ply_box.get().isdigit() else int(self.ply_box.get()) self.ttt.human_turn = self.human_first self.update_pieces() self.start() def _init_board(self): cnt = 0 for b, board in enumerate(self.ttt.board): for x, row in enumerate(board): for y, cell in enumerate(row): padding = (0, 0) if y == 2 and b != 2: padding = (0, 12) btn = Button(self.ttt_frames[b], width=8, height=4, \ command=lambda x=cell: self.place_human(x)) btn.grid(row=x, column=y, padx=padding) self.button_pos[cnt] = btn cnt += 1 def start(self): if not self.ttt.human_turn: self.place_computer()
class Cockpit(ttkFrame): ''' Remote controller GUI ''' KEY_ANG_SPEED = "ang-speed" KEY_ANGLES = "angles" KEY_ACCEL = "accel" PID_KEYS = ["P", "I", "D"] DEFAULT_DRONE_IP = "192.168.1.130" DEFAULT_DRONE_PORT = 2121 DIR_NONE = 0 DIR_VERTICAL = 1 DIR_HORIZONTAL = 2 MAX_ACCEL = 10.0 #TODO angles. Replace by m/s² MAX_ACCEL_Z = 0.1 #m/s² MAX_ANGLE_SPEED = 50.0 #º/s def __init__(self, parent, isDummy = False, droneIp = DEFAULT_DRONE_IP, dronePort = DEFAULT_DRONE_PORT): ''' Constructor ''' ttkFrame.__init__(self, parent) self._started = IntVar() self._integralsEnabled = IntVar() self._target = [0.0] * 4 self._selectedPidConstats = "--" self._pidConstants = { Cockpit.KEY_ANG_SPEED:{ "X":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Y":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Z":{ "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ANGLES: { "X":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Y":{ "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ACCEL:{ "X":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Y":{ "P": 0.0, "I": 0.0, "D": 0.0 }, "Z":{ "P": 0.0, "I": 0.0, "D": 0.0 } } } self.parent = parent self.initUI() self._controlKeysLocked = False if not isDummy: self._link = INetLink(droneIp, dronePort) else: self._link = ConsoleLink() self._link.open() self._updateInfoThread = Thread(target=self._updateInfo) self._updateInfoThreadRunning = False self._readingState = False self._start() def initUI(self): self.parent.title("Drone control") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) self.parent.bind_all("<Key>", self._keyDown) self.parent.bind_all("<KeyRelease>", self._keyUp) if system() == "Linux": self.parent.bind_all("<Button-4>", self._onMouseWheelUp) self.parent.bind_all("<Button-5>", self._onMouseWheelDown) else: #case of Windows self.parent.bind_all("<MouseWheel>", self._onMouseWheel) #Commands commandsFrame = tkFrame(self) commandsFrame.grid(column=0, row=0, sticky="WE") self._startedCB = Checkbutton(commandsFrame, text="On", variable=self._started, command=self._startedCBChanged) self._startedCB.pack(side=LEFT, padx=4) self._integralsCB = Checkbutton(commandsFrame, text="Int.", variable=self._integralsEnabled, \ command=self._integralsCBChanged, state=DISABLED) self._integralsCB.pack(side=LEFT, padx=4) self._quitButton = Button(commandsFrame, text="Quit", command=self.exit) self._quitButton.pack(side=LEFT, padx=2, pady=2) # self._angleLbl = Label(commandsFrame, text="Angle") # self._angleLbl.pack(side=LEFT, padx=4) # # self._angleEntry = Entry(commandsFrame, state=DISABLED) # self._angleEntry.pack(side=LEFT) #Info infoFrame = tkFrame(self) infoFrame.grid(column=1, row=1, sticky="E", padx=4) #Throttle Label(infoFrame, text="Throttle").grid(column=0, row=0, sticky="WE") self._throttleTexts = [StringVar(),StringVar(),StringVar(),StringVar()] Entry(infoFrame, textvariable=self._throttleTexts[3], state=DISABLED, width=5).grid(column=0, row=1) Entry(infoFrame, textvariable=self._throttleTexts[0], state=DISABLED, width=5).grid(column=1, row=1) Entry(infoFrame, textvariable=self._throttleTexts[2], state=DISABLED, width=5).grid(column=0, row=2) Entry(infoFrame, textvariable=self._throttleTexts[1], state=DISABLED, width=5).grid(column=1, row=2) #Angles Label(infoFrame, text="Angles").grid(column=0, row=3, sticky="WE") self._angleTexts = [StringVar(),StringVar(),StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._angleTexts[index], state=DISABLED, width=5).grid(column=index, row=4) #Accels Label(infoFrame, text="Accels").grid(column=0, row=5, sticky="WE") self._accelTexts = [StringVar(),StringVar(),StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._accelTexts[index], state=DISABLED, width=5).grid(column=index, row=6) #Speeds Label(infoFrame, text="Speeds").grid(column=0, row=7, sticky="WE") self._speedTexts = [StringVar(),StringVar(),StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._speedTexts[index], state=DISABLED, width=5).grid(column=index, row=8) #Height Label(infoFrame, text="Height").grid(column=0, row=9, sticky="W") self._heightText = StringVar() Entry(infoFrame, state=DISABLED, width=5).grid(column=1, row=9) #control controlFrame = tkFrame(self) controlFrame.grid(column=0, row=1, sticky="W") self._throttle = DoubleVar() self._thrustScale = Scale(controlFrame, orient=VERTICAL, from_=100.0, to=-100.0, \ tickinterval=0, variable=self._throttle, \ length=200, showvalue=1, \ state=DISABLED, command=self._onThrustScaleChanged) self._thrustScale.bind("<Double-Button-1>", self._onThrustScaleDoubleButton1, "+") self._thrustScale.grid(column=0) self._shiftCanvas = Canvas(controlFrame, bg="white", height=400, width=400, \ relief=SUNKEN) self._shiftCanvas.bind("<Button-1>", self._onMouseButton1) #self._shiftCanvas.bind("<ButtonRelease-1>", self._onMouseButtonRelease1) self._shiftCanvas.bind("<B1-Motion>", self._onMouseButton1Motion) self._shiftCanvas.bind("<Double-Button-1>", self._onMouseDoubleButton1) self._shiftCanvas.bind("<Button-3>", self._onMouseButton3) #self._shiftCanvas.bind("<ButtonRelease-3>", self._onMouseButtonRelease3) self._shiftCanvas.bind("<B3-Motion>", self._onMouseButton3Motion) self._shiftCanvas.grid(row=0,column=1, padx=2, pady=2) self._shiftCanvas.create_oval(2, 2, 400, 400, outline="#ff0000") self._shiftCanvas.create_line(201, 2, 201, 400, fill="#ff0000") self._shiftCanvas.create_line(2, 201, 400, 201, fill="#ff0000") self._shiftMarker = self._shiftCanvas.create_oval(197, 197, 205, 205, outline="#0000ff", fill="#0000ff") self._yaw = DoubleVar() self._yawScale = Scale(controlFrame, orient=HORIZONTAL, from_=-100.0, to=100.0, \ tickinterval=0, variable=self._yaw, \ length=200, showvalue=1, \ command=self._onYawScaleChanged) self._yawScale.bind("<Double-Button-1>", self._onYawScaleDoubleButton1, "+") self._yawScale.grid(row=1, column=1) self._controlKeyActive = False #PID calibration pidCalibrationFrame = tkFrame(self) pidCalibrationFrame.grid(column=0, row=2, sticky="WE"); self._pidSelected = StringVar() self._pidSelected.set("--") self._pidListBox = OptionMenu(pidCalibrationFrame, self._pidSelected, "--", \ Cockpit.KEY_ANG_SPEED, Cockpit.KEY_ANGLES, Cockpit.KEY_ACCEL, \ command=self._onPidListBoxChanged) self._pidListBox.pack(side=LEFT, padx=2) self._pidListBox.config(width=10) self._axisSelected = StringVar() self._axisSelected.set("--") self._axisListBox = OptionMenu(pidCalibrationFrame, self._axisSelected, "--", "X", "Y", "Z", \ command=self._onAxisListBoxChanged) self._axisListBox.pack(side=LEFT, padx=2) self._axisListBox.config(state=DISABLED) Label(pidCalibrationFrame, text="P").pack(side=LEFT, padx=(14, 2)) self._pidPString = StringVar() self._pidPString.set("0.00") self._pidPSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=100.0, increment=0.01, state=DISABLED, \ textvariable=self._pidPString, command=self._onPidSpinboxChanged) self._pidPSpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="I").pack(side=LEFT, padx=(14, 2)) self._pidIString = StringVar() self._pidIString.set("0.00") self._pidISpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=100.0, increment=0.01, state=DISABLED, \ textvariable=self._pidIString, command=self._onPidSpinboxChanged) self._pidISpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="D").pack(side=LEFT, padx=(14, 2)) self._pidDString = StringVar() self._pidDString.set("0.00") self._pidDSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=100.0, increment=0.01, state=DISABLED, \ textvariable=self._pidDString, command=self._onPidSpinboxChanged) self._pidDSpinbox.pack(side=LEFT, padx=2) #debug debugFrame = tkFrame(self) debugFrame.grid(column=0, row=3, sticky="WE") self._debugMsg = Message(debugFrame, anchor="nw", justify=LEFT, relief=SUNKEN, width=300) self._debugMsg.pack(fill=BOTH, expand=1) def _start(self): self._readDroneConfig() def exit(self): self._link.send({"key": "close", "data": None}) self._stopUpdateInfoThread() self._link.close() self.quit() def _updateTarget(self): markerCoords = self._shiftCanvas.coords(self._shiftMarker) coords = ((markerCoords[0] + markerCoords[2]) / 2, (markerCoords[1] + markerCoords[3]) / 2) self._target[1] = float(coords[0] - 201) * Cockpit.MAX_ACCEL / 200.0 self._target[0] = float(coords[1] - 201) * Cockpit.MAX_ACCEL / 200.0 #Remote control uses clockwise angle, but the drone's referece system uses counter-clockwise angle self._target[2] = -self._yaw.get() * Cockpit.MAX_ANGLE_SPEED / 100.0 self._target[3] = self._throttle.get() * Cockpit.MAX_ACCEL_Z / 100.0 self._sendTarget() def _keyDown(self, event): if event.keysym == "Escape": self._throttle.set(0) self._started.set(0) self._thrustScale.config(state=DISABLED) self._stopUpdateInfoThread() self._sendIsStarted() elif event.keysym.startswith("Control"): self._controlKeyActive = True elif not self._controlKeysLocked and self._controlKeyActive: if event.keysym == "Up": self._thrustScaleUp() elif event.keysym == "Down": self._thrustScaleDown() elif event.keysym == "Left": self._yawLeft() elif event.keysym == "Right": self._yawRight() elif event.keysym == "space": self._yawReset() self._thrustReset() elif not self._controlKeysLocked and not self._controlKeyActive: if event.keysym == "Up": self._moveShiftCanvasMarker((0,-5)) elif event.keysym == "Down": self._moveShiftCanvasMarker((0,5)) elif event.keysym == "Left": self._moveShiftCanvasMarker((-5,0)) elif event.keysym == "Right": self._moveShiftCanvasMarker((5,0)) elif event.keysym == "space": self._resetShiftCanvasMarker() def _keyUp(self, eventArgs): if eventArgs.keysym.startswith("Control"): self._controlKeyActive = False def _onMouseButton1(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseButtonRelease1(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 197, 197, 205, 205) def _limitCoordsToSize(self, coords, size): if coords[0] > size: x = size elif coords[0] < 0: x = 0 else: x = coords[0] if coords[1] > size: y = size elif coords[1] < 0: y = 0 else: y = coords[1] return (x,y) def _moveShiftCanvasMarker(self, shift): lastCoords = self._shiftCanvas.coords(self._shiftMarker) newCoords = (lastCoords[0] + shift[0], lastCoords[1] + shift[1]) newCoords = self._limitCoordsToSize(newCoords, 400) self._shiftCanvas.coords(self._shiftMarker, newCoords[0], newCoords[1], newCoords[0] + 8, newCoords[1] + 8) self._updateTarget() def _resetShiftCanvasMarker(self): self._shiftCanvas.coords(self._shiftMarker, 197, 197, 205, 205) self._updateTarget() def _onMouseButton1Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseDoubleButton1(self, eventArgs): self._resetShiftCanvasMarker() def _onMouseButton3(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) self._mouseDirection = Cockpit.DIR_NONE def _onMouseButtonRelease3(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 197, 197, 205, 205) def _onMouseButton3Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) if self._mouseDirection == Cockpit.DIR_NONE: if abs(deltaCoords[0]) > abs(deltaCoords[1]): self._mouseDirection = Cockpit.DIR_HORIZONTAL else: self._mouseDirection = Cockpit.DIR_VERTICAL if self._mouseDirection == Cockpit.DIR_HORIZONTAL: deltaCoords = (deltaCoords[0], 0) else: deltaCoords = (0, deltaCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _thrustScaleUp(self): if self._started.get(): newValue = self._thrustScale.get() + 1 self._thrustScale.set(newValue) self._updateTarget() def _thrustScaleDown(self): if self._started.get(): newValue = self._thrustScale.get() - 1 self._thrustScale.set(newValue) self._updateTarget() def _thrustReset(self): if self._started.get(): self._thrustScale.set(0.0) self._updateTarget() def _onThrustScaleDoubleButton1(self, eventArgs): self._thrustReset() return "break" def _yawRight(self): newValue = self._yaw.get() + 1 self._yaw.set(newValue) self._updateTarget() def _yawLeft(self): newValue = self._yaw.get() - 1 self._yaw.set(newValue) self._updateTarget() def _yawReset(self): self._yaw.set(0) self._updateTarget() def _onMouseWheelUp(self, eventArgs): if not self._controlKeyActive: self._thrustScaleUp() else: self._yawRight() def _onMouseWheelDown(self, eventArgs): if not self._controlKeyActive: self._thrustScaleDown() else: self._yawLeft() def _onMouseWheel(self, eventArgs): factor = int(eventArgs.delta/120) if not self._controlKeyActive: if self._started.get(): newValue = self._thrustScale.get() + factor self._thrustScale.set(newValue) self._updateTarget() else: newValue = self._yaw.get() + factor self._yaw.set(newValue) self._updateTarget() def _onYawScaleChanged(self, eventArgs): self._updateTarget() def _onYawScaleDoubleButton1(self, eventArgs): self._yawReset() return "break" def _startedCBChanged(self): if not self._started.get(): self._throttle.set(0) self._thrustScale.config(state=DISABLED) self._integralsCB.config(state=DISABLED) self._stopUpdateInfoThread() else: self._thrustScale.config(state="normal") self._integralsCB.config(state="normal") self._startUpdateInfoThread() self._sendIsStarted() def _integralsCBChanged(self): self._link.send({"key": "integrals", "data":self._integralsEnabled.get() != 0}) def _onThrustScaleChanged(self, eventArgs): self._updateTarget() def _sendTarget(self): self._link.send({"key": "target", "data": self._target}) def _sendIsStarted(self): isStarted = self._started.get() != 0 self._link.send({"key": "is-started", "data": isStarted}) def _sendPidCalibrationData(self): if self._pidSelected.get() != "--" and self._axisSelected.get() != "--": pidData = { "pid": self._pidSelected.get(), "axis": self._axisSelected.get(), "p": float(self._pidPSpinbox.get()), "i": float(self._pidISpinbox.get()), "d": float(self._pidDSpinbox.get())} self._link.send({"key": "pid-calibration", "data": pidData}) def _updatePidCalibrationData(self): pid = self._pidSelected.get() axis = self._axisSelected.get() if pid != "--" and axis != "--": self._pidConstants[pid][axis]["P"] = float(self._pidPSpinbox.get()) self._pidConstants[pid][axis]["I"] = float(self._pidISpinbox.get()) self._pidConstants[pid][axis]["D"] = float(self._pidDSpinbox.get()) def _readDroneConfig(self): self._link.send({"key": "read-drone-config", "data": None}, self._onDroneConfigRead) def _readDroneState(self): if not self._readingState: self._readingState = True self._link.send({"key": "read-drone-state", "data": None}, self._onDroneStateRead) def _readPidConfigItem(self, message, cockpitKey, axises, configKeys): for i in range(len(axises)): for j in range(len(Cockpit.PID_KEYS)): self._pidConstants[cockpitKey][axises[i]][Cockpit.PID_KEYS[j]] = message[configKeys[j]][i] def _onDroneConfigRead(self, message): #TODO Show current configuration within the GUI (at least relevant settings) if message: #Angle-speeds self._readPidConfigItem(message, Cockpit.KEY_ANG_SPEED, ["X", "Y", "Z"], \ [Configuration.PID_ANGLES_SPEED_KP, \ Configuration.PID_ANGLES_SPEED_KI, \ Configuration.PID_ANGLES_SPEED_KD]) #Angles self._readPidConfigItem(message, Cockpit.KEY_ANGLES, ["X", "Y"], \ [Configuration.PID_ANGLES_KP, \ Configuration.PID_ANGLES_KI, \ Configuration.PID_ANGLES_KD]) #Accels self._readPidConfigItem(message, Cockpit.KEY_ACCEL, ["X", "Y", "Z"], \ [Configuration.PID_ACCEL_KP, \ Configuration.PID_ACCEL_KI, \ Configuration.PID_ACCEL_KD]) def _onDroneStateRead(self, state): if state: for index in range(4): self._throttleTexts[index].set("{0:.3f}".format(state["_throttles"][index])) for index in range(3): self._accelTexts[index].set("{0:.3f}".format(state["_accels"][index])) self._angleTexts[index].set("{0:.3f}".format(state["_angles"][index])) else: self._stopUpdateInfoThread() self._readingState = False def _onPidSpinboxChanged(self): self._updatePidCalibrationData() self._sendPidCalibrationData() def _onPidListBoxChanged(self, pid): self._axisSelected.set("--") self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._selectedPidConstats = pid if pid == "--": self._axisListBox.config(state=DISABLED) self._controlKeysLocked = False else: self._axisListBox.config(state="normal") self._controlKeysLocked = True def _onAxisListBoxChanged(self, axis): if axis == "--" or (self._selectedPidConstats == Cockpit.KEY_ANGLES and axis == "Z"): self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._controlKeysLocked = axis != "--" else: self._pidPString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["P"])) self._pidIString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["I"])) self._pidDString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["D"])) self._pidPSpinbox.config(state="normal") self._pidISpinbox.config(state="normal") self._pidDSpinbox.config(state="normal") self._controlKeysLocked = True def _updateInfo(self): while self._updateInfoThreadRunning: self._readDroneState() time.sleep(1.0) def _startUpdateInfoThread(self): self._updateInfoThreadRunning = True if not self._updateInfoThread.isAlive(): self._updateInfoThread.start() def _stopUpdateInfoThread(self): self._updateInfoThreadRunning = False if self._updateInfoThread.isAlive(): self._updateInfoThread.join()
class Cockpit(ttkFrame): ''' Remote device GUI ''' #TODO: 20160415 DPM - Set these values from configuration file #--- config THROTTLE_BY_USER = True THROTTLE_RESOLUTION = 0.1 # Joystick enabled or not, if any JOYSTICK_ENABLED = True DEFAULT_DRONE_IP = "192.168.1.130" DEFAULT_DRONE_PORT = 2121 #--- end config KEY_ANG_SPEED = "ang-speed" KEY_ANGLES = "angles" KEY_ACCEL = "accel" PID_KEYS = ["P", "I", "D"] DIR_NONE = 0 DIR_VERTICAL = 1 DIR_HORIZONTAL = 2 def __init__(self, parent, isDummy=False, droneIp=DEFAULT_DRONE_IP, dronePort=DEFAULT_DRONE_PORT): ''' Constructor ''' ttkFrame.__init__(self, parent) self._target = [0.0] * 4 self._selectedPidConstats = "--" self._pidConstants = { Cockpit.KEY_ANG_SPEED: { "X": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Y": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Z": { "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ANGLES: { "X": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Y": { "P": 0.0, "I": 0.0, "D": 0.0 } }, Cockpit.KEY_ACCEL: { "X": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Y": { "P": 0.0, "I": 0.0, "D": 0.0 }, "Z": { "P": 0.0, "I": 0.0, "D": 0.0 } } } self.parent = parent self.initUI() self._controlKeysLocked = False if not isDummy: self._link = INetLink(droneIp, dronePort) else: self._link = ConsoleLink() self._link.open() self._updateInfoThread = Thread(target=self._updateInfo) self._updateInfoThreadRunning = False self._readingState = False self._start() def initUI(self): self.parent.title("Drone control") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) self.parent.bind_all("<Key>", self._keyDown) self.parent.bind_all("<KeyRelease>", self._keyUp) if system() == "Linux": self.parent.bind_all("<Button-4>", self._onMouseWheelUp) self.parent.bind_all("<Button-5>", self._onMouseWheelDown) else: #case of Windows self.parent.bind_all("<MouseWheel>", self._onMouseWheel) #Commands commandsFrame = tkFrame(self) commandsFrame.grid(column=0, row=0, sticky="WE") self._started = IntVar() self._startedCB = Checkbutton(commandsFrame, text="On", variable=self._started, command=self._startedCBChanged) self._startedCB.pack(side=LEFT, padx=4) # self._integralsCB = Checkbutton(commandsFrame, text="Int.", variable=self._integralsEnabled, \ # command=self._integralsCBChanged, state=DISABLED) # self._integralsCB.pack(side=LEFT, padx=4) self._quitButton = Button(commandsFrame, text="Quit", command=self.exit) self._quitButton.pack(side=LEFT, padx=2, pady=2) # self._angleLbl = Label(commandsFrame, text="Angle") # self._angleLbl.pack(side=LEFT, padx=4) # # self._angleEntry = Entry(commandsFrame, state=DISABLED) # self._angleEntry.pack(side=LEFT) #Info infoFrame = tkFrame(self) infoFrame.grid(column=1, row=1, sticky="NE", padx=4) #Throttle Label(infoFrame, text="Throttle").grid(column=0, row=0, sticky="WE") self._throttleTexts = [ StringVar(), StringVar(), StringVar(), StringVar() ] Entry(infoFrame, textvariable=self._throttleTexts[3], state=DISABLED, width=5).grid(column=0, row=1) Entry(infoFrame, textvariable=self._throttleTexts[0], state=DISABLED, width=5).grid(column=1, row=1) Entry(infoFrame, textvariable=self._throttleTexts[2], state=DISABLED, width=5).grid(column=0, row=2) Entry(infoFrame, textvariable=self._throttleTexts[1], state=DISABLED, width=5).grid(column=1, row=2) #Angles Label(infoFrame, text="Angles").grid(column=0, row=3, sticky="WE") self._angleTexts = [StringVar(), StringVar(), StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._angleTexts[index], state=DISABLED, width=5).grid(column=index, row=4) #Accels Label(infoFrame, text="Accels").grid(column=0, row=5, sticky="WE") self._accelTexts = [StringVar(), StringVar(), StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._accelTexts[index], state=DISABLED, width=5).grid(column=index, row=6) #Speeds Label(infoFrame, text="Speeds").grid(column=0, row=7, sticky="WE") self._speedTexts = [StringVar(), StringVar(), StringVar()] for index in range(3): Entry(infoFrame, textvariable=self._speedTexts[index], state=DISABLED, width=5).grid(column=index, row=8) #Height Label(infoFrame, text="Height").grid(column=0, row=9, sticky="E") self._heightText = StringVar() Entry(infoFrame, textvariable=self._heightText, state=DISABLED, width=5).grid(column=1, row=9) #Loop rate Label(infoFrame, text="Loop @").grid(column=0, row=10, sticky="E") self._loopRateText = StringVar() Entry(infoFrame, textvariable=self._loopRateText, state=DISABLED, width=5).grid(column=1, row=10) Label(infoFrame, text="Hz").grid(column=2, row=10, sticky="W") #control controlFrame = tkFrame(self) controlFrame.grid(column=0, row=1, sticky="W") self._throttle = DoubleVar() if Cockpit.THROTTLE_BY_USER: self._thrustScale = Scale(controlFrame, orient=VERTICAL, from_=100.0, to=0.0, \ tickinterval=0, variable=self._throttle, resolution=Cockpit.THROTTLE_RESOLUTION, \ length=200, showvalue=1, \ state=DISABLED, command=self._onThrustScaleChanged) else: self._thrustScale = Scale(controlFrame, orient=VERTICAL, from_=100.0, to=-100.0, \ tickinterval=0, variable=self._throttle, \ length=200, showvalue=1, \ state=DISABLED, command=self._onThrustScaleChanged) self._thrustScale.bind("<Double-Button-1>", self._onThrustScaleDoubleButton1, "+") self._thrustScale.grid(column=0) self._shiftCanvas = Canvas(controlFrame, bg="white", height=400, width=400, \ relief=SUNKEN) self._shiftCanvas.bind("<Button-1>", self._onMouseButton1) #self._shiftCanvas.bind("<ButtonRelease-1>", self._onMouseButtonRelease1) self._shiftCanvas.bind("<B1-Motion>", self._onMouseButton1Motion) self._shiftCanvas.bind("<Double-Button-1>", self._onMouseDoubleButton1) self._shiftCanvas.bind("<Button-3>", self._onMouseButton3) #self._shiftCanvas.bind("<ButtonRelease-3>", self._onMouseButtonRelease3) self._shiftCanvas.bind("<B3-Motion>", self._onMouseButton3Motion) self._shiftCanvas.grid(row=0, column=1, padx=2, pady=2) self._shiftCanvas.create_oval(1, 1, 400, 400, outline="#ff0000") self._shiftCanvas.create_line(200, 2, 200, 400, fill="#ff0000") self._shiftCanvas.create_line(2, 200, 400, 200, fill="#ff0000") self._shiftMarker = self._shiftCanvas.create_oval(196, 196, 204, 204, outline="#0000ff", fill="#0000ff") self._yaw = DoubleVar() self._yawScale = Scale(controlFrame, orient=HORIZONTAL, from_=-100.0, to=100.0, \ tickinterval=0, variable=self._yaw, \ length=200, showvalue=1, \ command=self._onYawScaleChanged) self._yawScale.bind("<Double-Button-1>", self._onYawScaleDoubleButton1, "+") self._yawScale.grid(row=1, column=1) self._controlKeyActive = False #PID calibration pidCalibrationFrame = tkFrame(self) pidCalibrationFrame.grid(column=0, row=2, sticky="WE") self._pidSelected = StringVar() self._pidSelected.set("--") self._pidListBox = OptionMenu(pidCalibrationFrame, self._pidSelected, "--", \ Cockpit.KEY_ANG_SPEED, Cockpit.KEY_ANGLES, Cockpit.KEY_ACCEL, \ command=self._onPidListBoxChanged) self._pidListBox.pack(side=LEFT, padx=2) self._pidListBox.config(width=10) self._axisSelected = StringVar() self._axisSelected.set("--") self._axisListBox = OptionMenu(pidCalibrationFrame, self._axisSelected, "--", "X", "Y", "Z", \ command=self._onAxisListBoxChanged) self._axisListBox.pack(side=LEFT, padx=2) self._axisListBox.config(state=DISABLED) Label(pidCalibrationFrame, text="P").pack(side=LEFT, padx=(14, 2)) self._pidPString = StringVar() self._pidPString.set("0.00") self._pidPSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=10000.0, increment=0.01, state=DISABLED, \ textvariable=self._pidPString, command=self._onPidSpinboxChanged) self._pidPSpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="I").pack(side=LEFT, padx=(14, 2)) self._pidIString = StringVar() self._pidIString.set("0.00") self._pidISpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=10000.0, increment=0.01, state=DISABLED, \ textvariable=self._pidIString, command=self._onPidSpinboxChanged) self._pidISpinbox.pack(side=LEFT, padx=2) Label(pidCalibrationFrame, text="D").pack(side=LEFT, padx=(14, 2)) self._pidDString = StringVar() self._pidDString.set("0.00") self._pidDSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=10000.0, increment=0.01, state=DISABLED, \ textvariable=self._pidDString, command=self._onPidSpinboxChanged) self._pidDSpinbox.pack(side=LEFT, padx=2) #debug debugFrame = tkFrame(self) debugFrame.grid(column=0, row=3, sticky="WE") self._debugMsg = Message(debugFrame, anchor="nw", justify=LEFT, relief=SUNKEN, width=300) self._debugMsg.pack(fill=BOTH, expand=1) def _start(self): self._readDroneConfig() if Cockpit.JOYSTICK_ENABLED: self._joystickManager = JoystickManager.getInstance() self._joystickManager.start() joysticks = self._joystickManager.getJoysticks() if len(joysticks) != 0: self._joystick = joysticks[0] self._joystick.onAxisChanged += self._onJoystickAxisChanged self._joystick.onButtonPressed += self._onJoystickButtonPressed else: self._joystick = None def _onJoystickAxisChanged(self, sender, index): if self._started.get() and sender == self._joystick: axisValue = self._joystick.getAxisValue(index) if index == 0: self._yaw.set(axisValue) self._updateTarget() elif index == 1 and not Cockpit.THROTTLE_BY_USER: thrust = -axisValue self._throttle.set(thrust) self._updateTarget() elif index == 2 and Cockpit.THROTTLE_BY_USER: rowThrottle = (axisValue + 100.0) / 2.0 if rowThrottle < 10.0: throttle = rowThrottle * 6.0 elif rowThrottle < 90.0: throttle = 60.0 + ((rowThrottle - 10.0) / 8.0) else: throttle = 70.0 + (rowThrottle - 90.0) * 3.0 self._throttle.set(throttle) self._sendThrottle() elif index == 3: x = 196 + axisValue * 2 lastCoords = self._shiftCanvas.coords(self._shiftMarker) coords = (x, lastCoords[1]) self._plotShiftCanvasMarker(coords) elif index == 4: y = 196 + axisValue * 2 lastCoords = self._shiftCanvas.coords(self._shiftMarker) coords = (lastCoords[0], y) self._plotShiftCanvasMarker(coords) def _onJoystickButtonPressed(self, sender, index): if sender == self._joystick and index == 7: if self._started.get() == 0: self._startedCB.select() else: self._startedCB.deselect() # Tkinter's widgets seem not to be calling the event-handler # when they are changed programmatically. Therefore, the # even-handler is called explicitly here. self._startedCBChanged() def exit(self): self._link.send({"key": "close", "data": None}) self._stopUpdateInfoThread() self._link.close() if Cockpit.JOYSTICK_ENABLED: self._joystickManager.stop() self.quit() def _updateTarget(self): markerCoords = self._shiftCanvas.coords(self._shiftMarker) coords = ((markerCoords[0] + markerCoords[2]) / 2, (markerCoords[1] + markerCoords[3]) / 2) self._target[0] = float( coords[1] - 200) / 2.0 # X-axis angle / X-axis acceleration self._target[1] = float( coords[0] - 200) / 2.0 # Y-axis angle / Y-axis acceleration #Remote control uses clockwise angle, but the drone's referece system uses counter-clockwise angle self._target[2] = -self._yaw.get() # Z-axis angular speed # Z-axis acceleration (thrust). Only when the motor throttle is not controlled by user directly if Cockpit.THROTTLE_BY_USER: self._target[3] = 0.0 else: self._target[3] = self._throttle.get() self._sendTarget() def _keyDown(self, event): if event.keysym == "Escape": self._throttle.set(0) self._started.set(0) self._thrustScale.config(state=DISABLED) self._stopUpdateInfoThread() self._sendIsStarted() elif event.keysym.startswith("Control"): self._controlKeyActive = True elif not self._controlKeysLocked and self._controlKeyActive: if event.keysym == "Up": self._thrustScaleUp() elif event.keysym == "Down": self._thrustScaleDown() elif event.keysym == "Left": self._yawLeft() elif event.keysym == "Right": self._yawRight() elif event.keysym == "space": self._yawReset() if not Cockpit.THROTTLE_BY_USER: self._thrustReset() elif not self._controlKeysLocked and not self._controlKeyActive: if event.keysym == "Up": self._moveShiftCanvasMarker((0, -5)) elif event.keysym == "Down": self._moveShiftCanvasMarker((0, 5)) elif event.keysym == "Left": self._moveShiftCanvasMarker((-5, 0)) elif event.keysym == "Right": self._moveShiftCanvasMarker((5, 0)) elif event.keysym == "space": self._resetShiftCanvasMarker() def _keyUp(self, eventArgs): if eventArgs.keysym.startswith("Control"): self._controlKeyActive = False def _onMouseButton1(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseButtonRelease1(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 196, 196, 204, 204) def _limitCoordsToSize(self, coords, size, width): maxSize = size - (width / 2.0) minSize = -(width / 2.0) if coords[0] > maxSize: x = maxSize elif coords[0] < minSize: x = minSize else: x = coords[0] if coords[1] > maxSize: y = maxSize elif coords[1] < minSize: y = minSize else: y = coords[1] return (x, y) def _plotShiftCanvasMarker(self, coords): coords = self._limitCoordsToSize(coords, 400, 8) self._shiftCanvas.coords(self._shiftMarker, coords[0], coords[1], coords[0] + 8, coords[1] + 8) self._updateTarget() def _moveShiftCanvasMarker(self, shift): lastCoords = self._shiftCanvas.coords(self._shiftMarker) newCoords = (lastCoords[0] + shift[0], lastCoords[1] + shift[1]) self._plotShiftCanvasMarker(newCoords) def _resetShiftCanvasMarker(self): self._shiftCanvas.coords(self._shiftMarker, 196, 196, 204, 204) self._updateTarget() def _onMouseButton1Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _onMouseDoubleButton1(self, eventArgs): self._resetShiftCanvasMarker() def _onMouseButton3(self, eventArgs): self._lastMouseCoords = (eventArgs.x, eventArgs.y) self._mouseDirection = Cockpit.DIR_NONE def _onMouseButtonRelease3(self, eventArgs): self._shiftCanvas.coords(self._shiftMarker, 196, 196, 204, 204) def _onMouseButton3Motion(self, eventArgs): deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1]) if self._mouseDirection == Cockpit.DIR_NONE: if abs(deltaCoords[0]) > abs(deltaCoords[1]): self._mouseDirection = Cockpit.DIR_HORIZONTAL else: self._mouseDirection = Cockpit.DIR_VERTICAL if self._mouseDirection == Cockpit.DIR_HORIZONTAL: deltaCoords = (deltaCoords[0], 0) else: deltaCoords = (0, deltaCoords[1]) self._moveShiftCanvasMarker(deltaCoords) self._lastMouseCoords = (eventArgs.x, eventArgs.y) def _thrustScaleUp(self): #TODO: 20160526 DPM: El valor de incremento de aceleración (1.0) puede ser muy alto if self._started.get(): newValue = self._thrustScale.get() \ + (Cockpit.THROTTLE_RESOLUTION if Cockpit.THROTTLE_BY_USER else 1.0) self._thrustScale.set(newValue) self._updateTarget() def _thrustScaleDown(self): #TODO: 20160526 DPM: El valor de decremento de aceleración (1.0) puede ser muy alto if self._started.get(): newValue = self._thrustScale.get() \ - (Cockpit.THROTTLE_RESOLUTION if Cockpit.THROTTLE_BY_USER else 1.0) self._thrustScale.set(newValue) self._updateTarget() def _thrustReset(self): if self._started.get(): self._thrustScale.set(0.0) self._updateTarget() def _onThrustScaleDoubleButton1(self, eventArgs): self._thrustReset() return "break" def _yawRight(self): newValue = self._yaw.get() + 1 self._yaw.set(newValue) self._updateTarget() def _yawLeft(self): newValue = self._yaw.get() - 1 self._yaw.set(newValue) self._updateTarget() def _yawReset(self): self._yaw.set(0) self._updateTarget() def _onMouseWheelUp(self, eventArgs): if not self._controlKeyActive: self._thrustScaleUp() else: self._yawRight() def _onMouseWheelDown(self, eventArgs): if not self._controlKeyActive: self._thrustScaleDown() else: self._yawLeft() def _onMouseWheel(self, eventArgs): factor = eventArgs.delta / (1200.0 if Cockpit.THROTTLE_BY_USER and not self._controlKeyActive else 120.0) if not self._controlKeyActive: if self._started.get(): newValue = self._thrustScale.get() + factor self._thrustScale.set(newValue) self._updateTarget() else: newValue = self._yaw.get() + factor self._yaw.set(newValue) self._updateTarget() def _onYawScaleChanged(self, eventArgs): self._updateTarget() def _onYawScaleDoubleButton1(self, eventArgs): self._yawReset() return "break" def _startedCBChanged(self): if not self._started.get(): self._throttle.set(0) self._thrustScale.config(state=DISABLED) #self._integralsCB.config(state=DISABLED) self._stopUpdateInfoThread() else: self._thrustScale.config(state="normal") #self._integralsCB.config(state="normal") self._startUpdateInfoThread() self._sendIsStarted() # def _integralsCBChanged(self): # # self._link.send({"key": "integrals", "data":self._integralsEnabled.get() != 0}) # def _onThrustScaleChanged(self, eventArgs): if Cockpit.THROTTLE_BY_USER: self._sendThrottle() else: self._updateTarget() def _sendThrottle(self): self._link.send({"key": "throttle", "data": self._throttle.get()}) def _sendTarget(self): self._link.send({"key": "target", "data": self._target}) def _sendIsStarted(self): isStarted = self._started.get() != 0 self._link.send({"key": "is-started", "data": isStarted}) def _sendPidCalibrationData(self): if self._pidSelected.get() != "--" and self._axisSelected.get( ) != "--": pidData = { "pid": self._pidSelected.get(), "axis": self._axisSelected.get(), "p": float(self._pidPSpinbox.get()), "i": float(self._pidISpinbox.get()), "d": float(self._pidDSpinbox.get()) } self._link.send({"key": "pid-calibration", "data": pidData}) def _updatePidCalibrationData(self): pid = self._pidSelected.get() axis = self._axisSelected.get() if pid != "--" and axis != "--": self._pidConstants[pid][axis]["P"] = float(self._pidPSpinbox.get()) self._pidConstants[pid][axis]["I"] = float(self._pidISpinbox.get()) self._pidConstants[pid][axis]["D"] = float(self._pidDSpinbox.get()) def _readDroneConfig(self): self._link.send({ "key": "read-drone-config", "data": None }, self._onDroneConfigRead) def _readDroneState(self): if not self._readingState: self._readingState = True self._link.send({ "key": "read-drone-state", "data": None }, self._onDroneStateRead) def _readPidConfigItem(self, message, cockpitKey, axises, configKeys): for i in range(len(axises)): for j in range(len(Cockpit.PID_KEYS)): self._pidConstants[cockpitKey][axises[i]][ Cockpit.PID_KEYS[j]] = message[configKeys[j]][i] def _onDroneConfigRead(self, message): #TODO Show current configuration within the GUI (at least relevant settings) if message: #Angle-speeds self._readPidConfigItem(message, Cockpit.KEY_ANG_SPEED, ["X", "Y", "Z"], \ [Configuration.PID_ANGLES_SPEED_KP, \ Configuration.PID_ANGLES_SPEED_KI, \ Configuration.PID_ANGLES_SPEED_KD]) #Angles self._readPidConfigItem(message, Cockpit.KEY_ANGLES, ["X", "Y"], \ [Configuration.PID_ANGLES_KP, \ Configuration.PID_ANGLES_KI, \ Configuration.PID_ANGLES_KD]) #Accels self._readPidConfigItem(message, Cockpit.KEY_ACCEL, ["X", "Y", "Z"], \ [Configuration.PID_ACCEL_KP, \ Configuration.PID_ACCEL_KI, \ Configuration.PID_ACCEL_KD]) def _onDroneStateRead(self, state): if state: for index in range(4): self._throttleTexts[index].set("{0:.3f}".format( state["_throttles"][index])) for index in range(3): self._accelTexts[index].set("{0:.3f}".format( state["_accels"][index])) self._angleTexts[index].set("{0:.3f}".format( state["_angles"][index])) currentPeriod = state["_currentPeriod"] if currentPeriod > 0.0: freq = 1.0 / currentPeriod self._loopRateText.set("{0:.3f}".format(freq)) else: self._loopRateText.set("--") else: self._stopUpdateInfoThread() self._readingState = False def _onPidSpinboxChanged(self): self._updatePidCalibrationData() self._sendPidCalibrationData() def _onPidListBoxChanged(self, pid): self._axisSelected.set("--") self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._selectedPidConstats = pid if pid == "--": self._axisListBox.config(state=DISABLED) self._controlKeysLocked = False else: self._axisListBox.config(state="normal") self._controlKeysLocked = True def _onAxisListBoxChanged(self, axis): if axis == "--" or (self._selectedPidConstats == Cockpit.KEY_ANGLES and axis == "Z"): self._pidPString.set("--") self._pidIString.set("--") self._pidDString.set("--") self._pidPSpinbox.config(state=DISABLED) self._pidISpinbox.config(state=DISABLED) self._pidDSpinbox.config(state=DISABLED) self._controlKeysLocked = axis != "--" else: self._pidPString.set("{:.2f}".format( self._pidConstants[self._selectedPidConstats][axis]["P"])) self._pidIString.set("{:.2f}".format( self._pidConstants[self._selectedPidConstats][axis]["I"])) self._pidDString.set("{:.2f}".format( self._pidConstants[self._selectedPidConstats][axis]["D"])) self._pidPSpinbox.config(state="normal") self._pidISpinbox.config(state="normal") self._pidDSpinbox.config(state="normal") self._controlKeysLocked = True def _updateInfo(self): while self._updateInfoThreadRunning: self._readDroneState() time.sleep(1.0) def _startUpdateInfoThread(self): self._updateInfoThreadRunning = True if not self._updateInfoThread.isAlive(): self._updateInfoThread.start() def _stopUpdateInfoThread(self): self._updateInfoThreadRunning = False if self._updateInfoThread.isAlive(): self._updateInfoThread.join()
class ScanDialog(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.worker = None self.elapsed = 0 self.settings = Settings(self) # self.initUI() follows self.parent.title("Scan Images") self.pack(fill=BOTH, expand=1) r = 0 # current grid row Label(self, text="Name prefix:").grid(row=r, column=0) Label(self, text="Number suffix:").grid(row=r, column=1) r += 1 self.newName = StringVar() self.newName.set('Scan_') newName = Entry(self, textvariable=self.newName, width=60) newName.grid(row=1, column=0) newName.bind("<Return>", lambda event: self.scan()) newName.bind("<KP_Enter>", lambda event: self.scan()) newName.bind("<Escape>", lambda event: self.parent.destroy()) newName.focus_set() self.newNameEntry = newName self.numberSuffix = Spinbox(self, from_=1, to=999) self.numberSuffix.bind("<Return>", lambda event: self.scan()) self.numberSuffix.bind("<KP_Enter>", lambda event: self.scan()) self.numberSuffix.grid(row=r, column=1) r += 1 self.okButton = Button(self, text="Scan", command=self.scan, width=60, height=5) self.okButton.grid(row=r, column=0) cancelButton = Button(self, text="Cancel", command=self.parent.destroy) cancelButton.grid(row=r, column=1) r += 1 settings_panel = tk.Frame(self) panel = tk.Frame(settings_panel) tk.Label(panel, text="Paper Format").pack() tk.Radiobutton(panel, text="A4", value=1.0, variable=self.settings.scale).pack(anchor=tk.W) tk.Radiobutton(panel, text="A5", value=2 ** (-0.5), variable=self.settings.scale).pack(anchor=tk.W) tk.Radiobutton(panel, text="A6", value=0.5, variable=self.settings.scale).pack(anchor=tk.W) panel.pack(side=tk.LEFT, anchor=tk.N) panel = tk.Frame(settings_panel) tk.Label(panel, text="File Format").pack() tk.Radiobutton(panel, text="PNG", value='.png', variable=self.settings.extension).pack(anchor=tk.W) tk.Radiobutton(panel, text="JPG", value='.jpg', variable=self.settings.extension).pack(anchor=tk.W) panel.pack(side=tk.LEFT, anchor=tk.N) panel = tk.Frame(settings_panel) tk.Label(panel, text="Scan Mode").pack() tk.Radiobutton(panel, text="Color", value='color', variable=self.settings.scan_mode).pack(anchor=tk.W) tk.Radiobutton(panel, text="Gray", value='gray', variable=self.settings.scan_mode).pack(anchor=tk.W) tk.Radiobutton(panel, text="Lineart", value='lineart', variable=self.settings.scan_mode).pack(anchor=tk.W) panel.pack(side=tk.LEFT, anchor=tk.N) settings_panel.grid(row=r, column=0, columnspan=2) r += 1 self.statusLabel = Label(self, text="Idle") self.statusLabel.grid(row=r, column=0, columnspan=2) def _checkAlive(self): if self.worker is None: return if self.worker.is_alive(): self.after(100, self._checkAlive) self.elapsed += 1 self.statusLabel.config(text='Scanning, please wait... (%.1f s)' % (self.elapsed/10.0)) else: self.worker = None self.okButton.config(state=NORMAL) self.numberSuffix.invoke('buttonup') self.newNameEntry.focus_set() self.statusLabel.config(text='Idle (last scan: %.1f s)' % (self.elapsed/10.0)) def _ext(self): return self.settings.ext() def scan(self): target = '%s%03d%s' % (self.newName.get(), int(self.numberSuffix.get()), self._ext(), ) if os.path.exists(target): if not tkMessageBox.askokcancel(title='Scan Images', message='File exists. Overwrite?'): print 'Not scanning: %s - file exists!' % target new_name = self.newName.get() for i in xrange(int(self.numberSuffix.get()), 1000): new_target = '%s%03d.%s' % (new_name, int(self.numberSuffix.get()), self._ext(), ) if not os.path.exists(new_target): print 'Next available filename: %s' % (new_target, ) self.numberSuffix.delete(0, 'end') self.numberSuffix.insert(0, i) break return print "Scanning to filename '%s' ..." % (target, ) if s is None: print('No scanner present. Connect and restart application.') return if self.worker is None: self.worker = ScanWorker(target, self.settings) self.worker.start() self.elapsed = 0 self.after(100, self._checkAlive) self.okButton.config(state=DISABLED) self.statusLabel.config(text='Scanning, please wait...') else: print "Error - not started, worker exists."
class SettingWindow(Toplevel): def __init__(self, master, max_num_features, num_frames, mser_image): Toplevel.__init__(self, master) self.protocol('WM_DELETE_WINDOW', self.withdraw) self.notebook = ttk.Notebook(self) frame_feats = ttk.Frame(self.notebook) frame_forest = ttk.Frame(self.notebook) frame_mser = ttk.Frame(self.notebook) frame_other = ttk.Frame(self.notebook) self.notebook.add(frame_feats, text="Features ") self.notebook.add(frame_forest, text=" Forest ") self.notebook.add(frame_mser, text=" MSER ") self.notebook.add(frame_other, text=" Other ") self.max_num_feats = max_num_features self.selection = None self.mser_image = mser_image rand_row = random.randint(1, 512-200) rand_col = random.randint(1, 512-110) self.mser_area = mser_image[rand_row:rand_row+180, rand_col:rand_col+100] # read images from icons folder self.hf0_img = PhotoImage(file="./icons/hf0.gif") self.hf1_img = PhotoImage(file="./icons/hf1.gif") self.hf2_img = PhotoImage(file="./icons/hf2.gif") self.hf3_img = PhotoImage(file="./icons/hf3.gif") self.hf4_img = PhotoImage(file="./icons/hf4.gif") self.hf5_img = PhotoImage(file="./icons/hf5.gif") self.features_vars = list() for i in range(max_num_features): self.features_vars.append(IntVar()) Label(frame_feats, text="Patch size (" + u"\N{GREEK SMALL LETTER PI}" + "):").grid(row=0, column=0, pady=5) self.patch_size_spinbox = Spinbox(frame_feats, from_=3, to=30, width=3) self.patch_size_spinbox.delete(0, END) self.patch_size_spinbox.insert(END, 10) self.patch_size_spinbox.grid(row=0, column=1, padx=5) f1 = ttk.Labelframe(frame_feats, text='Mean filter') f1.grid(row=1, columnspan=2) Label(f1, text=u"\N{GREEK SMALL LETTER PI}").grid(row=0, column=0) Checkbutton(f1, text="R", variable=self.features_vars[0]).grid(row=0, column=1) Checkbutton(f1, text="G", variable=self.features_vars[1]).grid(row=0, column=2) Checkbutton(f1, text="B", variable=self.features_vars[2]).grid(row=0, column=3) Label(f1, text=u"\N{GREEK SMALL LETTER PI}" + "/2").grid(row=1, column=0) Checkbutton(f1, text="R", variable=self.features_vars[3]).grid(row=1, column=1) Checkbutton(f1, text="G", variable=self.features_vars[4]).grid(row=1, column=2) Checkbutton(f1, text="B", variable=self.features_vars[5]).grid(row=1, column=3) f2 = ttk.Labelframe(frame_feats, text="Gaussian filter") f2.grid(row=2, columnspan=2) Label(f2, text=str(1.0)).grid(row=0, column=0) Checkbutton(f2, text="R", variable=self.features_vars[6]).grid(row=0, column=1) Checkbutton(f2, text="G", variable=self.features_vars[7]).grid(row=0, column=2) Checkbutton(f2, text="B", variable=self.features_vars[8]).grid(row=0, column=3) Label(f2, text=str(3.5)).grid(row=1, column=0) Checkbutton(f2, text="R", variable=self.features_vars[9]).grid(row=1, column=1) Checkbutton(f2, text="G", variable=self.features_vars[10]).grid(row=1, column=2) Checkbutton(f2, text="B", variable=self.features_vars[11]).grid(row=1, column=3) f3 = ttk.Labelframe(frame_feats, text="Laplacian of gaussian") f3.grid(row=3, columnspan=2) Label(f3, text=str(2.0)).grid(row=0, column=0) Checkbutton(f3, text="R", variable=self.features_vars[12]).grid(row=0, column=1) Checkbutton(f3, text="G", variable=self.features_vars[13]).grid(row=0, column=2) Checkbutton(f3, text="B", variable=self.features_vars[14]).grid(row=0, column=3) Label(f3, text=str(3.5)).grid(row=1, column=0) Checkbutton(f3, text="R", variable=self.features_vars[15]).grid(row=1, column=1) Checkbutton(f3, text="G", variable=self.features_vars[16]).grid(row=1, column=2) Checkbutton(f3, text="B", variable=self.features_vars[17]).grid(row=1, column=3) f4 = ttk.Labelframe(frame_feats, text="Haar-like features") f4.grid(row=1, rowspan=2, column=3, padx=5) Checkbutton(f4, image=self.hf0_img, variable=self.features_vars[18]).grid(row=0, column=0) Checkbutton(f4, image=self.hf1_img, variable=self.features_vars[19]).grid(row=0, column=1) Checkbutton(f4, image=self.hf2_img, variable=self.features_vars[20]).grid(row=1, column=0) Checkbutton(f4, image=self.hf3_img, variable=self.features_vars[21]).grid(row=1, column=1) Checkbutton(f4, image=self.hf4_img, variable=self.features_vars[22]).grid(row=2, column=0) Checkbutton(f4, image=self.hf5_img, variable=self.features_vars[23]).grid(row=2, column=1) buttons_paned_window = PanedWindow(frame_feats, orient=VERTICAL) buttons_paned_window.grid(row=3, column=3) self.select_all_button = Button(buttons_paned_window, text="Select all", command=self._select_all) buttons_paned_window.add(self.select_all_button) self.clear_selection_button = Button(buttons_paned_window, text="Clear selection", command=self._clear_selection) buttons_paned_window.add(self.clear_selection_button) # default values for j in [0, 1, 3, 6, 7, 9, 15, 21, 23]: self.features_vars[j].set(1) # FOREST FRAMES # number of trees f5 = ttk.Labelframe(frame_forest, text="Number of trees") f5.grid(row=0, columnspan=2, pady=5, padx=5) Label(f5, text="N").grid(row=1, column=0) self.num_trees_scale = Scale(f5, from_=5, to=500, resolution=5, orient=HORIZONTAL) self.num_trees_scale.set(300) self.num_trees_scale.grid(row=0, column=1, rowspan=2) # depth single tree f6 = ttk.Labelframe(frame_forest, text="Depth single tree") f6.grid(row=1, columnspan=2, pady=5, padx=5) Label(f6, text="d").grid(row=1, column=0) self.depth_tree_scale = Scale(f6, from_=2, to=20, orient=HORIZONTAL) self.depth_tree_scale.set(3) self.depth_tree_scale.grid(row=0, column=1, rowspan=2) # percentage number of features f7 = ttk.Labelframe(frame_forest, text="% subset of features") f7.grid(row=2, columnspan=2, pady=5, padx=5) Label(f7, text="m").grid(row=1, column=0) self.percentage_feats_scale = Scale(f7, from_=0.0, to=1, resolution=0.05, orient=HORIZONTAL) self.percentage_feats_scale.set(0.5) self.percentage_feats_scale.grid(row=0, column=1, rowspan=2) # mser frame # delta f8 = ttk.Labelframe(frame_mser, text="Delta") f8.grid(row=0, columnspan=2, pady=5, padx=5) Label(f8, text=u"\N{GREEK SMALL LETTER DELTA}").grid(row=1, column=0) self.delta_scale = Scale(f8, from_=1, to=10, resolution=1, orient=HORIZONTAL) self.delta_scale.set(2) self.delta_scale.grid(row=0, column=1, rowspan=2) # min area f9 = ttk.Labelframe(frame_mser, text="Minimum area") f9.grid(row=1, columnspan=2, pady=5, padx=5) Label(f9, text="m").grid(row=1, column=0) self.min_area_scale = Scale(f9, from_=2, to=200, orient=HORIZONTAL) self.min_area_scale.set(10) self.min_area_scale.grid(row=0, column=1, rowspan=2) # percentage number of features f10 = ttk.Labelframe(frame_mser, text="Maximum area") f10.grid(row=2, columnspan=2, pady=5, padx=5) Label(f10, text="M").grid(row=1, column=0) self.max_area_scale = Scale(f10, from_=50, to=1000, resolution=5, orient=HORIZONTAL) self.max_area_scale.set(350) self.max_area_scale.grid(row=0, column=1, rowspan=2) # mser image f11 = ttk.Labelframe(frame_mser) f11.grid(row=0, rowspan=3, column=2, padx=5) self.mser_img_array = Image.fromarray(self.mser_area, "RGB") self.mser_img = ImageTk.PhotoImage(self.mser_img_array) img_label = Label(f11, image=self.mser_img) img_label.grid(row=0, column=0) buttons_p_w_mser = PanedWindow(f11, orient=HORIZONTAL) try_button = Button(f11, text="Try", command=self.try_mser) buttons_p_w_mser.add(try_button) change_button = Button(f11, text="New img", command=self.change_mser) buttons_p_w_mser.add(change_button) buttons_p_w_mser.grid(row=1, column=0) # other frame f12 = ttk.Labelframe(frame_other, text="Refinement") f12.grid(row=0, columnspan=2, pady=5, padx=5) Label(f12, text=u"\N{GREEK CAPITAL LETTER PHI}_l").grid(row=1, column=0) self.low_thresh_scale = Scale(f12, from_=0, to=1, resolution=0.05, orient=HORIZONTAL, length=90) self.low_thresh_scale.set(0.45) self.low_thresh_scale.grid(row=0, column=1, rowspan=2) Label(f12, text=u"\N{GREEK CAPITAL LETTER PHI}_h").grid(row=3, column=0) self.high_thresh_scale = Scale(f12, from_=0, to=1, resolution=0.05, orient=HORIZONTAL, length=90) self.high_thresh_scale.set(0.65) self.high_thresh_scale.grid(row=2, column=1, rowspan=2) f13 = ttk.Labelframe(frame_other, text="Dots distance") f13.grid(row=1, columnspan=2, pady=5, padx=5) Label(f13, text=u" \N{GREEK SMALL LETTER SIGMA}").grid(row=1, column=0) self.dots_distance_scale = Scale(f13, from_=1, to=20, resolution=1, orient=HORIZONTAL, length=90) self.dots_distance_scale.set(6) self.dots_distance_scale.grid(row=0, column=1, rowspan=2) f14 = ttk.Labelframe(frame_other, text="Tracks") f14.grid(row=0, column=3, pady=5, padx=5) Label(f14, text="N").grid(row=1, column=0) self.num_frames_tracks_spinbox = Spinbox(f14, from_=2, to=num_frames, width=10) self.num_frames_tracks_spinbox.delete(0, END) self.num_frames_tracks_spinbox.insert(END, num_frames) self.num_frames_tracks_spinbox.grid(row=0, column=1, rowspan=2) Label(f14, text=u"\N{GREEK SMALL LETTER TAU}").grid(row=3, column=0) self.gaps_scale = Scale(f14, from_=1, to=10, resolution=1, orient=HORIZONTAL, length=90) self.gaps_scale.set(2) self.gaps_scale.grid(row=2, column=1, rowspan=2) self.notebook.pack(padx=1, pady=1) save_button = Button(self, text=" Save and Close window ", command=self.withdraw) save_button.pack(pady=2) def _select_all(self): for i, var in enumerate(self.features_vars): var.set(1) def _clear_selection(self): for i, var in enumerate(self.features_vars): var.set(0) def change_mser(self): rand_row = random.randint(1, 512-200) rand_col = random.randint(1, 512-110) self.mser_area = self.mser_image[rand_row:rand_row+180, rand_col:rand_col+100] self.update_mser_image(self.mser_area) def try_mser(self): delta = self.delta_scale.get() min_area = self.min_area_scale.get() max_area = self.max_area_scale.get() image = self.mser_area red_c = image[:,:,0] red_c = cv2.equalizeHist(red_c) det_img = image.copy() mser = cv2.MSER(delta, _min_area=min_area, _max_area=max_area) regions = mser.detect(red_c) cp = list() new_c = np.zeros(self.mser_area.shape, dtype=np.uint8) for r in regions: for point in r: cp.append(point) det_img[point[1], point[0], 0] = 0 det_img[point[1], point[0], 1] = 0 det_img[point[1], point[0], 2] = 204 #new_c[point[1], point[0]] = 255 self.update_mser_image(det_img) def update_mser_image(self, new_image): self.mser_img_array = Image.fromarray(new_image) self.mser_img.paste(self.mser_img_array) def get_patch_size(self): patch_size = self.patch_size_spinbox.get() return int(patch_size) def get_num_frames_tracks(self): num_frames_tracks = self.num_frames_tracks_spinbox.get() return int(num_frames_tracks) def get_mser_opts(self): return [self.delta_scale.get(), self.min_area_scale.get(), self.max_area_scale.get()] def get_forest_opts(self): return [self.num_trees_scale.get(), self.depth_tree_scale.get(), self.percentage_feats_scale.get()] def get_low_thresh(self): return self.low_thresh_scale.get() def get_high_thresh(self): return self.high_thresh_scale.get() def get_dots_distance(self): return int(self.dots_distance_scale.get()) def get_selection_mask(self): if self.selection is not None: return self.selection selection_mask = np.zeros((self.max_num_feats, ), dtype='bool') for i, var in enumerate(self.features_vars): selection_mask[i] = var.get() self.selection = selection_mask return selection_mask
def ventanaImprimir(self): t = Toplevel(self) t.wm_title("Imprimir") Label(t, text="Numero de Copias por etiqueta").pack() w = Spinbox(t, from_=1, to=10) w.pack() buttonImprimir = Button(t, text="Imprimir", command=lambda:self.imprimir(int(w.get()),t)) buttonImprimir.pack()
class MacroEdit(Toplevel): def __init__(self, root, prtr, settings, log, fn, text, *arg): Toplevel.__init__(self, root, *arg) self.title("Macro Editor") self.fn = fn self.app = root self.settings = settings self.printer = prtr self.log = log self.macroList = self.settings.getMacroList() self.macroEntry = None if fn == None: title = "New macro" self.macroTitle = None self.newMacro = True else: self.newMacro = False self.macroTitle = title = os.path.basename(fn) for m in self.macroList: if self.macroTitle == m[MNAME]: self.macroEntry = m break self.f = LabelFrame(self, text=title) self.entry = Text(self.f, width=80, height=24, relief=RIDGE, bd=2) self.entry.grid(row=1, column=1) self.f.grid(row=1, column=1, columnspan=6) self.bSave = Button(self, text="Save", width=20, command=self.doSave) self.bSave.grid(row=2, column=1) self.bExit = Button(self, text="Exit", width=20, command=self.doExit) self.bExit.grid(row=2, column=2) self.cbvAddButton = IntVar() self.cbAddButton = Checkbutton(self, text="Add Button", variable=self.cbvAddButton, command=self.doAddButton) self.cbAddButton.grid(row=2, column=3) self.buttonText = Entry(self, width=12) self.buttonText.grid(row=2, column=4) l = Label(self, text="Column:", justify=RIGHT) l.grid(row=2, column=5, sticky=E) self.spCol = Spinbox(self, values=[1,2,3], width=12, justify=RIGHT) self.spCol.grid(row=2, column=6) l = Label(self, text="Row:", justify=RIGHT) l.grid(row=3, column=5, sticky=E) self.spRow = Spinbox(self, values=[1,2,3,4,5,6,7,8,9,10], width=12, justify=RIGHT) self.spRow.grid(row=3, column=6) if self.macroEntry != None: self.cbvAddButton.set(1) self.spRow.delete(0, END) self.spRow.insert(0, self.macroEntry[MROW]) self.spCol.delete(0, END) self.spCol.insert(0, self.macroEntry[MCOL]) self.buttonText.delete(0, END) self.buttonText.insert(0, self.macroEntry[MTEXT]) self.initialButtonInfo = [1, self.macroEntry[MCOL], self.macroEntry[MROW], self.macroEntry[MTEXT]] else: self.cbvAddButton.set(0) self.spRow.delete(0, END) self.spRow.insert(0, 1) self.spCol.delete(0, END) self.spCol.insert(0, 1) self.buttonText.delete(0, END) self.initialButtonInfo = [0, 1, 1, ""] self.doAddButton() self.startText = text self.entry.delete("1.0", END) self.entry.insert(END, self.startText) self.entry.edit_modified(False) self.grab_set() self.app.wait_window(self) def doAddButton(self): if self.cbvAddButton.get() == 1: self.buttonText.config(state=NORMAL) self.spRow.config(state=NORMAL) self.spCol.config(state=NORMAL) else: self.buttonText.config(state=DISABLED) self.spRow.config(state=DISABLED) self.spCol.config(state=DISABLED) def buttonInfoChanged(self): if self.cbvAddButton.get() != self.initialButtonInfo[0]: return True if self.initialButtonInfo[1] != int(self.spCol.get()): return True if self.initialButtonInfo[2] != int(self.spRow.get()): return True return self.initialButtonInfo[3] != self.buttonText.get() def doSave(self): if self.cbvAddButton.get() == 1 and self.buttonInfoChanged(): c = int(self.spCol.get()) r = int(self.spRow.get()) for m in self.macroList: if c == m[MCOL] and r == m[MROW]: if self.newMacro or self.macroTitle != m[MNAME]: showerror("Duplicate Location", "That position is already in use by another macro", parent=self) return buttonInfo = [c, r, self.buttonText.get()] else: buttonInfo = None if self.entry.edit_modified() or self.buttonInfoChanged(): l = self.entry.get("1.0", END).rstrip() if self.app.macroEditSave(self.fn, l, buttonInfo, self): self.entry.edit_modified(False) self.initialButtonInfo[0] = self.cbvAddButton.get() self.initialButtonInfo[1] = int(self.spCol.get()) self.initialButtonInfo[2] = int(self.spRow.get()) self.initialButtonInfo[3] = self.buttonText.get() def doExit(self): if self.entry.edit_modified() or self.buttonInfoChanged(): if not tkMessageBox.askyesno("Exit?", "Are you sure you want to exit and lose changes?", parent=self): return self.app.macroEditExit(self.fn) self.destroy()
class Outlier4dfp(Frame): def __init__(self, parent, filename=None, filename_roi=None, filename_csv=None, dirname=None, obj_return_value=None): Frame.__init__(self, parent) self.parent = parent self.obj_return_value = obj_return_value # check filenames if filename is None or not os.path.isfile(filename): if dirname is not None: filename = tkFileDialog.askopenfilename(initialdir=dirname) else: filename = tkFileDialog.askopenfilename() if not os.path.isfile(filename): parent.destroy() return if filename_roi is None or not os.path.isfile(filename_roi): if dirname is not None: filename_roi = tkFileDialog.askopenfilename(initialdir=dirname) else: filename_roi = tkFileDialog.askopenfilename() if not os.path.isfile(filename_roi): parent.destroy() return if dirname is None: self.dirname = os.path.dirname(filename) else: self.dirname = dirname if filename_csv is None: filename_csv = os.path.join( self.dirname, filename_wo_ext(os.path.basename(filename)) + '.csv') self.filename = filename self.filename_roi = filename_roi self.filename_csv = filename_csv self.dat = nib.load(filename).get_data() self.shape = self.dat.shape print self.shape dz = self.shape[2] df = self.shape[-1] self.badenc = np.zeros((dz, df), dtype=np.int16) self.prev = np.zeros((dz, df), dtype=np.int16) self.z = 0 self.initUI() self.run() #if filename_csv is not None and os.path.isfile(filename_csv): # self.run_read() def reset(self): dz = self.shape[2] df = self.shape[-1] self.badenc[:, :] = np.zeros((dz, df), dtype=np.int16) self.prev[:, :] = np.zeros((dz, df), dtype=np.int16) def make_checkbox_all(self, frame, width=4, ncol=20): self.lst_checkbox_slices_values = [] self.lst_checkbox_slices = [] ii = 0 for z in range(self.shape[2]): self.lst_checkbox_slices_values.append( [BooleanVar() for f in range(self.shape[3])]) boxes = [ Checkbutton(frame, text=str('%s' % f), variable=self.lst_checkbox_slices_values[z][f], width=width) for f in range(self.shape[3]) ] jj = 0 for f in range(self.shape[3]): btn = boxes[f] btn.grid(row=z, column=f) jj += 1 if ncol is not None and ncol <= jj: ii += 1 jj = 0 self.lst_checkbox_slices.append(boxes) if jj > 0: ii += 1 def make_checkbox(self, frame, width=4, ncol=20): #self.lst_checkbox_slices_values = [BooleanVar() for f in range(self.shape[3])] self.lst_checkbox_slices_values = [ IntVar() for f in range(self.shape[3]) ] self.lst_checkbox_slices = [ Checkbutton(frame, text=str('%s' % f), variable=self.lst_checkbox_slices_values[f], width=width, command=functools.partial(self.click_check, f)) for f in range(self.shape[3]) ] ii = 0 jj = 0 for f in range(self.shape[3]): btn = self.lst_checkbox_slices[f] btn.grid(row=ii, column=jj) jj += 1 if ncol is not None and ncol <= jj: jj = 0 ii += 1 if jj > 0: ii += 1 def click_check(self, f): value = self.lst_checkbox_slices_values[f].get() if value == 1: value = 0 else: value = 1 self.lst_checkbox_slices_values[f].set(value) self.badenc[self.z, f] = value print self.z, f, self.badenc[self.z, f] def set_z(self, z=None): if z is None: z = self.z else: self.z = z for i in range(len(self.lst_checkbox_slices_values)): chkbox = self.lst_checkbox_slices_values[i] # FIXME # avail chkbox if self.prev[z, i] > 0 or self.badenc[z, i] > 0: to_check = True to_check = 1 self.lst_checkbox_slices[i].select() else: to_check = False to_check = 0 self.lst_checkbox_slices[i].deselect() chkbox.set(to_check) # avail def run(self): dat = self.dat roi = nib.load(self.filename_roi).get_data().astype(bool) print dat.shape print roi.shape dz = self.shape[2] df = self.shape[3] self.values = np.zeros((dz, df), dtype=dat.dtype) for z in range(dz): for f in range(df): self.values[z, f] = dat[:, :, z, f][roi[:, :, z]].mean() self.draw_slice() def draw_slice(self): z = self.z df = self.shape[3] print z #r = self.frame_graph.axes.boxplot(self.values.T) sorted_values = np.sort(self.values[z, :]) q1 = sorted_values[df / 4] q2 = sorted_values[df / 2] q3 = sorted_values[df - df / 4] iqr = q3 - q1 #if1 = q1 - 1.5*iqr #if2 = q3 + 1.5*iqr of1 = q1 - 3 * iqr of2 = q3 + 3 * iqr xx = np.arange(df) z_mean = self.values[z, :].mean() z_std = self.values[z, :].std() #z_min = self.values[z,:].min() #z_max = self.values[z,:].max() #self.frame_graph.axes.hold(False) self.frame_graph.axes.clear() self.frame_graph.axes.plot([0, df], [z_mean, z_mean], 'k-') #self.frame_graph.axes.hold(True) self.frame_graph.axes.plot([0, df], [z_mean + 1 * z_std, z_mean + 1 * z_std], 'y--') self.frame_graph.axes.plot([0, df], [z_mean + 2 * z_std, z_mean + 2 * z_std], 'g--') self.frame_graph.axes.plot([0, df], [z_mean + 3 * z_std, z_mean + 3 * z_std], 'b--') self.frame_graph.axes.plot([0, df], [of2, of2], 'r-') for f in range(df): if self.prev[z, f] > 0: self.frame_graph.axes.plot(f, self.values[z, f], 'ko') self.frame_graph.axes.text(f + 0.2, self.values[z, f], str(f)) elif of1 < self.values[z, f] < of2: #if self.values[z,f] < z_mean + 3*z_std: self.frame_graph.axes.plot(f, self.values[z, f], 'bo') if self.values[z, f] > z_mean + 3 * z_std: self.frame_graph.axes.text(f + 0.2, self.values[z, f], str(f)) else: self.frame_graph.axes.plot(f, self.values[z, f], 'ro') self.frame_graph.axes.text(f + 0.2, self.values[z, f], str(f)) self.frame_graph.draw() self.set_z(z) def run_read(self): filename = tkFileDialog.askopenfilename(initialdir=self.dirname) if filename == '': return with open(filename) as f: values = [[int(tmp) for tmp in line.strip().split(',')] for line in f.readlines()] self.prev[:, :] = values def run_save(self): filename = tkFileDialog.asksaveasfilename( initialdir=os.path.dirname(self.filename_csv), initialfile=os.path.basename(self.filename_csv)) if filename == '': return dz = self.shape[2] df = self.shape[-1] badenc = self.badenc.copy() badenc[self.prev > 0] = 1 with open(filename, 'w') as f: for z in range(dz): f.write('%s\n' % (','.join([str(tmp) for tmp in badenc[z, :]]))) with open(os.path.join(self.dirname, 'badenc.dat'), 'wb') as fout: if False: for z in range(dz): row = struct.pack('i' * df, *badenc[z, :]) fout.write(row) fout.write('%s %s\n' % (dz, df)) for z in range(dz): row = ' '.join(str(tmp) for tmp in badenc[z, :]) fout.write('%s\n' % row) if self.obj_return_value is not None: self.obj_return_value.delete(0, len(self.obj_return_value.get())) self.obj_return_value.insert(0, filename) def initUI(self): self.frame_top = Frame(self) self.frame_graph = MplCanvas(self) self.frame_bottom = Frame(self) Label(self.frame_top, text='Z = ').pack(side=LEFT) self.spin_z = Spinbox(self.frame_top, from_=0, to=self.shape[2] - 1, increment=1, command=self.change_z) self.spin_z.pack(side=LEFT) self.make_checkbox(self.frame_bottom, width=4) Label(self.frame_top, text=' CSV').pack(side=LEFT) self.txt_filename_csv = Entry(self.frame_top) self.txt_filename_csv.pack(side=LEFT) self.button_read = Button(self.frame_top, text='Read', command=self.run_read) self.button_read.pack(side=LEFT) self.button_save = Button(self.frame_top, text='Save', command=self.run_save) self.button_save.pack(side=LEFT) Label(self.frame_top, text=' ').pack(side=LEFT) button_reset = Button(self.frame_top, text='Reset', command=self.reset).pack(side=LEFT) self.frame_top.pack(side=TOP) self.frame_graph.get_tk_widget().pack(fill=BOTH, expand=TRUE) self.frame_bottom.pack(fill=BOTH, expand=TRUE) self.pack(fill=BOTH, expand=True) def change_z(self): self.z = int(self.spin_z.get()) self.draw_slice() def reset_box(self, box, text): box.delete(0, len(box.get())) box.insert(0, text)
class CreateMask(Frame): def __init__(self, parent, filename=None, dirname=None, mask4d=False, obj_return_value=None): Frame.__init__(self, parent) self.parent = parent self.obj_return_value = obj_return_value if dirname is None: self.dirname = '' else: self.dirname = dirname if filename is None or not os.path.isfile(filename): if dirname is not None: filename = tkFileDialog.askopenfilename(initialdir=dirname) else: filename = tkFileDialog.askopenfilename() if not os.path.isfile(filename): parent.destroy() return self.readImage(filename) _, _, filename_mask = create_mask.set_filenames(filename) self.filename_mask = filename_mask + '.nii.gz' if mask4d: if len(self.shape) > 3: self.mask_base = np.zeros(self.shape, dtype=np.int8) self.mask = self.mask_base[:, :, :, 0] self.mask4d = True else: self.mask = np.zeros(self.shape[:3], dtype=np.int8) self.mask4d = False self.volume = 0 self.initUI() self.modifyUI() self.drawSlice() def initUI(self): self.frame_bottom = Frame(self) self.frame_main = Frame(self) def modifyUI(self): frame_bottom = self.frame_bottom frame_main = self.frame_main self.sliceView = [ ImshowMplCanvas(frame_main) for k in range(self.shape[2]) ] self.sliceSpin = [ MySpinBox(frame_main, k) for k in range(self.shape[2]) ] #Layout self.parent.title('CreateMask') max_spin = 0 if self.mask4d: max_spin = self.shape[3] - 1 self.spin_volume = Spinbox(frame_bottom, from_=0, to=max_spin, increment=1, command=self.change_volume, width=8) self.reset_box(self.spin_volume, 0) buttonMask = Button(frame_bottom, text='B0 from Bval...', command=self.createBaseImage) buttonMaskCreate = Button(frame_bottom, text='Create', command=self.createMaskAll) buttonSave = Button(frame_bottom, text='Save', command=self.saveMask) self.drawmask = BooleanVar() buttonDrawMask = Checkbutton(frame_bottom, text='Draw Mask', variable=self.drawmask, command=self.drawSlice) num_col = 3 num_row = 6 for col in range(self.shape[2] / num_row): for k in range(num_row): ind = col * num_row + k if ind >= self.shape[2]: break self.sliceView[ind].get_tk_widget().grid(row=k, column=col * num_col, sticky=NSEW) self.sliceSpin[ind].grid(row=k, column=col * num_col + 1) self.frame_main.grid_columnconfigure(col * num_col, weight=1) for k in range(num_row): self.frame_main.grid_rowconfigure(k, weight=1) self.spin_volume.grid(row=0, column=0) buttonMask.grid(row=0, column=1) buttonMaskCreate.grid(row=0, column=2) buttonSave.grid(row=0, column=3) buttonDrawMask.grid(row=0, column=5) frame_bottom.pack(side=BOTTOM) frame_main.pack(fill=BOTH, expand=TRUE) self.pack(fill=BOTH, expand=True) def change_volume(self): if not self.mask4d: return self.volume = int(self.spin_volume.get()) self.data = self.data_base[:, :, :, self.volume] self.mask = self.mask_base[:, :, :, self.volume] self.drawSlice() def saveMask(self): print 'save mask to %s' % self.filename_mask if self.mask4d: img_out = nib.Nifti1Image(self.mask_base, self.img.get_affine(), self.img.get_header()) else: img_out = nib.Nifti1Image(self.mask, self.img.get_affine(), self.img.get_header()) nib.save(img_out, os.path.join(self.dirname, self.filename_mask)) if self.obj_return_value is not None: self.obj_return_value.delete(0, len(self.obj_return_value.get())) self.obj_return_value.insert(0, os.path.basename(self.filename_mask)) def readImage(self, filename): self.filename = filename self.img = nib.load(filename) self.shape = self.img.shape self.data_base = self.img.get_data() if len(self.data_base.shape) > 3: for f in range(self.data_base.shape[3]): for z in range(self.data_base.shape[2]): self.data_base[:, :, z, f] = ndimage.gaussian_filter( self.data_base[:, :, z, f], sigma=0.5) self.data = self.data_base[:, :, :, 0] else: for z in range(self.data_base.shape[2]): self.data_base[:, :, z] = ndimage.gaussian_filter( self.data_base[:, :, z], sigma=0.5) self.data = self.data_base def createBaseImage(self): bvalFilename = tkFileDialog.askopenfilename() if bvalFilename: b0frames = parameter.get_b0_from_bval(bvalFilename) print self.filename, b0frames filename, filename_fltd, filename_mask = create_mask.set_filenames( self.filename) create_mask.make_base_image(filename, filename_fltd + '.nii.gz', b0frames) self.filename_mask = filename_mask + '.nii.gz' self.readImage(filename_fltd + '.nii.gz') self.drawSlice() def setSpinBoxConnect(self): for k in range(self.shape[2]): #self.sliceSpin[k].valueChanged.connect(lambda: self.createMaskSlice(k)) #self.sliceSpin[k].configure(command=lambda:self.createMaskSlice(k, self.sliceSpin[k])) self.sliceSpin[k].configure(command=functools.partial( self.createMaskSlice, k, self.sliceSpin[k])) def drawSlice(self): for k in range(self.shape[2]): self.sliceView[k].drawData(self.data[:, :, k], self.mask[:, :, k], self.drawmask.get()) def reset_box(self, box, text): box.delete(0, len(box.get())) box.insert(0, text) def createMaskSlice(self, k, value=None): self.mask[:, :, k] = 0 if value is None: value_i = int(self.sliceSpin[k].get()) else: value_i = int(value.get()) #create_mask.mask_from_threshold(self.com_x[k], self.com_y[k], self.sliceSpin[k].value(), self.data, self.mask, k) create_mask.mask_from_threshold(self.com_x[k], self.com_y[k], value_i, self.data, self.mask, k) self.sliceView[k].drawData(self.data[:, :, k], self.mask[:, :, k], self.drawmask.get()) def createMaskAll(self): self.drawmask.set(True) hdr = self.img.get_header() zooms = hdr.get_zooms() shape = self.shape self.com_x, self.com_y, roi_x, roi_y = create_mask.calculate_com( self.data, zooms) for k in range(shape[2]): dat_subflt = self.data[roi_x[k][0]:roi_x[k][1], roi_y[k][0]:roi_y[k][1], k].flatten() #threshold = np.mean(dat_subflt) threshold = np.mean(dat_subflt) + np.std(dat_subflt) self.reset_box(self.sliceSpin[k], int(threshold)) self.createMaskSlice(k) #create_mask.mask_from_threshold(self.com_x[k], self.com_y[k], threshold, self.data, self.mask, k) #self.drawSlice() self.setSpinBoxConnect()
class pump_ui(object): def __init__(self): master = Tk() master.style = ttk.Style() master.style.theme_use("default") master.config(bg=background_colour) master.resizable( 0, 0 ) # Disable resizing the UI to prevent having to make widget placing dynamic master.winfo_toplevel().title(frame_title) master.iconbitmap("bitcoin.ico") # Create pumper assistant to store data on the current BTC and alt holdings. self.pumper = pumper() self.create_title(master) self.create_api_info(master, previous_row=0) self.create_auto_sell(master, previous_row=3) self.create_stop_loss(master, previous_row=4) self.create_order_type(master, previous_row=6) self.create_fee_type(master, previous_row=7) self.create_btc_balance_picker(master, previous_row=8) self.create_alt_ticker(master, previous_row=10) self.create_pump_and_sell_buttons(master, previous_row=11) self.create_current_profit(master, previous_row=12) self.create_output_box(master, rightmost_column=1) # Can hardcode API key and Secret #self.api_key_entry.delete(0,END) #self.api_key_entry.insert(0,"KEY") #self.api_key_entry.config(state=DISABLED) #self.api_secret_entry.delete(0, END) #self.api_secret_entry.insert(0, "SECRET") #self.api_secret_entry.config(state=DISABLED) # Display the UI, this can only be called once per program. # Nothing in the main Python script will be run after creating the UI because of this. master.mainloop() def create_title(self, master, previous_row=-1, previous_column=-1): empty = Label(master, text=frame_title) empty.grid(row=previous_row + 1, column=previous_column + 2, columnspan=1) empty.config(bg=background_colour, fg=label_font_colour) def create_api_info(self, master, previous_row=-1, previous_column=-1): api_key_lbl = Label(master, text="API Key:") api_key_lbl.grid(row=previous_row + 1, column=previous_column + 1, columnspan=1, sticky=E, padx=(3, 0)) api_key_lbl.config(bg=background_colour, fg=label_font_colour) self.api_key_entry = Entry(master, highlightthickness=0, bd=0, width=21, show="*") self.api_key_entry.config(borderwidth=2, relief=default_relief) self.api_key_entry.grid(row=previous_row + 1, column=previous_column + 2) api_secret_lbl = Label(master, text="API Secret:") api_secret_lbl.grid(row=previous_row + 2, column=previous_column + 1, columnspan=1, sticky=E, padx=(3, 0)) api_secret_lbl.config(bg=background_colour, fg=label_font_colour) self.api_secret_entry = Entry(master, highlightthickness=0, bd=0, width=21, show="*") self.api_secret_entry.config(borderwidth=2, relief=default_relief) self.api_secret_entry.grid(row=previous_row + 2, column=previous_column + 2) self.api_connect_btn = Button(master, text="Connect To Binance", command=self.on_connect_api) self.api_connect_btn.grid(row=previous_row + 3, column=previous_column + 2, columnspan=1, sticky=W + E, padx=10, pady=(0, 3)) self.api_connect_btn.config(highlightbackground=background_colour) def create_auto_sell(self, master, previous_row=-1, previous_column=-1): auto_sell_lbl = Label(master, text="Auto Sell (%):") auto_sell_lbl.grid(row=previous_row + 1, column=previous_column + 1, columnspan=1, sticky=E, padx=(3, 0)) auto_sell_lbl.config(bg=background_colour, fg=label_font_colour) self.auto_sell_spinbox = Spinbox(master, from_=1.0, to=300.0, increment=1.0, highlightbackground=background_colour) self.auto_sell_spinbox.config(borderwidth=2, relief=default_relief) self.auto_sell_spinbox.grid(row=previous_row + 1, column=previous_column + 2) self.auto_sell_spinbox.delete(0, "end") self.auto_sell_spinbox.insert(0, 50) def create_stop_loss(self, master, previous_row=-1, previous_column=-1): stop_loss_lbl = Label(master, text="Stop Loss (%):") stop_loss_lbl.grid(row=previous_row + 1, column=previous_column + 1, columnspan=1, sticky=E, padx=(3, 0)) stop_loss_lbl.config(bg=background_colour, fg=label_font_colour) self.stop_loss_spinbox = Spinbox(master, from_=-100.0, to=-10.0, increment=1.0, highlightbackground=background_colour) self.stop_loss_spinbox.config(borderwidth=2, relief=default_relief) self.stop_loss_spinbox.grid(row=previous_row + 1, column=previous_column + 2) self.stop_loss_spinbox.delete(0, "end") self.stop_loss_spinbox.insert(0, -10) def create_btc_balance_picker(self, master, previous_row=-1, previous_column=-1): self.btc_balance_str = StringVar() btc_balance_lbl = Label(master, textvar=self.btc_balance_str) btc_balance_lbl.grid(row=previous_row + 1, column=previous_column + 1, columnspan=2, sticky=W + E, padx=(3, 0)) btc_balance_lbl.config(bg=background_colour, fg=label_font_colour) self.set_available_btc_balance(Decimal(0)) btc_to_use_label = Label(master, text="BTC to spend:", bg=background_colour, fg=label_font_colour) btc_to_use_label.grid(row=previous_row + 2, column=previous_column + 1, sticky=E, padx=(3, 0)) self.btc_to_use_spinbox = Spinbox( master, from_=minimum_trade, to=minimum_trade, increment=btc_to_use_increment, highlightbackground=background_colour) self.btc_to_use_spinbox.config(borderwidth=2, relief=default_relief) self.btc_to_use_spinbox.grid(row=previous_row + 2, column=previous_column + 2) def create_order_type(self, master, previous_row=-1, previous_column=-1): order_type_lbl = Label(master, text="Entry Type:") order_type_lbl.grid(row=previous_row + 1, column=previous_column + 1, sticky=E, padx=(3, 0)) order_type_lbl.config(bg=background_colour, fg=label_font_colour) self.is_entry_market = True def change_order_type(*args): self.is_entry_market = (self.order_type.get() == "Market Buy \/") self.order_type = StringVar() self.order_type.trace( "w", change_order_type ) # Reduces how much work is done when the pump starts choices = {"Market Buy \/", "Limit Buy \/"} self.entry_type_option_menu = OptionMenu(master, self.order_type, *choices) self.entry_type_option_menu.grid(row=previous_row + 1, column=previous_column + 2, sticky=W + E, padx=8) self.entry_type_option_menu.config(highlightthickness=0) self.entry_type_option_menu.configure(indicatoron=0) self.order_type.set("Market Buy \/") def create_fee_type(self, master, previous_row=-1, previous_column=-1): fee_type_lbl = Label(master, text="Fee Type:") fee_type_lbl.grid(row=previous_row + 1, column=previous_column + 1, sticky=E, padx=(3, 0)) fee_type_lbl.config(bg=background_colour, fg=label_font_colour) self.is_using_bnb = True def change_fee_type(*args): self.is_using_bnb = ( self.order_type.get() == "Binance Coin (BNB) \/") self.fee_type = StringVar() self.fee_type.trace( "w", change_fee_type ) # Reduces how much work is done when the pump starts choices = {"Binance Coin (BNB) \/", "0.1% Of All Trades \/"} self.fee_type_option_menu = OptionMenu(master, self.fee_type, *choices) self.fee_type_option_menu.grid(row=previous_row + 1, column=previous_column + 2, sticky=W + E, padx=8) self.fee_type_option_menu.config(highlightthickness=0) self.fee_type_option_menu.configure(indicatoron=0) self.fee_type.set("Binance Coin (BNB) \/") def create_pump_and_sell_buttons(self, master, previous_row=-1, previous_column=-1): # Manual sell button can only be activated after initiating a pump. self.manual_sell_btn = Button(master, text="Sell", state=DISABLED, command=self.on_manual_sell) self.manual_sell_btn.grid(row=previous_row + 1, column=previous_column + 1, sticky=W + E, padx=(3, 0)) self.manual_sell_btn.config(highlightbackground=background_colour) self.pump_btn = Button(master, text="Pump", command=self.on_pump) self.pump_btn.grid(row=previous_row + 1, column=previous_column + 2, sticky=W + E, padx=8) self.pump_btn.config(highlightbackground=background_colour, state=DISABLED) def create_alt_ticker(self, master, previous_row=-1, previous_column=-1): ticker_lbl = Label(master, text="Ticker To Pump:") ticker_lbl.grid(row=previous_row + 1, column=previous_column + 1, columnspan=1, sticky=E, padx=(3, 0), pady=(0, 8)) ticker_lbl.config(bg=background_colour, fg=label_font_colour) self.ticker_entry = Entry(master, highlightthickness=0, bd=0, width=21) self.ticker_entry.config(borderwidth=2, relief=default_relief) self.ticker_entry.grid(row=previous_row + 1, column=previous_column + 2, pady=8) self.ticker_entry.bind('<Return>', self.on_pump_shortcut) def create_current_profit(self, master, previous_row=-1, previous_column=-1): self.current_profit_str = StringVar() current_profit_lbl = Label(master, textvar=self.current_profit_str) current_profit_lbl.grid(row=previous_row + 1, column=previous_column + 1, columnspan=2, sticky=W + E, padx=3, pady=(0, 3)) current_profit_lbl.config(bg=background_colour, fg=label_font_colour) self.current_profit_str.set("Current Profit: 0%") def create_output_box(self, master, rightmost_column): self.pump_output = StringVar() console_lbl = Label(master, textvar=self.pump_output, borderwidth=2, relief=default_relief, anchor=N) console_lbl.grid(row=0, column=rightmost_column + 1, columnspan=1, rowspan=14, padx=(10, 0), pady=0) console_lbl.config(width=50, height=22, bg="black", font=Font(family="Courier", size=9), fg="white") self.lines = 0 def disable_pre_pump_options(self): # Change the buttons that can be clicked to prevent the user # from trying to pump multiple coins with one bot. self.manual_sell_btn.config(state=NORMAL) self.pump_btn.config(state=DISABLED) self.btc_to_use_spinbox.config(state=DISABLED) self.ticker_entry.config(state=DISABLED) self.auto_sell_spinbox.config(state=DISABLED) self.stop_loss_spinbox.config(state=DISABLED) self.api_key_entry.config( state=DISABLED) # Comment out if hardcoding key self.api_secret_entry.config( state=DISABLED) # Comment out if hardcoding secret self.api_connect_btn.config(state=DISABLED) self.entry_type_option_menu.config(state=DISABLED) self.fee_type_option_menu.config(state=DISABLED) def enable_pump_options(self): # Change the buttons that can be clicked to prevent the user # from trying to pump multiple coins with one bot. self.manual_sell_btn.config(state=DISABLED) self.pump_btn.config(state=NORMAL) self.btc_to_use_spinbox.config(state=NORMAL) self.ticker_entry.config(state=NORMAL) self.auto_sell_spinbox.config(state=NORMAL) self.stop_loss_spinbox.config(state=NORMAL) self.api_key_entry.config( state=NORMAL) # Comment out if hardcoding key self.api_secret_entry.config( state=NORMAL) # Comment out if hardcoding secret self.api_connect_btn.config(state=NORMAL) self.entry_type_option_menu.config(state=NORMAL) self.fee_type_option_menu.config(state=NORMAL) def set_available_btc_balance(self, btc_balance): self.pumper.btc_balance = btc_balance self.btc_balance_str.set("Available Balance: " + readable_btc_balance(btc_balance)) def set_current_profit(self, current_profit): self.current_profit_str.set( "Current Profit: " + '{0:.3f}'.format(round(current_profit * Decimal(100), 3)) + "%") def write_to_console(self, line): self.lines += 1 if self.lines > max_lines_in_console: i = self.pump_output.get().index('\n') self.pump_output.set(self.pump_output.get()[i + 1:] + "\n" + line) elif self.lines == 1: self.pump_output.set(line) else: self.pump_output.set(self.pump_output.get() + "\n" + line) #### Button Behaviour #### def on_pump(self): try: api = self.api btc_to_use = Decimal(self.btc_to_use_spinbox.get()) except InvalidOperation: # The BTC to spend box is empty. self.write_to_console("Stop!") self.write_to_console("BTC to spend cannot be empty.") return except AttributeError: # There is no API object. self.write_to_console( "You need to connect to Binance before pumping.") return if btc_to_use >= minimum_trade: if btc_to_use <= self.pumper.btc_balance: target_profit_percentage = Decimal( float(self.auto_sell_spinbox.get()) / 100.0) # Validate auto-sell and stop loss if target_profit_percentage <= Decimal(0): self.write_to_console("Auto sell has to be positive.") return if Decimal(self.stop_loss_spinbox.get()) >= Decimal(0): self.write_to_console("Stop loss has to be negative.") return ticker = self.ticker_entry.get().upper() # Empty strings are False in Python if ticker: full_ticker = api.full_ticker_for(ticker) try: alt = self.api.get_ticker(symbol=full_ticker) except BinanceAPIException, e: logging.debug(str(e)) self.write_to_console("Invalid ticker.") return alt_value = Decimal(alt["askPrice"]) # Used in console output decimal_points = minimum_decimals_in_quantity.get( full_ticker, 0) self.pumper.decimal_points_in_alt = decimal_points self.pumper.set_up(btc_to_use, target_profit_percentage, alt_value, ticker) if self.is_entry_market: self.pumper.alt_holdings = api.market_buy( self.pumper.btc_to_use, full_ticker, self.is_using_bnb) self.write_to_console( "Bought " + readable_alt_balance( decimal_points, pumper=self.pumper) + " with " + readable_btc_balance(btc_to_use) + ".") else: highest_bid = Decimal(alt["bidPrice"]) if alt_value - highest_bid <= Decimal(0.00000001): to_bid = highest_bid else: # Bid between the highest bid and the lowest ask for the best odds of being filled. to_bid = (alt_value - highest_bid) / 2 + highest_bid to_bid = Decimal( floor(to_bid * Decimal(100000000.0)) ) / Decimal(100000000.0) self.pumper.starting_alt_value = to_bid expected = api.limit_buy( btc_to_alt(btc_to_use, alt_value), pumper, full_ticker, to_bid, self.is_using_bnb) self.write_to_console("Buying " + readable_alt_balance( decimal_points, alt_amount=expected, ticker=ticker ) + " for " + readable_btc_balance(btc_to_use) + ".") self.write_to_console( "This is a limit order, it may not get filled.") self.disable_pre_pump_options() self.set_stop_loss() self.start_monitoring_orderbook(full_ticker) else: # The user is trying to trade with more than they actually have. self.write_to_console("You did not enter a ticker.") else: # The user is trying to trade with more than they actually have. self.write_to_console("Stop!") self.write_to_console( "You are trying to spend more BTC than you have.") else:
class Temperatures(LabelFrame): def __init__(self, root, prtr, settings, log, *arg): LabelFrame.__init__(self, root, *arg, text="Temperatures") self.app = root self.printer = prtr self.settings = settings self.log = log l = Label(self, text="Extruder:", justify=LEFT) l.grid(row=1, column=1, sticky=W) self.statExt = Canvas(self, width=150, height=STAT_HEIGHT, bd=2, bg="white", relief=RIDGE) self.statExt.grid(row=1, column=2, columnspan=2) self.setStatus(self.statExt, UNKNOWN) self.bSetExt = Button(self, text="Set", width=4, command=self.doSetExt) self.bSetExt.grid(row=2, column=1, padx=2) self.spExtTemp = Spinbox(self, values=self.formatTemps(self.settings.extemps), width=12, justify=RIGHT) self.spExtTemp.grid(row=2, column=2) if self.settings.lastexttemp != None: self.spExtTemp.delete(0, END) self.spExtTemp.insert(0, self.settings.lastexttemp) self.spExtTemp.bind("<FocusOut>", self.valExtTemp) self.spExtTemp.bind("<Leave>", self.valExtTemp) self.bOffExt = Button(self, text="Off", width=4, command=self.doOffExt) self.bOffExt.grid(row=2, column=3, padx=2) self.frameExt = Frame(self) self.frameExt.grid(row=3, column=1, columnspan=3) self.legendExt = Canvas(self.frameExt, width=LEGEND_WIDTH, height=100, bd=2, bg="white", relief=RIDGE) self.legendExt.pack(side=LEFT, padx=0, pady=2) self.canvasExt = Canvas(self.frameExt, width=200, height=100, bd=2, bg="white", relief=RIDGE) self.canvasExt.pack(side=LEFT, padx=0, pady=2) self.drawExtAxes() self.extFan = IntVar() if self.settings.fanwithextruder: self.extFan.set(1) else: self.extFan.set(0) self.cb = Checkbutton(self, text="Fan on with extruder", variable=self.extFan, command=self.fanCheck) self.cb.grid(row=4, column=1, columnspan=3) self.forceExtTemp = IntVar() if self.settings.forceexttemp: self.forceExtTemp.set(1) else: self.forceExtTemp.set(0) self.forceExtTempCheck() self.bForceExt = Checkbutton(self, text="Force Ext Temp", variable=self.forceExtTemp, command=self.forceExtTempCheck) self.bForceExt.grid(row=5, column=1, columnspan=3) l = Label(self, text="Bed:", justify=LEFT) l.grid(row=6, column=1, sticky=W) self.statBed = Canvas(self, width=150, height=STAT_HEIGHT, bd=2, bg="white", relief=RIDGE) self.statBed.grid(row=6, column=2, columnspan=2) self.setStatus(self.statBed, UNKNOWN) self.bSetBed = Button(self, text="Set", width=4, command=self.doSetBed) self.bSetBed.grid(row=7, column=1, padx=2) self.spBedTemp = Spinbox(self, values=self.formatTemps(self.settings.bedtemps), width=12, justify=RIGHT) self.spBedTemp.grid(row=7, column=2) if self.settings.lastbedtemp != None: self.spBedTemp.delete(0, END) self.spBedTemp.insert(0, self.settings.lastbedtemp) self.spBedTemp.bind("<FocusOut>", self.valBedTemp) self.spBedTemp.bind("<Leave>", self.valBedTemp) self.bOffBed = Button(self, text="Off", width=4, command=self.doOffBed) self.bOffBed.grid(row=7, column=3, padx=2) self.frameBed = Frame(self) self.frameBed.grid(row=8, column=1, columnspan=3) self.legendBed = Canvas(self.frameBed, width=LEGEND_WIDTH, height=100, bd=2, bg="white", relief=RIDGE) self.legendBed.pack(side=LEFT, padx=0, pady=2) self.canvasBed = Canvas(self.frameBed, width=200, height=100, bd=2, bg="white", relief=RIDGE) self.canvasBed.pack(side=LEFT, padx=0, pady=2) self.drawBedAxes() self.dataBed = [] self.dataExt = [] self.forceBedTemp = IntVar() if self.settings.forcebedtemp: self.forceBedTemp.set(1) else: self.forceBedTemp.set(0) self.forceBedTempCheck() self.bForceBed = Checkbutton(self, text="Force Bed Temp", variable=self.forceBedTemp, command=self.forceBedTempCheck) self.bForceBed.grid(row=9, column=1, columnspan=3) self.monTemp = IntVar() self.monTemp.set(1) self.monCheck() self.cb = Checkbutton(self, text="Monitor temperatures", variable=self.monTemp, command=self.monCheck) self.cb.grid(row=10, column=1, columnspan=3) self.rptre = re.compile("ok *T:([0-9\.]+) *\/([0-9\.]+) *B:([0-9\.]+) *\/([0-9\.]+)") def fanCheck(self): self.settings.fanwithextruder = (self.extFan.get() == 1) self.settings.setModified() def forceBedTempCheck(self): if self.forceBedTemp.get() == 1: if not self.settings.forcebedtemp: self.settings.forcebedtemp = True self.settings.setModified() else: if self.settings.forcebedtemp: self.settings.forcebedtemp = False self.settings.setModified() def forceExtTempCheck(self): if self.forceExtTemp.get() == 1: if not self.settings.forceexttemp: self.settings.forceexttemp = True self.settings.setModified() else: if self.settings.forceexttemp: self.settings.forceexttemp = False self.settings.setModified() def monCheck(self): if self.monTemp.get() == 1: self.app.monitorTemp = True self.printer.tempcb = self.tempcb else: self.app.monitorTemp = False self.dataBed = [] self.canvasBed.delete("GRAPH") self.canvasBed.delete("TARGET") self.dataExt = [] self.canvasExt.delete("GRAPH") self.canvasExt.delete("TARGET") self.printer.tempcb = None self.setStatus(self.statExt, UNKNOWN) self.setStatus(self.statBed, UNKNOWN) def tempcb(self, l): m = self.rptre.search(l) t = m.groups() if len(t) != 4: return self.app.exttemp = float(t[0]) if self.settings.forceexttemp: tp = float(t[1]) if tp >= 0.05 and tp < self.app.exttarget: self.log.logMsg("Asserting hot end temperature of %.2f" % self.app.exttarget) self.printer.send_now("M104 S%s" % self.app.exttarget) else: self.app.exttarget = tp else: self.app.exttarget = float(t[1]) self.app.bedtemp = float(t[2]) if self.settings.forcebedtemp: tp = float(t[3]) if tp >= 0.05 and tp < self.app.bedtarget: self.log.logMsg("Asserting bed temperature of %.2f" % self.app.bedtarget) self.printer.send_now("M140 S%s" % self.app.bedtarget) else: self.app.bedtarget = tp else: self.app.bedtarget = float(t[3]) self.updateTempDisplay(float(t[2]), float(t[0])) def updateTempDisplay(self, tBed, tExt): self.addBedDatapoint(tBed) self.addExtDatapoint(tExt) if self.app.exttarget < 0.05: # unit is off if self.app.exttemp > 35: self.setStatus(self.statExt, COOLING) else: self.setStatus(self.statExt, COOL) else: # unit is on if self.app.exttarget - self.app.exttemp < 0.05: self.setStatus(self.statExt, HOT) else: self.setStatus(self.statExt, HEATING) if self.app.bedtarget < 0.05: # unit is off if self.app.bedtemp > 35: self.setStatus(self.statBed, COOLING) else: self.setStatus(self.statBed, COOL) else: # unit is on if self.app.bedtarget - self.app.bedtemp < 0.05: self.setStatus(self.statBed, HOT) else: self.setStatus(self.statBed, HEATING) def formatTemps(self, t): def cmptemp(a, b): ta = int(a.split()[0]) tb = int(b.split()[0]) return cmp(ta, tb) result = [x for x in t] return sorted(result, cmptemp) def doSetExt(self): if self.app.printerAvailable(cmd="M104"): if self.settings.fanwithextruder and self.app.FanSpeed != 255: if self.settings.forcefanspeed: self.logger.logMsg("Asserting fan speed of %d" % self.app.FanSpeed) else: self.app.toolbar.setFanSpeed(255) temp = self.spExtTemp.get().split(' ')[0] self.printer.send_now("M104 S%s" % temp) self.app.exttarget = float(temp) def valExtTemp(self, *arg): invalid = False try: choice = self.spExtTemp.get() x = int(choice.split(' ')[0]) except: self.log.logMsg("Value for Extruder temperature not a valid integer") invalid = True else: if x <=0 or x >MAXEXTTEMP: self.log.logMsg("Value for Extruder temperature out of range(0-%d" % MAXEXTTEMP) invalid = True if invalid: self.spExtTemp.delete(0, END) self.spExtTemp.insert(0, "%s" % self.formatTemps(self.settings.extemps)[0]) else: if choice not in self.settings.extemps: self.settings.extemps.append(choice) self.settings.setModified() self.spExtTemp.config(values=self.formatTemps(self.settings.extemps)) self.spExtTemp.delete(0, END) self.spExtTemp.insert(0, choice) if self.settings.lastexttemp != choice: self.settings.lastexttemp = choice self.settings.setModified() return True def doOffExt(self, *arg): if self.app.printerAvailable(cmd="M104"): if self.settings.fanwithextruder and self.app.FanSpeed != 0: if self.settings.forcefanspeed: self.logger.logMsg("Asserting fan speed of %d" % self.app.FanSpeed) else: self.app.toolbar.setFanSpeed(0) self.printer.send_now("M104 S0") def drawExtAxes(self): self.canvasExt.delete("AXES") for i in range(0, MAXEXTTEMP, 50): y = 100.0-(float(i)/MAXEXTTEMP*100.0) self.canvasExt.create_line(0, y, 200, y, fill="#C0C0C0", tags="AXES") self.legendExt.create_text(LEGEND_WIDTH, y+7, anchor=SE, tags="AXES", text="%3d" % i) def addExtDatapoint(self, t): c = (t/MAXEXTTEMP)*100.0 self.dataExt.append(c) while len(self.dataExt) >= MAXDATAPOINTS: del(self.dataExt[0]) self.canvasExt.delete("TARGET") self.canvasExt.delete("GRAPH") target = int((self.app.exttarget/MAXEXTTEMP)*100.0) if target != 0: self.canvasExt.create_line(0, 100-target, 200, 100-target, fill="blue", tags="TARGET") self.canvasExt.create_text(10, 100-target, anchor=SW, tags="TARGET", fill="blue", text="%3.3d" % int(self.app.exttarget)) start = MAXDATAPOINTS - len(self.dataExt) pTemp = 0 for i in range(start, MAXDATAPOINTS): if i != start: self.canvasExt.create_line(i*5, 100-pTemp, (i+1)*5, 100-self.dataExt[i-start], fill="red", tags="GRAPH") pTemp = self.dataExt[i-start] self.canvasExt.create_text(190, 100-c, anchor=SE, tags="GRAPH", fill="red", text="%3.3d" % int(t)) def setStatus(self, w, status): if status == UNKNOWN: c = "black" s = "??" elif status == HEATING: c = "orange" s = "heating" elif status == HOT: c = "red" s = "hot" elif status == COOLING: c = "blue" s = "cooling" elif status == COOL: c = "green" s = "cool" else: c = "black" s = "??" w.config(bg=c) w.delete(ALL) w.create_text(75, STAT_HEIGHT-5, text=s, fill="white") def doSetBed(self): if self.app.printerAvailable(cmd="M140"): temp = self.spBedTemp.get().split(' ')[0] self.printer.send_now("M140 S%s" % temp) self.app.bedtarget = float(temp) def valBedTemp(self, *arg): invalid = False try: choice = self.spBedTemp.get() x = int(choice.split(' ')[0]) except: self.log.logMsg("Value for Bed temperature not a valid integer") invalid = True else: if x <=0 or x >MAXEXTTEMP: self.log.logMsg("Value for Bed temperature out of range(0-%d" % MAXBEDTEMP) invalid = True if invalid: self.spBedTemp.delete(0, END) self.spBedTemp.insert(0, "%s" % self.formatTemps(self.settings.bedtemps)[0]) else: if choice not in self.settings.bedtemps: self.settings.bedtemps.append(choice) self.settings.setModified() self.spBedTemp.config(values=self.formatTemps(self.settings.bedtemps)) self.spBedTemp.delete(0, END) self.spBedTemp.insert(0, choice) if self.settings.lastbedtemp != choice: self.settings.lastbedtemp = choice self.settings.setModified() return True def doOffBed(self, *arg): if self.app.printerAvailable(cmd="M140"): self.printer.send_now("M140 S0") def addBedDatapoint(self, t): c = (t/MAXBEDTEMP)*100.0 self.dataBed.append(c) while len(self.dataBed) >= MAXDATAPOINTS: del(self.dataBed[0]) self.canvasBed.delete("TARGET") self.canvasBed.delete("GRAPH") target = int((self.app.bedtarget/MAXBEDTEMP)*100.0) if target != 0: self.canvasBed.create_line(0, 100-target, 200, 100-target, fill="blue", tags="TARGET") self.canvasBed.create_text(10, 100-target, anchor=SW, tags="TARGET", fill="blue", text="%3.3d" % int(self.app.bedtarget)) start = MAXDATAPOINTS - len(self.dataBed) pTemp = 0 for i in range(start, MAXDATAPOINTS): if i != start: self.canvasBed.create_line((i-1)*5, 100-pTemp, i*5, 100-self.dataBed[i-start], fill="red", tags="GRAPH") pTemp = self.dataBed[i-start] self.canvasBed.create_text(190, 100-c, anchor=SE, tags="GRAPH", fill="red", text="%3.3d" % int(t)) def drawBedAxes(self): self.canvasBed.delete("AXES") for i in range(0, MAXBEDTEMP, 50): y = 100.0-(float(i)/MAXBEDTEMP*100.0) self.canvasBed.create_line(0, y, 200, y, fill="#C0C0C0", tags="AXES") self.legendBed.create_text(LEGEND_WIDTH, y+7, anchor=SE, tags="AXES", text="%3d" % i)
class ToolBar(Frame): def __init__(self, root, printer, settings, logger, *arg): self.app = root self.printer = printer self.settings = settings self.logger = logger self.app.printing = False self.app.connected = False self.app.paused = False Frame.__init__(self, root, *arg) topBar = Frame(self) topBar.grid(row=1, column=1, columnspan=3, sticky=W) speedBar = Frame(self) speedBar.grid(row=1, column=5, sticky=E) bottomBar = Frame(self) bottomBar.grid(row=2, column=1, columnspan=6, sticky=W+E) self.bPort = Button(topBar, text="Port", width=BWIDTH, command=self.doPort) self.bPort.pack(side=LEFT, padx=2, pady=2) ports = self.scanSerial() self.spPort = Spinbox(topBar, values=ports, state="readonly") self.spPort.pack(side=LEFT, padx=2, pady=2) l = Label(topBar, text=" @ ") l.pack(side=LEFT, padx=2, pady=2) self.spBaud = Spinbox(topBar, values=baudChoices) self.spBaud.pack(side=LEFT, padx=2, pady=2) self.spBaud.delete(0, END) self.spBaud.insert(0, 115200) self.spBaud.config(state="readonly") self.bConnectMode = CM_CONNECT self.bConnect = Button(topBar, text=connectText[CM_CONNECT], width=BWIDTH, command=self.doConnect) self.bConnect.pack(side=LEFT, padx=2, pady=2) if len(ports) == 0: self.bConnect.config(state=DISABLED) else: self.bConnect.config(state=NORMAL) self.bReset = Button(topBar, text="Reset", width=BWIDTH, command=self.doReset, state=DISABLED) self.bReset.pack(side=LEFT, padx=2, pady=2) l = Label(speedBar, text="Speed:", justify=RIGHT) l.grid(row=1, column=1, sticky=E) self._speedJob = None self.currentSpeed = self.app.FeedMultiply self.scSpeed = Scale(speedBar, from_=MINSPEED, to=MAXSPEED, orient=HORIZONTAL, command=self.updateSpeedCommand) self.scSpeed.grid(row=1, column=2) self.scSpeed.set(self.currentSpeed); l = Label(speedBar, text="Fan:", width=10, anchor=E, justify=RIGHT) l.grid(row=1, column=3, sticky=E) self._fanJob = None self.currentFanSpeed = self.app.FanSpeed self.scFan = Scale(speedBar, from_=0, to=255, orient=HORIZONTAL, command=self.updateFanSpeedCommand) self.scFan.grid(row=1, column=4) self.scFan.set(self.currentFanSpeed); if self.settings.speedcommand is not None: self.cbvAssertFan = IntVar() if self.settings.forcefanspeed: self.cbvAssertFan.set(1) else: self.cbvAssertFan.set(0) self.cbAssertFan = Checkbutton(speedBar, text="Force", variable=self.cbvAssertFan, command=self.clickAssertFan) self.cbAssertFan.grid(row=1, column=5) self.bSliceMode = SM_SLICE self.bSlice = Button(bottomBar, text=sliceText[SM_SLICE], width=BWIDTH*2, command=self.doSlice) self.bSlice.pack(side=LEFT, padx=2, pady=2) self.bLoad = Button(bottomBar, text="Load GCode", width=BWIDTH, command=self.doLoad) self.bLoad.pack(side=LEFT, padx=2, pady=2) self.setSliceText() self.bSD = Button(bottomBar, text="SD", width=BWIDTH, command=self.doSD, state=DISABLED) self.bSD.pack(side=LEFT, padx=2, pady=2) self.bPrintMode = PR_PRINT self.bPrint = Button(bottomBar, text=printText[PR_PRINT], width=BWIDTH, command=self.doPrint, state=DISABLED) self.bPrint.pack(side=LEFT, padx=2, pady=2) self.bPauseMode = PM_PAUSE self.bPause = Button(bottomBar, text=pauseText[PM_PAUSE], width=BWIDTH, command=self.doPause, state=DISABLED) self.bPause.pack(side=LEFT, padx=2, pady=2) self.bAbandon = Button(bottomBar, text="Abandon SD Print", width=BWIDTH+8, command=self.doAbandon, state=DISABLED) self.bAbandon.pack(side=LEFT, padx=2, pady=2) self.cbvLiftOnPause = IntVar() if self.settings.liftonpause: self.cbvLiftOnPause.set(1) else: self.cbvLiftOnPause.set(0) self.cbLiftOnPause = Checkbutton(bottomBar, text="Lift Head/Retract on Pause", variable=self.cbvLiftOnPause, command=self.clickLiftOnPause) self.cbLiftOnPause.pack(side=LEFT, padx=2) self.cbvResumeAtPause = IntVar() if self.settings.resumeatpausepoint: self.cbvResumeAtPause.set(1) else: self.cbvResumeAtPause.set(0) self.cbResumeAtPause = Checkbutton(bottomBar, text="Resume print at pause point", variable=self.cbvResumeAtPause, command=self.clickResumeAtPause) self.cbResumeAtPause.pack(side=LEFT, padx=2) def clickAssertFan(self): if self.cbvAssertFan.get() == 1: self.settings.forcefanspeed = True self.settings.setModified() else: self.settings.forcefanspeed = False self.settings.setModified() def clickLiftOnPause(self): if self.cbvLiftOnPause.get() == 1: self.settings.liftonpause = True self.settings.setModified() else: self.settings.liftonpause = False self.settings.setModified() def clickResumeAtPause(self): if self.cbvResumeAtPause.get() == 1: self.settings.resumeatpausepoint = True self.settings.setModified() else: self.settings.resumeatpausepoint = False self.settings.setModified() def setSliceText(self): if self.settings.slicer == SLIC3R: sl = "slic3r:%s" % self.app.slic3r.getProfile() else: sl = "skeinforge:%s" % self.app.skeinforge.getProfile() sliceText[SM_SLICE] = "Slice (%s)" % sl if self.bSliceMode == SM_SLICE: self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) lt = len(sliceText[SM_SLICE])+2 if lt < BWIDTH: lt = BWIDTH self.bSlice.config(text=sliceText[SM_SLICE], width=lt) def updateSpeedCommand(self, *arg): if self._speedJob: self.app.master.after_cancel(self._speedJob) self._speedJob = self.app.master.after(500, self.updateSpeed) def updateSpeed(self, *arg): v = self.scSpeed.get() self.setSpeed(v) def setSpeed(self, v): if v < MINSPEED or v > MAXSPEED: self.logger.logMsg("Attempt to change speed outside of allowable range (%d-%d)" % (MINSPEED, MAXSPEED)) elif int(v) != self.currentSpeed: if self.app.connected: self.currentSpeed = int(v) self.logger.logMsg("changing speed percentage to %d%%" % self.currentSpeed) cmd = "M220 S%d" % self.currentSpeed self.printer.send_now(cmd) else: self.logger.logMsg("Printer is off-line") self.scSpeed.set(self.currentSpeed) def updateFanSpeedCommand(self, *arg): if self._fanJob: self.app.master.after_cancel(self._fanJob) self._fanJob = self.app.master.after(500, self.updateFanSpeed) def updateFanSpeed(self, *arg): v = self.scFan.get() self.setFanSpeed(v) self.app.FanSpeed = v def forceFanSpeed(self, v): self.currentFanSpeed = -1 self.setFanSpeed(v) def setFanSpeed(self, v): if int(v) != self.currentFanSpeed: if self.app.connected: self.currentFanSpeed = int(v) cmd = "M106 S%d" % self.currentFanSpeed self.printer.send_now(cmd) else: self.logger.logMsg("Printer is off-line") self.scFan.set(self.currentFanSpeed) def syncSpeeds(self): self.currentSpeed = self.app.FeedMultiply self.scSpeed.set(self.currentSpeed) self.currentFanSpeed = self.app.FanSpeed self.scFan.set(self.currentFanSpeed) def initializeToolbar(self): self.bReset.config(state=DISABLED) self.bSliceMode = SM_SLICE self.bSlice.config(text=sliceText[SM_SLICE]) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) if not self.app.sdprinting and not self.app.sdpaused: self.bPrintMode = PR_PRINT self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPauseMode = PM_PAUSE self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) def setSDPrint(self): self.bPause.config(text=pauseText[PM_PAUSE], state=NORMAL) self.bPauseMode = PM_PAUSE self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT self.bAbandon.config(state=NORMAL) def clearSDPrint(self): self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) self.bPauseMode = PM_PAUSE self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT self.bAbandon.config(state=DISABLED) self.checkAllowPrint() def setCancelMode(self): self.bSliceMode = SM_CANCEL self.bSlice.config(text=sliceText[SM_CANCEL], width=BWIDTH) self.bLoad.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.allowSliceMenu(False) def setLoading(self, flag): if flag: self.bLoad.config(state=DISABLED) self.bSlice.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.allowSliceMenu(False) else: self.bLoad.config(state=NORMAL) self.bSlice.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) def clearCancelMode(self): self.bSliceMode = SM_SLICE lt = len(sliceText[SM_SLICE])+2 if lt < BWIDTH: lt = BWIDTH self.bSlice.config(text=sliceText[SM_SLICE], width=lt) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) def doConnect(self): if self.bConnectMode == CM_CONNECT: port = self.spPort.get() baud = int(self.spBaud.get()) self.printer.onlinecb = self.onlinecb try: self.printer.connect(port, baud) except SerialException: self.logger.logMsg("Unable to open printer port %s" % port) else: if self.app.printing: self.logger.logMsg("Please wait until printing has finished or is paused") else: self.printer.disconnect() self.printer.onlinecb = None self.app.printerConnected(False) # self.app.connected = False self.bConnectMode = CM_CONNECT self.bConnect.config(text=connectText[CM_CONNECT]) self.bReset.config(state=DISABLED) self.bSD.config(state=DISABLED) if self.app.paused: self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) self.bPauseMode = PM_PAUSE self.app.printing = False self.app.paused = False def doReset(self): if tkMessageBox.askyesno("Reset?", "Are you sure you want to reset the printer?", parent=self.app): self.printer.reset() self.printer.printing = 0 self.app.printing = False self.bSlice.config(state=NORMAL) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) self.bPrintMode = PR_PRINT self.bPrint.config(text=printText[PR_PRINT], state=NORMAL) if self.app.paused: self.printer.paused = 0 self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) self.bPauseMode = PM_PAUSE self.app.paused = False def onlinecb(self): self.logger.logMsg("Printer is on-line") self.app.printerConnected(True) # self.app.connected = True self.bConnectMode = CM_DISCONNECT self.bConnect.config(text=connectText[CM_DISCONNECT]) self.bReset.config(state=NORMAL) self.bSD.config(state=NORMAL) self.checkAllowPrint() def checkAllowPrint(self): if self.app.connected and len(self.app.gcode) != 0 and not self.app.printing and not self.app.sdprinting: self.bPrint.config(text=printText[PR_PRINT], state=NORMAL) self.bPrintMode = PR_PRINT def printComplete(self): self.app.endTime = time.time() self.app.elapsedTime = self.app.endTime - self.app.startTime self.logger.logMsg("Printing completed at %s" % time.strftime('%H:%M:%S', time.localtime())) self.logger.logMsg("Total elapsed time: %s" % formatElapsed(self.app.elapsedTime)) self.bPrintMode = PR_PRINT self.bPrint.config(text=printText[PR_PRINT], state=NORMAL) self.bPauseMode = PM_PAUSE self.bPause.config(text=pauseText[PM_PAUSE], state=DISABLED) self.app.printing = False self.bSlice.config(state=NORMAL) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) self.app.paused = False self.app.gc.updatePrintProgress(0) self.app.closeAllReports() if self.settings.endprintmacro is not None: self.app.doMacro(name=self.settings.endprintmacro, silent=True) def doPause(self): if self.bPauseMode == PM_PAUSE: if self.app.printing: self.app.paused = True self.printer.pause() elif self.app.sdprinting: self.app.sdpaused = True self.printer.send_now("M25") self.app.sdprinting = False self.bPause.config(text=pauseText[PM_RESUME]) self.bPauseMode = PM_RESUME self.bPrint.config(text=printText[PR_RESTART], state=NORMAL) self.bPrintMode = PR_RESTART else: if self.app.sdpaused: self.printer.send_now("M24") self.app.sdpaused = False self.app.sdprinting = True self.bPause.config(text=pauseText[PM_PAUSE]) self.bPauseMode = PM_PAUSE self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT else: self.hitPrint = False if self.settings.resumeatpausepoint: self.printer.send_now("G1 X%s Y%s F%s" % (self.app.pausePoint[XAxis], self.app.pausePoint[YAxis], self.settings.xyfeed)) self.printer.send_now("G1 Z%s F%s" % (self.app.pausePoint[ZAxis], self.settings.zfeed)) self.printer.send_now("G92 E%s" % self.app.pausePoint[EAxis]) self.printer.send_now("G1 F%s" % self.settings.xyfeed) self.printer.startcb = self.startcb self.printer.resume() def doAbandon(self): self.printer.send_now("M25") self.app.sdpaused = False self.app.sdprinting = False self.clearSDPrint() def doSlice(self): if self.bSliceMode == SM_SLICE: self.bLoad.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.openSTLFile() else: self.app.slicerCancel = True self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) def doLoad(self): self.app.openGCodeFile() def doPrint(self): if self.app.sdpaused: self.printer.send_now("M26 S0") self.printer.send_now("M24") self.app.sdpaused = False self.app.sdprinting = True self.bPause.config(text=pauseText[PM_PAUSE]) self.bPauseMode = PM_PAUSE self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT else: if len(self.app.gcode) == 0: self.logger.logMsg("Nothing to print") else: #if not self.app.paused: self.app.gc.updatePrintProgress(0, restart=True) self.bPauseMode = PM_PAUSE self.bPause.config(text=pauseText[PM_PAUSE], state=NORMAL) self.hitPrint = True self.printer.startcb = self.startcb self.printer.startprint(self.app.gcode) def startcb(self): self.printer.startcb = None if not self.app.paused: self.app.startTime = time.time() self.logger.logMsg("Printing Started at %s" % time.strftime('%H:%M:%S', time.localtime(self.app.startTime))) self.app.printing = True self.bSlice.config(state=DISABLED) self.bLoad.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.allowSliceMenu(False) self.bPrint.config(text=printText[PR_RESTART], state=DISABLED) self.bPrintMode = PR_RESTART self.printer.endcb = self.endcb else: if self.hitPrint: self.app.startTime = time.time() self.logger.logMsg("Printing restarted at %s" % time.strftime('%H:%M:%S', time.localtime())) else: self.logger.logMsg("Printing resumed at %s" % time.strftime('%H:%M:%S', time.localtime())) self.bPause.config(text=pauseText[PM_PAUSE]) self.bPauseMode = PM_PAUSE self.app.printing = True self.bSlice.config(state=DISABLED) self.bLoad.config(state=DISABLED) self.app.allowLoadGCodeMenu(False) self.app.allowSliceMenu(False) self.app.paused = False self.bPrint.config(state=DISABLED) self.printer.endcb = self.endcb def endcb(self): self.printer.endcb = None if not self.app.paused: self.printComplete() if self.app.sduploading: self.app.sduploading = False self.printer.send_now("M29") else: self.app.event_generate(MWM_REQUESTPOSITIONREPORT) # record the current printer position and how many intervals were willing to wait fo the response self.maxWait114 = MAXWAIT114; self.waitPos = self.printer.queueindex self.check114Response() def check114Response(self) : # tick off 1 interval, and make sure we haven't either received the report or we've waited max intervals self.maxWait114 -= 1 if self.app.m114count == 0 or self.maxWait114 <= 0: # one way or the other we're done waiting here if self.maxWait114 <= 0: self.app.m114count = 0 self.logger.logMsg("Location report not received - proceeding") self.app.pausePoint[XAxis] = self.app.location[XAxis] self.app.pausePoint[YAxis] = self.app.location[YAxis] self.app.pausePoint[ZAxis] = self.app.location[ZAxis] self.app.pausePoint[EAxis] = self.app.location[EAxis] self.logger.logMsg("Pause location: X:%f Y:%f Z:%f E:%f" % (self.app.pausePoint[XAxis], self.app.pausePoint[YAxis], self.app.pausePoint[ZAxis], self.app.pausePoint[EAxis])) if self.settings.liftonpause: self.printer.send_now("G1 E%s F%s" % (self.app.pausePoint[EAxis]-2, self.settings.efeed)) self.printer.send_now("G1 Z%s F%s" % (self.app.pausePoint[ZAxis]+10, self.settings.zfeed)) self.bPause.config(text=pauseText[PM_RESUME]) self.bPauseMode = PM_RESUME self.app.printing = False self.bSlice.config(state=NORMAL) self.bLoad.config(state=NORMAL) self.app.allowLoadGCodeMenu(True) self.app.allowSliceMenu(True) if self.app.sduploading: self.bPrint.config(text=printText[PR_PRINT], state=DISABLED) self.bPrintMode = PR_PRINT else: self.bPrint.config(text=printText[PR_RESTART], state=NORMAL) self.bPrintMode = PR_RESTART else: # we still are waiting for the report, but reset everything if the printer is still moving if self.waitPos != self.printer.queueindex: self.waitPos = self.printer.queueindex self.maxWait114 = MAXWAIT114 self.app.master.after(500, self.check114Response) def doPort(self): l = self.scanSerial() self.spPort.config(values=l) if len(l) == 0: self.bConnect.config(state=DISABLED) else: self.bConnect.config(state=NORMAL) def scanSerial(self): """scan for available ports. return a list of device names.""" baselist=[] if os.name=="nt": try: key=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,"HARDWARE\\DEVICEMAP\\SERIALCOMM") i=0 while(1): baselist+=[_winreg.EnumValue(key,i)[1]] i+=1 except: pass return baselist+glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyACM*') +glob.glob("/dev/tty.*")+glob.glob("/dev/cu.*")+glob.glob("/dev/rfcomm*") def doSD(self): self.app.doSD()
def initUI(self): # Main Window self.parent.title("gBot") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) # Debug Window Toplevel1 = Toplevel(self) Toplevel1.title("gBot Debug Console") self.pack(fill=BOTH, expand=1) TL_T1 = Text(Toplevel1, width=50) TL_T1.pack() Toplevel1.state("withdrawn") Toplevel1.protocol('WM_DELETE_WINDOW', lambda:Toplevel1.state("withdrawn")) Toplevel1.attributes("-topmost", True) # Username Input L1 = Label(self, text="G+ User Name") L1.grid(row=0, column=0, sticky=E, ipady=1) E1 = Entry(self, width=30) E1.grid(row=0, column=1, ipady=1, sticky=E) # Password Input L2 = Label(self, text="G+ Password") L2.grid(row=1, column=0, sticky=E, ipady=1) E2 = Entry(self, width=30) E2.grid(row=1, column=1, ipady=1, sticky=E) # Output Path Input L3 = Label(self, text="Output Path") L3.grid(row=2, column=0, sticky=E, pady=1) E3 = Entry(self, width=30) E3.grid(row=2, column=1, ipady=1, sticky=E) E3.insert(0, "%s\links.txt" % (os.getcwd())) # Num Posts L4 = Label(self, text="# Of Posts") L4.grid(row=3, column=0, sticky=E, pady=1) S1 = Spinbox(self, from_=1, to=9999999, width=28) S1.grid(row=3, column=1, ipady=1, sticky=E) # Post Input T1 = Text(self, width=30) T1.grid(row=5, columnspan=2, sticky=W+E, pady=1) # Start button B1 = Button(self, text="Start Posting", command=lambda:self.doPosting(B1,TL_T1,E1.get(),E2.get(),T1.get(1.0,END),E3.get(),S1.get())) B1.grid(row=6,columnspan=2, sticky=W+E) # Debug button B2 = Button(self, text="Debug log", command=lambda:Toplevel1.state("normal")) B2.grid(row=7, columnspan=2, sticky=W+E) self.addDebug(TL_T1,"Started successfully")
class SettingWindow(Toplevel): def __init__(self, master, max_num_features, num_frames, mser_image): Toplevel.__init__(self, master) self.protocol('WM_DELETE_WINDOW', self.withdraw) self.notebook = ttk.Notebook(self) frame_feats = ttk.Frame(self.notebook) frame_forest = ttk.Frame(self.notebook) frame_mser = ttk.Frame(self.notebook) frame_other = ttk.Frame(self.notebook) self.notebook.add(frame_feats, text="Features ") self.notebook.add(frame_forest, text=" Forest ") self.notebook.add(frame_mser, text=" MSER ") self.notebook.add(frame_other, text=" Other ") self.max_num_feats = max_num_features self.selection = None self.mser_image = mser_image rand_row = random.randint(1, 512 - 200) rand_col = random.randint(1, 512 - 110) self.mser_area = mser_image[rand_row:rand_row + 180, rand_col:rand_col + 100] # read images from icons folder self.hf0_img = PhotoImage(file="./icons/hf0.gif") self.hf1_img = PhotoImage(file="./icons/hf1.gif") self.hf2_img = PhotoImage(file="./icons/hf2.gif") self.hf3_img = PhotoImage(file="./icons/hf3.gif") self.hf4_img = PhotoImage(file="./icons/hf4.gif") self.hf5_img = PhotoImage(file="./icons/hf5.gif") self.features_vars = list() for i in range(max_num_features): self.features_vars.append(IntVar()) Label(frame_feats, text="Patch size (" + u"\N{GREEK SMALL LETTER PI}" + "):").grid( row=0, column=0, pady=5) self.patch_size_spinbox = Spinbox(frame_feats, from_=3, to=30, width=3) self.patch_size_spinbox.delete(0, END) self.patch_size_spinbox.insert(END, 10) self.patch_size_spinbox.grid(row=0, column=1, padx=5) f1 = ttk.Labelframe(frame_feats, text='Mean filter') f1.grid(row=1, columnspan=2) Label(f1, text=u"\N{GREEK SMALL LETTER PI}").grid(row=0, column=0) Checkbutton(f1, text="R", variable=self.features_vars[0]).grid(row=0, column=1) Checkbutton(f1, text="G", variable=self.features_vars[1]).grid(row=0, column=2) Checkbutton(f1, text="B", variable=self.features_vars[2]).grid(row=0, column=3) Label(f1, text=u"\N{GREEK SMALL LETTER PI}" + "/2").grid(row=1, column=0) Checkbutton(f1, text="R", variable=self.features_vars[3]).grid(row=1, column=1) Checkbutton(f1, text="G", variable=self.features_vars[4]).grid(row=1, column=2) Checkbutton(f1, text="B", variable=self.features_vars[5]).grid(row=1, column=3) f2 = ttk.Labelframe(frame_feats, text="Gaussian filter") f2.grid(row=2, columnspan=2) Label(f2, text=str(1.0)).grid(row=0, column=0) Checkbutton(f2, text="R", variable=self.features_vars[6]).grid(row=0, column=1) Checkbutton(f2, text="G", variable=self.features_vars[7]).grid(row=0, column=2) Checkbutton(f2, text="B", variable=self.features_vars[8]).grid(row=0, column=3) Label(f2, text=str(3.5)).grid(row=1, column=0) Checkbutton(f2, text="R", variable=self.features_vars[9]).grid(row=1, column=1) Checkbutton(f2, text="G", variable=self.features_vars[10]).grid(row=1, column=2) Checkbutton(f2, text="B", variable=self.features_vars[11]).grid(row=1, column=3) f3 = ttk.Labelframe(frame_feats, text="Laplacian of gaussian") f3.grid(row=3, columnspan=2) Label(f3, text=str(2.0)).grid(row=0, column=0) Checkbutton(f3, text="R", variable=self.features_vars[12]).grid(row=0, column=1) Checkbutton(f3, text="G", variable=self.features_vars[13]).grid(row=0, column=2) Checkbutton(f3, text="B", variable=self.features_vars[14]).grid(row=0, column=3) Label(f3, text=str(3.5)).grid(row=1, column=0) Checkbutton(f3, text="R", variable=self.features_vars[15]).grid(row=1, column=1) Checkbutton(f3, text="G", variable=self.features_vars[16]).grid(row=1, column=2) Checkbutton(f3, text="B", variable=self.features_vars[17]).grid(row=1, column=3) f4 = ttk.Labelframe(frame_feats, text="Haar-like features") f4.grid(row=1, rowspan=2, column=3, padx=5) Checkbutton(f4, image=self.hf0_img, variable=self.features_vars[18]).grid(row=0, column=0) Checkbutton(f4, image=self.hf1_img, variable=self.features_vars[19]).grid(row=0, column=1) Checkbutton(f4, image=self.hf2_img, variable=self.features_vars[20]).grid(row=1, column=0) Checkbutton(f4, image=self.hf3_img, variable=self.features_vars[21]).grid(row=1, column=1) Checkbutton(f4, image=self.hf4_img, variable=self.features_vars[22]).grid(row=2, column=0) Checkbutton(f4, image=self.hf5_img, variable=self.features_vars[23]).grid(row=2, column=1) buttons_paned_window = PanedWindow(frame_feats, orient=VERTICAL) buttons_paned_window.grid(row=3, column=3) self.select_all_button = Button(buttons_paned_window, text="Select all", command=self._select_all) buttons_paned_window.add(self.select_all_button) self.clear_selection_button = Button(buttons_paned_window, text="Clear selection", command=self._clear_selection) buttons_paned_window.add(self.clear_selection_button) # default values for j in [0, 1, 3, 6, 7, 9, 15, 21, 23]: self.features_vars[j].set(1) # FOREST FRAMES # number of trees f5 = ttk.Labelframe(frame_forest, text="Number of trees") f5.grid(row=0, columnspan=2, pady=5, padx=5) Label(f5, text="N").grid(row=1, column=0) self.num_trees_scale = Scale(f5, from_=5, to=500, resolution=5, orient=HORIZONTAL) self.num_trees_scale.set(300) self.num_trees_scale.grid(row=0, column=1, rowspan=2) # depth single tree f6 = ttk.Labelframe(frame_forest, text="Depth single tree") f6.grid(row=1, columnspan=2, pady=5, padx=5) Label(f6, text="d").grid(row=1, column=0) self.depth_tree_scale = Scale(f6, from_=2, to=20, orient=HORIZONTAL) self.depth_tree_scale.set(3) self.depth_tree_scale.grid(row=0, column=1, rowspan=2) # percentage number of features f7 = ttk.Labelframe(frame_forest, text="% subset of features") f7.grid(row=2, columnspan=2, pady=5, padx=5) Label(f7, text="m").grid(row=1, column=0) self.percentage_feats_scale = Scale(f7, from_=0.0, to=1, resolution=0.05, orient=HORIZONTAL) self.percentage_feats_scale.set(0.5) self.percentage_feats_scale.grid(row=0, column=1, rowspan=2) # mser frame # delta f8 = ttk.Labelframe(frame_mser, text="Delta") f8.grid(row=0, columnspan=2, pady=5, padx=5) Label(f8, text=u"\N{GREEK SMALL LETTER DELTA}").grid(row=1, column=0) self.delta_scale = Scale(f8, from_=1, to=10, resolution=1, orient=HORIZONTAL) self.delta_scale.set(2) self.delta_scale.grid(row=0, column=1, rowspan=2) # min area f9 = ttk.Labelframe(frame_mser, text="Minimum area") f9.grid(row=1, columnspan=2, pady=5, padx=5) Label(f9, text="m").grid(row=1, column=0) self.min_area_scale = Scale(f9, from_=2, to=200, orient=HORIZONTAL) self.min_area_scale.set(10) self.min_area_scale.grid(row=0, column=1, rowspan=2) # percentage number of features f10 = ttk.Labelframe(frame_mser, text="Maximum area") f10.grid(row=2, columnspan=2, pady=5, padx=5) Label(f10, text="M").grid(row=1, column=0) self.max_area_scale = Scale(f10, from_=50, to=1000, resolution=5, orient=HORIZONTAL) self.max_area_scale.set(350) self.max_area_scale.grid(row=0, column=1, rowspan=2) # mser image f11 = ttk.Labelframe(frame_mser) f11.grid(row=0, rowspan=3, column=2, padx=5) self.mser_img_array = Image.fromarray(self.mser_area, "RGB") self.mser_img = ImageTk.PhotoImage(self.mser_img_array) img_label = Label(f11, image=self.mser_img) img_label.grid(row=0, column=0) buttons_p_w_mser = PanedWindow(f11, orient=HORIZONTAL) try_button = Button(f11, text="Try", command=self.try_mser) buttons_p_w_mser.add(try_button) change_button = Button(f11, text="New img", command=self.change_mser) buttons_p_w_mser.add(change_button) buttons_p_w_mser.grid(row=1, column=0) # other frame f12 = ttk.Labelframe(frame_other, text="Refinement") f12.grid(row=0, columnspan=2, pady=5, padx=5) Label(f12, text=u"\N{GREEK CAPITAL LETTER PHI}_l").grid(row=1, column=0) self.low_thresh_scale = Scale(f12, from_=0, to=1, resolution=0.05, orient=HORIZONTAL, length=90) self.low_thresh_scale.set(0.45) self.low_thresh_scale.grid(row=0, column=1, rowspan=2) Label(f12, text=u"\N{GREEK CAPITAL LETTER PHI}_h").grid(row=3, column=0) self.high_thresh_scale = Scale(f12, from_=0, to=1, resolution=0.05, orient=HORIZONTAL, length=90) self.high_thresh_scale.set(0.65) self.high_thresh_scale.grid(row=2, column=1, rowspan=2) f13 = ttk.Labelframe(frame_other, text="Dots distance") f13.grid(row=1, columnspan=2, pady=5, padx=5) Label(f13, text=u" \N{GREEK SMALL LETTER SIGMA}").grid(row=1, column=0) self.dots_distance_scale = Scale(f13, from_=1, to=20, resolution=1, orient=HORIZONTAL, length=90) self.dots_distance_scale.set(6) self.dots_distance_scale.grid(row=0, column=1, rowspan=2) f14 = ttk.Labelframe(frame_other, text="Tracks") f14.grid(row=0, column=3, pady=5, padx=5) Label(f14, text="N").grid(row=1, column=0) self.num_frames_tracks_spinbox = Spinbox(f14, from_=2, to=num_frames, width=10) self.num_frames_tracks_spinbox.delete(0, END) self.num_frames_tracks_spinbox.insert(END, num_frames) self.num_frames_tracks_spinbox.grid(row=0, column=1, rowspan=2) Label(f14, text=u"\N{GREEK SMALL LETTER TAU}").grid(row=3, column=0) self.gaps_scale = Scale(f14, from_=1, to=10, resolution=1, orient=HORIZONTAL, length=90) self.gaps_scale.set(2) self.gaps_scale.grid(row=2, column=1, rowspan=2) self.notebook.pack(padx=1, pady=1) save_button = Button(self, text=" Save and Close window ", command=self.withdraw) save_button.pack(pady=2) def _select_all(self): for i, var in enumerate(self.features_vars): var.set(1) def _clear_selection(self): for i, var in enumerate(self.features_vars): var.set(0) def change_mser(self): rand_row = random.randint(1, 512 - 200) rand_col = random.randint(1, 512 - 110) self.mser_area = self.mser_image[rand_row:rand_row + 180, rand_col:rand_col + 100] self.update_mser_image(self.mser_area) def try_mser(self): delta = self.delta_scale.get() min_area = self.min_area_scale.get() max_area = self.max_area_scale.get() image = self.mser_area red_c = image[:, :, 0] red_c = cv2.equalizeHist(red_c) det_img = image.copy() mser = cv2.MSER(delta, _min_area=min_area, _max_area=max_area) regions = mser.detect(red_c) cp = list() new_c = np.zeros(self.mser_area.shape, dtype=np.uint8) for r in regions: for point in r: cp.append(point) det_img[point[1], point[0], 0] = 0 det_img[point[1], point[0], 1] = 0 det_img[point[1], point[0], 2] = 204 #new_c[point[1], point[0]] = 255 self.update_mser_image(det_img) def update_mser_image(self, new_image): self.mser_img_array = Image.fromarray(new_image) self.mser_img.paste(self.mser_img_array) def get_patch_size(self): patch_size = self.patch_size_spinbox.get() return int(patch_size) def get_num_frames_tracks(self): num_frames_tracks = self.num_frames_tracks_spinbox.get() return int(num_frames_tracks) def get_mser_opts(self): return [ self.delta_scale.get(), self.min_area_scale.get(), self.max_area_scale.get() ] def get_forest_opts(self): return [ self.num_trees_scale.get(), self.depth_tree_scale.get(), self.percentage_feats_scale.get() ] def get_low_thresh(self): return self.low_thresh_scale.get() def get_high_thresh(self): return self.high_thresh_scale.get() def get_dots_distance(self): return int(self.dots_distance_scale.get()) def get_selection_mask(self): if self.selection is not None: return self.selection selection_mask = np.zeros((self.max_num_feats, ), dtype='bool') for i, var in enumerate(self.features_vars): selection_mask[i] = var.get() self.selection = selection_mask return selection_mask