class WebcamWindow(Window): def __init__(self, tui): super().__init__(tui) self.body.contents.append((Text("Available Webcams"), ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.walker = SimpleFocusListWalker([]) listbox = ListBox(self.walker) self.body.contents.append((listbox, ('weight', 1))) self.body.focus_position = 2 self.add_hotkey('U', self.update, "update") self.update() def update(self): def camlist_cb(json_msg): self.walker.clear() def button_cb(locator, url, button): webbrowser.open_new(url) self.tui.pop_window() self.display_errors(json_msg) webcams = json_msg.get('webcams', None) if not webcams: return for locator, webcam in webcams.items(): name = webcam["name"] url = webcam["url"] button = Button(name) urwid.connect_signal(button, 'click', button_cb, user_args=[locator, url]) self.walker.append(AttrMap(button, None, focus_map='selected')) self.tui.controller.get_camlist(camlist_cb)
class DeviceListWindow(Window): def __init__(self, tui): super().__init__(tui) self.body.contents.append( (Text("Available CNC Devices"), ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.walker = SimpleFocusListWalker([]) listbox = ListBox(self.walker) self.body.contents.append((listbox, ('weight', 1))) self.body.focus_position = 2 self.add_hotkey('U', self.update, "update") self.add_hotkey('w', self.webcams, "webcams") self.add_hotkey('a', self.actions, "actions") self.update() def update(self): def devlist_cb(json_msg): self.walker.clear() def button_cb(locator, device, button): window = DeviceWindow(self.tui, locator, device) self.tui.push_window(window) self.display_errors(json_msg) devices = json_msg.get('devices') if not devices: return for locator, device in devices.items(): button = Button(device) urwid.connect_signal(button, 'click', button_cb, user_args=[locator, device]) self.walker.append(AttrMap(button, None, focus_map='selected')) self.tui.controller.get_devlist(devlist_cb)
class ActionWindow(Window): def __init__(self, tui): super().__init__(tui) self.body.contents.append((Text("Available Actions"), ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.walker = SimpleFocusListWalker([]) listbox = ListBox(self.walker) self.body.contents.append((listbox, ('weight', 1))) self.body.focus_position = 2 self.add_hotkey('U', self.update, "update") self.update() def update(self): def action_cb(json_msg): self.walker.clear() def button_cb(cmd, button): self.tui.controller.action(cmd, None) self.tui.pop_window() self.display_errors(json_msg) actions = json_msg.get('actions', None) if not actions: return for action in actions: cmd = action['command'] label = action['short'] description = action['long'] button = Button("[{}] - {}".format(label, description)) urwid.connect_signal(button, 'click', button_cb, user_args=[cmd]) self.walker.append(AttrMap(button, None, focus_map='selected')) self.tui.controller.get_actions(action_cb)
class DeviceWindow(Window): def __init__(self, tui, locator, device): super().__init__(tui) self.body.contents.append( (Text("Selected device \"{}\"".format(device)), ('pack', None))) fn = self.tui.controller.get_filename(locator) self.filename_txt = Text("Selected file \"{}\"".format(fn)) self.body.contents.append((self.filename_txt, ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.statuswidget = Pile([]) self.body.contents.append((self.statuswidget, ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.locator = locator self.device = device self.walker = SimpleFocusListWalker([]) listbox = ListBox(self.walker) self.body.contents.append((listbox, ('weight', 1))) self.body.focus_position = 5 self.add_hotkey('U', self.update, "update") self.add_hotkey('w', self.webcams, "webcams") self.add_hotkey('a', self.actions, "actions") self.status = defaultdict(str) self.update() def start(self): self.update() self.tui.controller.subscribe( partial(self.update_status_cb, self.statuswidget), self.locator) def stop(self): self.tui.controller.unsubscribe(None, self.locator) def update_status_cb(self, container, json_msg): self.display_errors(json_msg) ## TODO filter error from loop below. The results should be in a known ## section like 'i3' data = json_msg.get(self.locator, {}) for key, value in data.items(): self.status[key] = value def parse_time(status, container, ignore): try: tstart = float(status['starttime']) tstop = float(status['stoptime']) offset = self.tui.controller.time_offset if tstop == -1: d = time.time() - tstart - offset else: d = tstop - tstart t = time.asctime(time.localtime(tstart + offset)) txt = "Started at {} ({}h{}m)".format(t, int(d / 3600), int(d % 3600) // 60) attr = 'Flabel' w = AttrMap(Text(txt), attr, attr) container.contents.append((w, container.options('pack'))) except ValueError: pass finally: ignore.append('starttime') #ignore.append('stoptime') ## leave for debugging for now def parse_progress(status, container, ignore): try: fsize = int(status['filesize']) fprog = int(status['progress']) tstart = float(status['starttime']) tstop = float(status['stoptime']) cz = float(status['current_z']) fz = float(status['final_z']) ce = float(status['current_e']) fe = float(status['final_e']) offset = self.tui.controller.time_offset if tstop == -1: d = time.time() - tstart - offset else: d = tstop - tstart rate = fprog / fsize attr = 'Flabel' def div(a, b): if b == 0: return 0 return a / b label = "File: {:0.0f}/{:0.0f} Kb ({:0.2f}%)".format( fprog / 1024, fsize / 1024, rate * 100) bar = NamedProgressBar(label, 'progress_normal', 'progress_complete', fprog, fsize, 'status') container.contents.append((bar, container.options('pack'))) label = "Z Travel: {:0.2f}/{:0.2f} mm ({:0.2f}%)".format( cz, fz, div(cz, fz) * 100) bar = NamedProgressBar(label, 'progress_normal', 'progress_complete', cz, fz, 'status') container.contents.append((bar, container.options('pack'))) label = "Extruded: {:0.0f}/{:0.0f} mm ({:0.2f}%)".format( ce, fe, div(ce, fe) * 100) bar = NamedProgressBar(label, 'progress_normal', 'progress_complete', ce, fe, 'status') container.contents.append((bar, container.options('pack'))) except: pass finally: ignore.append('filesize') ignore.append('progress') ignore.append('current_z') ignore.append('final_z') ignore.append('current_e') ignore.append('final_e') def parse_status(status, container, ignore): stat_cols = Columns([], 0) w = make_w(stat_cols, 'connected', 'connected', 'disconnected') w = make_w(stat_cols, 'idle', 'active', 'idle', reverse=True) w = make_w(stat_cols, 'paused', 'paused', 'operating') container.contents.append((stat_cols, container.options('pack'))) ignore.append('connected') ignore.append('idle') ignore.append('paused') def make_w(container, key, Tlabel, Flabel, reverse=False): if (self.status[key] == True) ^ reverse: label = "[{}]".format(Tlabel) attr = 'Tlabel' else: label = "[{}]".format(Flabel) attr = 'Flabel' w = AttrMap(Text(label), attr, attr) container.contents.append((w, container.options('pack'))) ignore = ['last_temp_request'] # We really got to properly parse this to some struct first container.contents.clear() ## progress parse_status(self.status, container, ignore) parse_progress(self.status, container, ignore) parse_time(self.status, container, ignore) for key, value in sorted(self.status.items()): if key in ignore: continue attr = 'Flabel' w = AttrMap(Text("{}: {}".format(key, value)), attr, attr) container.contents.append((w, container.options('pack'))) def update_status(self): self.tui.controller.get_data( partial(self.update_status_cb, self.statuswidget), self.locator) def update(self): self.update_status() self.walker.clear() locator = self.locator def cmd_cb(json_msg): self.display_errors(json_msg) self.update_status() fn = self.tui.controller.get_filename(locator) self.filename_txt.set_text("Selected file \"{}\"".format(fn)) button = Button("[c] Connect") def button_cb(button, locator): self.tui.controller.connect(cmd_cb, locator) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('c', partial(button_cb, button, locator), "connect", omit_header=True) button = Button("[D] Disconnect") def button_cb(button, locator): self.tui.controller.disconnect(cmd_cb, locator) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('D', partial(button_cb, button, locator), "disconnect", omit_header=True) button = Button("[s] Start") def button_cb(button, locator): fn = self.tui.controller.get_filename(locator) if not fn: self.footer.set_text("Please Select a file first") return self.tui.controller.start( cmd_cb, locator, self.tui.controller.get_filename(locator)) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('s', partial(button_cb, button, locator), "start", omit_header=True) button = Button("[S] Stop (ask nicely to stop)") def button_cb(button, locator): self.tui.controller.stop(cmd_cb, locator) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('S', partial(button_cb, button, locator), "stop", omit_header=True) button = Button("[!] Abort (Interrupt then disconnect)") def button_cb(button, locator): self.tui.controller.abort(cmd_cb, locator) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('!', partial(button_cb, button, locator), "abort", omit_header=True) button = Button("[p] Pause") def button_cb(button, locator): self.tui.controller.pause(cmd_cb, locator) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('p', partial(button_cb, button, locator), "pause", omit_header=True) button = Button("[r] Resume") def button_cb(button, locator): self.tui.controller.resume(cmd_cb, locator) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('r', partial(button_cb, button, locator), "resume", omit_header=True) button = Button("[l] Load File") def button_cb(button, locator): window = FileListWindow(self.tui, locator, self.device) self.tui.push_window(window) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('l', partial(button_cb, button, locator), "load", omit_header=True) button = Button("[m] Manual Control") def button_cb(button, locator): window = ManualControlWindow(self.tui, locator, self.device) self.tui.push_window(window) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('m', partial(button_cb, button, locator), "manual", omit_header=True)
class ManualControlWindow(Window): def __init__(self, tui, locator, device): super().__init__(tui) self.locator = locator self.device = device self.add_hotkey('U', self.update, "update") self.add_hotkey('w', self.webcams, "webcams") self.add_hotkey('a', self.actions, "actions") self.body.contents.append( (Text("Selected device \"{}\"".format(device)), ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.walker = SimpleFocusListWalker([]) listbox = ListBox(self.walker) self.body.contents.append((listbox, ('weight', 1))) self.body.focus_position = 2 self.update() def update(self): self.walker.clear() locator = self.locator def cmd_cb(json_msg): self.display_errors(json_msg) button = Button("[h] Home (G28 W)") def button_cb(button, locator): self.tui.controller.action("gcode {} 'G28 W'".format(locator), cmd_cb) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('h', partial(button_cb, button, locator), "home", omit_header=True) def edit_cb(txt): self.tui.controller.action("gcode {} 'G90'".format(locator), cmd_cb) self.tui.controller.action( "gcode {} 'G1 X{}'".format(locator, txt), cmd_cb) edit = CB_Edit("Move to ABS X (mm): ", "0", None, edit_cb, NUMERICALS) self.walker.append(AttrMap(edit, None, focus_map='selected')) def edit_cb(txt): self.tui.controller.action("gcode {} 'G90'".format(locator), cmd_cb) self.tui.controller.action( "gcode {} 'G1 Y{}'".format(locator, txt), cmd_cb) edit = CB_Edit("Move to ABS Y (mm): ", "0", None, edit_cb, NUMERICALS) self.walker.append(AttrMap(edit, None, focus_map='selected')) def edit_cb(txt): self.tui.controller.action("gcode {} 'G90'".format(locator), cmd_cb) self.tui.controller.action( "gcode {} 'G1 Z{}'".format(locator, txt), cmd_cb) edit = CB_Edit("Move to ABS Z (mm): ", "0", None, edit_cb, NUMERICALS) self.walker.append(AttrMap(edit, None, focus_map='selected')) def edit_cb(txt): self.tui.controller.action( "gcode {} 'G1 F{}'".format(locator, txt), cmd_cb) edit = CB_Edit("Set Feedrate mm/min: ", "", None, edit_cb, NUMERICALS) self.walker.append(AttrMap(edit, None, focus_map='selected')) def edit_cb(txt): self.tui.controller.action( "gcode {} 'M104 S{}'".format(locator, txt), cmd_cb) edit = CB_Edit("Set Extruder Temperature °C: ", "0", None, edit_cb, NUMERICALS) self.walker.append(AttrMap(edit, None, focus_map='selected')) def edit_cb(txt): self.tui.controller.action( "gcode {} 'M140 S{}'".format(locator, txt), cmd_cb) edit = CB_Edit("Set Bed Temperature °C: ", "0", None, edit_cb, NUMERICALS) self.walker.append(AttrMap(edit, None, focus_map='selected')) button = Button("[L] Load filament (G1 E100 F300)") def button_cb(button, locator): self.tui.controller.action("gcode {} 'M83'".format(locator), cmd_cb) self.tui.controller.action( "gcode {} 'G1 E100 F300'".format(locator), cmd_cb) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('L', partial(button_cb, button, locator), "load", omit_header=True) button = Button("[U] Unload filament (G1 E-100 F2000)") def button_cb(button, locator): self.tui.controller.action("gcode {} 'M83'".format(locator), cmd_cb) self.tui.controller.action( "gcode {} 'G1 E-100 F2000'".format(locator), cmd_cb) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('U', partial(button_cb, button, locator), "unload", omit_header=True) button = Button("[r] Release steppers (M18)") def button_cb(button, locator): self.tui.controller.action("gcode {} 'M18'".format(locator), cmd_cb) urwid.connect_signal(button, 'click', button_cb, locator) self.walker.append(AttrMap(button, None, focus_map='selected')) self.add_hotkey('r', partial(button_cb, button, locator), "release", omit_header=True) self.walker.append(Divider()) def enter_cb(edit, txt): if edit.hjkl_active: edit.hjkl_active = False edit.set_edit_text("[enter] to activate / deactivate") else: edit.hjkl_active = True edit.set_edit_text("") self.footer.set_text("Use keys 'hjklaz' to move printer.") def edit_cb(edit, txt): if not edit.hjkl_active: return key = txt[-1] m = {'h': "X-", 'l': "X", 'j': "Y-", 'k': "Y", 'a': "Z", 'z': "Z-"} if key not in m: self.footer.set_text( "key '{}' not handled. Please use keys 'hjklaz' to move printer." .format(key)) return edit.set_edit_text("{}".format(m[key])) increment = 10 c = "gcode {} 'G1 ".format(locator) + m[key] + "{}'".format( increment) self.tui.controller.action("gcode {} 'G91'".format(locator), cmd_cb) self.tui.controller.action(c, cmd_cb) #self.tui.controller.action("gcode {} 'G1 F{}'".format(locator, txt), cmd_cb) edit = CB_Edit("VI Move: ", "[enter] to activate", edit_cb, enter_cb, [i for i in "hjklaz"]) edit.type_cb = partial(edit_cb, edit) ##hack to solve cyclic dependency edit.enter_cb = partial(enter_cb, edit) ##hack to solve cyclic dependency edit.hjkl_active = False self.walker.append(AttrMap(edit, None, focus_map='selected'))
class FileListWindow(Window): def __init__(self, tui, locator, device): super().__init__(tui) self.body.contents.append( (Text("Select file to load on \"{}\"".format(device)), ('pack', None))) self.body.contents.append((Divider(), ('pack', None))) self.device = device self.locator = locator self.device = device self.all_files = [] def limit(regexp): self.regexp = regexp self.populate_list() def enter(edit_text): if len(self.walker) < 1: self.tui.pop_window() return button = self.walker[0] button.keypress(1, 'enter') editbox = CB_Edit("Limit (regexp): ", "", limit, enter) self.body.contents.append((editbox, ('pack', 1))) self.body.contents.append((Divider(), ('pack', 1))) self.walker = SimpleFocusListWalker([]) listbox = ListBox(self.walker) self.body.contents.append((listbox, ('weight', 1))) self.body.focus_position = 2 self.update() self.regexp = ".*" def populate_list(self): try: p = re.compile(self.regexp, re.IGNORECASE) except re.error: filtered_files = self.all_files else: filtered_files = filter(p.search, self.all_files) def button_cb(locator, filename, button): self.filename = filename self.tui.controller.set_filename(locator, filename) self.tui.pop_window() self.walker.clear() for line in filtered_files: filename = line.strip() button = Button(filename) urwid.connect_signal(button, 'click', button_cb, user_args=[self.locator, filename]) self.walker.append(AttrMap(button, None, focus_map='selected')) def update(self): def filelist_cb(json_msg): self.display_errors(json_msg) self.all_files = json_msg.get('files', []) self.populate_list() self.tui.controller.get_filelist(filelist_cb, self.locator)