From a2e979f8472b145fb732a838f8155e3dd1807d8b Mon Sep 17 00:00:00 2001 From: jruthe Date: Tue, 14 Jul 2020 08:13:35 -0400 Subject: [PATCH] Updated menu system. Move panel working --- KlipperScreen.config | 73 +++++++++++++-- KlippyGtk.py | 62 +++++++++++-- KlippyWebsocket.py | 2 +- README.md | 125 ++++++++++++++++++++++++- panels/__init__.py | 7 +- panels/example.py | 21 +++++ panels/idle_status.py | 48 ---------- panels/job_status.py | 100 ++++++++++++++++++++ panels/main_menu.py | 34 +++++++ panels/menu.py | 69 ++++++++++++++ panels/move.py | 112 +++++++++++++++++++++++ panels/splash_screen.py | 37 ++++++++ panels/temperature.py | 32 +++++++ screen.py | 167 +++++++++++++++++++++------------- scripts/disablescreenblank.sh | 11 +++ style.css | 44 ++++++++- 16 files changed, 812 insertions(+), 132 deletions(-) create mode 100644 panels/example.py delete mode 100644 panels/idle_status.py create mode 100644 panels/job_status.py create mode 100644 panels/main_menu.py create mode 100644 panels/menu.py create mode 100644 panels/move.py create mode 100644 panels/splash_screen.py create mode 100644 panels/temperature.py create mode 100755 scripts/disablescreenblank.sh diff --git a/KlipperScreen.config b/KlipperScreen.config index 43022e5d9..9fff480d9 100644 --- a/KlipperScreen.config +++ b/KlipperScreen.config @@ -2,6 +2,7 @@ { "name": "Home", "icon": "home", + "panel": "menu", "items": [ { "name": "Home All", @@ -30,20 +31,76 @@ ] }, { - "name": "Filament" , - "icon": "filament" + "name": "Actions" , + "icon": "actions", + "panel": "menu", + "items": [ + { + "name": "Move", + "icon": "move", + "panel": "move" + }, + { + "name": "Extrude", + "icon": "filament", + "panel": "extrude_multitool" + }, + { + "name": "Fan", + "icon": "fan", + "panel": "fan" + }, + { + "name": "Temperature", + "icon": "heat-up", + "panel": "temperature" + }, + { + "name": "Control", + "icon": "control", + "panel": "control" + }, + { + "name": "ToolChanger", + "icon": "toolchanger", + "panel": "toolchanger" + } + ] }, { - "name": "Move", - "icon": "move" + "name": "Filament", + "icon": "filament" }, { - "name": "Menu", - "icon": "actions" + "name": "Configuration", + "icon": "control", + "panel": "menu", + "items": [ + { + "name": "Bed Level", + "icon": "bed-level", + "panel": "bed-level" + }, + { + "name": "ZOffsets", + "icon": "z-offset-increase", + "panel": "nozzle-calibration" + }, + { + "name": "Network", + "icon": "network", + "panel": "network" + }, + { + "name": "System", + "icon": "info", + "panel": "system" + } + ] }, { - "name": "Control", - "icon": "control" + "name": "Print", + "icon": "print" } ] diff --git a/KlippyGtk.py b/KlippyGtk.py index 2a8561417..b62188ceb 100644 --- a/KlippyGtk.py +++ b/KlippyGtk.py @@ -1,7 +1,8 @@ +# -*- coding: utf-8 -*- import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, GLib +from gi.repository import Gtk, Gdk, GdkPixbuf, GLib class KlippyGtk: labels = {} @@ -9,16 +10,36 @@ class KlippyGtk: #def __init__ (self): @staticmethod - def ImageLabel(image_name, text): - box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, relief=2) + def ImageLabel(image_name, text, size=20, style=False): + box1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15) image = Gtk.Image() #TODO: update file reference image.set_from_file("/opt/printer/OctoScreen/styles/z-bolt/images/" + str(image_name) + ".svg") + + pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale("/opt/printer/OctoScreen/styles/z-bolt/images/" + str(image_name) + ".svg", 20, 20, True) + image.set_from_pixbuf(pixbuf) + label = Gtk.Label() label.set_text(text) - box1.add(image) + box1.add(image) #, size, size) box1.add(label) - return box1 + + if style != False: + ctx = box1.get_style_context() + ctx.add_class(style) + + return {"l": label, "b": box1} + + @staticmethod + def ProgressBar(style=False): + bar = Gtk.ProgressBar() + + if style != False: + print "Styling bar " + style + ctx = bar.get_style_context() + ctx.add_class(style) + + return bar @staticmethod def ButtonImage(image_name, label, style=False): @@ -32,6 +53,7 @@ def ButtonImage(image_name, label, style=False): b.set_can_focus(False) b.set_image_position(Gtk.PositionType.TOP) b.set_always_show_image(True) + b.props.relief = Gtk.ReliefStyle.NONE if style != False: ctx = b.get_style_context() @@ -39,8 +61,34 @@ def ButtonImage(image_name, label, style=False): return b + @staticmethod + def ToggleButton(text): + b = Gtk.ToggleButton(text) + b.props.relief = Gtk.ReliefStyle.NONE + b.set_hexpand(True) + b.set_vexpand(True) + return b + + @staticmethod + def formatFileName(name): + name = name.split('/')[-1] if "/" in name else name + name = name.split('.gcod')[0] if ".gcode" in name else name + if len(name) > 25: + return name[0:25] + "\n" + name[25:50] + return name + + + @staticmethod + def formatTimeString(seconds): + time = int(seconds) + text = "" + if time/3600 !=0: + text += str(time/3600)+"h " + text += str(time/60%60)+"m "+str(time%60)+"s" + return text + @staticmethod def formatTemperatureString(temp, target): if (target > temp-2 and target < temp+2) or round(target,0) == 0: - return str(round(temp,2)) + "C" - return str(round(temp,2)) + "C -> " + str(round(target,2)) + "C" + return str(round(temp,2)) + "°C" #°C →" + return str(round(temp,2)) + "°C → " + str(round(target,2)) + "°C" diff --git a/KlippyWebsocket.py b/KlippyWebsocket.py index 306d11eb5..b34902114 100644 --- a/KlippyWebsocket.py +++ b/KlippyWebsocket.py @@ -56,7 +56,7 @@ def connect (self): def on_message(self, message): result = json.loads(message) - print json.dumps(result, indent=2) + #print json.dumps(result, indent=2) GLib.idle_add(self._callback, result['method'], result['params'][0]) def send_method(self, method, params): diff --git a/README.md b/README.md index 5526f1750..fd82828d2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,125 @@ -Repository for Klipper Screen +# OctoScreen [![GitHub release](https://img.shields.io/github/release/Z-Bolt/OctoScreen.svg)](https://github.com/Z-Bolt/OctoScreen/releases) [![license](https://img.shields.io/github/license/Z-Bolt/OctoScreen.svg)]() +_OctoScreen_, LCD touch interface for our Octoprint based on GTK+3, that allows you to control your 3D Printer, like you can do with any [LCD panel](http://reprap.org/wiki/RepRapTouch), but using _OctoPrint_ and a Raspberry Pi. It's a _X application_ to be executed directly in the X Server without any windows +manager or browser, as _frontend of a [OctoPrint](http://octoprint.org) server_ in a Raspberry Pi +equipped with any [Touch Screen](https://www.waveshare.com/wiki/4.3inch_HDMI_LCD_(B)). + + + + +### These are some of the functionalities supported: + +- Print jobs monitoring. +- Temperature and Filament management. +- Jogging operations. +- Wifi connection management +- Toolchanger management tools + +### How this is different from TouchUI? + +[TouchUI](http://plugins.octoprint.org/plugins/touchui/), is an amazing plugin +for Octoprint, was created as a responsive design for access to OctoPrint, +from low resolution devices, such as smartphones, tablets, etc. + +Executing TouchUI under a RPi w/TFT modules, presents two big problems, +first isn't optimized to be used with resistive touch screens with low resolutions +like 480x320 and second requires a browser to be access, consuming a lot of +resources. + +This is the main reason because I develop this X application to be executed +in my 3d printer. + +## [Roadmap](https://github.com/Z-Bolt/OctoScreen/projects/2) + +Installation +------------ + +### Dependencies + +*OctoScreen* is based on [Golang](golang.org), usually this means that is +dependency-less, but in this case [GTK+3](https://developer.gnome.org/gtk3/3.0/gtk.html) +is used, this means that GTK+3 libraries are required to be installed on +the system. Be sure that graphical environment is additionally installed. + +If you are using `Raspbian` or any other `Debian` based distribution, required packages can +be installed using: + +```sh +sudo apt-get install libgtk-3-0 xserver-xorg xinit x11-xserver-utils +``` +Also you may need to install video drive, usually **no screens found** error indicates this. Please find manual here: +[https://github.com/ssvb/xf86-video-fbturbo/wiki/Installation](https://github.com/ssvb/xf86-video-fbturbo/wiki/Installation) + + +### Install from .deb package + +The recommended way to install *OctoScreen* is use the `.deb` packages +from the [Releases](https://github.com/Z-Bolt/OctoScreen/releases) page. The packages +are available for Debian Stretch based distributions such as Raspbian and OctoPi. + +For example for a Raspbian Stretch: +```sh +wget https://github.com/Z-Bolt/OctoScreen/releases/download/v2.5.1/octoscreen_2.5-1_armhf.deb +sudo dpkg -i octoscreen_2.5-1_armhf.deb +``` + +### Install from source + +The compilation and packaging tasks are managed by the [`Makefile`](Makefile) +and backed on [Docker](Dockerfile). Docker is used to avoid installing any other +dependencies since all the operations are done inside of the container. + +If you need to install docker inside `Raspbian` or any other linux distrubution +just run: + +```sh +curl -fsSL get.docker.com -o get-docker.sh +sh get-docker.sh +``` + +> You can read more about this at [`docker-install`](https://github.com/docker/docker-install) + +To compile the project, assuming that you already cloned this repository, just +execute the `build` target, this will generate in `build` folder all the binaries +and debian packages: + +```sh +> make build +> ls -1 build/ +``` + +If you are using `Raspbian` you can install any of the `.deb` generated packages. +If not, just use the compiled binary. + +Configuration +------------- + +### Basic Configuration + +The basic configuration is handled via environment variables, if you are using +the `.deb` package you can configure it at `/etc/octoscreen/config`. + +- `OCTOPRINT_CONFIG_FILE` - Location of the OctoPrint's config.yaml file. If empty the file will be searched at the `pi` home folder or the current user. Only used for locally installed OctoPrint servers. + +- `OCTOPRINT_HOST` - OctoPrint HTTP address, example `http://localhost:5000`, if OctoPrint is locally installed will be read from the config file. + +- `OCTOPRINT_APIKEY` - OctoScreen expects an [API key]( http://docs.octoprint.org/en/master/api/general.html) to be supplied. This API key can be either the globally configured one or a user specific one if “Access Control”. if OctoPrint is locally installed will be read from the config file. + +- `OCTOSCREEN_STYLE_PATH` - Several themes are supported, and style configurations can be done through CSS. This variable defines the location of the application theme. + +- `OCTOSCREEN_RESOLUTION` - Resolution of the application, should be configured to the resolution of your screen. Optimal resolution for OctoScreen is no less than 800x480, so if the physical resolution of your screen is 480x320, it's recommended to set the software resolution 800x533. If you are using Raspbian you can do it by changing [`hdmi_cvt`](https://www.raspberrypi.org/documentation/configuration/config-txt/video.md) param in `/boot/config.txt` file. + +### [Menu Configuration](https://github.com/Z-Bolt/OctoScreen/blob/master/docs/README.md) + +### Custom controls and commands + +Custom [controls](http://docs.octoprint.org/en/master/configuration/config_yaml.html#controls) to execute GCODE instructions and [commands](http://docs.octoprint.org/en/master/configuration/config_yaml.html#system) to execute shell commands can be defined in the `config.yaml` file. + +The controls are limit to static controls without `inputs`. + +License +------- + +GNU Affero General Public License v3.0, see [LICENSE](LICENSE) + +This project is a hard fork from [Octiprint-TFT](https://github.com/mcuadros/OctoPrint-TFT) created by [@mcuadros](https://github.com/mcuadros/OctoPrint-TFT) diff --git a/panels/__init__.py b/panels/__init__.py index 7f60ad05a..4199563ed 100644 --- a/panels/__init__.py +++ b/panels/__init__.py @@ -1 +1,6 @@ -from idle_status import * +from job_status import * +from main_menu import * +from menu import * +from move import * +from splash_screen import * +from temperature import * diff --git a/panels/example.py b/panels/example.py new file mode 100644 index 000000000..1a6bf3b25 --- /dev/null +++ b/panels/example.py @@ -0,0 +1,21 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GLib + +from KlippyGtk import KlippyGtk + +class ExamplePanel: + _screen = None + labels = {} + + def __init__(self, screen): + self._screen = screen + + + def initialize(self, menu): + # Create gtk items here + + def get(self): + # Return gtk item + return self.grid diff --git a/panels/idle_status.py b/panels/idle_status.py deleted file mode 100644 index 181bd9b0d..000000000 --- a/panels/idle_status.py +++ /dev/null @@ -1,48 +0,0 @@ -import gi - -gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, Gdk, GLib - -from KlippyGtk import KlippyGtk - -class IdleStatusPanel: - _screen = None - labels = {} - - def __init__(self, screen): - self._screen = screen - print "init" - - - # def initialize(self): - # box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - # image = Gtk.Image() - # #TODO: update file reference - # image.set_from_file("/opt/printer/OctoScreen/styles/z-bolt/images/bed.svg") - # label = Gtk.Label() - # label.set_text("0C / 0C") - # self.bed_temp_label = label - # box1.add(image) - # box1.add(self.bed_temp_label) - # - # return box1 - def initialize(self): - box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - grid = Gtk.Grid() - grid.set_row_homogeneous(True) - grid.set_column_homogeneous(True) - - for i in range(self._screen.number_tools): - self.labels["tool" + str(i)] = KlippyGtk.ButtonImage("extruder-"+str(i+1), "0C / 0C") - grid.attach(self.labels["tool" + str(i)], 0, 0, 1, 1) - - self.labels['bed'] = KlippyGtk.ButtonImage("bed", "0C / 0C") - grid.attach(self.labels['bed'], 0, 1, 1, 1) - #box.add(KlippyGtk.ButtonImage("bed", "0C / 0C")) - - box.add(grid) - return box - - def update_temp(self, dev, temp, target): - if self.labels.has_key(dev): - self.labels[dev].set_label(KlippyGtk.formatTemperatureString(temp, target)) diff --git a/panels/job_status.py b/panels/job_status.py new file mode 100644 index 000000000..07f9ac23a --- /dev/null +++ b/panels/job_status.py @@ -0,0 +1,100 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GLib + +from KlippyGtk import KlippyGtk + +class JobStatusPanel: + _screen = None + labels = {} + + def __init__ (self, screen): + self._screen = screen + + def initialize(self): + grid = Gtk.Grid() + grid.set_row_homogeneous(True) + grid.set_column_homogeneous(True) + + self.labels['progress'] = KlippyGtk.ProgressBar("printing-progress-bar") + self.labels['progress'].set_vexpand(True) + self.labels['progress'].set_valign(Gtk.Align.CENTER) + self.labels['progress'].set_show_text(True) + self.labels['progress'].set_margin_top(10) + self.labels['progress'].set_margin_end(20) + + self.labels['file'] = KlippyGtk.ImageLabel("file","",20,"printing-status-label") + self.labels['time'] = KlippyGtk.ImageLabel("speed-step","time",20,"printing-status-label") + self.labels['time_left'] = KlippyGtk.ImageLabel("speed-step","time_left",20,"printing-status-label") + info = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + info.props.valign = Gtk.Align.CENTER + info.set_hexpand(True) + info.set_vexpand(True) + info.add(self.labels['file']['b']) + info.add(self.labels['time']['b']) + #info.add(self.labels['time_left']['b']) + + grid.attach(info,2,0,2,1) + + grid.attach(self.labels['progress'], 2, 1, 2, 1) + + self.labels['tool0'] = KlippyGtk.ButtonImage("extruder-1", KlippyGtk.formatTemperatureString(0, 0)) + self.labels['tool0'].set_sensitive(False) + grid.attach(self.labels['tool0'], 0, 0, 1, 1) + #self.labels['tool1'] = KlippyGtk.ButtonImage("extruder-2", KlippyGtk.formatTemperatureString(0, 0)) + #self.labels['tool1'].set_sensitive(False) + #grid.attach(self.labels['tool1'], 1, 0, 1, 1) + self.labels['bed'] = KlippyGtk.ButtonImage("bed", KlippyGtk.formatTemperatureString(0, 0)) + self.labels['bed'].set_sensitive(False) + grid.attach(self.labels['bed'], 0, 2, 1, 1) + + self.labels['resume'] = KlippyGtk.ButtonImage("resume","Resume","color1") + self.labels['resume'].connect("clicked",self.resume) + #grid.attach(self.labels['play'], 1, 2, 1, 1) + self.labels['pause'] = KlippyGtk.ButtonImage("pause","Pause","color1" ) + self.labels['pause'].connect("clicked",self.pause) + grid.attach(self.labels['pause'], 1, 2, 1, 1) + self.labels['stop'] = KlippyGtk.ButtonImage("stop","Stop","color2") + grid.attach(self.labels['stop'], 2, 2, 1, 1) + self.labels['control'] = KlippyGtk.ButtonImage("control","Control","color3") + grid.attach(self.labels['control'], 3, 2, 1, 1) + + self.grid = grid + + return self.grid + #m.Grid().Attach(m.createInfoBox(), 3, 0, 2, 1) + #m.Grid().Attach(m.createProgressBar(), 3, 1, 2, 1) + #m.Grid().Attach(m.createPauseButton(), 2, 2, 1, 1) + #m.Grid().Attach(m.createStopButton(), 3, 2, 1, 1) + #m.Grid().Attach(m.createMenuButton(), 4, 2, 1, 1) + #m.Grid().Attach(m.createCompleteButton(), 2, 2, 3, 1) + + def get(self): + return self.grid + + def resume(self, widget): + self.grid.attach(self.labels['pause'], 1, 2, 1, 1) + self.grid.remove(self.labels['resume']) + self._screen.show_all() + + def pause(self, widget): + self.grid.attach(self.labels['resume'], 1, 2, 1, 1) + self.grid.remove(self.labels['pause']) + self._screen.show_all() + + + def update_image_text(self, label, text): + if label in self.labels and 'l' in self.labels[label]: + self.labels[label]['l'].set_text(text) + + def update_progress (self, progress): + #progress = round(progress,2) + #whole = (progress%1) * 100 + + self.labels['progress'].set_fraction(progress) + #self.labels['progress'].set_text(str(int(whole)) + "%") + + def update_temp(self, dev, temp, target): + if self.labels.has_key(dev): + self.labels[dev].set_label(KlippyGtk.formatTemperatureString(temp, target)) diff --git a/panels/main_menu.py b/panels/main_menu.py new file mode 100644 index 000000000..b135c5eda --- /dev/null +++ b/panels/main_menu.py @@ -0,0 +1,34 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GLib + +from KlippyGtk import KlippyGtk +from menu import MenuPanel + +class MainPanel(MenuPanel): + + def initialize(self, items): + print "### Making MainMenu" + grid = Gtk.Grid() + grid.set_row_homogeneous(True) + grid.set_column_homogeneous(True) + + # Create Extruders and bed icons + eq_grid = Gtk.Grid() + eq_grid.set_row_homogeneous(True) + eq_grid.set_column_homogeneous(True) + + for i in range(self._screen.number_tools): + self.labels["tool" + str(i)] = KlippyGtk.ButtonImage("extruder-"+str(i+1), KlippyGtk.formatTemperatureString(0, 0)) + eq_grid.attach(self.labels["tool" + str(i)], 0, 0, 1, 1) + + self.labels['bed'] = KlippyGtk.ButtonImage("bed", KlippyGtk.formatTemperatureString(0, 0)) + eq_grid.attach(self.labels['bed'], 0, 1, 1, 1) + + grid.attach(eq_grid, 0, 0, 1, 1) + grid.attach(self.arrangeMenuItems(items, 2, True), 1, 0, 1, 1) + self.grid = grid + + #def get(self): + # return self.grid diff --git a/panels/menu.py b/panels/menu.py new file mode 100644 index 000000000..bb5cc8200 --- /dev/null +++ b/panels/menu.py @@ -0,0 +1,69 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GLib + +from KlippyGtk import KlippyGtk + +class MenuPanel: + _screen = None + labels = {} + + def __init__(self, screen): + self._screen = screen + + + def initialize(self, items): + print "### Making a new menu" + + grid = self.arrangeMenuItems(items, 4) + + b = KlippyGtk.ButtonImage('back', 'Back') + b.connect("clicked", self._screen._menu_go_back) + grid.attach(b, 3, 1, 1, 1) + + self.grid = grid + + def get(self): + return self.grid + + def arrangeMenuItems (self, items, columns, expandLast=False): + grid = Gtk.Grid() + grid.set_row_homogeneous(True) + grid.set_column_homogeneous(True) + + l = len(items) + i = 0 + for i in range(l): + col = i % columns + row = round(i/columns, 0) + width = 1 + if expandLast == True and i+1 == l and l%2 == 1: + width = 2 + b = KlippyGtk.ButtonImage( + items[i]['icon'], items[i]['name'], "color"+str((i%4)+1) + ) + + if "panel" in items[i]: + b.connect("clicked", self.menu_item_clicked, items[i]['panel'], items[i]) + elif "items" in items[i]: + b.connect("clicked", self._screen._go_to_submenu, items[i]['name']) + elif "method" in items[i]: + params = items[i]['params'] if "params" in items[i] else {} + b.connect("clicked", self._screen._send_action, items[i]['method'], params) + + grid.attach(b, col, row, width, 1) + + i += 1 + print b.get_style_context() + print b.get_default_style() + + return grid + + def menu_item_clicked(self, widget, panel, item): + print "### Creating panel "+ item['panel'] + if "items" in item: + self._screen.show_panel("_".join(self._screen._cur_panels) + '_' + item['name'], item['panel'], 1, False, items=item['items']) + return + self._screen.show_panel("_".join(self._screen._cur_panels) + '_' + item['name'], item['panel'], 1, False) + return diff --git a/panels/move.py b/panels/move.py new file mode 100644 index 000000000..977638bb2 --- /dev/null +++ b/panels/move.py @@ -0,0 +1,112 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GLib + +from KlippyGtk import KlippyGtk + +class MovePanel: + _screen = None + labels = {} + distance = 1 + distances = ['.1','1','5','10'] + + + def __init__(self, screen): + self._screen = screen + + + def initialize(self): + + grid = Gtk.Grid() + grid.set_column_homogeneous(True) + grid.set_row_homogeneous(True) + + self.labels['x+'] = KlippyGtk.ButtonImage("move-x+", "X+", "color1") + self.labels['x+'].connect("clicked", self.move, "X", "+") + self.labels['x-'] = KlippyGtk.ButtonImage("move-x-", "X-", "color1") + self.labels['x-'].connect("clicked", self.move, "X", "-") + + self.labels['y+'] = KlippyGtk.ButtonImage("move-y+", "Y+", "color2") + self.labels['y+'].connect("clicked", self.move, "Y", "+") + self.labels['y-'] = KlippyGtk.ButtonImage("move-y-", "Y-", "color2") + self.labels['y-'].connect("clicked", self.move, "Y", "-") + + self.labels['z+'] = KlippyGtk.ButtonImage("move-z-", "Z+", "color3") + self.labels['z+'].connect("clicked", self.move, "Z", "+") + self.labels['z-'] = KlippyGtk.ButtonImage("move-z+", "Z-", "color3") + self.labels['z-'].connect("clicked", self.move, "Z", "-") + + self.labels['home'] = KlippyGtk.ButtonImage("home", "Home All") + self.labels['home'].connect("clicked", self.home) + + + grid.attach(self.labels['x+'], 0, 1, 1, 1) + grid.attach(self.labels['x-'], 2, 1, 1, 1) + grid.attach(self.labels['y+'], 1, 0, 1, 1) + grid.attach(self.labels['y-'], 1, 2, 1, 1) + grid.attach(self.labels['z+'], 0, 0, 1, 1) + grid.attach(self.labels['z-'], 2, 0, 1, 1) + + grid.attach(self.labels['home'], 0, 2, 1, 1) + + distgrid = Gtk.Grid(row_spacing=0) + j = 0; + for i in self.distances: + self.labels[i] = KlippyGtk.ToggleButton(i+"mm") + self.labels[i].connect("clicked", self.change_distance, i) + ctx = self.labels[i].get_style_context() + if j == 0: + ctx.add_class("distbutton_top") + elif j == len(self.distances)-1: + ctx.add_class("distbutton_bottom") + else: + ctx.add_class("distbutton") + if i == "1": + ctx.add_class("distbutton_active") + distgrid.attach(self.labels[i], 0, j, 1, 1) + j += 1 + + self.labels["1"].set_active(True) + + grid.set_row_spacing(0) + grid.attach(distgrid, 3, 0, 1, 2) + + + + b = KlippyGtk.ButtonImage('back', 'Back') + b.connect("clicked", self._screen._menu_go_back) + grid.attach(b, 3, 2, 1, 1) + + self.grid = grid + + def get(self): + return self.grid + + def home(self, widget): + self._screen._ws.send_method("post_printer_gcode", {"script": "G28"}) + + def change_distance(self, widget, distance): + if self.distance == distance: + return + print "### Distance " + str(distance) + + ctx = self.labels[str(self.distance)].get_style_context() + ctx.remove_class("distbutton_active") + + self.distance = distance + ctx = self.labels[self.distance].get_style_context() + ctx.add_class("distbutton_active") + for i in self.distances: + if i == self.distance: + continue + self.labels[str(i)].set_active(False) + + def move(self, widget, axis, dir): + dist = str(self.distance) if dir == "+" else "-" + str(self.distance) + print "# Moving " + axis + " " + dist + "mm" + + + self._screen._ws.send_method("post_printer_gcode", {"script": "G91"}) + print "G1 "+axis+dist + #self._screen._ws.send_method("post_printer_gcode", {"script": "G1 "+axis+dist}) diff --git a/panels/splash_screen.py b/panels/splash_screen.py new file mode 100644 index 000000000..441230cef --- /dev/null +++ b/panels/splash_screen.py @@ -0,0 +1,37 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GLib + +from KlippyGtk import KlippyGtk + +class SplashScreenPanel: + _screen = None + labels = {} + box = None + + def __init__(self, screen): + self._screen = screen + + + def initialize(self): + image = Gtk.Image() + #TODO: update file reference + image.set_from_file("/opt/printer/OctoScreen/styles/z-bolt/images/logo.png") + + label = Gtk.Label() + label.set_text("Initializing printer...") + #label = Gtk.Button(label="Initializing printer...") + #label.connect("clicked", self.printer_initialize) + + main = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15) + main.pack_start(image, True, True, 10) + main.pack_end(label, True, True, 10) + + box = Gtk.VBox() + box.add(main) + + self.box = box + + def get(self): + return self.box diff --git a/panels/temperature.py b/panels/temperature.py new file mode 100644 index 000000000..a314a2ab7 --- /dev/null +++ b/panels/temperature.py @@ -0,0 +1,32 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GLib + +from KlippyGtk import KlippyGtk + +class TemperaturePanel: + _screen = None + labels = {} + + def __init__(self, screen): + self._screen = screen + + + def initialize(self): + grid = Gtk.Grid() + grid.set_column_homogeneous(True) + grid.set_row_homogeneous(True) + + + + + b = KlippyGtk.ButtonImage('back', 'Back') + b.connect("clicked", self._screen._menu_go_back) + grid.attach(b, 3, 2, 1, 1) + + self.grid = grid + + def get(self): + # Return gtk item + return self.grid diff --git a/screen.py b/screen.py index eba2a6633..35fc50225 100644 --- a/screen.py +++ b/screen.py @@ -13,7 +13,7 @@ from KlippyWebsocket import KlippyWebsocket from KlippyGtk import KlippyGtk -from panels import IdleStatusPanel +from panels import * config = "/opt/printer/KlipperScreen/KlipperScreen.config" @@ -25,6 +25,7 @@ class KlipperScreen(Gtk.Window): panels = {} _cur_panels = [] + filename = "" def __init__(self): self.read_config() @@ -32,22 +33,71 @@ def __init__(self): Gtk.Window.__init__(self) self.set_default_size(Gdk.Screen.get_width(Gdk.Screen.get_default()), Gdk.Screen.get_height(Gdk.Screen.get_default())) + print str(Gdk.Screen.get_width(Gdk.Screen.get_default()))+"x"+str(Gdk.Screen.get_height(Gdk.Screen.get_default())) - r = requests.get("http://127.0.0.1:7125/printer/info") #, headers={"x-api-key":api_key}) - if r.status_code != 200: - self.printer_initializing() - self.create_websocket() - return + self.show_panel('splash_screen', "SplashScreenPanel") - data = json.loads(r.content) + ready = False - if data['result']['is_ready'] != True: - self.printer_initializing() - self.create_websocket() - return + while ready == False: + r = requests.get("http://127.0.0.1:7125/printer/info") #, headers={"x-api-key":api_key}) + if r.status_code != 200: + time.sleep(1) + continue + + data = json.loads(r.content) + if data['result']['is_ready'] != True: + time.sleep(1) + continue + ready = True + + r = requests.get("http://127.0.0.1:7125/printer/status?toolhead") self.create_websocket() - self.main_panel() + + #TODO: Check that we get good data + data = json.loads(r.content) + + if data['result']['toolhead']['status'] == "Printing": + self.show_panel('job_status',"JobStatusPanel") + else: + self.show_panel('main_panel',"MainPanel",2,items=self._config) + + + def show_panel(self, panel_name, type, remove=None, pop=True, **kwargs): + if remove == 2: + self._remove_all_panels() + elif remove == 1: + self._remove_current_panel(pop) + if panel_name not in self.panels: + if type == "SplashScreenPanel": + self.panels[panel_name] = SplashScreenPanel(self) + elif type == "MainPanel": + self.panels[panel_name] = MainPanel(self) + elif type == "menu": + self.panels[panel_name] = MenuPanel(self) + print panel_name + elif type == "JobStatusPanel": + self.panels[panel_name] = JobStatusPanel(self) + elif type == "move": + self.panels[panel_name] = MovePanel(self) + elif type == "temperature": + self.panels[panel_name] = TemperaturePanel(self) + #Temporary for development + else: + self.panels[panel_name] = MovePanel(self) + + if kwargs != {}: + print self.panels + print kwargs + self.panels[panel_name].initialize(**kwargs) + else : + self.panels[panel_name].initialize() + + self.add(self.panels[panel_name].get()) + self.show_all() + self._cur_panels.append(panel_name) + print self._cur_panels def read_config (self): @@ -67,24 +117,10 @@ def init_style(self): def splash_screen(self): if "splash_screen" not in self.panels: - image = Gtk.Image() - #TODO: update file reference - image.set_from_file("/opt/printer/OctoScreen/styles/z-bolt/images/logo.png") - - label = Gtk.Label() - label.set_text("Initializing printer...") - #label = Gtk.Button(label="Initializing printer...") - #label.connect("clicked", self.printer_initialize) - - main = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15) - main.pack_start(image, True, True, 10) - main.pack_end(label, True, True, 10) - - box = Gtk.VBox() - box.add(main) - self.panels['splash_screen'] = box + self.panels['splash_screen'] = SplashScreenPanel(self) + self.panels['splash_screen'].initialize() - self.add(self.panels['splash_screen']) + self.add(self.panels['splash_screen'].get()) self.show_all() self._cur_panels = ['splash_screen'] @@ -93,38 +129,18 @@ def create_websocket(self): self._ws.connect() self._curr = 0 - def main_panel (self): - if "main_panel" not in self.panels: - box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - - grid = Gtk.Grid() - grid.set_row_homogeneous(True) - grid.set_column_homogeneous(True) - - self.panels['idle_status'] = IdleStatusPanel(self) - - - grid.attach(self.panels['idle_status'].initialize(), 0, 0, 1, 1) - #grid.attach(box2, 1, 0, 1, 1) - - grid.attach(self.arrangeMenuItems(self._config,2), 1, 0, 1, 1) - - self.panels['main_screen'] = grid - - print ("### Adding main panel") - self.add(self.panels['main_screen']) - self.show_all() - self._cur_panels.append("main_screen") - - def _go_to_submenu(self, widget, menu): - print "#### Go to submenu " + str(menu) - self._remove_current_panel(False) + def _go_to_submenu(self, widget, name): + print "#### Go to submenu " + str(name) + #self._remove_current_panel(False) # Find current menu item panels = list(self._cur_panels) - cur_item = self._find_current_menu_item(menu, self._config, panels.pop(0)) + cur_item = self._find_current_menu_item(name, self._config, panels.pop(0)) menu = cur_item['items'] + print menu + self.show_panel("_".join(self._cur_panels) + '_' + name, "menu", 1, False, menu=menu) + return grid = self.arrangeMenuItems(menu, 4) @@ -140,9 +156,10 @@ def _go_to_submenu(self, widget, menu): def _find_current_menu_item(self, menu, items, names): - # TODO: Make recursive - return items[menu] - + for item in items: + if item['name'] == menu: + return item + #TODO: Add error check def _remove_all_panels(self): while len(self._cur_panels) > 0: @@ -155,13 +172,13 @@ def _remove_current_panel(self, pop=True): self.remove( self.panels[ self._cur_panels[-1] - ] + ].get() ) if pop == True: print "Popping _cur_panels" self._cur_panels.pop() if len(self._cur_panels) > 0: - self.add(self.panels[self._cur_panels[-1]]) + self.add(self.panels[self._cur_panels[-1]].get()) self.show_all() def _menu_go_back (self, widget): @@ -192,6 +209,32 @@ def _websocket_callback(self, action, data): round(data['extruder']['temperature'],1), round(data['extruder']['target'],1) ) + if "job_status" in self.panels: + #print json.dumps(data, indent=2) + if "heater_bed" in data: + self.panels['job_status'].update_temp( + "bed", + round(data['heater_bed']['temperature'],1), + round(data['heater_bed']['target'],1) + ) + if "extruder" in data and data['extruder'] != "extruder": + self.panels['job_status'].update_temp( + "tool0", + round(data['extruder']['temperature'],1), + round(data['extruder']['target'],1) + ) + if "virtual_sdcard" in data: + print data['virtual_sdcard'] + if "current_file" in data['virtual_sdcard'] and self.filename != data['virtual_sdcard']['current_file']: + if data['virtual_sdcard']['current_file'] != "": + file = KlippyGtk.formatFileName(data['virtual_sdcard']['current_file']) + else: + file = "Unknown" + #self.panels['job_status'].update_image_text("file", file) + if "print_duration" in data['virtual_sdcard']: + self.panels['job_status'].update_image_text("time", "Time: " +KlippyGtk.formatTimeString(data['virtual_sdcard']['print_duration'])) + if "progress" in data['virtual_sdcard']: + self.panels['job_status'].update_progress(data['virtual_sdcard']['progress']) def set_bed_temp (self, num, target): @@ -239,7 +282,7 @@ def printer_initializing(self): def printer_ready(self): self._remove_all_panels() - self.main_panel() + self.show_panel('main_panel',"MainPanel",2,items=self._config) def on_button1_clicked(self, widget): print("Hello") diff --git a/scripts/disablescreenblank.sh b/scripts/disablescreenblank.sh new file mode 100755 index 000000000..32b55b350 --- /dev/null +++ b/scripts/disablescreenblank.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +screen=${1:-0} + +# wait 10s for the display manager service to start and attach to screen +sleep 5 + +/usr/bin/xset -display :$screen s off # deactivate screen saver +/usr/bin/xset -display :$screen -dpms # disable DPMS +/usr/bin/xset -display :$screen s noblank # disable screen blanking + diff --git a/style.css b/style.css index dec54b632..fd88d63b6 100644 --- a/style.css +++ b/style.css @@ -20,8 +20,11 @@ scrollbar slider { } progress, trough { - min-height: 20px; + min-height: 40px; font-size: 50px; + background-color: #404E57; + color: #404E57; + border: #000 1px solid; } progress { @@ -30,11 +33,16 @@ progress { font-weight: lighter; } -.printing-progress-bar{ - font-size: 60px; +.printing-progress-bar { + font-size: 25px; color: #00C9B4; } +.printing-progress-bar text { + font-size: 25px; + color: #404e57; +} + .printing-state { opacity: 0.5; } @@ -78,6 +86,34 @@ button.file-list { margin: 0px; } +.distbutton_top { + border: 3px solid #fff; + border-bottom: 0px; + border-top-right-radius: 15px; + border-top-left-radius: 15px; + margin-bottom: 0px; +} + +.distbutton { + border: 3px solid #fff; + border-top: 3px solid #ccc; + border-bottom: 0px; + margin-top: 0; + margin-bottom: 0px; +} + +.distbutton_bottom { + border: 3px solid #fff; + border-top: 3px solid #ccc; + border-bottom-right-radius: 20px; + border-bottom-left-radius: 20px; + margin-top: 0; +} + +.distbutton_active { + background-color: #20303D; +} + entry { font-size: 30px; background-color: #20292F; @@ -149,7 +185,7 @@ button.keyboard { .printing-status-label { padding-top: 5px; padding-bottom: 3px; - + color: #ccc; font-size: 24px; }