class Window(Frame): def __init__(self, master=None): if not headless: Frame.__init__(self, master) self.master = master self.init_window() def settrainer(self, n): global power_curve, user_defaults user_defaults["power_curve"] = n power_curve = n if n == "power_calc_factors_imagic.txt": self.PowerCurveVariable.set("I-Magic") elif n == "power_calc_factors_fortius.txt": self.PowerCurveVariable.set("Fortius") elif n == "power_calc_factors_custom.txt": self.PowerCurveVariable.set("Custom") def init_window(self): global user_defaults self.grid() # Setup menu content### self.master.title("Antifier v0.9") self.master.option_add("*tearOff", False) # allowing the widget to take the full space of the root window self.pack(fill=BOTH, expand=1) # creating a menu instance menu = Menu(self.master) self.master.config(menu=menu) # create the Setup object) Setup = Menu(menu) # add commands to the Setup option Setup.add_command(label="Head Unit", command=self.HeadUnit_window) subSetup = Menu(Setup) subSetup.add_command( label="iMagic", command=lambda p="power_calc_factors_imagic.txt": self.settrainer( p), ) subSetup.add_command( label="Fortius", command=lambda p="power_calc_factors_fortius.txt": self.settrainer( p), ) subSetup.add_command( label="Custom Curve", command=lambda p="power_calc_factors_custom.txt": self.settrainer( p), ) Setup.add_cascade(label="Power Curve", menu=subSetup) Setup.add_separator() Setup.add_command(label="Exit", command=self.EXITbutton) # added "Setup" to our menu menu.add_cascade(label="Setup", menu=Setup) # create the Options object) Options = Menu(menu) # add commands to the Options option Options.add_command(label="Debug", command=self.DebugButton) Options.add_command(label="Simulate Trainer", command=self.Simulatebutton) Options.add_command(label="Power Factor", command=self.PowerFactor_Window) # added "Options" to our menu menu.add_cascade(label="Options", menu=Options) # create the Help object Help = Menu(menu) # adds a command to the Help option. Help.add_command(label="Readme", command=self.Readme) Help.add_command(label="Zwift shortcuts", command=self.Zwift_shortcuts) # added "Help" to our menu menu.add_cascade(label="Help", menu=Help) # Setup GUI buttons and labels### self.FindHWbutton = Button(self, height=1, width=15, text=u"1. Locate HW", command=self.ScanForHW) self.FindHWbutton.grid(column=0, row=0) label = Label(self, height=1, width=10, text="Head Unit") label.grid(column=0, row=1, sticky="EW") self.trainerVariable = StringVar() label = Label(self, textvariable=self.trainerVariable, anchor="w", fg="black", bg="grey") label.grid(column=1, row=1, columnspan=2, sticky="EW") label = Label(self, height=1, width=10, text="Power curve") label.grid(column=0, row=2, sticky="EW") self.PowerCurveVariable = StringVar() label = Label( self, textvariable=self.PowerCurveVariable, anchor="w", fg="black", bg="grey", ) label.grid(column=1, row=2, columnspan=2, sticky="EW") if "power_curve" in user_defaults: self.settrainer(user_defaults["power_curve"]) label = Label(self, height=1, width=10, text="ANT+") label.grid(column=0, row=3, sticky="EW") self.ANTVariable = StringVar() label = Label(self, textvariable=self.ANTVariable, anchor="w", fg="black", bg="grey") label.grid(column=1, row=3, columnspan=2, sticky="EW") label = Label(self, text="Power factor") label.grid(column=0, row=4, sticky="EW") self.PowerFactorVariable = StringVar() label = Label( self, textvariable=self.PowerFactorVariable, anchor="w", fg="black", bg="grey", ) label.grid(column=1, row=4, columnspan=2, sticky="EW") self.RunoffButton = Button(self, height=1, width=15, text=u"2. Perform Runoff", command=self.Runoff) self.RunoffButton.grid(column=0, row=5) self.RunoffButton.config(state="disabled") self.runoffVariable = StringVar() label = Label( self, textvariable=self.runoffVariable, anchor="w", fg="black", bg="grey", width=40, ) label.grid(column=1, row=5, columnspan=2, sticky="EW") self.StartAPPbutton = Button(self, height=1, width=15, text=u"3. Start script", command=self.Start) self.StartAPPbutton.grid(column=0, row=6) self.StartAPPbutton.config(state="disabled") self.StopAPPbutton = Button( self, height=1, width=15, text=u"Stop script", command=self.Stop, state="disabled", ) self.StopAPPbutton.grid(column=1, row=6) label = Label(self, text="Speed") label.grid(column=0, row=7, sticky="EW") self.SpeedVariable = StringVar() label = Label(self, textvariable=self.SpeedVariable, anchor="w", fg="black", bg="grey") label.grid(column=1, row=7, columnspan=2, sticky="EW") self.SpeedVariable.set(u"0") label = Label(self, text="Heartrate") label.grid(column=0, row=8, sticky="EW") self.HeartrateVariable = StringVar() label = Label(self, textvariable=self.HeartrateVariable, anchor="w", fg="black", bg="grey") label.grid(column=1, row=8, columnspan=2, sticky="EW") self.HeartrateVariable.set(u"0") label = Label(self, text="Cadence") label.grid(column=0, row=9, sticky="EW") self.CadenceVariable = StringVar() label = Label(self, textvariable=self.CadenceVariable, anchor="w", fg="black", bg="grey") label.grid(column=1, row=9, columnspan=2, sticky="EW") self.CadenceVariable.set(u"0") label = Label(self, text="Power") label.grid(column=0, row=10, sticky="EW") self.PowerVariable = StringVar() label = Label(self, textvariable=self.PowerVariable, anchor="w", fg="black", bg="grey") label.grid(column=1, row=10, columnspan=2, sticky="EW") self.PowerVariable.set(u"0") label = Label(self, text="Slope") label.grid(column=0, row=11, sticky="EW") self.SlopeVariable = StringVar() label = Label(self, textvariable=self.SlopeVariable, anchor="w", fg="black", bg="grey") label.grid(column=1, row=11, columnspan=2, sticky="EW") label = Label(self, text="Target Power") label.grid(column=0, row=12, sticky="EW") self.TargetPowerVariable = StringVar() label = Label( self, textvariable=self.TargetPowerVariable, anchor="w", fg="black", bg="grey", ) label.grid(column=1, row=12, columnspan=2, sticky="EW") label = Label(self, text="Resistance Level") label.grid(column=0, row=13, sticky="EW") self.ResistanceLevelVariable = StringVar() label = Label( self, textvariable=self.ResistanceLevelVariable, anchor="w", fg="black", bg="grey", ) label.grid(column=1, row=13, columnspan=2, sticky="EW") self.ResistanceLevelVariable.set(u"0") def PowerFactor_Window(self): self.PowerFactor_Window = Toplevel(self.master) self.app = PowerFactor_Window(self.PowerFactor_Window) def HeadUnit_window(self): self.HeadUnitWindow = Toplevel(self.master) self.app = HeadUnit_Window(self.HeadUnitWindow) def Runoff(self): global runoff_loop_running def run(): global dev_trainer, runoff_loop_running rolldown = False rolldown_time = 0 speed = 0 # self.InstructionsVariable.set(''' # CALIBRATION TIPS: # 1. Tyre pressure 100psi (unloaded and cold) aim for 7.2s rolloff # 2. Warm up for 2 mins, then cycle 30kph-40kph for 30s # 3. Speed up to above 40kph then stop pedalling and freewheel # 4. Rolldown timer will start automatically when you hit 40kph, so stop pedalling quickly! # ''') while runoff_loop_running: # loop every 100ms last_measured_time = time.time() * 1000 # receive data from trainer speed, pedecho, heart_rate, force_index, cadence = trainer.receive( dev_trainer) # get data from device self.SpeedVariable.set(speed) if speed == "Not found": self.TrainerStatusVariable.set( "Check trainer is powered on") # send data to trainer resistance_level = 6 trainer.send(dev_trainer, resistance_level, pedecho) if speed > 40: # speed above 40, start rolldown self.runoffVariable.set( "Rolldown timer started - STOP PEDALLING!") rolldown = True if speed <= 40 and rolldown: # rolldown timer starts when dips below 40 if rolldown_time == 0: rolldown_time = time.time( ) # set initial rolldown time self.runoffVariable.set( "Rolldown timer started - STOP PEDALLING! %s " % (round((time.time() - rolldown_time), 1))) if speed < 0.1 and rolldown: # wheel stopped runoff_loop_running = False # break loop self.runoffVariable.set( "Rolldown time = %s seconds (aim 7s)" % round( (time.time() - rolldown_time), 1)) time_to_process_loop = time.time() * 1000 - last_measured_time sleep_time = 0.1 - (time_to_process_loop) / 1000 if sleep_time < 0: sleep_time = 0 time.sleep(sleep_time) self.RunoffButton.config( text="2. Perform Runoff") # reset runoff button self.StartAPPbutton.config(state="normal") if self.RunoffButton.cget( "text") == "2. Perform Runoff": # start runoff self.runoffVariable.set("Cycle to above 40kph then stop") self.RunoffButton.config(text="2. Stop Runoff") self.StartAPPbutton.config(state="disabled") runoff_loop_running = True # loop switch t1 = threading.Thread(target=run) t1.start() else: # stop loop runoff_loop_running = False self.runoffVariable.set("Stopped") self.RunoffButton.config(text="2. Perform Runoff") self.StartAPPbutton.config(state="normal") def Readme(self): os.startfile("README.txt") def Zwift_shortcuts(self): os.startfile("Zwift_shortcuts.txt") def EXITbutton(self): self.destroy() exit() def DebugButton(self): global debug if debug: debug = True else: debug = False def Simulatebutton(self): global simulatetrainer simulatetrainer = True def Stop(self): global switch switch = False self.StartAPPbutton.config(state="normal") self.StopAPPbutton.config(state="disabled") def ScanForHW(self): global dev_trainer, dev_ant, simulatetrainer # get ant stick if debug: print("get ant stick") if not dev_ant: dev_ant, msg = ant.get_ant(debug) if not dev_ant: if not headless: self.ANTVariable.set(msg) return False if not headless: self.ANTVariable.set(msg) if not headless: self.PowerFactorVariable.set(powerfactor) if debug: print("get trainer") # find trainer model for Windows and Linux if not dev_trainer: # find trainer if simulatetrainer: if not headless: self.trainerVariable.set(u"Simulated Trainer") else: print("Simulated Trainer") else: dev_trainer = trainer.get_trainer() if not dev_trainer: if not headless: self.trainerVariable.set("Trainer not detected") else: print("Trainer not detected") return False else: if not headless: self.trainerVariable.set("Trainer detected") else: print("Trainer detected") trainer.initialise_trainer( dev_trainer) # initialise trainer if not headless: self.StartAPPbutton.config(state="normal") if not simulatetrainer: self.RunoffButton.config(state="normal") self.FindHWbutton.config(state="disabled") return True def Start(self): def run(): global dev_ant, dev_trainer, simulatetrainer, switch, power_curve if power_curve == "": if not headless: self.PowerCurveVariable.set( "Choose a power curve under setup menu") self.StartAPPbutton.config(state="normal") self.StopAPPbutton.config(state="disabled") return pc_dict = trainer.parse_factors( power_curve) # get power curve dictionary if len(pc_dict) != 14: if not headless: self.PowerCurveVariable.set( "Need 14 levels for power curve") self.StartAPPbutton.config(state="normal") self.StopAPPbutton.config(state="disabled") return pc_sorted_keys = sorted(pc_dict.iterkeys()) # -1,-0,2,3 etc. if debug: print("reset ant stick") ant.antreset(dev_ant, debug) # reset dongle if debug: print("calibrate ant stick") ant.calibrate(dev_ant, debug) # calibrate ANT+ dongle if debug: print("calibrate ant stick FE-C") ant.master_channel_config(dev_ant, debug) # calibrate ANT+ channel FE-C if debug: print("calibrate ant stick HR") ant.second_channel_config(dev_ant, debug) # calibrate ANT+ channel HR if not headless: self.RunoffButton.config(state="disabled") else: print("Ctrl-C to exit") speed, cadence, power, heart_rate = (0, ) * 4 # initialise values grade = False target_power = False accumulated_power = 0 heart_beat_event_time = time.time() * 1000 heart_beat_event_time_start_cycle = time.time() * 1000 heart_toggle = 0 heart_beat_count = 0 switch = True cot_start = time.time() eventcounter = 0 # p.44 [10] general fe data, [19] eqpt type trainer, [89] acc value time since start in 0.25s r/over 64s, [8c] acc value time dist travelled in m r/over 256m, # [8d] [20] speed lsb msb 0.001m/s, [00] hr, [30] capabilities bit field accumulated_time = time.time() * 1000 distance_travelled = 0 last_dist_time = time.time() * 1000 # p.60 [19] specific trainer data, [10] counter rollover 256, [5a] inst cadence, [b0] acc power lsb, [47] acc power msb (r/over 65536W), [1b] inst power lsb, # [01] bits 0-3 inst power MSB bits 4-7 trainer status bit, [30] flags bit field last_measured_time = time.time() * 1000 try: while switch: if debug: print("Running", round(time.time() * 1000 - last_measured_time)) last_measured_time = time.time() * 1000 if eventcounter >= 256: eventcounter = 0 # TRAINER- SHOULD WRITE THEN READ 70MS LATER REALLY # ##################GET DATA FROM TRAINER################## if simulatetrainer: speed, pedecho, heart_rate, force_index, cadence = ( 20, 0, 70, 5, 90, ) else: speed, pedecho, heart_rate, force_index, cadence = trainer.receive( dev_trainer) # get data from device if speed == "Not Found": speed, pedecho, heart_rate, force_index, cadence = 0, 0, 0, 0, 0 if not headless: self.trainerVariable.set( "Cannot read from trainer") else: print("Cannot read from trainer") else: if not headless: self.trainerVariable.set("Trainer detected") # print force_index factors = pc_dict[pc_sorted_keys[force_index]] calc_power = int(speed * factors[0] + factors[1]) if calc_power < 0: calc_power = 0 if debug: print(speed, pedecho, heart_rate, force_index, cadence, calc_power) # ##################SEND DATA TO TRAINER################## # send resistance data to trainer if debug: print( datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f") [:-3], "GRADE", grade, "%") # set resistance level if not grade and not target_power: # if trainer not been been set a grade or target power grade = 0 resistance_level = len( pc_dict) - 1 # set to highest by default if grade is not False: # find resistance for grade for idx, g in enumerate(sorted(pc_dict)): if g >= grade: # find resistance value immediately above grade set by zwift resistance_level = idx break elif target_power: # get resistance closest for power target if speed < 10: speed = 10 # default to at least 10 kph closest = 1000 for idx, g in enumerate(sorted(pc_dict)): # iterate up power_at_level = int(speed * pc_dict[g][0] + pc_dict[g][1]) # print idx,g,power_at_level if (target_power - power_at_level)**2 < closest**2: resistance_level = idx closest = ((target_power - power_at_level)**2)**0.5 # print resistance_level if not simulatetrainer: trainer.send(dev_trainer, resistance_level, pedecho) # time.sleep(0.2)#simulated trainer timeout # ############BROADCAST AND RECEIVE ANT+ data############## if speed == "Not Found": speed, pedecho, calc_power, cadence = 0, 0, 0, 0 if calc_power >= 4094: calc_power = 4093 accumulated_power += calc_power if accumulated_power >= 65536: accumulated_power = 0 if ( eventcounter + 1 ) % 66 == 0 or eventcounter % 66 == 0: # send first and second manufacturer's info packet newdata = "a4 09 4e 00 50 ff ff 01 0f 00 85 83 bb 00 00" elif (eventcounter + 32) % 66 == 0 or ( eventcounter + 33 ) % 66 == 0: # send first and second product info packet newdata = "a4 09 4e 00 51 ff ff 01 01 00 00 00 b2 00 00" elif eventcounter % 3 == 0: # send general fe data every 3 packets accumulated_time_counter = int( (time.time() * 1000 - accumulated_time) / 1000 / 0.25) # time since start in 0.25 seconds if (accumulated_time_counter >= 256 ): # rollover at 64 seconds (256 quarter secs) accumulated_time_counter = 0 accumulated_time = time.time() * 1000 newdata = "{0}{1}{2}".format( "a4 09 4e 00 10 19 ", hex(accumulated_time_counter)[2:].zfill(2), " 8c 8d 20 00 30 72 00 00", ) # set time distance_travelled_since_last_loop = ( (time.time() * 1000 - last_dist_time) / 1000 * speed * 1000 / 3600 ) # speed reported in kph- convert to m/s last_dist_time = time.time( ) * 1000 # reset last loop time distance_travelled += distance_travelled_since_last_loop if distance_travelled >= 256: # reset at 256m distance_travelled = 0 newdata = "{0}{1}{2}".format( newdata[:21], hex(int(distance_travelled))[2:].zfill(2), newdata[23:], ) # set distance travelled hexspeed = hex(int(speed * 1000 * 1000 / 3600))[2:].zfill(4) newdata = "{0}{1}{2}{3}{4}".format( newdata[:24], hexspeed[2:], " ", hexspeed[:2], newdata[29:]) # set speed newdata = "{0}{1}{2}".format( newdata[:36], ant.calc_checksum(newdata), newdata[38:]) # recalculate checksum else: # send specific trainer data newdata = "{0}{1}{2}".format( "a4 09 4e 00 19 ", hex(eventcounter)[2:].zfill(2), " 5a b0 47 1b 01 30 6d 00 00", ) # increment event count if cadence >= 254: cadence = 253 newdata = "{0}{1}{2}".format( newdata[:18], hex(cadence)[2:].zfill(2), newdata[20:]) # instant cadence hexaccumulated_power = hex( int(accumulated_power))[2:].zfill(4) newdata = "{0}{1}{2}{3}{4}".format( newdata[:21], hexaccumulated_power[2:], " ", hexaccumulated_power[:2], newdata[26:], ) # set accumulated power hexinstant_power = hex(int(calc_power))[2:].zfill(4) hexinstant_power_lsb = hexinstant_power[2:] newdata = "{0}{1}{2}".format( newdata[:27], hexinstant_power_lsb, newdata[29:]) # set power lsb byte hexinstant_power_msb = hexinstant_power[:2] bits_0_to_3 = bin(int(hexinstant_power_msb, 16))[2:].zfill(4) power_msb_trainer_status_byte = "0000" + bits_0_to_3 newdata = "{0}{1}{2}".format( newdata[:30], hex(int(power_msb_trainer_status_byte))[2:].zfill( 2), newdata[32:], ) # set mixed trainer data power msb byte newdata = "{0}{1}{2}".format( newdata[:36], ant.calc_checksum(newdata), newdata[38:]) # recalculate checksum if debug: print( datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f") [:-3], "TRAINER DATA", newdata) reply = ant.send_ant([newdata], dev_ant, debug) # reply = [] # if rv[6:8]=="33": # rtn = {'grade' : int(rv[18:20]+rv[16:18],16) * 0.01 - 200} #7% in zwift = 3.5% grade in ANT+ matching = [s for s in reply if "a4094f0033" in s] # target resistance # 0x33 a4094f00 33 ffffffff964fff f7 is gradient message if matching: grade = ( int(matching[0][20:22] + matching[0][18:20], 16) * 0.01 - 200) target_power = False if not headless: self.SlopeVariable.set(round(grade, 1)) if not headless: self.TargetPowerVariable.set("") if debug: print(grade, matching[0]) else: matching = [s for s in reply if "a4094f0031" in s] # target watts # 0x31 a4094f00 31 ffffffffff5c02 72 is target power message in 0.25w 0x025c = 604 = 151W if matching: target_power = (int( matching[0][22:24] + matching[0][20:22], 16) / 4) grade = False if not headless: self.TargetPowerVariable.set(target_power) if not headless: self.SlopeVariable.set("") # ##################HR####################### # HR format # D00000693_-_ANT+_Device_Profile_-_Heart_Rate_Rev_2.1.pdf # [00][FF][FF][FF][55][03][01][48]p. 18 [00] bits 0:6 data page no, bit 7 toggle every 4th message, [ff][ff][ff] (reserved for page 0), [55][03] heart beat event time [lsb][ msb] rollover 64s, [01] heart beat count rollover 256, [instant heart rate]max 256 # [00][FF][FF][FF][55][03][01][48] # [00][FF][FF][FF][AA][06][02][48] # [00][FF][FF][FF][AA][06][02][48] # [80][FF][FF][FF][AA][06][02][48] # [80][FF][FF][FF][AA][06][02][48] # [80][FF][FF][FF][FF][09][03][48] # [80][FF][FF][FF][FF][09][03][48] # [00][FF][FF][FF][FF][09][03][48] # [00][FF][FF][FF][54][0D][04][48] # [00][FF][FF][FF][54][0D][04][48] # [00][FF][FF][FF][54][0D][04][48] # every 65th message send manufacturer and product info -apge 2 and page 3 # [82][0F][01][00][00][3A][12][48] - [82] page 2 with toggle on (repeat 4 times) # [83][01][01][33][4F][3F][13][48] - [83] page 3 with toggle on # if eventcounter > 40: heart_rate = 100 #comment out in production if heart_rate > 0: # i.e. heart rate belt attached if eventcounter % 4 == 0: # toggle bit every 4 counts if heart_toggle == 0: heart_toggle = 128 else: heart_toggle = 0 # check if heart beat has occurred as tacx only reports instanatenous heart rate data # last heart beat is at heart_beat_event_time # if now - heart_beat_event_time > time taken for hr to occur, trigger beat. 70 bpm = beat every 60/70 seconds if (time.time() * 1000 - heart_beat_event_time ) >= (60 / float(heart_rate)) * 1000: heart_beat_count += 1 # increment heart beat count heart_beat_event_time += ( 60 / float(heart_rate) ) * 1000 # reset last time of heart beat if (heart_beat_event_time - heart_beat_event_time_start_cycle >= 64000): # rollover every 64s heart_beat_event_time = ( time.time() * 1000 ) # reset last heart beat event heart_beat_event_time_start_cycle = ( time.time() * 1000) # reset start of cycle if heart_beat_count >= 256: heart_beat_count = 0 if heart_rate >= 256: heart_rate = 255 hex_heart_beat_time = int( (heart_beat_event_time - heart_beat_event_time_start_cycle) * 1.024) # convert ms to 1/1024 of a second hex_heart_beat_time = hex( hex_heart_beat_time)[2:].zfill(4) hr_byte_4 = hex_heart_beat_time[2:] hr_byte_5 = hex_heart_beat_time[:2] hr_byte_6 = hex(heart_beat_count)[2:].zfill(2) hr_byte_7 = hex(heart_rate)[2:].zfill(2) # data page 1,6,7 every 80s if ( eventcounter % 65 == 0 or (eventcounter + 1) % 65 == 0 or (eventcounter + 2) % 65 == 0 or (eventcounter + 3) % 65 == 0 ): # send first and second manufacturer's info packet hr_byte_0 = hex(2 + heart_toggle)[2:].zfill(2) hr_byte_1 = "0f" hr_byte_2 = "01" hr_byte_3 = "00" # [82][0F][01][00][00][3A][12][48] elif ((eventcounter + 31) % 65 == 0 or (eventcounter + 32) % 65 == 0 or (eventcounter + 33) % 65 == 0 or (eventcounter + 34) % 65 == 0 ): # send first and second product info packet hr_byte_0 = hex(3 + heart_toggle)[2:].zfill(2) hr_byte_1 = "01" hr_byte_2 = "01" hr_byte_3 = "33" # [83][01][01][33][4F][3F][13][48] elif ((eventcounter + 11) % 65 == 0 or (eventcounter + 12) % 65 == 0 or (eventcounter + 13) % 65 == 0 or (eventcounter + 44) % 65 == 0 ): # send page 0x01 cumulative operating time cot = int((time.time() - cot_start) / 2) cot_hex = hex(cot)[2:].zfill(6) hr_byte_0 = hex(1 + heart_toggle)[2:].zfill(2) hr_byte_1 = cot_hex[4:6] hr_byte_2 = cot_hex[2:4] hr_byte_3 = cot_hex[0:2] elif ((eventcounter + 21) % 65 == 0 or (eventcounter + 22) % 65 == 0 or (eventcounter + 23) % 65 == 0 or (eventcounter + 24) % 65 == 0): # send page 0x06 capabilities hr_byte_0 = hex(6 + heart_toggle)[2:].zfill(2) hr_byte_1 = "ff" hr_byte_2 = "00" hr_byte_3 = "00" elif ((eventcounter + 41) % 65 == 0 or (eventcounter + 42) % 65 == 0 or (eventcounter + 43) % 65 == 0 or (eventcounter + 44) % 65 == 0): # send page 0x07 battery hr_byte_0 = hex(7 + heart_toggle)[2:].zfill(2) hr_byte_1 = "64" hr_byte_2 = "55" hr_byte_3 = "13" else: # send page 0 hr_byte_0 = hex(0 + heart_toggle)[2:].zfill(2) hr_byte_1 = "ff" hr_byte_2 = "ff" hr_byte_3 = "ff" hrdata = ("a4 09 4e 01 " + hr_byte_0 + " " + hr_byte_1 + " " + hr_byte_2 + " " + hr_byte_3 + " " + hr_byte_4 + " " + hr_byte_5 + " " + hr_byte_6 + " " + hr_byte_7 + " 02 00 00") hrdata = ("a4 09 4e 01 " + hr_byte_0 + " " + hr_byte_1 + " " + hr_byte_2 + " " + hr_byte_3 + " " + hr_byte_4 + " " + hr_byte_5 + " " + hr_byte_6 + " " + hr_byte_7 + " " + ant.calc_checksum(hrdata) + " 00 00") time.sleep(0.125) # sleep for 125ms if debug: print( datetime.utcnow().strftime( "%Y-%m-%d %H:%M:%S.%f")[:-3], "HEART RATE", hrdata) ant.send_ant([hrdata], dev_ant, debug) # ###################wait #################### # add wait so we only send every 250ms time_to_process_loop = time.time( ) * 1000 - last_measured_time sleep_time = 0.25 - (time_to_process_loop) / 1000 if sleep_time < 0: sleep_time = 0 time.sleep(sleep_time) eventcounter += 1 if not headless: self.SpeedVariable.set(speed) self.HeartrateVariable.set(heart_rate) self.CadenceVariable.set(cadence) self.PowerVariable.set(calc_power) self.ResistanceLevelVariable.set(resistance_level) elif eventcounter % 4 == 0: print("Power %sW, HR %s, Cadence %s, Resistance %s" % ( calc_power, heart_rate, cadence, resistance_level, )) except KeyboardInterrupt: print("Stopped") ant.antreset(dev_ant, debug) # reset dongle if os.name == "posix": # close serial port to ANT stick on Linux dev_ant.close() if debug: print("stopped") if not headless: self.RunoffButton.config(state="normal") with open("user_defaults", "wb") as handle: # save defaults pickle.dump(user_defaults, handle, protocol=pickle.HIGHEST_PROTOCOL) if not headless: self.FindHWbutton.config(state="disabled") self.StartAPPbutton.config(state="disabled") self.StopAPPbutton.config(state="normal") thread = threading.Thread(target=run) thread.start() else: run()
class LED8x8AndButton(object): def __init__(self, buttonDownCallback=None, buttonUpCallback=None): self.root = Tk() self.root.title("8x8") self.buttonUpCallback = buttonUpCallback self.buttonDownCallback = buttonDownCallback self.pixels = [] for y in xrange(8): for x in xrange(8): bt = Button(self.root, bg="gray", width=2, height=1, state="disabled") self.pixels.append(bt) bt.grid(column=x, row=y) self.but = Button(self.root, text="#", width=3, height=1) self.but.grid(column=3, row=8, columnspan=2) self.butColor = Button(self.root, state="disabled", width=3) self.butColor.grid(column=1, row=8, columnspan=2) self.orgColor = self.butColor.cget("bg") self.but.bind("<Button-1>", self._buttonDown) self.but.bind("<ButtonRelease-1>", self._buttonUp) def mainLoop(self): self.root.mainloop() def _setXY(self, x, y, val): if val == 0: self.pixels[y * 8 + x].configure(bg="gray") elif val == 1: self.pixels[y * 8 + x].configure(bg="green") elif val == 2: self.pixels[y * 8 + x].configure(bg="red") elif val == 3: self.pixels[y * 8 + x].configure(bg="orange") else: self.pixels[y * 8 + x].configure(bg="white") def setButtonColor(self, val): if val == 0: self.butColor.configure(bg=self.orgColor) elif val == 1: self.butColor.configure(bg="green") elif val == 2: self.butColor.configure(bg="red") elif val == 3: self.butColor.configure(bg="orange") else: self.butColor.configure(bg="white") def writeDisplay(self, image): pos = 0 for x in xrange(8): sv = 1 v1 = ord(image[pos]) v2 = ord(image[pos + 1]) pos = pos + 2 for y in xrange(8): pv = 0 if (v1 & sv) != 0: pv = pv | 1 if (v2 & sv) != 0: pv = pv | 2 sv = sv << 1 self._setXY(7 - x, y, pv) def _buttonDown(self, event): if self.buttonDownCallback: self.buttonDownCallback() def _buttonUp(self, event): if self.buttonUpCallback: self.buttonUpCallback()
class GetKeysDialog(Toplevel): def __init__(self,parent,title,action,currentKeySequences,_htest=False): """ action - string, the name of the virtual event these keys will be mapped to currentKeys - list, a list of all key sequence lists currently mapped to virtual events, for overlap checking _htest - bool, change box location when running htest """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) self.resizable(height=FALSE,width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Cancel) self.parent = parent self.action=action self.currentKeySequences=currentKeySequences self.result='' self.keyString=StringVar(self) self.keyString.set('') self.SetModifiersForPlatform() # set self.modifiers, self.modifier_label self.modifier_vars = [] for modifier in self.modifiers: variable = StringVar(self) variable.set('') self.modifier_vars.append(variable) self.advanced = False self.CreateWidgets() self.LoadFinalKeyList() self.withdraw() #hide while setting geometry self.update_idletasks() self.geometry( "+%d+%d" % ( parent.winfo_rootx() + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), parent.winfo_rooty() + ((parent.winfo_height()/2 - self.winfo_reqheight()/2) if not _htest else 150) ) ) #centre dialog over parent (or below htest box) self.deiconify() #geometry set, unhide self.wait_window() def CreateWidgets(self): frameMain = Frame(self,borderwidth=2,relief=SUNKEN) frameMain.pack(side=TOP,expand=TRUE,fill=BOTH) frameButtons=Frame(self) frameButtons.pack(side=BOTTOM,fill=X) self.buttonOK = Button(frameButtons,text='OK', width=8,command=self.OK) self.buttonOK.grid(row=0,column=0,padx=5,pady=5) self.buttonCancel = Button(frameButtons,text='Cancel', width=8,command=self.Cancel) self.buttonCancel.grid(row=0,column=1,padx=5,pady=5) self.frameKeySeqBasic = Frame(frameMain) self.frameKeySeqAdvanced = Frame(frameMain) self.frameControlsBasic = Frame(frameMain) self.frameHelpAdvanced = Frame(frameMain) self.frameKeySeqAdvanced.grid(row=0,column=0,sticky=NSEW,padx=5,pady=5) self.frameKeySeqBasic.grid(row=0,column=0,sticky=NSEW,padx=5,pady=5) self.frameKeySeqBasic.lift() self.frameHelpAdvanced.grid(row=1,column=0,sticky=NSEW,padx=5) self.frameControlsBasic.grid(row=1,column=0,sticky=NSEW,padx=5) self.frameControlsBasic.lift() self.buttonLevel = Button(frameMain,command=self.ToggleLevel, text='Advanced Key Binding Entry >>') self.buttonLevel.grid(row=2,column=0,stick=EW,padx=5,pady=5) labelTitleBasic = Label(self.frameKeySeqBasic, text="New keys for '"+self.action+"' :") labelTitleBasic.pack(anchor=W) labelKeysBasic = Label(self.frameKeySeqBasic,justify=LEFT, textvariable=self.keyString,relief=GROOVE,borderwidth=2) labelKeysBasic.pack(ipadx=5,ipady=5,fill=X) self.modifier_checkbuttons = {} column = 0 for modifier, variable in zip(self.modifiers, self.modifier_vars): label = self.modifier_label.get(modifier, modifier) check=Checkbutton(self.frameControlsBasic, command=self.BuildKeyString, text=label,variable=variable,onvalue=modifier,offvalue='') check.grid(row=0,column=column,padx=2,sticky=W) self.modifier_checkbuttons[modifier] = check column += 1 labelFnAdvice=Label(self.frameControlsBasic,justify=LEFT, text=\ "Select the desired modifier keys\n"+ "above, and the final key from the\n"+ "list on the right.\n\n" + "Use upper case Symbols when using\n" + "the Shift modifier. (Letters will be\n" + "converted automatically.)") labelFnAdvice.grid(row=1,column=0,columnspan=4,padx=2,sticky=W) self.listKeysFinal=Listbox(self.frameControlsBasic,width=15,height=10, selectmode=SINGLE) self.listKeysFinal.bind('<ButtonRelease-1>',self.FinalKeySelected) self.listKeysFinal.grid(row=0,column=4,rowspan=4,sticky=NS) scrollKeysFinal=Scrollbar(self.frameControlsBasic,orient=VERTICAL, command=self.listKeysFinal.yview) self.listKeysFinal.config(yscrollcommand=scrollKeysFinal.set) scrollKeysFinal.grid(row=0,column=5,rowspan=4,sticky=NS) self.buttonClear=Button(self.frameControlsBasic, text='Clear Keys',command=self.ClearKeySeq) self.buttonClear.grid(row=2,column=0,columnspan=4) labelTitleAdvanced = Label(self.frameKeySeqAdvanced,justify=LEFT, text="Enter new binding(s) for '"+self.action+"' :\n"+ "(These bindings will not be checked for validity!)") labelTitleAdvanced.pack(anchor=W) self.entryKeysAdvanced=Entry(self.frameKeySeqAdvanced, textvariable=self.keyString) self.entryKeysAdvanced.pack(fill=X) labelHelpAdvanced=Label(self.frameHelpAdvanced,justify=LEFT, text="Key bindings are specified using Tkinter keysyms as\n"+ "in these samples: <Control-f>, <Shift-F2>, <F12>,\n" "<Control-space>, <Meta-less>, <Control-Alt-Shift-X>.\n" "Upper case is used when the Shift modifier is present!\n\n" + "'Emacs style' multi-keystroke bindings are specified as\n" + "follows: <Control-x><Control-y>, where the first key\n" + "is the 'do-nothing' keybinding.\n\n" + "Multiple separate bindings for one action should be\n"+ "separated by a space, eg., <Alt-v> <Meta-v>." ) labelHelpAdvanced.grid(row=0,column=0,sticky=NSEW) def SetModifiersForPlatform(self): """Determine list of names of key modifiers for this platform. The names are used to build Tk bindings -- it doesn't matter if the keyboard has these keys, it matters if Tk understands them. The order is also important: key binding equality depends on it, so config-keys.def must use the same ordering. """ if sys.platform == "darwin": self.modifiers = ['Shift', 'Control', 'Option', 'Command'] else: self.modifiers = ['Control', 'Alt', 'Shift'] self.modifier_label = {'Control': 'Ctrl'} # short name def ToggleLevel(self): if self.buttonLevel.cget('text')[:8]=='Advanced': self.ClearKeySeq() self.buttonLevel.config(text='<< Basic Key Binding Entry') self.frameKeySeqAdvanced.lift() self.frameHelpAdvanced.lift() self.entryKeysAdvanced.focus_set() self.advanced = True else: self.ClearKeySeq() self.buttonLevel.config(text='Advanced Key Binding Entry >>') self.frameKeySeqBasic.lift() self.frameControlsBasic.lift() self.advanced = False def FinalKeySelected(self,event): self.BuildKeyString() def BuildKeyString(self): keyList = modifiers = self.GetModifiers() finalKey = self.listKeysFinal.get(ANCHOR) if finalKey: finalKey = self.TranslateKey(finalKey, modifiers) keyList.append(finalKey) self.keyString.set('<' + string.join(keyList,'-') + '>') def GetModifiers(self): modList = [variable.get() for variable in self.modifier_vars] return [mod for mod in modList if mod] def ClearKeySeq(self): self.listKeysFinal.select_clear(0,END) self.listKeysFinal.yview(MOVETO, '0.0') for variable in self.modifier_vars: variable.set('') self.keyString.set('') def LoadFinalKeyList(self): #these tuples are also available for use in validity checks self.functionKeys=('F1','F2','F2','F4','F5','F6','F7','F8','F9', 'F10','F11','F12') self.alphanumKeys=tuple(string.ascii_lowercase+string.digits) self.punctuationKeys=tuple('~!@#%^&*()_-+={}[]|;:,.<>/?') self.whitespaceKeys=('Tab','Space','Return') self.editKeys=('BackSpace','Delete','Insert') self.moveKeys=('Home','End','Page Up','Page Down','Left Arrow', 'Right Arrow','Up Arrow','Down Arrow') #make a tuple of most of the useful common 'final' keys keys=(self.alphanumKeys+self.punctuationKeys+self.functionKeys+ self.whitespaceKeys+self.editKeys+self.moveKeys) self.listKeysFinal.insert(END, *keys) def TranslateKey(self, key, modifiers): "Translate from keycap symbol to the Tkinter keysym" translateDict = {'Space':'space', '~':'asciitilde','!':'exclam','@':'at','#':'numbersign', '%':'percent','^':'asciicircum','&':'ampersand','*':'asterisk', '(':'parenleft',')':'parenright','_':'underscore','-':'minus', '+':'plus','=':'equal','{':'braceleft','}':'braceright', '[':'bracketleft',']':'bracketright','|':'bar',';':'semicolon', ':':'colon',',':'comma','.':'period','<':'less','>':'greater', '/':'slash','?':'question','Page Up':'Prior','Page Down':'Next', 'Left Arrow':'Left','Right Arrow':'Right','Up Arrow':'Up', 'Down Arrow': 'Down', 'Tab':'Tab'} if key in translateDict.keys(): key = translateDict[key] if 'Shift' in modifiers and key in string.ascii_lowercase: key = key.upper() key = 'Key-' + key return key def OK(self, event=None): if self.advanced or self.KeysOK(): # doesn't check advanced string yet self.result=self.keyString.get() self.destroy() def Cancel(self, event=None): self.result='' self.destroy() def KeysOK(self): '''Validity check on user's 'basic' keybinding selection. Doesn't check the string produced by the advanced dialog because 'modifiers' isn't set. ''' keys = self.keyString.get() keys.strip() finalKey = self.listKeysFinal.get(ANCHOR) modifiers = self.GetModifiers() # create a key sequence list for overlap check: keySequence = keys.split() keysOK = False title = 'Key Sequence Error' if not keys: tkMessageBox.showerror(title=title, parent=self, message='No keys specified.') elif not keys.endswith('>'): tkMessageBox.showerror(title=title, parent=self, message='Missing the final Key') elif (not modifiers and finalKey not in self.functionKeys + self.moveKeys): tkMessageBox.showerror(title=title, parent=self, message='No modifier key(s) specified.') elif (modifiers == ['Shift']) \ and (finalKey not in self.functionKeys + self.moveKeys + ('Tab', 'Space')): msg = 'The shift modifier by itself may not be used with'\ ' this key symbol.' tkMessageBox.showerror(title=title, parent=self, message=msg) elif keySequence in self.currentKeySequences: msg = 'This key combination is already in use.' tkMessageBox.showerror(title=title, parent=self, message=msg) else: keysOK = True return keysOK
class GetKeysDialog(Toplevel): def __init__(self, parent, title, action, currentKeySequences, _htest=False): """ action - string, the name of the virtual event these keys will be mapped to currentKeys - list, a list of all key sequence lists currently mapped to virtual events, for overlap checking _htest - bool, change box location when running htest """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) self.resizable(height=FALSE, width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Cancel) self.parent = parent self.action = action self.currentKeySequences = currentKeySequences self.result = '' self.keyString = StringVar(self) self.keyString.set('') self.SetModifiersForPlatform( ) # set self.modifiers, self.modifier_label self.modifier_vars = [] for modifier in self.modifiers: variable = StringVar(self) variable.set('') self.modifier_vars.append(variable) self.advanced = False self.CreateWidgets() self.LoadFinalKeyList() self.withdraw() #hide while setting geometry self.update_idletasks() self.geometry("+%d+%d" % (parent.winfo_rootx() + (parent.winfo_width() / 2 - self.winfo_reqwidth() / 2), parent.winfo_rooty() + ((parent.winfo_height() / 2 - self.winfo_reqheight() / 2) if not _htest else 150)) ) #centre dialog over parent (or below htest box) self.deiconify() #geometry set, unhide self.wait_window() def CreateWidgets(self): frameMain = Frame(self, borderwidth=2, relief=SUNKEN) frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) frameButtons = Frame(self) frameButtons.pack(side=BOTTOM, fill=X) self.buttonOK = Button(frameButtons, text='OK', width=8, command=self.OK) self.buttonOK.grid(row=0, column=0, padx=5, pady=5) self.buttonCancel = Button(frameButtons, text='Cancel', width=8, command=self.Cancel) self.buttonCancel.grid(row=0, column=1, padx=5, pady=5) self.frameKeySeqBasic = Frame(frameMain) self.frameKeySeqAdvanced = Frame(frameMain) self.frameControlsBasic = Frame(frameMain) self.frameHelpAdvanced = Frame(frameMain) self.frameKeySeqAdvanced.grid(row=0, column=0, sticky=NSEW, padx=5, pady=5) self.frameKeySeqBasic.grid(row=0, column=0, sticky=NSEW, padx=5, pady=5) self.frameKeySeqBasic.lift() self.frameHelpAdvanced.grid(row=1, column=0, sticky=NSEW, padx=5) self.frameControlsBasic.grid(row=1, column=0, sticky=NSEW, padx=5) self.frameControlsBasic.lift() self.buttonLevel = Button(frameMain, command=self.ToggleLevel, text='Advanced Key Binding Entry >>') self.buttonLevel.grid(row=2, column=0, stick=EW, padx=5, pady=5) labelTitleBasic = Label(self.frameKeySeqBasic, text="New keys for '" + self.action + "' :") labelTitleBasic.pack(anchor=W) labelKeysBasic = Label(self.frameKeySeqBasic, justify=LEFT, textvariable=self.keyString, relief=GROOVE, borderwidth=2) labelKeysBasic.pack(ipadx=5, ipady=5, fill=X) self.modifier_checkbuttons = {} column = 0 for modifier, variable in zip(self.modifiers, self.modifier_vars): label = self.modifier_label.get(modifier, modifier) check = Checkbutton(self.frameControlsBasic, command=self.BuildKeyString, text=label, variable=variable, onvalue=modifier, offvalue='') check.grid(row=0, column=column, padx=2, sticky=W) self.modifier_checkbuttons[modifier] = check column += 1 labelFnAdvice=Label(self.frameControlsBasic,justify=LEFT, text=\ "Select the desired modifier keys\n"+ "above, and the final key from the\n"+ "list on the right.\n\n" + "Use upper case Symbols when using\n" + "the Shift modifier. (Letters will be\n" + "converted automatically.)") labelFnAdvice.grid(row=1, column=0, columnspan=4, padx=2, sticky=W) self.listKeysFinal = Listbox(self.frameControlsBasic, width=15, height=10, selectmode=SINGLE) self.listKeysFinal.bind('<ButtonRelease-1>', self.FinalKeySelected) self.listKeysFinal.grid(row=0, column=4, rowspan=4, sticky=NS) scrollKeysFinal = Scrollbar(self.frameControlsBasic, orient=VERTICAL, command=self.listKeysFinal.yview) self.listKeysFinal.config(yscrollcommand=scrollKeysFinal.set) scrollKeysFinal.grid(row=0, column=5, rowspan=4, sticky=NS) self.buttonClear = Button(self.frameControlsBasic, text='Clear Keys', command=self.ClearKeySeq) self.buttonClear.grid(row=2, column=0, columnspan=4) labelTitleAdvanced = Label( self.frameKeySeqAdvanced, justify=LEFT, text="Enter new binding(s) for '" + self.action + "' :\n" + "(These bindings will not be checked for validity!)") labelTitleAdvanced.pack(anchor=W) self.entryKeysAdvanced = Entry(self.frameKeySeqAdvanced, textvariable=self.keyString) self.entryKeysAdvanced.pack(fill=X) labelHelpAdvanced = Label( self.frameHelpAdvanced, justify=LEFT, text="Key bindings are specified using Tkinter keysyms as\n" + "in these samples: <Control-f>, <Shift-F2>, <F12>,\n" "<Control-space>, <Meta-less>, <Control-Alt-Shift-X>.\n" "Upper case is used when the Shift modifier is present!\n\n" + "'Emacs style' multi-keystroke bindings are specified as\n" + "follows: <Control-x><Control-y>, where the first key\n" + "is the 'do-nothing' keybinding.\n\n" + "Multiple separate bindings for one action should be\n" + "separated by a space, eg., <Alt-v> <Meta-v>.") labelHelpAdvanced.grid(row=0, column=0, sticky=NSEW) def SetModifiersForPlatform(self): """Determine list of names of key modifiers for this platform. The names are used to build Tk bindings -- it doesn't matter if the keyboard has these keys, it matters if Tk understands them. The order is also important: key binding equality depends on it, so config-keys.def must use the same ordering. """ if sys.platform == "darwin": self.modifiers = ['Shift', 'Control', 'Option', 'Command'] else: self.modifiers = ['Control', 'Alt', 'Shift'] self.modifier_label = {'Control': 'Ctrl'} # short name def ToggleLevel(self): if self.buttonLevel.cget('text')[:8] == 'Advanced': self.ClearKeySeq() self.buttonLevel.config(text='<< Basic Key Binding Entry') self.frameKeySeqAdvanced.lift() self.frameHelpAdvanced.lift() self.entryKeysAdvanced.focus_set() self.advanced = True else: self.ClearKeySeq() self.buttonLevel.config(text='Advanced Key Binding Entry >>') self.frameKeySeqBasic.lift() self.frameControlsBasic.lift() self.advanced = False def FinalKeySelected(self, event): self.BuildKeyString() def BuildKeyString(self): keyList = modifiers = self.GetModifiers() finalKey = self.listKeysFinal.get(ANCHOR) if finalKey: finalKey = self.TranslateKey(finalKey, modifiers) keyList.append(finalKey) self.keyString.set('<' + string.join(keyList, '-') + '>') def GetModifiers(self): modList = [variable.get() for variable in self.modifier_vars] return [mod for mod in modList if mod] def ClearKeySeq(self): self.listKeysFinal.select_clear(0, END) self.listKeysFinal.yview(MOVETO, '0.0') for variable in self.modifier_vars: variable.set('') self.keyString.set('') def LoadFinalKeyList(self): #these tuples are also available for use in validity checks self.functionKeys = ('F1', 'F2', 'F2', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12') self.alphanumKeys = tuple(string.ascii_lowercase + string.digits) self.punctuationKeys = tuple('~!@#%^&*()_-+={}[]|;:,.<>/?') self.whitespaceKeys = ('Tab', 'Space', 'Return') self.editKeys = ('BackSpace', 'Delete', 'Insert') self.moveKeys = ('Home', 'End', 'Page Up', 'Page Down', 'Left Arrow', 'Right Arrow', 'Up Arrow', 'Down Arrow') #make a tuple of most of the useful common 'final' keys keys = (self.alphanumKeys + self.punctuationKeys + self.functionKeys + self.whitespaceKeys + self.editKeys + self.moveKeys) self.listKeysFinal.insert(END, *keys) def TranslateKey(self, key, modifiers): "Translate from keycap symbol to the Tkinter keysym" translateDict = { 'Space': 'space', '~': 'asciitilde', '!': 'exclam', '@': 'at', '#': 'numbersign', '%': 'percent', '^': 'asciicircum', '&': 'ampersand', '*': 'asterisk', '(': 'parenleft', ')': 'parenright', '_': 'underscore', '-': 'minus', '+': 'plus', '=': 'equal', '{': 'braceleft', '}': 'braceright', '[': 'bracketleft', ']': 'bracketright', '|': 'bar', ';': 'semicolon', ':': 'colon', ',': 'comma', '.': 'period', '<': 'less', '>': 'greater', '/': 'slash', '?': 'question', 'Page Up': 'Prior', 'Page Down': 'Next', 'Left Arrow': 'Left', 'Right Arrow': 'Right', 'Up Arrow': 'Up', 'Down Arrow': 'Down', 'Tab': 'Tab' } if key in translateDict.keys(): key = translateDict[key] if 'Shift' in modifiers and key in string.ascii_lowercase: key = key.upper() key = 'Key-' + key return key def OK(self, event=None): if self.advanced or self.KeysOK(): # doesn't check advanced string yet self.result = self.keyString.get() self.destroy() def Cancel(self, event=None): self.result = '' self.destroy() def KeysOK(self): '''Validity check on user's 'basic' keybinding selection. Doesn't check the string produced by the advanced dialog because 'modifiers' isn't set. ''' keys = self.keyString.get() keys.strip() finalKey = self.listKeysFinal.get(ANCHOR) modifiers = self.GetModifiers() # create a key sequence list for overlap check: keySequence = keys.split() keysOK = False title = 'Key Sequence Error' if not keys: tkMessageBox.showerror(title=title, parent=self, message='No keys specified.') elif not keys.endswith('>'): tkMessageBox.showerror(title=title, parent=self, message='Missing the final Key') elif (not modifiers and finalKey not in self.functionKeys + self.moveKeys): tkMessageBox.showerror(title=title, parent=self, message='No modifier key(s) specified.') elif (modifiers == ['Shift']) \ and (finalKey not in self.functionKeys + self.moveKeys + ('Tab', 'Space')): msg = 'The shift modifier by itself may not be used with'\ ' this key symbol.' tkMessageBox.showerror(title=title, parent=self, message=msg) elif keySequence in self.currentKeySequences: msg = 'This key combination is already in use.' tkMessageBox.showerror(title=title, parent=self, message=msg) else: keysOK = True return keysOK