def display_menu(menu, prompt='>>>', header='', info=''): if len(menu) > Y_SIZE - 5: raise ValueError("Too many menu options") # print the header t.wipe() display_header(header) # print the menu items for k, v in menu.items(): print("{}) {}".format(k, v)) # print prompt and info print(prompt, end=' ') x, y = t.get_pos() print('\n\n' + info) t.set_pos(x, y) # get user's choice k = None while k not in menu: k = input('') t.set_pos(x, y) print(' ' * (X_SIZE - x), end='') t.set_pos(x, y) return k
def settings(): global CONFIG t.wipe() settings_menu = OrderedDict() settings_menu['e'] = "Denomination enable/inhibit" settings_menu['s'] = "Denomination security" settings_menu['d'] = "Direction enable/inhibit" settings_menu['o'] = "Optional functions" settings_menu['b'] = "Bar code ticket options" settings_menu['q'] = "Back" choice = display_menu(settings_menu, '>>>', "Settings", "Changes will take effect next time bill validator is initialized") if choice == 'e': denom_settings() elif choice == 's': security_settings() elif choice == 'd': direction_settings() elif choice == 'o': opt_settings() elif choice == 'b': t.wipe() print("Barcode settings not available.") input("Press enter to go back") return
def settings(): global CONFIG t.wipe() settings_menu = OrderedDict() settings_menu['e'] = "Denomination enable/inhibit" settings_menu['s'] = "Denomination security" settings_menu['d'] = "Direction enable/inhibit" settings_menu['o'] = "Optional functions" settings_menu['b'] = "Bar code ticket options" settings_menu['q'] = "Back" choice = display_menu( settings_menu, '>>>', "Settings", "Changes will take effect next time bill validator is initialized") if choice == 'e': denom_settings() elif choice == 's': security_settings() elif choice == 'd': direction_settings() elif choice == 'o': opt_settings() elif choice == 'b': t.wipe() print("Barcode settings not available.") input("Press enter to go back") return
def __init__(self, prompt=' ? ', sep=':'): sys.ps1 = sys.ps2 = '' self.cols, self.rows = termutils.get_size() self.prompt = prompt self.sep = sep self.use_color_scheme() termutils.set_color(self.fg, self.bg) termutils.wipe()
def confirm_quit(msg="Press enter to quit. "): """Wait for user to hit 'enter' before program close.""" try: input(msg) except EOFError: pass termutils.reset_color() termutils.wipe() exit()
def _draw_page(self, page): sep = self.ui.sep termutils.wipe() self._draw_hdr() termutils.set_color(self.ui.fg, self.ui.bg) for i, opt in enumerate(page): if opt is None: print('') continue print("{}{} {}".format(i, sep, opt.name))
def kb_loop(bv, stdout_lock, bv_lock): global CONFIG print("Press Q at any time to quit, or H for help") while True: with stdout_lock: opt = t.get_key(0.1) if opt is not None: opt = opt.lower() if opt == b'q': bv.bv_on = False with open(CONFIG_FILE, 'w') as f: CONFIG.write(f) return elif opt == b'h': print("Q - Quit\n" "H - Help\n" "S - Settings menu\n" "R - Reset and initialize bill validator\n" "P - Pause bill validator\n" "M - Stop polling " "and return to main menu") elif opt == b'm': return elif opt == b's': with stdout_lock: logging.debug("Entered settings menu from status poll") settings() logging.debug("Exited settings menu") bv.bv_status = None # print current status after returning t.wipe() elif opt == b'r': with bv_lock: logging.debug("Sending reset command") status = None while status != id003.ACK: bv.send_command(id003.RESET) status, data = bv.read_response() time.sleep(0.2) logging.debug("Received ACK") if bv.req_status()[0] == id003.INITIALIZE: denom = get_denoms() sec = get_security() dir = get_directions() opt = get_optional() logging.info("Initializing bill validator") bv.initialize(denom, sec, dir, opt) bv.bv_status = None elif opt == b'p': print("Not implemented yet")
def denom_settings(): global CONFIG t.wipe() display_header("Denomination enable/inhibit settings") opts = dict() set_opts = OrderedDict() for i, k in enumerate(CONFIG['bv.denom_inhibit'].keys()): if id003.DENOM_MAP[k] in id003.ESCROW_USA: denom = id003.ESCROW_USA[id003.DENOM_MAP[k]] else: denom = None denom_enabled = CONFIG['bv.denom_inhibit'].getboolean(k) opts[i] = k # index into this config section set_opts[k] = denom_enabled # cache settings before writing to config if denom is not None: line = k + ' (' + denom + '):\t\t' else: line = k + ':\t\t\t' if denom_enabled: line += 'X' else: line += '_' print(line) print( "\n\nIf a denom is inhibited through these settings that's not inhibited by the\n" "appropriate DIP switch on the BV, the BV will go into INHIBIT status." ) print( "\nPress Enter to save and go back, or Esc to go back without saving") t.set_pos(25, 3) max_opt = len(CONFIG['bv.denom_inhibit']) - 1 cur_opt = 0 while True: x, y = t.get_pos() c = t.getch() if c == b'\xe0H' and cur_opt > 0: # up t.set_pos(x, y - 1) cur_opt -= 1 elif c == b'\xe0P' and cur_opt < max_opt: # down t.set_pos(x, y + 1) cur_opt += 1 elif c == b'\t' and cur_opt == max_opt: # wrap around to first option t.set_pos(x, 3) cur_opt = 0 elif c == b'\t': # next option, same as down t.set_pos(x, y + 1) cur_opt += 1 elif c == b'X' or c == b'x': set_opts[opts[cur_opt]] = True print('X', end='') if cur_opt < max_opt: t.set_pos(x, y + 1) cur_opt += 1 else: t.set_pos(x, y) elif c == b' ': set_opts[opts[cur_opt]] = False print('_', end='') if cur_opt < max_opt: t.set_pos(x, y + 1) cur_opt += 1 else: t.set_pos(x, y) elif c == b'\r': # save and go back CONFIG['bv.denom_inhibit'] = set_opts return elif c == b'\x1b': # escape, go back without saving return
def security_settings(): global CONFIG t.wipe() display_header("Denomination security settings") opts = dict() set_opts = OrderedDict() for i, k in enumerate(CONFIG['bv.security'].keys()): if id003.DENOM_MAP[k] in id003.ESCROW_USA: denom = id003.ESCROW_USA[id003.DENOM_MAP[k]] else: denom = None denom_enabled = CONFIG['bv.security'].getboolean(k) opts[i] = k set_opts[k] = denom_enabled if denom is not None: line = k + ' (' + denom + '):\t\t' else: line = k + ':\t\t\t' if denom_enabled: line += 'X' else: line += '_' print(line) print("\n\nX = high security, _ = low security") print( "\nPress Enter to save and go back, or Esc to go back without saving") t.set_pos(25, 3) max_opt = len(CONFIG['bv.security']) - 1 cur_opt = 0 while True: x, y = t.get_pos() c = t.getch() if c == b'\xe0H' and cur_opt > 0: # up t.set_pos(x, y - 1) cur_opt -= 1 elif c == b'\xe0P' and cur_opt < max_opt: # down t.set_pos(x, y + 1) cur_opt += 1 elif c == b'\t' and cur_opt == max_opt: # wrap around to first option t.set_pos(x, 3) cur_opt = 0 elif c == b'\t': # next option, same as down t.set_pos(x, y + 1) cur_opt += 1 elif c == b'X' or c == b'x': set_opts[opts[cur_opt]] = True print('X', end='') if cur_opt < max_opt: t.set_pos(x, y + 1) cur_opt += 1 else: t.set_pos(x, y) elif c == b' ': set_opts[opts[cur_opt]] = False print('_', end='') if cur_opt < max_opt: t.set_pos(x, y + 1) cur_opt += 1 else: t.set_pos(x, y) elif c == b'\r': # save and go back CONFIG['bv.security'] = set_opts return elif c == b'\x1b': # escape, go back without saving return
def direction_settings(): global CONFIG t.wipe() display_header("Direction ihibit settings") opts = dict() set_opts = OrderedDict() for i, k in enumerate(CONFIG['bv.direction'].keys()): dir_enabled = CONFIG['bv.direction'].getboolean(k) opts[i] = k set_opts[k] = dir_enabled if k == 'fa': print("Front side up, left side in:\t\t", end='') elif k == 'fb': print("Front side up, right side in:\t\t", end='') elif k == 'bb': print("Back side up, left side in:\t\t", end='') elif k == 'ba': print("Back side up, right side in:\t\t", end='') start_x, start_y = t.get_pos() if dir_enabled: print('X') else: print('_') print("\n\n_ = enabled, X = inhibited") print( "\nPress Enter to save and go back, or Esc to go back without saving") t.set_pos(start_x, 3) max_opt = len(CONFIG['bv.direction']) - 1 cur_opt = 0 while True: x, y = t.get_pos() c = t.getch() if c == b'\xe0H' and cur_opt > 0: # up t.set_pos(x, y - 1) cur_opt -= 1 elif c == b'\xe0P' and cur_opt < max_opt: # down t.set_pos(x, y + 1) cur_opt += 1 elif c == b'\t' and cur_opt == max_opt: # wrap around to first option t.set_pos(x, 3) cur_opt = 0 elif c == b'\t': # next option, same as down t.set_pos(x, y + 1) cur_opt += 1 elif c == b'X' or c == b'x': set_opts[opts[cur_opt]] = True print('X', end='') if cur_opt < max_opt: t.set_pos(x, y + 1) cur_opt += 1 else: t.set_pos(x, y) elif c == b' ': set_opts[opts[cur_opt]] = False print('_', end='') if cur_opt < max_opt: t.set_pos(x, y + 1) cur_opt += 1 else: t.set_pos(x, y) elif c == b'\r': # save and go back CONFIG['bv.direction'] = set_opts return elif c == b'\x1b': # escape, go back without saving return
def opt_settings(): global CONFIG t.wipe() display_header("Optional function settings") opts = dict() set_opts = OrderedDict() opt_txt = { 'power_recovery': "Power recovery:\t\t\t\t", 'auto_retry': "Auto-retry operaton:\t\t\t", '24_char_barcode': "Accept 24-character barcodes:\t\t", 'near_full': "Stacker nearly full event:\t\t", 'entrance_event': "Entrance sensor event:\t\t\t", 'encryption': "Encryption:\t\t\t\t", } for i, k in enumerate(CONFIG['bv.optional'].keys()): opt_enabled = CONFIG['bv.optional'].getboolean(k) opts[i] = k set_opts[k] = opt_enabled print(opt_txt[k], end='') start_x, start_y = t.get_pos() if opt_enabled: print('X') else: print('_') print("\n\n_ = disabled, X = enabled") print( "\nPress Enter to save and go back, or Esc to go back without saving") t.set_pos(start_x, 3) max_opt = len(CONFIG['bv.optional']) - 1 cur_opt = 0 while True: x, y = t.get_pos() c = t.getch() if c == b'\xe0H' and cur_opt > 0: # up t.set_pos(x, y - 1) cur_opt -= 1 elif c == b'\xe0P' and cur_opt < max_opt: # down t.set_pos(x, y + 1) cur_opt += 1 elif c == b'\t' and cur_opt == max_opt: # wrap around to first option t.set_pos(x, 3) cur_opt = 0 elif c == b'\t': # next option, same as down t.set_pos(x, y + 1) cur_opt += 1 elif c == b'X' or c == b'x': set_opts[opts[cur_opt]] = True print('X', end='') if cur_opt < max_opt: t.set_pos(x, y + 1) cur_opt += 1 else: t.set_pos(x, y) elif c == b' ': set_opts[opts[cur_opt]] = False print('_', end='') if cur_opt < max_opt: t.set_pos(x, y + 1) cur_opt += 1 else: t.set_pos(x, y) elif c == b'\r': # save and go back CONFIG['bv.optional'] = set_opts return elif c == b'\x1b': # escape, go back without saving return
def opt_settings(): global CONFIG t.wipe() display_header("Optional function settings") opts = dict() set_opts = OrderedDict() opt_txt = { 'power_recovery': "Power recovery:\t\t\t\t", 'auto_retry': "Auto-retry operaton:\t\t\t", '24_char_barcode': "Accept 24-character barcodes:\t\t", 'near_full': "Stacker nearly full event:\t\t", 'entrance_event': "Entrance sensor event:\t\t\t", 'encryption': "Encryption:\t\t\t\t", } for i, k in enumerate(CONFIG['bv.optional'].keys()): opt_enabled = CONFIG['bv.optional'].getboolean(k) opts[i] = k set_opts[k] = opt_enabled print(opt_txt[k], end='') start_x, start_y = t.get_pos() if opt_enabled: print('X') else: print('_') print("\n\n_ = disabled, X = enabled") print("\nPress Enter to save and go back, or Esc to go back without saving") t.set_pos(start_x, 3) max_opt = len(CONFIG['bv.optional']) - 1 cur_opt = 0 while True: x, y = t.get_pos() c = t.getch() if c == b'\xe0H' and cur_opt > 0: # up t.set_pos(x, y-1) cur_opt -= 1 elif c == b'\xe0P' and cur_opt < max_opt: # down t.set_pos(x, y+1) cur_opt += 1 elif c == b'\t' and cur_opt == max_opt: # wrap around to first option t.set_pos(x, 3) cur_opt = 0 elif c == b'\t': # next option, same as down t.set_pos(x, y+1) cur_opt += 1 elif c == b'X' or c == b'x': set_opts[opts[cur_opt]] = True print('X', end='') if cur_opt < max_opt: t.set_pos(x, y+1) cur_opt += 1 else: t.set_pos(x, y) elif c == b' ': set_opts[opts[cur_opt]] = False print('_', end='') if cur_opt < max_opt: t.set_pos(x, y+1) cur_opt += 1 else: t.set_pos(x, y) elif c == b'\r': # save and go back CONFIG['bv.optional'] = set_opts return elif c == b'\x1b': # escape, go back without saving return
def direction_settings(): global CONFIG t.wipe() display_header("Direction ihibit settings") opts = dict() set_opts = OrderedDict() for i, k in enumerate(CONFIG['bv.direction'].keys()): dir_enabled = CONFIG['bv.direction'].getboolean(k) opts[i] = k set_opts[k] = dir_enabled if k == 'fa': print("Front side up, left side in:\t\t", end='') elif k == 'fb': print("Front side up, right side in:\t\t", end='') elif k == 'bb': print("Back side up, left side in:\t\t", end='') elif k == 'ba': print("Back side up, right side in:\t\t", end='') start_x, start_y = t.get_pos() if dir_enabled: print('X') else: print('_') print("\n\n_ = enabled, X = inhibited") print("\nPress Enter to save and go back, or Esc to go back without saving") t.set_pos(start_x, 3) max_opt = len(CONFIG['bv.direction']) - 1 cur_opt = 0 while True: x, y = t.get_pos() c = t.getch() if c == b'\xe0H' and cur_opt > 0: # up t.set_pos(x, y-1) cur_opt -= 1 elif c == b'\xe0P' and cur_opt < max_opt: # down t.set_pos(x, y+1) cur_opt += 1 elif c == b'\t' and cur_opt == max_opt: # wrap around to first option t.set_pos(x, 3) cur_opt = 0 elif c == b'\t': # next option, same as down t.set_pos(x, y+1) cur_opt += 1 elif c == b'X' or c == b'x': set_opts[opts[cur_opt]] = True print('X', end='') if cur_opt < max_opt: t.set_pos(x, y+1) cur_opt += 1 else: t.set_pos(x, y) elif c == b' ': set_opts[opts[cur_opt]] = False print('_', end='') if cur_opt < max_opt: t.set_pos(x, y+1) cur_opt += 1 else: t.set_pos(x, y) elif c == b'\r': # save and go back CONFIG['bv.direction'] = set_opts return elif c == b'\x1b': # escape, go back without saving return
def use_color_scheme(self, scheme="default"): """Sets the following colors: fg, bg - global hdr_fg, hdr_bg - header dlg_fg, dlg_bg - dialog box but_fg, but_bg - buttons bdr_fg, bdr_bg - borders sel_fg, sel_bg - selection ps1_fg, ps1_bg - prompt dir_clr - directory file_clr - file """ if scheme == "default": self.fg = 7 self.bg = 0 self.hdr_fg = 6 self.hdr_bg = 0 self.dlg_fg = 7 self.dlg_bg = 4 self.but_fg = 0 self.but_bg = 7 self.bdr_fg = 7 self.bdr_bg = 4 self.sel_fg = 7 self.sel_bg = 1 self.ps1_fg = 7 self.ps1_bg = 0 self.dir_clr = 3 self.file_clr = 7 termutils.set_bright() elif scheme == "curses": self.fg = 7 self.bg = 4 self.hdr_fg = 6 self.hdr_bg = 4 self.dlg_fg = 7 self.dlg_bg = 4 self.but_fg = 0 self.but_bg = 7 self.bdr_fg = 7 self.bdr_bg = 4 self.sel_fg = 7 self.sel_bg = 1 self.ps1_fg = 3 self.ps1_bg = 4 self.dir_clr = 3 self.file_clr = 7 termutils.set_bright() elif scheme == "as400": self.fg = 2 self.bg = 0 self.hdr_fg = 7 self.hdr_bg = 0 self.dlg_fg = 6 self.dlg_bg = 0 self.but_fg = 0 self.but_bg = 6 self.bdr_fg = 7 self.bdr_bg = 6 self.sel_fg = 0 self.sel_bg = 7 self.ps1_fg = 2 self.ps1_bg = 0 self.dir_clr = 5 self.file_clr = 3 termutils.set_bright() termutils.set_color(self.fg, self.bg) termutils.wipe()
def security_settings(): global CONFIG t.wipe() display_header("Denomination security settings") opts = dict() set_opts = OrderedDict() for i, k in enumerate(CONFIG['bv.security'].keys()): if id003.DENOM_MAP[k] in id003.ESCROW_USA: denom = id003.ESCROW_USA[id003.DENOM_MAP[k]] else: denom = None denom_enabled = CONFIG['bv.security'].getboolean(k) opts[i] = k set_opts[k] = denom_enabled if denom is not None: line = k + ' (' + denom + '):\t\t' else: line = k + ':\t\t\t' if denom_enabled: line += 'X' else: line += '_' print(line) print("\n\nX = high security, _ = low security") print("\nPress Enter to save and go back, or Esc to go back without saving") t.set_pos(25, 3) max_opt = len(CONFIG['bv.security']) - 1 cur_opt = 0 while True: x, y = t.get_pos() c = t.getch() if c == b'\xe0H' and cur_opt > 0: # up t.set_pos(x, y-1) cur_opt -= 1 elif c == b'\xe0P' and cur_opt < max_opt: # down t.set_pos(x, y+1) cur_opt += 1 elif c == b'\t' and cur_opt == max_opt: # wrap around to first option t.set_pos(x, 3) cur_opt = 0 elif c == b'\t': # next option, same as down t.set_pos(x, y+1) cur_opt += 1 elif c == b'X' or c == b'x': set_opts[opts[cur_opt]] = True print('X', end='') if cur_opt < max_opt: t.set_pos(x, y+1) cur_opt += 1 else: t.set_pos(x, y) elif c == b' ': set_opts[opts[cur_opt]] = False print('_', end='') if cur_opt < max_opt: t.set_pos(x, y+1) cur_opt += 1 else: t.set_pos(x, y) elif c == b'\r': # save and go back CONFIG['bv.security'] = set_opts return elif c == b'\x1b': # escape, go back without saving return
def denom_settings(): global CONFIG t.wipe() display_header("Denomination enable/inhibit settings") opts = dict() set_opts = OrderedDict() for i, k in enumerate(CONFIG['bv.denom_inhibit'].keys()): if id003.DENOM_MAP[k] in id003.ESCROW_USA: denom = id003.ESCROW_USA[id003.DENOM_MAP[k]] else: denom = None denom_enabled = CONFIG['bv.denom_inhibit'].getboolean(k) opts[i] = k # index into this config section set_opts[k] = denom_enabled # cache settings before writing to config if denom is not None: line = k + ' (' + denom + '):\t\t' else: line = k + ':\t\t\t' if denom_enabled: line += 'X' else: line += '_' print(line) print("\n\nIf a denom is inhibited through these settings that's not inhibited by the\n" "appropriate DIP switch on the BV, the BV will go into INHIBIT status.") print("\nPress Enter to save and go back, or Esc to go back without saving") t.set_pos(25, 3) max_opt = len(CONFIG['bv.denom_inhibit']) - 1 cur_opt = 0 while True: x, y = t.get_pos() c = t.getch() if c == b'\xe0H' and cur_opt > 0: # up t.set_pos(x, y-1) cur_opt -= 1 elif c == b'\xe0P' and cur_opt < max_opt: # down t.set_pos(x, y+1) cur_opt += 1 elif c == b'\t' and cur_opt == max_opt: # wrap around to first option t.set_pos(x, 3) cur_opt = 0 elif c == b'\t': # next option, same as down t.set_pos(x, y+1) cur_opt += 1 elif c == b'X' or c == b'x': set_opts[opts[cur_opt]] = True print('X', end='') if cur_opt < max_opt: t.set_pos(x, y+1) cur_opt += 1 else: t.set_pos(x, y) elif c == b' ': set_opts[opts[cur_opt]] = False print('_', end='') if cur_opt < max_opt: t.set_pos(x, y+1) cur_opt += 1 else: t.set_pos(x, y) elif c == b'\r': # save and go back CONFIG['bv.denom_inhibit'] = set_opts return elif c == b'\x1b': # escape, go back without saving return
def thang(): termutils.wipe() confirm_quit()
sys.ps1 = "" sys.ps2 = "" # basic telnet client without keystroke buffering def tn_loop(key_pipe): tn = Telnet(HOST, PORT) tn.write(b'\n') while True: if key_pipe.poll(): tn.write(key_pipe.recv()) data = tn.read_very_eager() print(data.decode(), end='') if __name__ == '__main__': t.wipe() p_pipe, ch_pipe = Pipe() tn_conn = Process(target=tn_loop, args=(ch_pipe, )) tn_conn.start() while True: try: k = t.get_key() except: sys.exit() else: p_pipe.send(k)
def main(): global CONFIG comport = CONFIG['main']['comport'] poll_interval = float(CONFIG['main']['poll_interval']) main_menu = OrderedDict() main_menu['r'] = "Run" main_menu['s'] = "Settings" main_menu['c'] = "Select COM port" main_menu['q'] = "Quit" choice = display_menu(main_menu, '>>>', "ID-003 protocol analyzer", "Using COM port %s" % comport) if choice == 'r': t.wipe() raw = CONFIG['main'].getboolean('debug') try: bv = id003.BillVal(comport, log_raw=raw, threading=True) except SerialException: print("Unable to open serial port") q = 'x' while q not in 'qm': q = input("(Q)uit or (M)ain menu? ").lower() if q == 'q': return True elif q == 'm': return stdout_lock = threading.Lock() bv_lock = threading.Lock() poll_args = (bv, stdout_lock, bv_lock, poll_interval) poll_thread = threading.Thread(target=poll_loop, args=poll_args) kb_args = (bv, stdout_lock, bv_lock) kb_thread = threading.Thread(target=kb_loop, args=kb_args) poll_thread.start() while bv.bv_status != (id003.IDLE, b''): # wait for power-up before starting keyboard loop continue kb_thread.start() kb_thread.join() if not bv.bv_on: # kb_thread quit, not main menu bv.com.close() return True else: # terminate poll thread bv.bv_on = False poll_thread.join() bv.com.close() del poll_thread del kb_thread del bv return elif choice == 's': settings() return elif choice == 'c': t.wipe() com_menu = OrderedDict() ports = list(serial.tools.list_ports.comports()) for i, p in enumerate(ports): com_menu[str(i + 1)] = p.description com_menu['q'] = "Back to main menu" port = display_menu(com_menu, '>>>', "Select COM port") if port == 'q': return else: port = int(port) - 1 CONFIG['main']['comport'] = ports[port].device return elif choice == 'q': return True
def main(): global CONFIG comport = CONFIG['main']['comport'] poll_interval = float(CONFIG['main']['poll_interval']) main_menu = OrderedDict() main_menu['r'] = "Run" main_menu['s'] = "Settings" main_menu['c'] = "Select COM port" main_menu['q'] = "Quit" choice = display_menu(main_menu, '>>>', "ID-003 protocol analyzer", "Using COM port %s" % comport) if choice == 'r': t.wipe() raw = CONFIG['main'].getboolean('debug') try: bv = id003.BillVal(comport, log_raw=raw, threading=True) except SerialException: print("Unable to open serial port") q = 'x' while q not in 'qm': q = input("(Q)uit or (M)ain menu? ").lower() if q == 'q': return True elif q == 'm': return stdout_lock = threading.Lock() bv_lock = threading.Lock() poll_args = (bv, stdout_lock, bv_lock, poll_interval) poll_thread = threading.Thread(target=poll_loop, args=poll_args) kb_args = (bv, stdout_lock, bv_lock) kb_thread = threading.Thread(target=kb_loop, args=kb_args) poll_thread.start() while bv.bv_status != (id003.IDLE, b''): # wait for power-up before starting keyboard loop continue kb_thread.start() kb_thread.join() if not bv.bv_on: # kb_thread quit, not main menu bv.com.close() return True else: # terminate poll thread bv.bv_on = False poll_thread.join() bv.com.close() del poll_thread del kb_thread del bv return elif choice == 's': settings() return elif choice == 'c': t.wipe() com_menu = OrderedDict() ports = list(serial.tools.list_ports.comports()) for i, p in enumerate(ports): com_menu[str(i+1)] = p.description com_menu['q'] = "Back to main menu" port = display_menu(com_menu, '>>>', "Select COM port") if port == 'q': return else: port = int(port) - 1 CONFIG['main']['comport'] = ports[port].device return elif choice == 'q': return True