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:
parent
dd08d3d9c8
commit
5e31e3cfa6
25
README.md
25
README.md
@ -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
|
||||

|
||||
|
||||
|
||||
### 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).
|
||||
|
@ -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
|
||||
|
@ -75,6 +75,7 @@ class KlippyWebsocket(threading.Thread):
|
||||
|
||||
def close(self):
|
||||
self.closing = True
|
||||
self.ws.close()
|
||||
|
||||
def is_connected(self):
|
||||
return self.connected
|
||||
|
@ -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)):
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
33
panels/printer_select.py
Normal 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
|
@ -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'])
|
||||
|
@ -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()
|
||||
|
89
screen.py
89
screen.py
@ -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:
|
||||
|
5
styles/z-bolt/images/shuffle.svg
Normal file
5
styles/z-bolt/images/shuffle.svg
Normal 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 |
Loading…
x
Reference in New Issue
Block a user