Multiple printers (#85)

* screen/printer_select/splash_screen: Updates to allow changing between moonraker instances

* Updates to multiple printers

* settings: Display printer endpoints

* Update macros to be on a per-printer basis

* files: Changes to clear out file cache on printer switch

* job_status: Redo page for action bar

* splash_screen: Change icon

* websocket: Actually close the websocket

* printer: Fix error case

* splash_screen: show buttons update

* readme update
This commit is contained in:
jordanruthe 2021-03-05 18:30:59 -05:00 committed by GitHub
parent dd08d3d9c8
commit 5e31e3cfa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 253 additions and 104 deletions

View File

@ -1,21 +1,9 @@
# KlipperScreen
KlipperScreen is an idea based from [OctoScreen](https://github.com/Z-Bolt/OctoScreen/), but instead of needing OctoPrint or to compile go, KlipperScreen is python based and interacts directly with [Moonraker](https://github.com/arksine/moonraker), Klipper's API service.
Current feature list:
- [x] Homing
- [x] Preheating
- [x] Job Status and control
- [x] Temperature control
- [x] Extrude control
- [x] Fan control
- [x] Disable steppers
- [x] Configure Z Offset using PROBE_CALIBRATE
- [x] Print tuning (Z Babystepping, Speed Control, Flow Control)
- [x] Manual bed leveling assist
- [x] Using thumbnails from prusa on job status page
- [x] Scale UI based off of resolution
- [ ] Better system panel
- [ ] Wifi selection
KlipperScreen is touchscreen GUI for Klipper based 3D printers. KlipperScreen interfaces with [Klipper](https://github.com/KevinOConner/klipper) via [Moonraker](https://github.com/arksine/moonraker).
Multiple printers update is here! Please check the configuration information for specifying several printers.
[Changelog](docs/changelog.md)
@ -43,3 +31,8 @@ Main Menu
Job Status
![Job Status](docs/img/job_status.png)
### Inspiration
KlipperScreen was inspired by [OctoScreen](https://github.com/Z-Bolt/OctoScreen/) and the need for a touchscreen GUI that
will natively work with [Klipper](https://github.com/KevinOConner/klipper) and [Moonraker](https://github.com/arksine/moonraker).

View File

@ -1,5 +1,8 @@
## Changelog
#### 2021 03 05
* Multiple printers are now available in the main branch.
#### 2021 02 22
* Add configurable z_babystep intervals
* Add QUAD_GANTRY_LEVEL and Z_TILT_ADJUST to Homing menu

View File

@ -75,6 +75,7 @@ class KlippyWebsocket(threading.Thread):
def close(self):
self.closing = True
self.ws.close()
def is_connected(self):
return self.connected

View File

@ -88,7 +88,7 @@ class KlipperScreenConfig:
printer[8:]: {
"moonraker_host": self.config.get(printer, "moonraker_host", fallback="127.0.0.1"),
"moonraker_port": self.config.get(printer, "moonraker_port", fallback="7125"),
"moonraker_api_key": self.config.get(printer, "moonraker_api_key", fallback="")
"moonraker_api_key": self.config.get(printer, "moonraker_api_key", fallback=False)
}
})
if len(printers) <= 0:
@ -138,6 +138,7 @@ class KlipperScreenConfig:
return ["\n".join(user_def), None if saved_def == None else "\n".join(saved_def)]
def get_config_file_location(self, file):
logging.info("Passed config file: %s" % file)
if not path.exists(file):
file = "%s/%s" % (os.getcwd(), self.configfile_name)
if not path.exists(file):
@ -219,16 +220,17 @@ class KlipperScreenConfig:
save_config.add_section(opt['section'])
save_config.set(opt['section'], name, str(curval))
if "displayed_macros" in self.config.sections():
for item in self.config.options('displayed_macros'):
value = self.config['displayed_macros'].getboolean(item, fallback=True)
macro_sections = [i for i in self.config.sections() if i.startswith("displayed_macros")]
for macro_sec in macro_sections:
for item in self.config.options(macro_sec):
value = self.config[macro_sec].getboolean(item, fallback=True)
if value == False or (self.defined_config != None and
"displayed_macros" in self.defined_config.sections() and
self.defined_config['displayed_macros'].getboolean(item, fallback=True) == False and
self.defined_config['displayed_macros'].getboolean(item, fallback=True) != value):
if "displayed_macros" not in save_config.sections():
save_config.add_section("displayed_macros")
save_config.set("displayed_macros", item, str(value))
macro_sec in self.defined_config.sections() and
self.defined_config[macro_sec].getboolean(item, fallback=True) == False and
self.defined_config[macro_sec].getboolean(item, fallback=True) != value):
if macro_sec not in save_config.sections():
save_config.add_section(macro_sec)
save_config.set(macro_sec, item, str(value))
save_output = self._build_config_string(save_config).split("\n")
for i in range(len(save_output)):

View File

@ -14,11 +14,6 @@ from gi.repository import Gtk, Gdk, GLib
RESCAN_INTERVAL = 4
class KlippyFiles(Thread):
callbacks = []
filelist = []
files = {}
metadata_timeout = {}
timeout = None
thumbnail_dir = "/tmp/.KS-thumbnails"
def __init__(self, screen, *args, **kwargs):
@ -26,6 +21,12 @@ class KlippyFiles(Thread):
self.loop = None
self._poll_task = None
self._screen = screen
self.callbacks = []
self.files = {}
self.filelist = []
self.metadata_timeout = {}
logging.info("")
if not os.path.exists(self.thumbnail_dir):
os.makedirs(self.thumbnail_dir)
@ -43,9 +44,11 @@ class KlippyFiles(Thread):
with suppress(asyncio.CancelledError):
loop.run_until_complete(self._poll_task)
finally:
logging.info("Closing loop")
loop.close()
def stop(self):
logging.info("Trying to stop loop2")
self.loop.call_soon_threadsafe(self.loop.stop)
async def _poll(self):
@ -117,10 +120,6 @@ class KlippyFiles(Thread):
if callback in self.callbacks:
self.callbacks.pop(self.callbacks.index(callback))
def add_timeout(self):
if self.timeout == None:
self.timeout = GLib.timeout_add(4000, self.ret_files)
def file_exists(self, filename):
return True if filename in self.filelist else False
@ -141,18 +140,13 @@ class KlippyFiles(Thread):
return False
return "thumbnails" in self.files[filename] and len(self.files[filename]) > 0
def remove_timeout(self):
if self.timeout != None:
self.timeout = None
def request_metadata(self, filename):
if filename not in self.filelist:
return False
self._screen._ws.klippy.get_file_metadata(filename, self._callback)
async def ret_files(self, retval=True):
if not self._screen._ws.klippy.get_file_list(self._callback):
self.timeout = None
self._screen._ws.klippy.get_file_list(self._callback)
def ret_file_data (self, filename):
print("Getting file info for %s" % (filename))

View File

@ -7,6 +7,9 @@ from ks_includes.KlippyGcodes import KlippyGcodes
class Printer:
data = {}
devices = {}
power_devices = {}
state_callbacks = {
"disconnected": None,
"error": None,
@ -16,6 +19,7 @@ class Printer:
"startup": None,
"shutdown": None
}
tools = []
def __init__(self, printer_info, data):
self.state = "disconnected"
@ -148,6 +152,8 @@ class Printer:
return section in list(self.config)
def get_config_section_list(self, search=""):
if not hasattr(self, "config"):
return []
return [i for i in list(self.config) if i.startswith(search)]
def get_config_section(self, section):

View File

@ -12,7 +12,7 @@ class ScreenPanel:
title_spacing = 50
control = {}
def __init__(self, screen, title, back=True):
def __init__(self, screen, title, back=True, action_bar=True, printer_name=True):
self._screen = screen
self._config = screen._config
self._files = screen.files
@ -24,34 +24,41 @@ class ScreenPanel:
self.layout = Gtk.Layout()
self.layout.set_size(self._screen.width, self._screen.height)
action_bar_width = self._gtk.get_action_bar_width()
action_bar_width = self._gtk.get_action_bar_width() if action_bar == True else 0
self.control_grid = self._gtk.HomogeneousGrid()
self.control_grid.set_size_request(action_bar_width - 2, self._screen.height)
self.control_grid.get_style_context().add_class('action_bar')
if action_bar == True:
self.control_grid = self._gtk.HomogeneousGrid()
self.control_grid.set_size_request(action_bar_width - 2, self._screen.height)
self.control_grid.get_style_context().add_class('action_bar')
button_scale = self._gtk.get_header_image_scale()
logging.debug("Button scale: %s" % button_scale)
if back == True:
self.control['back'] = self._gtk.ButtonImage('back', None, None, button_scale[0], button_scale[1])
self.control['back'].connect("clicked", self._screen._menu_go_back)
self.control_grid.attach(self.control['back'], 0, 0, 1, 1)
button_scale = self._gtk.get_header_image_scale()
logging.debug("Button scale: %s" % button_scale)
self.control['home'] = self._gtk.ButtonImage('home', None, None, button_scale[0], button_scale[1])
self.control['home'].connect("clicked", self.menu_return, True)
self.control_grid.attach(self.control['home'], 0, 1, 1, 1)
if back == True:
self.control['back'] = self._gtk.ButtonImage('back', None, None, button_scale[0], button_scale[1])
self.control['back'].connect("clicked", self._screen._menu_go_back)
self.control_grid.attach(self.control['back'], 0, 0, 1, 1)
self.control['printer_select'] = Gtk.Label("")
self.control['home'] = self._gtk.ButtonImage('home', None, None, button_scale[0], button_scale[1])
self.control['home'].connect("clicked", self.menu_return, True)
self.control_grid.attach(self.control['home'], 0, 1, 1, 1)
else:
for i in range(2):
self.control['space%s' % i] = Gtk.Label("")
self.control_grid.attach(self.control['space%s' % i], 0, i, 1, 1)
if len(self._config.get_printers()) > 1:
self.control['printer_select'] = self._gtk.ButtonImage(
'shuffle', None, None, button_scale[0], button_scale[1])
self.control['printer_select'].connect("clicked", self._screen.show_printer_select)
else:
self.control['printer_select'] = Gtk.Label("")
self.control_grid.attach(self.control['printer_select'], 0, 2, 1, 1)
else:
for i in range(3):
self.control['space%s' % i] = Gtk.Label("")
self.control_grid.attach(self.control['space%s' % i], 0, i, 1, 1)
self.control['estop'] = self._gtk.ButtonImage('emergency', None, None, button_scale[0], button_scale[1])
self.control['estop'].connect("clicked", self.emergency_stop)
self.control_grid.attach(self.control['estop'], 0, 3, 1, 1)
#self.layout.put(self.control['estop'], int(self._screen.width/4*3 - button_scale[0]/2), 0)
self.control['estop'] = self._gtk.ButtonImage('emergency', None, None, button_scale[0], button_scale[1])
self.control['estop'].connect("clicked", self.emergency_stop)
self.control_grid.attach(self.control['estop'], 0, 3, 1, 1)
#self.layout.put(self.control['estop'], int(self._screen.width/4*3 - button_scale[0]/2), 0)
try:
env = Environment(extensions=["jinja2.ext.i18n"])
@ -66,13 +73,16 @@ class ScreenPanel:
self.title.set_hexpand(True)
self.title.set_halign(Gtk.Align.CENTER)
self.title.set_valign(Gtk.Align.CENTER)
self.set_title(title)
if printer_name == True:
self.set_title("%s | %s" % (self._screen.connected_printer, title))
else:
self.set_title(title)
self.content = Gtk.Box(spacing=0)
self.content.set_size_request(self._screen.width - action_bar_width, self._screen.height - self.title_spacing)
self.layout.put(self.control_grid, 0, 0)
if action_bar == True:
self.layout.put(self.control_grid, 0, 0)
self.layout.put(self.title, action_bar_width, 0)
self.layout.put(self.content, action_bar_width, self.title_spacing)

View File

@ -81,28 +81,27 @@ class MacroPanel(ScreenPanel):
def load_gcode_macros(self):
macros = self._screen.printer.get_gcode_macros()
section_name = "displayed_macros %s" % self._screen.connected_printer
logging.info("Macro section name [%s]" % section_name)
for x in macros:
macro = x[12:].strip()
if macro in self.loaded_macros:
continue
logging.debug("Evaluating '%s' value '%s'" % (macro.strip().lower(),
self._config.get_config().getboolean("displayed_macros", macro.lower(), fallback=True)))
if ("displayed_macros" not in self._config.get_config().sections() or
self._config.get_config().getboolean("displayed_macros", macro.lower(), fallback=True)):
if (section_name not in self._config.get_config().sections() or
self._config.get_config().getboolean(section_name, macro.lower(), fallback=True)):
self.add_gcode_macro(macro)
def run_gcode_macro(self, widget, macro):
self._screen._ws.klippy.gcode_script(macro)
def unload_gcode_macros(self):
section_name = "displayed_macros %s" % self._screen.connected_printer
for macro in self.loaded_macros:
logging.debug("Evaluating '%s' value '%s'" % (macro.strip().lower(),
self._config.get_config().getboolean("displayed_macros", macro.lower(), fallback=True)))
if ("displayed_macros" in self._config.get_config().sections() and
not self._config.get_config().getboolean("displayed_macros", macro.lower(), fallback=True)):
if (section_name in self._config.get_config().sections() and
not self._config.get_config().getboolean(section_name, macro.lower(), fallback=True)):
macros = sorted(self.macros)
pos = macros.index(macro)
self.labels['macros'].remove_row(pos)

View File

@ -18,16 +18,16 @@ class JobStatusPanel(ScreenPanel):
progress = 0
state = "printing"
def __init__(self, screen, title, back=False):
super().__init__(screen, title, False)
def initialize(self, panel_name):
_ = self.lang.gettext
self.layout = Gtk.Layout()
self.layout.set_size(self._screen.width, self._screen.height)
self.timeleft_type = "file"
self.create_buttons()
grid = self._gtk.HomogeneousGrid()
grid.set_size_request(self._screen.width, self._screen.height)
grid.set_row_homogeneous(False)
self.labels['button_grid'] = self._gtk.HomogeneousGrid()
@ -170,23 +170,27 @@ class JobStatusPanel(ScreenPanel):
sfe_grid.attach(fan_box, 2, 0, 1, 1)
self.labels['sfe_grid'] = sfe_grid
self.labels['i1_box'] = Gtk.VBox(spacing=0)
self.labels['i1_box'] = Gtk.HBox(spacing=0)
self.labels['i1_box'].set_vexpand(True)
self.labels['i1_box'].get_style_context().add_class("printing-info-box")
self.labels['i1_box'].set_valign(Gtk.Align.CENTER)
self.labels['i2_box'] = Gtk.VBox(spacing=0)
self.labels['i2_box'].set_vexpand(True)
self.labels['i2_box'].get_style_context().add_class("printing-info-box")
self.labels['i2_box'].set_valign(Gtk.Align.CENTER)
self.labels['info_grid'] = self._gtk.HomogeneousGrid()
self.labels['info_grid'].attach(self.labels['i1_box'], 0, 0, 1, 1)
self.labels['info_grid'].attach(self.labels['i2_box'], 1, 0, 1, 1)
grid.attach(overlay, 0, 0, 1, 1)
grid.attach(fi_box, 1, 0, 3, 1)
grid.attach(self.labels['i1_box'], 0, 1, 2, 2)
grid.attach(self.labels['i2_box'], 2, 1, 2, 2)
grid.attach(self.labels['info_grid'], 0, 1, 4, 2)
grid.attach(self.labels['button_grid'], 0, 3, 4, 1)
self.add_labels()
self.grid = grid
self.layout.put(grid, 0, 0)
self.content.add(grid)
self._screen.add_subscription(panel_name)
@ -250,8 +254,6 @@ class JobStatusPanel(ScreenPanel):
self.labels['cancel'].connect("clicked", self.cancel)
self.labels['control'] = self._gtk.ButtonImage("control",_("Control"),"color3")
self.labels['control'].connect("clicked", self._screen._go_to_submenu, "")
self.labels['estop'] = self._gtk.ButtonImage("emergency",_("Emergency Stop"),"color4")
self.labels['estop'].connect("clicked", self.emergency_stop)
self.labels['menu'] = self._gtk.ButtonImage("complete",_("Main Menu"),"color4")
self.labels['menu'].connect("clicked", self.close_panel)
self.labels['pause'] = self._gtk.ButtonImage("pause",_("Pause"),"color1" )
@ -484,12 +486,12 @@ class JobStatusPanel(ScreenPanel):
if self.state == "printing":
self.labels['button_grid'].attach(self.labels['pause'], 0, 0, 1, 1)
self.labels['button_grid'].attach(self.labels['cancel'], 1, 0, 1, 1)
self.labels['button_grid'].attach(self.labels['estop'], 2, 0, 1, 1)
self.labels['button_grid'].attach(Gtk.Label(""), 2, 0, 1, 1)
self.labels['button_grid'].attach(self.labels['control'], 3, 0, 1, 1)
elif self.state == "paused":
self.labels['button_grid'].attach(self.labels['resume'], 0, 0, 1, 1)
self.labels['button_grid'].attach(self.labels['cancel'], 1, 0, 1, 1)
self.labels['button_grid'].attach(self.labels['estop'], 2, 0, 1, 1)
self.labels['button_grid'].attach(Gtk.Label(""), 2, 0, 1, 1)
self.labels['button_grid'].attach(self.labels['control'], 3, 0, 1, 1)
elif self.state == "error" or self.state == "complete" or self.state == "cancelled":
self.labels['button_grid'].attach(Gtk.Label(""), 0, 0, 1, 1)

33
panels/printer_select.py Normal file
View File

@ -0,0 +1,33 @@
import gi
import logging
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GLib
from ks_includes.KlippyGcodes import KlippyGcodes
from ks_includes.screen_panel import ScreenPanel
logger = logging.getLogger("KlipperScreen.PrinterSelect")
def create_panel(*args):
return PrinterSelect(*args)
class PrinterSelect(ScreenPanel):
def __init__(self, screen, title, back=True, action_bar=True, printer_name=True):
super().__init__(screen, title, False, False, False)
def initialize(self, panel_name):
_ = self.lang.gettext
printers = self._config.get_printers()
box = Gtk.Box()
self.content.add(box)
i = 1
for printer in printers:
name = list(printer)[0]
self.labels[name] = self._gtk.ButtonImage("extruder",name,"color%s" % (i%4))
self.labels[name].connect("clicked", self._screen.connect_printer_widget, name)
box.add(self.labels[name])
i += 1

View File

@ -21,12 +21,23 @@ class SettingsPanel(ScreenPanel):
self.labels['main_box'] = self.create_box('main')
self.labels['macros_box'] = self.create_box('macros')
printbox = Gtk.Box(spacing=0)
printbox.set_vexpand(False)
self.labels['add_printer_button'] = self._gtk.Button(_("Add Printer"), "color1")
#printbox.add(self.labels['add_printer_button'])
self.labels['printers_box'] = self.create_box('printers', printbox)
options = self._config.get_configurable_options().copy()
options.append({"macros": {
"name": _("Displayed Macros"),
"type": "menu",
"menu": "macros"}
})
options.append({"printers": {
"name": _("Printer Connections"),
"type": "menu",
"menu": "printers"
}})
for option in options:
name = list(option)[0]
@ -36,14 +47,25 @@ class SettingsPanel(ScreenPanel):
macro = macro[12:]
self.macros[macro] = {
"name": macro,
"section": "displayed_macros",
"section": "displayed_macros %s" % self._screen.connected_printer,
"type": "macro"
}
for macro in list(self.macros):
self.add_option('macros', self.macros, macro, self.macros[macro])
logging.debug("Macros: %s" % self.macros)
self.printers = {}
for printer in self._config.get_printers():
logging.debug("Printer: %s" % printer)
pname = list(printer)[0]
self.printers[pname] = {
"name": pname,
"section": "printer %s" % pname,
"type": "printer",
"moonraker_host": printer[pname]['moonraker_host'],
"moonraker_port": printer[pname]['moonraker_port'],
}
self.add_option("printers", self.printers, pname, self.printers[pname])
self.control['back'].disconnect_by_func(self._screen._menu_go_back)
self.control['back'].connect("clicked", self.back)
@ -59,7 +81,7 @@ class SettingsPanel(ScreenPanel):
else:
self._screen._menu_go_back()
def create_box(self, name):
def create_box(self, name, insert=None):
# Create a scroll window for the macros
scroll = Gtk.ScrolledWindow()
scroll.set_property("overlay-scrolling", False)
@ -72,6 +94,8 @@ class SettingsPanel(ScreenPanel):
# Create a box to contain all of the above
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
box.set_vexpand(True)
if insert is not None:
box.pack_start(insert, False, False, 0)
box.pack_start(scroll, True, True, 0)
return box
@ -135,6 +159,15 @@ class SettingsPanel(ScreenPanel):
dropdown.set_entry_text_column(0)
dev.add(dropdown)
logging.debug("Children: %s" % dropdown.get_children())
elif option['type'] == "printer":
logging.debug("Option: %s" % option)
box = Gtk.Box()
box.set_vexpand(False)
label = Gtk.Label()
url = "%s:%s" % (option['moonraker_host'], option['moonraker_port'])
label.set_markup("<big>%s</big>\n%s" % (option['name'], url))
box.add(label)
dev.add(box)
elif option['type'] == "menu":
open = self._gtk.ButtonImage("open",None,"color3")
open.connect("clicked", self.load_menu, option['menu'])

View File

@ -61,9 +61,11 @@ class SplashScreenPanel(ScreenPanel):
_ = self.lang.gettext
if "firmware_restart" not in self.labels:
self.labels['printer_select'] = self._gtk.ButtonImage("shuffle",_("Change Printer"))
self.labels['printer_select'].connect("clicked", self._screen.show_printer_select)
self.labels['menu'] = self._gtk.ButtonImage("control",_("Menu"),"color4")
self.labels['menu'].connect("clicked", self._screen._go_to_submenu, "")
self.labels['power'] = self._gtk.ButtonImage("reboot",_("Power On Printer"),"color3")
self.labels['power'] = self._gtk.ButtonImage("shutdown",_("Power On Printer"),"color3")
self.labels['restart'] = self._gtk.ButtonImage("reboot",_("Restart"),"color1")
self.labels['restart'].connect("clicked", self.restart)
self.labels['firmware_restart'] = self._gtk.ButtonImage("restart",_("Firmware Restart"),"color2")
@ -82,7 +84,8 @@ class SplashScreenPanel(ScreenPanel):
self.labels['actions'].add(self.labels['restart'])
self.labels['actions'].add(self.labels['firmware_restart'])
self.labels['actions'].add(self.labels['menu'])
self.labels['actions'].show()
self.labels['actions'].add(self.labels['printer_select'])
self.labels['actions'].show_all()
def firmware_restart(self, widget):
self._screen._ws.klippy.restart_firmware()

View File

@ -54,6 +54,7 @@ class KlipperScreen(Gtk.Window):
""" Class for creating a screen for Klipper via HDMI """
_cur_panels = []
bed_temp_label = None
connecting = False
connected_printer = None
currentPanel = None
files = None
@ -66,6 +67,8 @@ class KlipperScreen(Gtk.Window):
panels = {}
popup_message = None
printer = None
printer_select_callbacks = []
printer_select_prepanel = None
rtl_languages = ['he_il']
subscriptions = []
shutdown = True
@ -116,22 +119,51 @@ class KlipperScreen(Gtk.Window):
self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.ARROW))
else:
self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.BLANK_CURSOR))
pname = list(self._config.get_printers()[0])[0]
self.connect_printer(pname, self._config.get_printers()[0][pname])
def connect_printer(self, name, data):
printers = self._config.get_printers()
logging.debug("Printers: %s" % printers)
if len(printers) == 1:
pname = list(self._config.get_printers()[0])[0]
self.connect_printer(pname)
else:
self.show_panel("printer_select","printer_select","Printer Select", 2)
def connect_printer_widget(self, widget, name):
self.connect_printer(name)
def connect_printer(self, name):
_ = self.lang.gettext
if self.connected_printer == name:
if self.printer_select_prepanel != None:
self.show_panel(self.printer_select_prepanel, "","", 2)
self.printer_select_prepanel = None
while len(self.printer_select_callbacks) > 0:
i = self.printer_select_callbacks.pop(0)
i()
return
self.printer_select_callbacks = []
self.printer_select_prepanel = None
if self.files is not None:
self.files.stop()
self.files = None
for printer in self._config.get_printers():
pname = list(printer)[0]
if pname != name:
continue
data = printer[pname]
break
if self._ws is not None:
self._ws.close()
self.connecting = True
logging.info("Connecting to printer: %s" % name)
self.apiclient = KlippyRest(self._config.get_main_config_option("moonraker_host"),
self._config.get_main_config_option("moonraker_port"),
self._config.get_main_config_option("moonraker_api_key", False))
self.apiclient = KlippyRest(data["moonraker_host"], data["moonraker_port"], data["moonraker_api_key"])
self.printer = Printer({
"software_version": "Unknown"
@ -149,6 +181,8 @@ class KlipperScreen(Gtk.Window):
self._remove_all_panels()
panels = list(self.panels)
if len(self.subscriptions) > 0:
self.subscriptions = []
for panel in panels:
del self.panels[panel]
self.printer_initializing(_("Connecting to %s") % name)
@ -169,19 +203,17 @@ class KlipperScreen(Gtk.Window):
self.printer.configure_power_devices(powerdevs['result'])
self.panels['splash_screen'].show_restart_buttons()
if self._ws is not None:
self._ws.close
self._ws = KlippyWebsocket(self,
{
"on_connect": self.init_printer,
"on_message": self._websocket_callback,
"on_close": self.printer_initializing
},
self._config.get_main_config_option("moonraker_host"),
self._config.get_main_config_option("moonraker_port")
data["moonraker_host"],
data["moonraker_port"]
)
self._ws.initial_connect()
self.connecting = False
self.files = KlippyFiles(self)
self.files.start()
@ -458,12 +490,25 @@ class KlipperScreen(Gtk.Window):
if self.dpms_timeout == None and functions.dpms_loaded == True:
self.dpms_timeout = GLib.timeout_add(1000, self.check_dpms_state)
def show_printer_select(self, widget=None):
logging.debug("Saving panel: %s" % self._cur_panels[0])
self.printer_select_prepanel = self._cur_panels[0]
self.show_panel("printer_select","printer_select","Printer Select", 2)
def state_disconnected(self):
if "printer_select" in self._cur_panels:
self.printer_select_callbacks = [self.state_disconnected]
return
_ = self.lang.gettext
logging.debug("### Going to disconnected")
self.printer_initializing(_("Klipper has disconnected"))
def state_error(self):
if "printer_select" in self._cur_panels:
self.printer_select_callbacks = [self.state_error]
return
_ = self.lang.gettext
msg = self.printer.get_stat("webhooks","state_message")
if "FIRMWARE_RESTART" in msg:
@ -484,26 +529,45 @@ class KlipperScreen(Gtk.Window):
self.printer_printing()
def state_printing(self):
if "printer_select" in self._cur_panels:
self.printer_select_callbacks = [self.state_printing]
return
if "job_status" not in self._cur_panels:
self.printer_printing()
def state_ready(self):
if "printer_select" in self._cur_panels:
self.printer_select_callbacks = [self.state_ready]
return
# Do not return to main menu if completing a job, timeouts/user input will return
if "job_status" in self._cur_panels or "main_menu" in self._cur_panels:
return
self.printer_ready()
def state_startup(self):
if "printer_select" in self._cur_panels:
self.printer_select_callbacks = [self.state_startup]
return
_ = self.lang.gettext
self.printer_initializing(_("Klipper is attempting to start"))
def state_shutdown(self):
if "printer_select" in self._cur_panels:
self.printer_select_callbacks = [self.state_shutdown]
return
_ = self.lang.gettext
self.printer_initializing(_("Klipper has shutdown"))
def _websocket_callback(self, action, data):
_ = self.lang.gettext
if self.connecting == True:
return
if action == "notify_klippy_disconnected":
logging.debug("Received notify_klippy_disconnected")
self.printer.change_state("disconnected")
@ -592,10 +656,11 @@ class KlipperScreen(Gtk.Window):
self.printer.configure_power_devices(powerdevs['result'])
def printer_ready(self):
_ = self.lang.gettext
self.close_popup_message()
# Force update to printer webhooks state in case the update is missed due to websocket subscribe not yet sent
self.printer.process_update({"webhooks":{"state":"ready","state_message": "Printer is ready"}})
self.show_panel('main_panel', "main_menu", self.connected_printer, 2,
self.show_panel('main_panel', "main_menu", _("Home"), 2,
items=self._config.get_menu_items("__main"), extrudercount=self.printer.get_extruder_count())
self.ws_subscribe()
if "job_status" in self.panels:

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24">
<path style="fill: #fff;" d="M17,3L22.25,7.5L17,12L22.25,16.5L17,21V18H14.26L11.44,15.18L13.56,13.06L15.5,15H17V12L17,9H15.5L6.5,18H2V15H5.26L14.26,6H17V3M2,6H6.5L9.32,8.82L7.2,10.94L5.26,9H2V6Z" />
</svg>

After

Width:  |  Height:  |  Size: 489 B