This should protect against first touch when the screen is blanked and help in cases where the driver implementation is not up to spec.
347 lines
13 KiB
Python
347 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
|
import gi
|
|
import logging
|
|
import os
|
|
import pathlib
|
|
|
|
gi.require_version("Gtk", "3.0")
|
|
from gi.repository import Gdk, GdkPixbuf, Gio, Gtk, Pango
|
|
|
|
klipperscreendir = pathlib.Path(__file__).parent.resolve().parent
|
|
|
|
class KlippyGtk:
|
|
labels = {}
|
|
keyboard_ratio = .22
|
|
width_ratio = 16
|
|
height_ratio = 9.375
|
|
|
|
def __init__(self, screen, width, height, theme, cursor, fontsize_type):
|
|
self.screen = screen
|
|
self.width = width
|
|
self.height = height
|
|
self.theme = theme
|
|
if self.screen.vertical_mode:
|
|
self.font_ratio = [33, 49]
|
|
else:
|
|
self.font_ratio = [43, 29]
|
|
self.font_size = int(min(
|
|
self.width / self.font_ratio[0],
|
|
self.height / self.font_ratio[1]
|
|
))
|
|
if fontsize_type == "small":
|
|
self.font_size = round(self.font_size * 0.91)
|
|
elif (fontsize_type == "large"):
|
|
self.font_size = round(self.font_size * 1.09)
|
|
self.header_size = int(round((self.width / self.width_ratio) / 1.33))
|
|
self.img_width = int(round(self.width / self.width_ratio))
|
|
self.img_height = int(round(self.height / self.height_ratio))
|
|
if self.screen.vertical_mode:
|
|
self.action_bar_width = int(self.width)
|
|
self.action_bar_height = int(self.height * .1)
|
|
else:
|
|
self.action_bar_width = int(self.width * .1)
|
|
self.action_bar_height = int(self.height)
|
|
self.cursor = cursor
|
|
|
|
self.color_list = {} # This is set by screen.py init_style()
|
|
|
|
for key in self.color_list:
|
|
if "base" in self.color_list[key]:
|
|
rgb = [int(self.color_list[key]['base'][i:i+2], 16) for i in range(0, 6, 2)]
|
|
self.color_list[key]['rgb'] = rgb
|
|
|
|
logging.debug("img width: %s height: %s" % (self.img_width, self.img_height))
|
|
|
|
def get_action_bar_width(self):
|
|
return self.action_bar_width
|
|
|
|
def get_action_bar_height(self):
|
|
return self.action_bar_height
|
|
|
|
def get_content_width(self):
|
|
return self.width - self.action_bar_width
|
|
|
|
def get_content_height(self):
|
|
return self.height - self.header_size
|
|
|
|
def get_font_size(self):
|
|
return self.font_size
|
|
|
|
def get_header_size(self):
|
|
return self.header_size
|
|
|
|
def get_image_width(self):
|
|
return self.img_width
|
|
|
|
def get_image_height(self):
|
|
return self.img_height
|
|
|
|
def get_keyboard_height(self):
|
|
return (self.width - self.get_action_bar_width()) * self.keyboard_ratio
|
|
|
|
def get_temp_color(self, device):
|
|
# logging.debug("Color list %s" % self.color_list)
|
|
if device not in self.color_list:
|
|
return False, False
|
|
|
|
if 'base' in self.color_list[device]:
|
|
rgb = self.color_list[device]['rgb'].copy()
|
|
if self.color_list[device]['state'] > 0:
|
|
rgb[1] = rgb[1] + self.color_list[device]['hsplit'] * self.color_list[device]['state']
|
|
self.color_list[device]['state'] += 1
|
|
color = '{:02X}{:02X}{:02X}'.format(rgb[0], rgb[1], rgb[2])
|
|
rgb = [x/255 for x in rgb]
|
|
# logging.debug("Assigning color: %s %s %s" % (device, rgb, color))
|
|
else:
|
|
colors = self.color_list[device]['colors']
|
|
if self.color_list[device]['state'] >= len(colors):
|
|
self.color_list[device]['state'] = 0
|
|
color = colors[self.color_list[device]['state'] % len(colors)]
|
|
rgb = [int(color[i:i+2], 16)/255 for i in range(0, 6, 2)]
|
|
self.color_list[device]['state'] += 1
|
|
# logging.debug("Assigning color: %s %s %s" % (device, rgb, color))
|
|
|
|
return rgb, color
|
|
|
|
def reset_temp_color(self):
|
|
for key in self.color_list:
|
|
self.color_list[key]['state'] = 0
|
|
|
|
|
|
def Label(self, label, style=None):
|
|
la = Gtk.Label(label)
|
|
if style is not None and style is not False:
|
|
la.get_style_context().add_class(style)
|
|
return la
|
|
|
|
def ImageLabel(self, image_name, text, size=20, style=False, width_scale=.32, height_scale=.32):
|
|
box1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
|
os.path.join(klipperscreendir, "styles", self.theme, "images", str(image_name) + ".svg"),
|
|
int(round(self.img_width * width_scale)), int(round(self.img_height * height_scale)), True)
|
|
|
|
image = Gtk.Image.new_from_pixbuf(pixbuf)
|
|
|
|
label = Gtk.Label()
|
|
label.set_markup(text)
|
|
box1.add(image)
|
|
box1.add(label)
|
|
|
|
if style is not False:
|
|
ctx = box1.get_style_context()
|
|
ctx.add_class(style)
|
|
|
|
return {"l": label, "b": box1}
|
|
|
|
def ImageMenuButton(self, image_name, text, size=20, style=False, width_scale=.32, height_scale=.32, popover=False):
|
|
box1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
|
os.path.join(klipperscreendir, "styles", self.theme, "images", str(image_name) + ".svg"),
|
|
int(round(self.img_width * width_scale)), int(round(self.img_height * height_scale)), True)
|
|
|
|
image = Gtk.Image.new_from_pixbuf(pixbuf)
|
|
|
|
mb = Gtk.MenuButton(label="", popover=popover)
|
|
label = mb.get_label()
|
|
box1.add(image)
|
|
box1.add(mb)
|
|
|
|
if style is not False:
|
|
ctx = box1.get_style_context()
|
|
ctx.add_class(style)
|
|
|
|
return {"l": label, "mb": mb, "b": box1}
|
|
|
|
def Image(self, image_name, style=False, width_scale=1, height_scale=1):
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
|
os.path.join(klipperscreendir, "styles", self.theme, "images", str(image_name)),
|
|
int(round(self.img_width * width_scale)), int(round(self.img_height * height_scale)), True)
|
|
|
|
return Gtk.Image.new_from_pixbuf(pixbuf)
|
|
|
|
def ImageFromFile(self, filename, style=False, width_scale=1, height_scale=1):
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
|
filename, int(round(self.img_width * width_scale)), int(round(self.img_height * height_scale)), True)
|
|
|
|
return Gtk.Image.new_from_pixbuf(pixbuf)
|
|
|
|
def PixbufFromFile(self, filename, style=False, width_scale=1, height_scale=1):
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
|
filename, int(round(self.img_width * width_scale)), int(round(self.img_height * height_scale)), True)
|
|
|
|
return pixbuf
|
|
|
|
def PixbufFromHttp(self, resource, style=False, width_scale=1, height_scale=1):
|
|
response = self.screen.apiclient.get_thumbnail_stream(resource)
|
|
if response is False:
|
|
return None
|
|
stream = Gio.MemoryInputStream.new_from_data(response, None)
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(
|
|
stream, int(round(self.img_width * width_scale)), int(round(self.img_height * height_scale)), True)
|
|
|
|
return pixbuf
|
|
|
|
def ProgressBar(self, style=False):
|
|
bar = Gtk.ProgressBar()
|
|
|
|
if style is not False:
|
|
ctx = bar.get_style_context()
|
|
ctx.add_class(style)
|
|
|
|
return bar
|
|
|
|
def Button(self, label=None, style=None):
|
|
b = Gtk.Button(label=label)
|
|
b.set_hexpand(True)
|
|
b.set_vexpand(True)
|
|
b.set_can_focus(False)
|
|
b.props.relief = Gtk.ReliefStyle.NONE
|
|
|
|
if style is not None:
|
|
b.get_style_context().add_class(style)
|
|
b.connect("clicked", self.screen.reset_screensaver_timeout)
|
|
return b
|
|
|
|
def ButtonImage(self, image_name, label=None, style=None, width_scale=1.38, height_scale=1.38,
|
|
position=Gtk.PositionType.TOP, word_wrap=True):
|
|
filename = os.path.join(klipperscreendir, "styles", self.theme, "images", str(image_name) + ".svg")
|
|
if not os.path.exists(filename):
|
|
logging.error("Unable to find button image (theme, image): (%s, %s)" % (self.theme, str(image_name)))
|
|
filename = os.path.join(klipperscreendir, "styles", self.theme, "images", "warning.svg")
|
|
|
|
b = Gtk.Button(label=label)
|
|
|
|
if os.path.exists(filename):
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
|
filename,
|
|
int(round(self.img_width * width_scale)),
|
|
int(round(self.img_height * height_scale)),
|
|
True
|
|
)
|
|
img = Gtk.Image.new_from_pixbuf(pixbuf)
|
|
b.set_image(img)
|
|
|
|
b.set_hexpand(True)
|
|
b.set_vexpand(True)
|
|
b.set_can_focus(False)
|
|
b.set_image_position(position)
|
|
b.set_always_show_image(True)
|
|
b.props.relief = Gtk.ReliefStyle.NONE
|
|
|
|
if word_wrap is True:
|
|
try:
|
|
# Get the label object
|
|
child = b.get_children()[0].get_children()[0].get_children()[1]
|
|
child.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
|
|
child.set_line_wrap(True)
|
|
except Exception:
|
|
pass
|
|
|
|
if style is not None:
|
|
b.get_style_context().add_class(style)
|
|
b.connect("clicked", self.screen.reset_screensaver_timeout)
|
|
return b
|
|
|
|
def Dialog(self, screen, buttons, content, callback=None, *args):
|
|
dialog = Gtk.Dialog()
|
|
dialog.set_default_size(screen.width, screen.height)
|
|
dialog.set_resizable(False)
|
|
dialog.set_transient_for(screen)
|
|
dialog.set_modal(True)
|
|
|
|
for i, button in enumerate(buttons):
|
|
dialog.add_button(button_text=button['name'], response_id=button['response'])
|
|
button = dialog.get_children()[0].get_children()[0].get_children()[0].get_children()[i]
|
|
button.get_child().set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
|
|
button.get_child().set_line_wrap(True)
|
|
button.set_size_request((screen.width-30)/3, screen.height/5)
|
|
|
|
dialog.connect("response", callback, *args)
|
|
dialog.get_style_context().add_class("dialog")
|
|
|
|
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(content)
|
|
|
|
dialog.show_all()
|
|
# Change cursor to blank
|
|
if self.cursor:
|
|
dialog.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.ARROW))
|
|
else:
|
|
dialog.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.BLANK_CURSOR))
|
|
|
|
return dialog
|
|
|
|
|
|
def ToggleButtonImage(self, image_name, label, style=False, width_scale=1.38, height_scale=1.38):
|
|
filename = os.path.join(klipperscreendir, "styles", self.theme, "images", str(image_name) + ".svg")
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
|
filename,
|
|
int(round(self.img_width * width_scale)),
|
|
int(round(self.img_height * height_scale)),
|
|
True
|
|
)
|
|
|
|
img = Gtk.Image.new_from_pixbuf(pixbuf)
|
|
|
|
b = Gtk.ToggleButton(label=label)
|
|
b.set_image(img)
|
|
b.set_hexpand(True)
|
|
b.set_vexpand(True)
|
|
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 is not False:
|
|
ctx = b.get_style_context()
|
|
ctx.add_class(style)
|
|
|
|
b.connect("clicked", self.screen.reset_screensaver_timeout)
|
|
return b
|
|
|
|
def HomogeneousGrid(self, width=None, height=None):
|
|
g = Gtk.Grid()
|
|
g.set_row_homogeneous(True)
|
|
g.set_column_homogeneous(True)
|
|
if width is not None and height is not None:
|
|
g.set_size_request(width, height)
|
|
return g
|
|
|
|
def ToggleButton(self, text):
|
|
b = Gtk.ToggleButton(text)
|
|
b.props.relief = Gtk.ReliefStyle.NONE
|
|
b.set_hexpand(True)
|
|
b.set_vexpand(True)
|
|
b.connect("clicked", self.screen.reset_screensaver_timeout)
|
|
return b
|
|
|
|
def formatFileName(self, 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
|
|
|
|
|
|
def formatTimeString(self, seconds):
|
|
time = int(seconds)
|
|
text = ""
|
|
if int(time/86400) != 0:
|
|
text += str(int(time/86400))+"d "
|
|
if int(time/3600) != 0:
|
|
text += str(int(time/3600) % 24)+"h "
|
|
if int(time/60) != 0:
|
|
text += str(int(time/60) % 60)+"m "
|
|
else:
|
|
text = str(time % 60)+"s"
|
|
return text
|
|
|
|
def formatTemperatureString(self, temp, target):
|
|
if (target > temp-2 and target < temp+2) or round(target, 0) == 0:
|
|
return str(round(temp, 1)) + "°C" # °C →"
|
|
return str(round(temp)) + " °C\n(" + str(round(target)) + ")"
|