alfrix b70524cd28 Screensaver if dpms is off
This should protect against first touch when the screen is blanked
and help in cases where the driver implementation is not up to spec.
2022-03-29 23:37:59 -03:00

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)) + ")"