Add update manager capability and revamp system panel to show all moonraker update clients

This commit is contained in:
Jordan 2021-06-09 21:33:08 -04:00 committed by jordanruthe
parent 23832e8ad6
commit 867ded34d9
6 changed files with 249 additions and 22 deletions

View File

@ -185,23 +185,20 @@ class KlippyGtk:
dialog.connect("response", callback, *args)
dialog.get_style_context().add_class("dialog")
grid = Gtk.Grid()
grid.set_size_request(screen.width - 60, -1)
grid.set_vexpand(True)
grid.set_halign(Gtk.Align.CENTER)
grid.set_valign(Gtk.Align.CENTER)
grid.add(content)
box = Gtk.Box()
box.set_size_request(screen.width - 60, 0)
box.set_vexpand(True)
content_area = dialog.get_content_area()
content_area.set_margin_start(15)
content_area.set_margin_end(15)
content_area.set_margin_top(15)
content_area.set_margin_bottom(15)
content_area.add(grid)
content_area.add(content)
dialog.show_all()
return dialog, grid
return dialog
def ToggleButtonImage(self, image_name, label, style=False, width_scale=1, height_scale=1):

View File

@ -21,8 +21,9 @@ class Printer:
}
tools = []
def __init__(self, printer_info, data):
def __init__(self, printer_info, data, state_execute_cb):
self.state = "disconnected"
self.state_cb = state_execute_cb
self.power_devices = {}
def reinit(self, printer_info, data):
@ -145,6 +146,7 @@ class Printer:
logging.debug("Adding callback for state: %s" % state)
Gdk.threads_add_idle(
GLib.PRIORITY_HIGH_IDLE,
self.state_cb,
self.state_callbacks[state],
prev_state
)

View File

@ -4,6 +4,7 @@ import os
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GLib
from datetime import datetime
from ks_includes.KlippyGcodes import KlippyGcodes
from ks_includes.screen_panel import ScreenPanel
@ -20,27 +21,30 @@ class SystemPanel(ScreenPanel):
restart = self._gtk.ButtonImage('refresh',"\n".join(_('Klipper Restart').split(' ')),'color1')
restart.connect("clicked", self.restart_klippy)
restart.set_vexpand(False)
firmrestart = self._gtk.ButtonImage('refresh',"\n".join(_('Firmware Restart').split(' ')),'color2')
firmrestart.connect("clicked", self.restart_klippy, "firmware")
firmrestart.set_vexpand(False)
ks_restart = self._gtk.ButtonImage('refresh',"\n".join(_('Restart Klipper Screen').split(' ')))
ks_restart.set_vexpand(False)
ks_restart.connect("clicked", self.restart_ks)
reboot = self._gtk.ButtonImage('refresh',_('System\nRestart'),'color3')
reboot.connect("clicked", self._screen._confirm_send_action,
_("Are you sure you wish to reboot the system?"), "machine.reboot")
reboot.set_vexpand(False)
shutdown = self._gtk.ButtonImage('shutdown',_('System\nShutdown'),'color4')
shutdown.connect("clicked", self._screen._confirm_send_action,
_("Are you sure you wish to shutdown the system?"), "machine.shutdown")
shutdown.set_vexpand(False)
info = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
info.set_vexpand(True)
info.set_valign(Gtk.Align.CENTER)
self.labels['loadavg'] = Gtk.Label("temp")
self.update_system_load()
self.system_timeout = GLib.timeout_add(1000, self.update_system_load)
#self.system_timeout = GLib.timeout_add(1000, self.update_system_load)
self.labels['klipper_version'] = Gtk.Label(_("Klipper Version") +
(": %s" % self._screen.printer.get_klipper_version()))
@ -53,8 +57,47 @@ class SystemPanel(ScreenPanel):
info.add(self.labels['klipper_version'])
info.add(self.labels['ks_version'])
scroll = Gtk.ScrolledWindow()
scroll.set_property("overlay-scrolling", False)
scroll.set_vexpand(True)
grid.attach(info, 0, 0, 5, 2)
infogrid = Gtk.Grid()
infogrid.get_style_context().add_class("system-program-grid")
update_resp = self._screen.apiclient.send_request("machine/update/status")
self.update_status = False
if update_resp == False:
logging.info("No update manager configured")
else:
self.update_status = update_resp['result']
vi = update_resp['result']['version_info']
items = sorted(list(vi))
i = 0
for prog in items:
self.labels[prog] = Gtk.Label("")
self.labels[prog].set_hexpand(True)
self.labels[prog].set_halign(Gtk.Align.START)
self.labels["%s_status" % prog] = self._gtk.Button()
self.labels["%s_status" % prog].set_hexpand(False)
self.labels["%s_status" % prog].connect("clicked", self.update_program, prog)
self.labels["%s_box" % prog] = Gtk.Box()
self.labels["%s_box" % prog].set_hexpand(False)
self.labels["%s_info" % prog] = self._gtk.ButtonImage("information",None, None, .7, .7)
self.labels["%s_info" % prog].connect("clicked", self.show_commit_history, prog)
self.labels["%s_box" % prog].pack_end(self.labels["%s_status" % prog], True, 0, 0)
logging.info("Updating program: %s " % prog)
self.update_program_info(prog)
infogrid.attach(self.labels["%s_box" % prog], 1, i, 1, 1)
infogrid.attach(self.labels[prog], 0, i, 1, 1)
i = i + 1
scroll.add(infogrid)
grid.attach(scroll, 0, 0, 5, 2)
grid.attach(restart, 0, 2, 1, 1)
grid.attach(firmrestart, 1, 2, 1, 1)
grid.attach(ks_restart, 2, 2, 1, 1)
@ -62,16 +105,163 @@ class SystemPanel(ScreenPanel):
grid.attach(shutdown, 4, 2, 1, 1)
self.content.add(grid)
self._screen.add_subscription(panel_name)
def update_system_load(self):
def activate(self):
self.get_updates()
def destroy_widget(self, widget, response_id):
widget.destroy()
def finish_updating(self, widget, response_id):
widget.destroy()
self._screen.set_updating(False)
self.get_updates()
def get_updates(self):
update_resp = self._screen.apiclient.send_request("machine/update/status")
if update_resp == False:
logging.info("No update manager configured")
else:
self.update_status = update_resp['result']
vi = update_resp['result']['version_info']
items = sorted(list(vi))
for prog in items:
self.update_program_info(prog)
def process_update(self, action, data):
if action == "notify_update_response":
logging.info("Update: %s" % data)
if 'application' in data and data['application'] == self.update_prog:
self.labels['update_progress'].set_text(self.labels['update_progress'].get_text().strip() + "\n" +
data['message'] + "\n")
adjustment = self.labels['update_scroll'].get_vadjustment()
adjustment.set_value( adjustment.get_upper() - adjustment.get_page_size() )
adjustment = self.labels['update_scroll'].show_all()
if data['complete'] == True:
self.update_dialog.set_response_sensitive(Gtk.ResponseType.CANCEL, True)
def show_commit_history(self, widget, program):
_ = self.lang.gettext
lavg = os.getloadavg()
self.labels['loadavg'].set_text(
_("Load Average") + (": %.2f %.2f %.2f" % (lavg[0], lavg[1], lavg[2]))
)
#TODO: Shouldn't need this
self.system_timeout = GLib.timeout_add(1000, self.update_system_load)
if self.update_status == False or program not in self.update_status['version_info']:
return
info = self.update_status['version_info'][program]
if info['version'] == info['remote_version']:
return
buttons = [
{"name":_("Go Back"), "response": Gtk.ResponseType.CANCEL}
]
scroll = Gtk.ScrolledWindow()
scroll.set_hexpand(True)
scroll.set_vexpand(True)
grid = Gtk.Grid()
grid.set_halign(Gtk.Align.START)
i = 0
date = ""
for c in info['commits_behind']:
ndate = datetime.fromtimestamp(int(c['date'])).strftime("%b %d")
if date != ndate:
date = ndate
label = Gtk.Label("")
label.set_markup("<b>%s</b>\n" % date)
grid.attach(label, 0, i, 1, 1)
i = i + 1
label = Gtk.Label()
label.set_markup("%s\n<i>%s</i> %s %s\n" % (c['subject'], c['author'], _("Commited"),"2 days ago"))
label.set_hexpand(True)
label.set_halign(Gtk.Align.START)
grid.attach(label, 0, i, 1, 1)
i = i + 1
scroll.add(grid)
dialog = self._gtk.Dialog(self._screen, buttons, scroll, self.destroy_widget)
def update_program(self, widget, program):
if self._screen.is_updating():
return
_ = self.lang.gettext
if self.update_status == False or program not in self.update_status['version_info']:
return
info = self.update_status['version_info'][program]
logging.info("program: %s" % info)
if "package_count" in info:
if info['package_count'] == 0:
return
else:
if info['version'] == info['remote_version']:
return
buttons = [
{"name":_("Finish"), "response": Gtk.ResponseType.CANCEL}
]
scroll = Gtk.ScrolledWindow()
scroll.set_hexpand(True)
scroll.set_vexpand(True)
self.labels['update_progress'] = Gtk.Label("%s %s%s" % (_("Starting update for"), program, _("...")))
self.labels['update_progress'].set_halign(Gtk.Align.START)
self.labels['update_progress'].set_valign(Gtk.Align.START)
scroll.add(self.labels['update_progress'])
self.labels['update_scroll'] = scroll
dialog = self._gtk.Dialog(self._screen, buttons, scroll, self.finish_updating)
dialog.set_response_sensitive(Gtk.ResponseType.CANCEL, False)
self.update_prog = program
self.update_dialog = dialog
if program in ['klipper','moonraker','system']:
logging.info("Sending machine.update.%s" % program)
self._screen._ws.send_method("machine.update.%s" % program)
else:
logging.info("Sending machine.update.client name: %s" % program)
self._screen._ws.send_method("machine.update.client", {"name": program})
self._screen.set_updating(True)
def update_program_info(self, p):
_ = self.lang.gettext
logging.info("Updating program: %s " % p)
if 'version_info' not in self.update_status or p not in self.update_status['version_info']:
return
info = self.update_status['version_info'][p]
logging.info("%s: %s" % (p, info))
if p != "system":
version = (info['full_version_string'] if "full_version_string" in info else info['version'])
if info['version'] == info['remote_version']:
self.labels[p].set_markup("<b>%s</b>\n%s" % (p, version))
self.labels["%s_status" % p].set_label(_("Up To Date"))
self.labels["%s_status" % p].set_sensitive(False)
if self.labels["%s_info" % p] in self.labels["%s_box" % p].get_children():
self.labels["%s_box" % p].remove(self.labels["%s_info" % p])
else:
self.labels[p].set_markup("<b>%s</b>\n%s -> %s" % (p, version, info['remote_version']))
self.labels["%s_status" % p].set_label(_("Update"))
self.labels["%s_status" % p].set_sensitive(True)
if not self.labels["%s_info" % p] in self.labels["%s_box" % p].get_children():
self.labels["%s_box" % p].pack_start(self.labels["%s_info" % p], True, 0, 0)
else:
self.labels[p].set_markup("<b>System</b>")
if info['package_count'] == 0:
self.labels["%s_status" % p].set_label(_("Up To Date"))
self.labels["%s_status" % p].set_sensitive(False)
else:
self.labels["%s_status" % p].set_label(_("Update"))
self.labels["%s_status" % p].set_sensitive(True)
def restart_klippy(self, widget, type=None):
if type == "firmware":

View File

@ -79,6 +79,8 @@ class KlipperScreen(Gtk.Window):
rtl_languages = ['he_il']
subscriptions = []
shutdown = True
updating = False
update_queue = []
_ws = None
def __init__(self, args, version):
@ -193,7 +195,7 @@ class KlipperScreen(Gtk.Window):
'virtual_sdcard': {
'is_active': False
}
})
}, self.state_execute)
self._remove_all_panels()
panels = list(self.panels)
@ -439,6 +441,9 @@ class KlipperScreen(Gtk.Window):
def is_printing(self):
return self.printer.get_state() == "printing"
def is_updating(self):
return self.updating
def _go_to_submenu(self, widget, name):
logging.info("#### Go to submenu " + str(name))
#self._remove_current_panel(False)
@ -540,11 +545,26 @@ 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 set_updating(self, updating=False):
if self.updating == True and updating == False:
if len(self.update_queue) > 0:
i = self.update_queue.pop()
self.update_queue = []
i[0](i[1])
self.updating = updating
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_execute(self, callback, prev_state):
if self.is_updating():
self.update_queue.append([callback, prev_state])
else:
callback(prev_state)
def state_disconnected(self, prev_state):
if "printer_select" in self._cur_panels:
self.printer_select_callbacks = [self.state_disconnected]
@ -557,7 +577,7 @@ class KlipperScreen(Gtk.Window):
for panel in list(self.panels):
if panel in ["printer_select","splash_screen"]:
continue
del self.panels[panel]
#del self.panels[panel]
def state_error(self, prev_state):
if "printer_select" in self._cur_panels:
@ -650,6 +670,8 @@ class KlipperScreen(Gtk.Window):
self.files.process_update(data)
elif action == "notify_metadata_update":
self.files.request_metadata(data['filename'])
elif action == "notify_update_response":
logging.info("%s: %s" % (action,data))
elif action == "notify_power_changed":
logging.debug("Power status changed: %s", data)
self.printer.process_power_update(data)

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 330 330" style="enable-background:new 0 0 330 330;" xml:space="preserve">
<g>
<path style="fill: #ffffff;" d="M165,0C74.019,0,0,74.02,0,165.001C0,255.982,74.019,330,165,330s165-74.018,165-164.999C330,74.02,255.981,0,165,0z
M165,300c-74.44,0-135-60.56-135-134.999C30,90.562,90.56,30,165,30s135,60.562,135,135.001C300,239.44,239.439,300,165,300z"/>
<path style="fill: #ffffff;" d="M164.998,70c-11.026,0-19.996,8.976-19.996,20.009c0,11.023,8.97,19.991,19.996,19.991
c11.026,0,19.996-8.968,19.996-19.991C184.994,78.976,176.024,70,164.998,70z"/>
<path style="fill: #ffffff;" d="M165,140c-8.284,0-15,6.716-15,15v90c0,8.284,6.716,15,15,15c8.284,0,15-6.716,15-15v-90C180,146.716,173.284,140,165,140z
"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -330,6 +330,8 @@ trough {
opacity: 0.8;
}
.system-program-grid
.warning {
background-color: rgba(30, 204, 39, 0.7);
}