From 3d6eed9d9526b77472ba7df29014b5768c594026 Mon Sep 17 00:00:00 2001
From: zkk <1007518571@qq.com>
Date: Mon, 16 Dec 2024 16:33:34 +0800
Subject: [PATCH] =?UTF-8?q?=E5=96=B7=E5=A4=B4=E5=81=8F=E7=A7=BB=E5=80=BC?=
=?UTF-8?q?=E6=A0=A1=E5=87=86=E5=8A=9F=E8=83=BD=E7=9A=84=E5=AE=9E=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
config/main_menu.conf | 5 +
panels/nozzle_offset.py | 322 ++++++++++++++++++++++++++
panels/offset_fine_tune.py | 214 +++++++++++++++++
screen.py | 8 +-
styles/dark/images/nozzle_offset.svg | 26 +++
styles/dark/images/offset_z.svg | 1 +
styles/light/images/nozzle_offset.svg | 31 +++
styles/light/images/offset_z.svg | 1 +
8 files changed, 607 insertions(+), 1 deletion(-)
create mode 100644 panels/nozzle_offset.py
create mode 100644 panels/offset_fine_tune.py
create mode 100644 styles/dark/images/nozzle_offset.svg
create mode 100644 styles/dark/images/offset_z.svg
create mode 100644 styles/light/images/nozzle_offset.svg
create mode 100644 styles/light/images/offset_z.svg
diff --git a/config/main_menu.conf b/config/main_menu.conf
index ad9a637d..0958b535 100644
--- a/config/main_menu.conf
+++ b/config/main_menu.conf
@@ -34,6 +34,11 @@ name: {{ gettext('Z Calibrate') }}
icon: z-farther
panel: zcalibrate
+[menu __main more nozzle_offset]
+name: {{ gettext('Nozzle Offset') }}
+icon: nozzle_offset
+panel: nozzle_offset
+
[menu __main more limits]
name: {{ gettext('Limits') }}
icon: fine-tune
diff --git a/panels/nozzle_offset.py b/panels/nozzle_offset.py
new file mode 100644
index 00000000..3b1a4b86
--- /dev/null
+++ b/panels/nozzle_offset.py
@@ -0,0 +1,322 @@
+import logging
+
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk, Pango
+
+from ks_includes.KlippyGcodes import KlippyGcodes
+from ks_includes.screen_panel import ScreenPanel
+
+
+class Panel(ScreenPanel):
+ widgets = {}
+ distances = [".01", ".05", ".1", ".5", "1", "5"]
+ distance = distances[-2]
+
+ def __init__(self, screen, title):
+ title = title or _("Nozzle Offset")
+ super().__init__(screen, title)
+
+ self.start_z_calibrate = False
+ self.z_hop_speed = 15.0
+ self.z_hop = 5.0
+ self.showing_input_box = False
+
+ self.offset_data = [
+ (_("Z Offset"), "z_offset_val", "0"),
+ (_("X Offset"), "x_offset_val", "0"),
+ (_("Y Offset"), "y_offset_val", "0"),
+ ]
+
+ for label_text, offset_key, value_text in self.offset_data:
+ self.widgets[offset_key[:8]] = Gtk.Label(label=label_text)
+ self.widgets[offset_key] = Gtk.Label(label=value_text)
+ event_box = Gtk.EventBox()
+ event_box.add(self.widgets[offset_key])
+ event_box.connect("button-release-event", self.change_offset, label_text, self.widgets[offset_key])
+ setattr(self, f"{offset_key[0]}_event_box", event_box)
+
+ pos = Gtk.Grid(row_homogeneous=True, column_homogeneous=True)
+ pos.attach(self.widgets["z_offset"], 0, 1, 2, 1)
+ pos.attach(self.z_event_box, 0, 2, 2, 1)
+ pos.attach(self.widgets["x_offset"], 0, 3, 1, 1)
+ pos.attach(self.x_event_box, 0, 4, 1, 1)
+ pos.attach(self.widgets["y_offset"], 1, 3, 1, 1)
+ pos.attach(self.y_event_box, 1, 4, 1, 1)
+ for label in pos.get_children():
+ if isinstance(label, Gtk.Label):
+ label.set_ellipsize(Pango.EllipsizeMode.END)
+ self.buttons = {
+ "z+": self._gtk.Button("z-farther", _("Lower Bed"), "color4"),
+ "z-": self._gtk.Button("z-closer", _("Raise Bed"), "color1"),
+ "start_z_offset": self._gtk.Button("offset_z", _("Z offset Calibrate"), "color3"),
+ "start_xy_offset": self._gtk.Button("resume", _("XY offset Calibrate"), "color3"),
+ "complete": self._gtk.Button("complete", _("Save"), "color3"),
+ "cancel": self._gtk.Button("cancel", _("Cancel"), "color2"),
+ }
+ self.buttons["z+"].connect("clicked", self.move, "z", "+")
+ self.buttons["z-"].connect("clicked", self.move, "z", "-")
+ self.buttons["complete"].connect("clicked", self.accept)
+ script = {"script": "ABORT"}
+ self.buttons["cancel"].connect("clicked", self.cancel)
+
+ self.popover = Gtk.Popover(position=Gtk.PositionType.BOTTOM)
+ script = {"script": "_NOZZLE_Z_OFFSET_CALIBRATE"}
+ self.buttons["start_z_offset"].connect("clicked", self.nozzle_z_offset)
+ script = {"script": "_NOZZLE_XY_OFFSET_CALIBRATE"}
+ self.buttons["start_xy_offset"].connect("clicked", self.nozzle_xy_offset)
+
+ distgrid = Gtk.Grid()
+ for j, i in enumerate(self.distances):
+ self.widgets[i] = self._gtk.Button(label=i)
+ self.widgets[i].set_direction(Gtk.TextDirection.LTR)
+ self.widgets[i].connect("clicked", self.change_distance, i)
+ ctx = self.widgets[i].get_style_context()
+ ctx.add_class("horizontal_togglebuttons")
+ if i == self.distance:
+ ctx.add_class("horizontal_togglebuttons_active")
+ distgrid.attach(self.widgets[i], j, 0, 1, 1)
+
+ self.widgets["move_dist"] = Gtk.Label(_("Move Distance (mm)"))
+ distances = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ distances.pack_start(self.widgets["move_dist"], True, True, 0)
+ distances.pack_start(distgrid, True, True, 0)
+
+ self.grid = Gtk.Grid(column_homogeneous=True)
+ if self._screen.vertical_mode:
+ if self._config.get_config()["main"].getboolean("invert_z", False):
+ self.grid.attach(self.buttons["z+"], 0, 1, 1, 1)
+ self.grid.attach(self.buttons["z-"], 0, 0, 1, 1)
+ else:
+ self.grid.attach(self.buttons["z+"], 0, 0, 1, 1)
+ self.grid.attach(self.buttons["z-"], 0, 1, 1, 1)
+ self.grid.attach(self.buttons["start_z_offset"], 1, 0, 1, 1)
+ self.grid.attach(pos, 1, 1, 2, 1)
+ self.grid.attach(self.buttons["start_xy_offset"], 2, 0, 1, 1)
+ self.grid.attach(self.buttons["complete"], 3, 0, 1, 1)
+ self.grid.attach(self.buttons["cancel"], 3, 1, 1, 1)
+ self.grid.attach(distances, 0, 2, 4, 1)
+ else:
+ if self._config.get_config()["main"].getboolean("invert_z", False):
+ self.grid.attach(self.buttons["z+"], 0, 2, 1, 1)
+ self.grid.attach(self.buttons["z-"], 0, 1, 1, 1)
+ else:
+ self.grid.attach(self.buttons["z+"], 0, 1, 1, 1)
+ self.grid.attach(self.buttons["z-"], 0, 2, 1, 1)
+ self.grid.attach(self.buttons["start_z_offset"], 0, 0, 2, 1)
+ self.grid.attach(self.buttons["start_xy_offset"], 2, 0, 2, 1)
+ self.grid.attach(pos, 1, 1, 2, 2)
+ self.grid.attach(self.buttons["complete"], 3, 1, 1, 1)
+ self.grid.attach(self.buttons["cancel"], 3, 2, 1, 1)
+ self.grid.attach(distances, 0, 3, 4, 1)
+ self.content.add(self.grid)
+
+ def nozzle_z_offset(self, widget):
+ text = (
+ _("Start testing the Z offset value of the second nozzle?\n")
+ + "\n\n"
+ + _("Please ensure that the Z Calibrate has been performed")
+ )
+ label = Gtk.Label(wrap=True, vexpand=True)
+ label.set_markup(text)
+ buttons = [
+ {"name": _("Accept"), "response": Gtk.ResponseType.OK, "style": "dialog-info"},
+ {"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": "dialog-error"},
+ ]
+ self._gtk.Dialog(_("Calibrate Nozzle Z Offset"), buttons, label, self.confirm_nozzle_z_offset)
+
+ def confirm_nozzle_z_offset(self, dialog, response_id):
+ self._gtk.remove_dialog(dialog)
+ if response_id == Gtk.ResponseType.OK:
+ self.start_z_calibrate = True
+ self.buttons_calibration_start()
+ self._screen._send_action(
+ self.buttons["start_z_offset"], "printer.gcode.script", {"script": "_NOZZLE_Z_OFFSET_CALIBRATE"}
+ )
+
+ def nozzle_xy_offset(self, widget):
+ text = (
+ _("This operation is about to print the model")
+ + "\n\n"
+ + _("Please load two different colored PLA filaments!")
+ )
+
+ label = Gtk.Label(wrap=True, vexpand=True)
+ label.set_markup(text)
+ buttons = [
+ {"name": _("Accept"), "response": Gtk.ResponseType.OK, "style": "dialog-info"},
+ {"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": "dialog-error"},
+ ]
+ self._gtk.Dialog(_("Calibrate Nozzle XY Offset"), buttons, label, self.confirm_nozzle_xy_offset)
+
+ def confirm_nozzle_xy_offset(self, dialog, response_id):
+ self._gtk.remove_dialog(dialog)
+ if response_id == Gtk.ResponseType.OK:
+ self._screen.offset_fine_tune_mode = True
+ self.buttons_calibration_start()
+ self._screen._send_action(
+ self.buttons["start_z_offset"], "printer.gcode.script", {"script": "_NOZZLE_XY_OFFSET_CALIBRATE"}
+ )
+
+ def change_offset(self, widget, event, title_label, offset_label):
+ self._create_input_box(title_label, offset_label)
+
+ def _create_input_box(self, title_label, offset_label):
+ current_val = offset_label.get_text()
+ title_label += " " + _("Current value:") + f"{current_val}"
+ for child in self.content.get_children():
+ self.content.remove(child)
+ lbl = Gtk.Label(label=title_label, halign=Gtk.Align.START, hexpand=False)
+ self.labels["entry"] = Gtk.Entry(hexpand=True)
+ self.labels["entry"].connect("focus-in-event", self._screen.show_keyboard)
+ save = self._gtk.Button("complete", _("Save"), "color3")
+ save.set_hexpand(False)
+ save.connect("clicked", self.store_value, offset_label)
+ box = Gtk.Box()
+ box.pack_start(self.labels["entry"], True, True, 5)
+ box.pack_start(save, False, False, 5)
+ self.labels["input_box"] = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=5, hexpand=True, vexpand=True, valign=Gtk.Align.CENTER
+ )
+ self.labels["input_box"].pack_start(lbl, True, True, 5)
+ self.labels["input_box"].pack_start(box, True, True, 5)
+ self.content.add(self.labels["input_box"])
+ self.labels["entry"].grab_focus_without_selecting()
+ self.showing_input_box = True
+
+ def hide_input_box(self):
+ self._screen.remove_keyboard()
+ for child in self.content.get_children():
+ self.content.remove(child)
+ self.content.add(self.grid)
+ self.content.show()
+ self.showing_input_box = False
+
+ def store_value(self, widget, offset_label):
+ val_text = self.labels["entry"].get_text()
+ try:
+ val = round(float(val_text), 3)
+ name = None
+ for label_text, offset_key, _ in self.offset_data:
+ if offset_label == self.widgets[offset_key]:
+ name = offset_key
+ break
+ if name is not None:
+ name = "nozzle_" + name
+ self.set_nozzle_offset(name, val)
+ if self.showing_input_box:
+ self.hide_input_box()
+ except ValueError:
+ self._screen.show_popup_message(_("Please enter a valid number"))
+
+ def set_nozzle_offset(self, option, value):
+ script = KlippyGcodes.set_save_variables(option, value)
+ self._screen._send_action(None, "printer.gcode.script", {"script": script})
+ logging.info(f"Set {option}:{value}")
+
+ def back(self):
+ if self.showing_input_box:
+ self.hide_input_box()
+ return True
+ return False
+
+ def _add_button(self, label, method, pobox):
+ popover_button = self._gtk.Button(label=label)
+ pobox.pack_start(popover_button, True, True, 5)
+
+ def on_popover_clicked(self, widget):
+ self.popover.set_relative_to(widget)
+ self.popover.show_all()
+
+ def activate(self):
+ is_sensitive = self.buttons["start_z_offset"].get_sensitive()
+ if is_sensitive:
+ self.buttons_not_z_calibration()
+ else:
+ self.buttons_calibration_start()
+
+ def deactivate(self):
+ if self.start_z_calibrate:
+ self.start_z_calibrate = False
+
+ def process_update(self, action, data):
+
+ if action == "notify_gcode_response":
+ if self.start_z_calibrate and "extruder1" in data:
+ self.buttons_z_calibration()
+ elif "action:resumed" in data:
+ return
+ if action != "notify_status_update":
+ return
+ if "save_variables" in data and "variables" in data["save_variables"]:
+ variables = data["save_variables"]["variables"]
+ nozzle_offsets = ["nozzle_z_offset_val", "nozzle_x_offset_val", "nozzle_y_offset_val"]
+
+ for offset in nozzle_offsets:
+ if offset in variables:
+ self.widgets[f'{offset.split("_")[1]}_offset_val'].set_text(f"{variables[offset]}")
+
+ if "gcode_move" in data or "toolhead" in data and "homed_axes" in data["toolhead"]:
+ homed_axes = self._printer.get_stat("toolhead", "homed_axes")
+
+ if "z" in homed_axes:
+ self.pos_z = round(data["gcode_move"]["gcode_position"][2], 3)
+ if self.start_z_calibrate:
+ self.widgets["z_offset_val"].set_text(f"{self.pos_z}")
+
+ def change_distance(self, widget, distance):
+ logging.info(f"### Distance {distance}")
+ self.widgets[f"{self.distance}"].get_style_context().remove_class("horizontal_togglebuttons_active")
+ self.widgets[f"{distance}"].get_style_context().add_class("horizontal_togglebuttons_active")
+ self.distance = distance
+
+ def move(self, widget, axis, direction):
+ dist = f"{direction}{self.distance}"
+ script = f"{KlippyGcodes.MOVE_RELATIVE}\nG0 {axis}{dist} F300"
+ self._screen._send_action(widget, "printer.gcode.script", {"script": script})
+ if self._printer.get_stat("gcode_move", "absolute_coordinates"):
+ self._screen._ws.klippy.gcode_script("G90")
+
+ def accept(self, widget):
+ logging.info("Accepting nozzle Z offset")
+ script = f"SET_GCODE_OFFSET Z={self.pos_z}"
+ self._screen._send_action(None, "printer.gcode.script", {"script": script})
+ script = f"G91\n G0 Z5 F6000"
+ self._screen._send_action(None, "printer.gcode.script", {"script": script})
+
+ self.set_nozzle_offset("nozzle_z_offset_val", self.pos_z)
+ self.buttons_not_z_calibration()
+ self.start_z_calibrate = False
+
+ def cancel(self, widget):
+ self.start_z_calibrate = False
+ self.buttons_not_z_calibration()
+ variables = self._printer.get_stat("save_variables", "variables")
+ if "nozzle_z_offset_val" in variables:
+ self.widgets["z_offset_val"].set_text(f"{variables['nozzle_z_offset_val']}")
+
+ def buttons_calibration_start(self):
+ self.buttons["start_z_offset"].set_sensitive(False)
+ self.buttons["start_xy_offset"].set_sensitive(False)
+
+ self.buttons["z+"].set_sensitive(False)
+ self.buttons["z-"].set_sensitive(False)
+ self.buttons["complete"].set_sensitive(False)
+ self.buttons["cancel"].set_sensitive(False)
+
+ def buttons_z_calibration(self):
+ self.buttons["start_z_offset"].set_sensitive(False)
+ self.buttons["start_xy_offset"].set_sensitive(False)
+ self.buttons["z+"].set_sensitive(True)
+ self.buttons["z-"].set_sensitive(True)
+ self.buttons["complete"].set_sensitive(True)
+ self.buttons["cancel"].set_sensitive(True)
+
+ def buttons_not_z_calibration(self):
+ self.buttons["start_z_offset"].set_sensitive(True)
+ self.buttons["start_xy_offset"].set_sensitive(True)
+ self.buttons["z+"].set_sensitive(False)
+ self.buttons["z-"].set_sensitive(False)
+ self.buttons["complete"].set_sensitive(False)
+ self.buttons["cancel"].set_sensitive(False)
diff --git a/panels/offset_fine_tune.py b/panels/offset_fine_tune.py
new file mode 100644
index 00000000..e479096b
--- /dev/null
+++ b/panels/offset_fine_tune.py
@@ -0,0 +1,214 @@
+import re
+import logging
+import gi
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+from ks_includes.KlippyGcodes import KlippyGcodes
+from ks_includes.screen_panel import ScreenPanel
+
+
+class Panel(ScreenPanel):
+ distances = [".01", ".05", "0.1"]
+ distance = distances[-2]
+
+ def __init__(self, screen, title):
+ title = title or _("offset fine tune")
+ super().__init__(screen, title)
+
+ self.state = "standby"
+
+ if self.ks_printer_cfg is not None:
+ dis = self.ks_printer_cfg.get("move_distances", "")
+ if re.match(r"^[0-9,\.\s]+$", dis):
+ dis = [str(i.strip()) for i in dis.split(",")]
+ if 1 < len(dis) <= 7:
+ self.distances = dis
+ self.distance = self.distances[-2]
+
+ self.labels["qr_code_box"] = Gtk.Grid(row_homogeneous=True, column_homogeneous=True)
+ qr_code = self._gtk.Image("wiki_qr_code", self._gtk.content_width * 0.46, self._gtk.content_height * 0.46)
+ qr_code_url = Gtk.Label(label="https://www.creatbot.com/en/faqs.html")
+ self.labels["qr_code_box"].attach(qr_code, 0, 0, 1, 3)
+ self.labels["qr_code_box"].attach(qr_code_url, 0, 2, 1, 1)
+
+ self.action_btn = {}
+ self.action_btn["fine_tune"] = self._gtk.Button("fine-tune", scale=0.7)
+ self.action_btn["pause"] = self._gtk.Button("pause", scale=0.8)
+ self.action_btn["resume"] = self._gtk.Button("resume", scale=0.8)
+ self.action_btn["stop"] = self._gtk.Button("stop", scale=0.8)
+
+ self.action_btn["fine_tune"].connect("clicked", self.menu_item_clicked, {"panel": "fine_tune"})
+ self.action_btn["pause"].connect("clicked", self.pause)
+ self.action_btn["resume"].connect("clicked", self.resume)
+ self.action_btn["stop"].connect("clicked", self.cancel)
+
+ self.labels["action_menu"] = Gtk.Grid(row_homogeneous=True, column_homogeneous=True)
+ self.labels["action_menu"].attach(self.action_btn["pause"], 0, 0, 1, 1)
+ self.labels["action_menu"].attach(self.action_btn["stop"], 1, 0, 1, 1)
+ self.labels["action_menu"].attach(self.action_btn["fine_tune"], 2, 0, 1, 1)
+
+ self.buttons = {
+ "x+": self._gtk.Button("arrow-right", "X+", "color1"),
+ "x-": self._gtk.Button("arrow-left", "X-", "color1"),
+ "y+": self._gtk.Button("arrow-up", "Y+", "color2"),
+ "y-": self._gtk.Button("arrow-down", "Y-", "color2"),
+ "z+": self._gtk.Button("z-farther", "Z+", "color3"),
+ "z-": self._gtk.Button("z-closer", "Z-", "color3"),
+ }
+ self.buttons["x+"].connect("clicked", self.move, "X", "+")
+ self.buttons["x-"].connect("clicked", self.move, "X", "-")
+ self.buttons["y+"].connect("clicked", self.move, "Y", "+")
+ self.buttons["y-"].connect("clicked", self.move, "Y", "-")
+ self.buttons["z+"].connect("clicked", self.move, "Z", "+")
+ self.buttons["z-"].connect("clicked", self.move, "Z", "-")
+
+ grid = Gtk.Grid(row_homogeneous=True, column_homogeneous=True)
+ if self._screen.vertical_mode:
+ if self._screen.lang_ltr:
+ grid.attach(self.buttons["x+"], 2, 1, 1, 1)
+ grid.attach(self.buttons["x-"], 0, 1, 1, 1)
+ grid.attach(self.buttons["z+"], 2, 2, 1, 1)
+ grid.attach(self.buttons["z-"], 0, 2, 1, 1)
+ else:
+ grid.attach(self.buttons["x+"], 0, 1, 1, 1)
+ grid.attach(self.buttons["x-"], 2, 1, 1, 1)
+ grid.attach(self.buttons["z+"], 0, 2, 1, 1)
+ grid.attach(self.buttons["z-"], 2, 2, 1, 1)
+
+ grid.attach(self.buttons["y+"], 1, 0, 1, 1)
+ grid.attach(self.buttons["y-"], 1, 2, 1, 1)
+
+ else:
+ if self._screen.lang_ltr:
+ grid.attach(self.buttons["x+"], 2, 1, 1, 1)
+ grid.attach(self.buttons["x-"], 0, 1, 1, 1)
+ else:
+ grid.attach(self.buttons["x+"], 0, 1, 1, 1)
+ grid.attach(self.buttons["x-"], 2, 1, 1, 1)
+ grid.attach(self.buttons["y+"], 1, 0, 1, 1)
+ grid.attach(self.buttons["y-"], 1, 2, 1, 1)
+ grid.attach(self.buttons["z-"], 3, 0, 1, 1)
+ grid.attach(self.buttons["z+"], 3, 2, 1, 1)
+
+ distgrid = Gtk.Grid()
+ for j, i in enumerate(self.distances):
+ self.labels[i] = self._gtk.Button(label=i)
+ self.labels[i].set_direction(Gtk.TextDirection.LTR)
+ self.labels[i].connect("clicked", self.change_distance, i)
+ ctx = self.labels[i].get_style_context()
+ ctx.add_class("horizontal_togglebuttons")
+ if i == self.distance:
+ ctx.add_class("horizontal_togglebuttons_active")
+ distgrid.attach(self.labels[i], j, 0, 1, 1)
+
+ for p in ("x_offset_val", "y_offset_val", "z_offset_val"):
+ self.labels[p] = Gtk.Label(f"{p[0].upper()} " + _("Offset") + ": 0")
+ self.labels["move_dist"] = Gtk.Label(label=_("Move Distance (mm)"))
+
+ bottomgrid = Gtk.Grid(row_homogeneous=True, column_homogeneous=True)
+ bottomgrid.set_direction(Gtk.TextDirection.LTR)
+ bottomgrid.attach(self.labels["x_offset_val"], 0, 0, 1, 1)
+ bottomgrid.attach(self.labels["y_offset_val"], 1, 0, 1, 1)
+ bottomgrid.attach(self.labels["z_offset_val"], 2, 0, 1, 1)
+ bottomgrid.attach(self.labels["move_dist"], 0, 1, 3, 1)
+
+ self.labels["move_menu"] = Gtk.Grid(row_homogeneous=True, column_homogeneous=True)
+ self.labels["move_menu"].attach(self.labels["qr_code_box"], 0, 0, 2, 4)
+ self.labels["move_menu"].attach(grid, 2, 0, 2, 3)
+ self.labels["move_menu"].attach(bottomgrid, 2, 3, 2, 1)
+ self.labels["move_menu"].attach(distgrid, 2, 4, 2, 1)
+ self.labels["move_menu"].attach(self.labels["action_menu"], 0, 4, 2, 1)
+
+ self.content.add(self.labels["move_menu"])
+
+ def process_update(self, action, data):
+ if action != "notify_status_update":
+ return
+ if "save_variables" in data and "variables" in data["save_variables"]:
+ variables = data["save_variables"]["variables"]
+ nozzle_offsets = ["nozzle_z_offset_val", "nozzle_x_offset_val", "nozzle_y_offset_val"]
+
+ for offset in nozzle_offsets:
+ if offset in variables:
+ axis = offset.split("_")[1]
+ self.labels[f"{axis}_offset_val"].set_text(
+ f"{axis.upper()} " + _("Offset") + f": {variables[offset]}"
+ )
+ if "print_stats" in data:
+ if "state" in data["print_stats"]:
+ self.state = data["print_stats"]["state"]
+ self.show_buttons_for_state()
+
+ def pause(self, widget):
+ self.disable_button("pause")
+ self._screen._ws.klippy.print_pause()
+ self._screen.show_all()
+
+ def resume(self, widget):
+ self.disable_button("resume")
+ self._screen._ws.klippy.print_resume()
+ self._screen.show_all()
+
+ def cancel(self, widget):
+ buttons = [
+ {"name": _("Cancel Print"), "response": Gtk.ResponseType.OK, "style": "dialog-error"},
+ {"name": _("Go Back"), "response": Gtk.ResponseType.CANCEL, "style": "dialog-info"},
+ ]
+ label = Gtk.Label(hexpand=True, vexpand=True, wrap=True)
+ label.set_markup(_("Are you sure you wish to cancel this test?"))
+ self._gtk.Dialog(_("Cancel"), buttons, label, self.cancel_confirm)
+
+ def cancel_confirm(self, dialog, response_id):
+ self._gtk.remove_dialog(dialog)
+ if response_id == Gtk.ResponseType.OK:
+ self.disable_button("pause", "resume", "stop", "fine_tune")
+ self._screen._ws.klippy.print_cancel()
+ logging.debug("Canceling print test")
+ return
+ if response_id == Gtk.ResponseType.CANCEL:
+ self.enable_button("pause", "stop", "fine_tune")
+ return
+
+ def disable_button(self, *args):
+ for arg in args:
+ self.action_btn[arg].set_sensitive(False)
+
+ def show_buttons_for_state(self):
+ self.labels["action_menu"].remove_row(0)
+ self.labels["action_menu"].insert_row(0)
+ if self.state == "paused":
+ self.labels["action_menu"].attach(self.action_btn["resume"], 0, 0, 1, 1)
+ self.labels["action_menu"].attach(self.action_btn["stop"], 1, 0, 1, 1)
+ self.labels["action_menu"].attach(self.action_btn["fine_tune"], 2, 0, 1, 1)
+ else:
+ self.labels["action_menu"].attach(self.action_btn["pause"], 0, 0, 1, 1)
+ self.labels["action_menu"].attach(self.action_btn["stop"], 1, 0, 1, 1)
+ self.labels["action_menu"].attach(self.action_btn["fine_tune"], 2, 0, 1, 1)
+ self.content.show_all()
+
+ def change_distance(self, widget, distance):
+ logging.info(f"### Distance {distance}")
+ self.labels[f"{self.distance}"].get_style_context().remove_class("horizontal_togglebuttons_active")
+ self.labels[f"{distance}"].get_style_context().add_class("horizontal_togglebuttons_active")
+ self.distance = distance
+
+ def move(self, widget, axis, direction):
+ axis = axis.lower()
+ offset_name = f"nozzle_{axis}_offset_val"
+ data = self._printer.data
+ last_val = 0
+ if "save_variables" in data and "variables" in data["save_variables"]:
+ variables = data["save_variables"]["variables"]
+ last_val = variables.get(offset_name, 0)
+ try:
+ expression = f"{last_val} {direction} {self.distance}"
+ result = eval(expression)
+ self.set_nozzle_offset(offset_name, round(float(result), 2))
+ except Exception as e:
+ logging.error(f"Error setting {offset_name}: eval failed with expression '{expression}'. Exception: {e}")
+
+ def set_nozzle_offset(self, option, value):
+ script = KlippyGcodes.set_save_variables(option, value)
+ self._screen._send_action(None, "printer.gcode.script", {"script": script})
+ logging.info(f"Set {option}:{value}")
diff --git a/screen.py b/screen.py
index 99562310..67b3dcc5 100755
--- a/screen.py
+++ b/screen.py
@@ -83,6 +83,7 @@ class KlipperScreen(Gtk.Window):
GLib.set_prgname('KlipperScreen')
self.blanking_time = 600
self.use_dpms = True
+ self.offset_fine_tune_mode = False
self.apiclient = None
self.dialogs = []
self.confirm = None
@@ -780,7 +781,10 @@ class KlipperScreen(Gtk.Window):
self.show_panel("extrude")
def state_printing(self):
- self.show_panel("job_status", remove_all=True)
+ if not self.offset_fine_tune_mode:
+ self.show_panel("job_status", remove_all=True)
+ else:
+ self.show_panel("offset_fine_tune", remove_all=True)
def state_ready(self, wait=True):
# Do not return to main menu if completing a job, timeouts/user input will return
@@ -797,6 +801,8 @@ class KlipperScreen(Gtk.Window):
else:
self.show_panel("main_menu", remove_all=True, items=self._config.get_menu_items("__main"))
self._ws.klippy.gcode_script("UPDATE_DELAYED_GCODE ID=_CHECK_POWER_LOSS_RECOVERY DURATION=0.1")
+ if self.offset_fine_tune_mode:
+ self.offset_fine_tune_mode = False
def state_startup(self):
self.printer_initializing(_("Klipper is attempting to start"))
diff --git a/styles/dark/images/nozzle_offset.svg b/styles/dark/images/nozzle_offset.svg
new file mode 100644
index 00000000..fffa9b85
--- /dev/null
+++ b/styles/dark/images/nozzle_offset.svg
@@ -0,0 +1,26 @@
+
diff --git a/styles/dark/images/offset_z.svg b/styles/dark/images/offset_z.svg
new file mode 100644
index 00000000..e1822abd
--- /dev/null
+++ b/styles/dark/images/offset_z.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/styles/light/images/nozzle_offset.svg b/styles/light/images/nozzle_offset.svg
new file mode 100644
index 00000000..f422b836
--- /dev/null
+++ b/styles/light/images/nozzle_offset.svg
@@ -0,0 +1,31 @@
+
diff --git a/styles/light/images/offset_z.svg b/styles/light/images/offset_z.svg
new file mode 100644
index 00000000..e1822abd
--- /dev/null
+++ b/styles/light/images/offset_z.svg
@@ -0,0 +1 @@
+
\ No newline at end of file