334 lines
12 KiB
Python
334 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
import gi
|
|
import logging
|
|
import os
|
|
import pathlib
|
|
|
|
gi.require_version("Gtk", "3.0")
|
|
from gi.repository import Gtk, Gdk, GdkPixbuf, Gio, GLib, Pango
|
|
|
|
klipperscreendir = pathlib.Path(__file__).parent.resolve().parent
|
|
|
|
class KlippyGtk:
|
|
labels = {}
|
|
font_ratio = [43, 29]
|
|
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
|
|
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))
|
|
self.action_bar_width = int(self.width * .1)
|
|
self.header_image_scale_width = 1.2
|
|
self.header_image_scale_height = 1.4
|
|
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_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_header_image_scale(self):
|
|
return [self.header_image_scale_width, self.header_image_scale_height]
|
|
|
|
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)
|
|
|
|
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)
|
|
|
|
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)
|
|
|
|
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)
|
|
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/3600) != 0:
|
|
text += str(int(time/3600))+"h "
|
|
text += str(int(time/60) % 60)+"m "+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)) + ")"
|