diff --git a/docs/changelog.md b/docs/changelog.md index 58165d3e..ec3ab0f3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,10 @@ ## Changelog +#### 2021 01 21 +* !! Important. matchbox-keyboard must be installed. Install script has changed to include this +* Added onscreen keyboard +* Can now add bed mesh profiles + #### 2021 01 20 * Add different time estimation methods to settings panel * Job status panel will use time estimation method selected from settings panel diff --git a/ks_includes/KlippyGcodes.py b/ks_includes/KlippyGcodes.py index 1a084f06..5fb6fd73 100644 --- a/ks_includes/KlippyGcodes.py +++ b/ks_includes/KlippyGcodes.py @@ -63,8 +63,12 @@ class KlippyGcodes: @staticmethod def bed_mesh_load(profile): - return "BED_MESH_PROFILE LOAD=%s" % profile + return "BED_MESH_PROFILE LOAD='%s'" % profile + + @staticmethod + def bed_mesh_remove(profile): + return "BED_MESH_PROFILE REMOVE='%s'" % profile @staticmethod def bed_mesh_save(profile): - return "BED_MESH_PROFILE SAVE=%s" % profile + return "BED_MESH_PROFILE SAVE='%s'" % profile diff --git a/ks_includes/locales/keyboard.xml b/ks_includes/locales/keyboard.xml new file mode 100644 index 00000000..4615fb20 --- /dev/null +++ b/ks_includes/locales/keyboard.xml @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/panels/bed_mesh.py b/panels/bed_mesh.py index 88a1f4cc..0784d4a0 100644 --- a/panels/bed_mesh.py +++ b/panels/bed_mesh.py @@ -19,6 +19,8 @@ class BedMeshPanel(ScreenPanel): def initialize(self, panel_name): _ = self.lang.gettext + self.show_create = False + scroll = Gtk.ScrolledWindow() scroll.set_property("overlay-scrolling", False) scroll.set_vexpand(True) @@ -27,17 +29,36 @@ class BedMeshPanel(ScreenPanel): self.labels['profiles'] = Gtk.Grid() scroll.add(self.labels['profiles']) + + addprofile = self._gtk.ButtonImage("increase"," %s" % _("Add bed mesh profile"), + "color1", .5, .5, Gtk.PositionType.LEFT) + addprofile.connect("clicked", self.show_create_profile) + addprofile.set_size_request(60,0) + addprofile.set_hexpand(False) + addprofile.set_halign(Gtk.Align.END) + abox = Gtk.Box(spacing=0) + abox.set_vexpand(False) + abox.add(addprofile) + # Create a box to contain all of the above box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) box.set_vexpand(True) - box.pack_start(scroll, True, True, 0) + box.pack_start(abox, False, False, 0) + box.pack_end(scroll, True, True, 0) self.load_meshes() - self.content.add(box) + self.labels['main_box'] = box + self.control['back'].disconnect_by_func(self._screen._menu_go_back) + self.control['back'].connect("clicked", self.back) + self.content.add(self.labels['main_box']) self._screen.add_subscription(panel_name) def activate(self): + for child in self.content.get_children(): + self.content.remove(child) + self.content.add(self.labels['main_box']) + am = self._screen.printer.get_stat("bed_mesh","profile_name") self.activate_mesh(am) @@ -47,7 +68,9 @@ class BedMeshPanel(ScreenPanel): logger.debug("Activating profile: %s %s" % (self.active_mesh, profile)) if profile != self.active_mesh: - if self.active_mesh != None: + if profile not in self.profiles: + self.add_profile(profile) + if self.active_mesh != None and self.active_mesh in self.profiles: a = self.profiles[self.active_mesh] a['buttons'].remove(a['refresh']) a['buttons'].pack_start(a['load'], False, False, 0) @@ -59,6 +82,8 @@ class BedMeshPanel(ScreenPanel): self._screen.show_all() def add_profile(self, profile): + _ = self.lang.gettext + frame = Gtk.Frame() frame.set_property("shadow-type",Gtk.ShadowType.NONE) @@ -70,13 +95,13 @@ class BedMeshPanel(ScreenPanel): name.set_line_wrap(True) name.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR) - load = self._gtk.ButtonImage("load","Load","color2") + load = self._gtk.ButtonImage("load",_("Load"),"color2") load.connect("clicked", self.send_load_mesh, profile) load.set_size_request(60,0) load.set_hexpand(False) load.set_halign(Gtk.Align.END) - refresh = self._gtk.ButtonImage("refresh","Calibrate","color4") + refresh = self._gtk.ButtonImage("refresh",_("Calibrate"),"color4") refresh.connect("clicked", self.calibrate_mesh) refresh.set_size_request(60,0) refresh.set_hexpand(False) @@ -88,12 +113,18 @@ class BedMeshPanel(ScreenPanel): info.set_hexpand(False) info.set_halign(Gtk.Align.END) - save = self._gtk.ButtonImage("sd","Save","color3") + save = self._gtk.ButtonImage("sd",_("Save"),"color3") save.connect("clicked", self.send_save_mesh, profile) save.set_size_request(60,0) save.set_hexpand(False) save.set_halign(Gtk.Align.END) + delete = self._gtk.ButtonImage("decrease",_("Delete"),"color3") + delete.connect("clicked", self.send_remove_mesh, profile) + delete.set_size_request(60,0) + delete.set_hexpand(False) + delete.set_halign(Gtk.Align.END) + labels = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) labels.add(name) @@ -116,6 +147,7 @@ class BedMeshPanel(ScreenPanel): if profile != "default": buttons.pack_end(save, False, False, 0) + buttons.pack_end(delete, False, False, 0) dev.add(buttons) frame.add(dev) @@ -129,8 +161,11 @@ class BedMeshPanel(ScreenPanel): "save": save } - profiles = sorted(self.profiles) - pos = profiles.index(profile) + l = list(self.profiles) + if "default" in l: + l.remove('default') + profiles = sorted(l) + pos = profiles.index(profile)+1 if profile != "default" else 0 self.labels['profiles'].insert_row(pos) self.labels['profiles'].attach(self.profiles[profile]['row'], 0, pos, 1, 1) @@ -138,6 +173,20 @@ class BedMeshPanel(ScreenPanel): #Gdk.threads_add_idle(GLib.PRIORITY_LOW, self.create_graph, profile) + def back(self, widget): + if self.show_create == True: + self.remove_create() + else: + self._screen._menu_go_back() + + def create_profile(self, widget): + name = self.labels['profile_name'].get_text() + if " " in name: + name = '"%s"' % name + + self._screen._ws.klippy.gcode_script("BED_MESH_PROFILE SAVE=%s" % name) + self.remove_create() + def calibrate_mesh(self, widget): self._screen._ws.klippy.gcode_script( "BED_MESH_CALIBRATE" @@ -155,6 +204,30 @@ class BedMeshPanel(ScreenPanel): if data['bed_mesh']['profile_name'] != self.active_mesh: self.activate_mesh(data['bed_mesh']['profile_name']) + def remove_create(self): + if self.show_create == False: + return + + self._screen.remove_keyboard() + for child in self.content.get_children(): + self.content.remove(child) + + self.show_create = False + self.content.add(self.labels['main_box']) + self.content.show() + + def remove_profile(self, profile): + if profile not in self.profiles: + return + + l = list(self.profiles) + if "default" in l: + l.remove('default') + profiles = sorted(l) + pos = profiles.index(profile)+1 if profile != "default" else 0 + self.labels['profiles'].remove_row(pos) + del self.profiles[profile] + def send_load_mesh(self, widget, profile): self._screen._ws.klippy.gcode_script( KlippyGcodes.bed_mesh_load(profile) @@ -165,6 +238,51 @@ class BedMeshPanel(ScreenPanel): KlippyGcodes.bed_mesh_save(profile) ) + def send_remove_mesh(self, widget, profile): + self._screen._ws.klippy.gcode_script( + KlippyGcodes.bed_mesh_remove(profile) + ) + self.remove_profile(profile) + + def show_create_profile(self, widget): + _ = self.lang.gettext + + for child in self.content.get_children(): + self.content.remove(child) + + if "create_profile" not in self.labels: + self.labels['create_profile'] = Gtk.VBox() + self.labels['create_profile'].set_valign(Gtk.Align.START) + + box = Gtk.Box(spacing=5) + box.set_size_request(self._screen.width, self._screen.height - self._gtk.get_header_size() - + self._screen.keyboard_height - 20) + box.set_hexpand(True) + box.set_vexpand(False) + self.labels['create_profile'].add(box) + + l = self._gtk.Label(_("Profile Name:")) + l.set_hexpand(False) + entry = Gtk.Entry() + entry.set_hexpand(True) + + save = self._gtk.ButtonImage("sd",_("Save"),"color3") + save.set_hexpand(False) + save.connect("clicked", self.create_profile) + + + self.labels['profile_name'] = entry + box.pack_start(l, False, False, 5) + box.pack_start(entry, True, True, 5) + box.pack_start(save, False, False, 5) + + self.show_create = True + self.labels['profile_name'].set_text('') + self.content.add(self.labels['create_profile']) + self.content.show() + self._screen.show_keyboard() + self.labels['profile_name'].grab_focus_without_selecting() + def show_mesh(self, widget, profile): _ = self.lang.gettext diff --git a/screen.py b/screen.py index 8803856b..3e466e1f 100644 --- a/screen.py +++ b/screen.py @@ -13,6 +13,7 @@ import importlib import logging import os import re +import signal import subprocess @@ -55,6 +56,8 @@ class KlipperScreen(Gtk.Window): currentPanel = None files = None filename = "" + keyboard = None + keyboard_height = 200 last_update = {} load_panel = {} number_tools = 1 @@ -218,9 +221,6 @@ class KlipperScreen(Gtk.Window): if hasattr(self.panels[panel_name],"process_update"): self.panels[panel_name].process_update("notify_status_update", self.printer.get_data()) - if hasattr(self.panels[panel_name],"activate"): - self.panels[panel_name].activate() - if remove == 2: self._remove_all_panels() elif remove == 1: @@ -228,6 +228,11 @@ class KlipperScreen(Gtk.Window): self.add(self.panels[panel_name].get()) self.show_all() + + if hasattr(self.panels[panel_name],"activate"): + self.panels[panel_name].activate() + self.show_all() + self._cur_panels.append(panel_name) logger.debug("Current panel hierarchy: %s", str(self._cur_panels)) @@ -360,10 +365,12 @@ class KlipperScreen(Gtk.Window): def _menu_go_back (self, widget=None): logger.info("#### Menu go back") + self.remove_keyboard() self._remove_current_panel() def _menu_go_home(self): logger.info("#### Menu go home") + self.remove_keyboard() while len(self._cur_panels) > 1: self._remove_current_panel() @@ -547,6 +554,51 @@ class KlipperScreen(Gtk.Window): self.close_popup_message() self.show_panel('job_status',"job_status", "Print Status", 2) + def show_keyboard(self, widget=None): + if self.keyboard is not None: + return + + env = os.environ.copy() + env["MB_KBD_CONFIG"] = "/home/pi/.matchbox/keyboard.xml" + env["MB_KBD_CONFIG"] = "ks_includes/locales/keyboard.xml" + #p = subprocess.Popen(["matchbox-keyboard", "--xid"], stdout=subprocess.PIPE, + # stderr=subprocess.PIPE, env=env) + p = subprocess.Popen(["onboard", "--xid"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + xid = int(p.stdout.readline()) + logger.debug("XID %s" % xid) + logger.debug("PID %s" % p.pid) + keyboard = Gtk.Socket() + #keyboard.connect("plug-added", self.plug_added) + box = Gtk.VBox() + box.set_size_request(self.width, self.keyboard_height) + box.add(keyboard) + + cur_panel = self.panels[self._cur_panels[-1]] + #for i in ['back','estop','home']: + # if i in cur_panel.control: + # cur_panel.control[i].set_sensitive(False) + cur_panel.get().put(box, 0, self.height - 200) + self.show_all() + keyboard.add_id(xid) + keyboard.show() + + self.keyboard = { + "box": box, + "panel": cur_panel.get(), + "process": p, + "socket": keyboard + } + + + def remove_keyboard(self, widget=None): + if self.keyboard is None: + return + + self.keyboard['panel'].remove(self.keyboard['box']) + os.kill(self.keyboard['process'].pid, signal.SIGTERM) + self.keyboard = None + + def get_software_version(): prog = ('git', '-C', os.path.dirname(__file__), 'describe', '--always', '--tags', '--long', '--dirty') diff --git a/scripts/KlipperScreen-install.sh b/scripts/KlipperScreen-install.sh index 9fbf214a..a332a313 100755 --- a/scripts/KlipperScreen-install.sh +++ b/scripts/KlipperScreen-install.sh @@ -20,7 +20,8 @@ install_packages() python3-gi-cairo \ python3-virtualenv \ gir1.2-gtk-3.0 \ - virtualenv + virtualenv \ + matchbox-keyboard } create_virtualenv()