From f29a349581dc7a8c2ab8ef8501607c989be43def Mon Sep 17 00:00:00 2001 From: B Stack Date: Fri, 14 Jun 2019 14:59:54 -0400 Subject: WIP: adding svg support part 1 --- logout-manager-tcl.py | 89 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 11 deletions(-) (limited to 'logout-manager-tcl.py') diff --git a/logout-manager-tcl.py b/logout-manager-tcl.py index 694a648..afe6632 100755 --- a/logout-manager-tcl.py +++ b/logout-manager-tcl.py @@ -20,13 +20,20 @@ # homedir https://stackoverflow.com/questions/4028904/how-to-get-the-home-directory-in-python # natural sort https://stackoverflow.com/questions/46228101/sort-list-of-strings-by-two-substrings-using-lambda-function/46228199#46228199 # tooltips https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter/41381685#41381685 +# rsvg https://stackoverflow.com/questions/10393675/rsvg-with-python-3-2-on-ubuntu/19744099#19744099 # Improve: # add svg support # Dependencies: -# python36-tkinter | python3-tk -# python36-pillow-tk | python3-pil.imagetk - -import os, sys, configparser, glob, re +# package: RPM | DPKG +# tkinter: python36-tkinter | python3-tk +# PIL: python36-pillow-tk | python3-pil.imagetk +# cairo: python36-cairo | python3-cairo +# Rsvg: python36-gobject | python3-gobject + +from gi import require_version +require_version('Rsvg', '2.0') +from gi.repository import Rsvg +import os, sys, configparser, glob, re, cairo import tkinter as tk import tkinter.ttk as ttk from functools import partial @@ -46,6 +53,48 @@ actions = lmlib.Actions # graphical classes and functions print("Loading graphics...") +def _alpha_blending(rgba, back): + "Return a rgb tuple composed from a rgba and back(ground) tuple/list." + paired = zip(rgba[:-1], back) + alpha = rgba[-1] + tmp = list() + for upper, lower in paired: + blend = (((255 - alpha) * lower) + (alpha * upper)) / 255 + tmp.append(blend) + + return(tuple(tmp)) + +def convert(bgra_buffer, width, height): + "Convert bgra buffer to photoimage put" + idx = 0 + end = len(bgra_buffer) + arguments = list() + + while idx < end: + rgba = (ord(bgra_buffer[idx + 2]), + ord(bgra_buffer[idx + 1]), + ord(bgra_buffer[idx + 0]), + ord(bgra_buffer[idx + 3])) + back = (255, 255, 255) + rgb = _alpha_blending(rgba, back) + arguments += rgb + idx += 4 + + template = ' '.join(height *['{%s}' % (''.join(width*["#%02x%02x%02x"]))]) + return(template % tuple(arguments)) + +def photoimage_from_svg(file_path_name): + '''Return a Tkinter.PhotoImage with the content set to the rendered SVG.''' + svg = rsvg.Handle(file=file_path_name) + width, height = svg.get_dimension_data()[:2] + surface = cairo.ImageSurface(cairo.FORMAT_RGB24, int(width), int(height)) + context = cairo.Context(surface) + svg.render_cairo(context) + image = tk.PhotoImage(width=width, height=height) + data = convert(surface.get_data(), width, height) + image.put(data) + return(image) + class Tooltip: ''' @@ -199,6 +248,7 @@ class Tooltip: self.tw = None def get_gtk3_default_theme(): + # WORKHERE: move this to lmlib # abstracted so it does not clutter get_scaled_icon name = "hicolor" gtk3_config_path = os.path.join(os.path.expanduser("~"),".config","gtk-3.0","settings.ini") @@ -212,6 +262,7 @@ def get_gtk3_default_theme(): except: # supposed failsafe name = "hicolor" + print("Found gtk3 default theme:",name) return name def tryint(s): @@ -230,10 +281,18 @@ def sort_sizes(x): def mynum(x, type = "all"): # return the complicated numerical value for the weird size options f = re.split("[^0-9]+",x) + try: + f0 = int(f[0]) + except: + f0 = 0 + try: + f1 = int(f[1]) + except: + f1 = 0 if type == "all": - return int(f[0]) * 100 + int( f[1] if len(f) >= 2 else 0 ) + return f0 * 100 + f1 if len(f) >= 2 else 0 else: - return int(f[0]) + return f0 def find_best_size_match(size, thelist): # return item from sorted thelist whose split("/")[5] is the first to meet or exceed the requested size @@ -252,9 +311,10 @@ def get_filename_of_icon(name, theme = "hicolor", size = 48, category = "actions theme = "hicolor" # first, find all files underneath /usr/share/icons/$THEME/$SIZE + # WORKHERE, allow a prioritization of category, but tolerate any category if the requested category has no matches but another category does print("Finding filename of icon, theme=",theme,"category=",category,"name=",name) # WORKHERE: this glob affects if scalable/ dir is included. When support for svg is added (which is beyond PIL), need to add a conditional and use a second glob if USE_SVG=1. - results = glob.glob("/usr/share/icons/"+theme+"/*[0-9]*/"+category+"/"+name+".*") + results = glob.glob("/usr/share/icons/"+theme+"/*/"+category+"/"+name+".*") # the sort arranges it so a Numix/24 dir comes before a Numix/24@2x dir results = sorted(results, key=sort_sizes) #print(results) @@ -262,7 +322,7 @@ def get_filename_of_icon(name, theme = "hicolor", size = 48, category = "actions filename = find_best_size_match(size,results) return filename -def get_scaled_icon(icon_name, size = 24, fallback_icon_name = "", icon_theme = "default"): +def get_scaled_icon(icon_name, size = 24, icon_theme = "default", fallback_icon_name = ""): iconfilename = None # if name is a specific filename, just use it. @@ -273,16 +333,21 @@ def get_scaled_icon(icon_name, size = 24, fallback_icon_name = "", icon_theme = if icon_theme == "default": # retrieve default theme from config file - #print("Discovering default icon theme...") + print("Discovering default icon theme...") icon_theme = get_gtk3_default_theme() # so now that icon_theme is defined, let us go find the icon that matches the requested name and size, in the actions category #print("Using icon theme",icon_theme) iconfilename = get_filename_of_icon(name=icon_name, theme=icon_theme, size=size, category=config.get_icon_category()) - + # So now that we think we have derived the correct filename... try: print("Trying icon file",iconfilename) - photo = Image.open(iconfilename) + # try an svg + if re.compile(".*\.svg").match(iconfilename): + print("Trying svg...") + photo = photoimage_from_svg(iconfilename) + else: + photo = Image.open(iconfilename) except: print("Error with icon file.") return None @@ -313,6 +378,7 @@ class App: #self.photoLogout = ImageTk.PhotoImage(self.photoLogout1,size="24x24") #self.photoLogout = get_scaled_icon("/usr/share/icons/Adwaita/48x48/actions/system-log-out.png", 24) #self.photoLogout = get_scaled_icon("system-log-out", 24, icon_theme="default") + print("Using configed icon theme:",config.get_icon_theme()) self.photoLogout = get_scaled_icon(config.get_logout_icon(), config.get_icon_size(), config.get_icon_theme()) self.buttonLogout = tk.Button(frame, text="Logout", underline=0, command=lambda: actions.logout(config), image=self.photoLogout, compound=tk.LEFT) master.bind_all("", partial(actions.logout,config)) @@ -360,6 +426,7 @@ imgicon = get_scaled_icon(config.get_logout_icon(),24) #else: # #imgicon = PhotoImage_from_png(file=os.path.join("/usr/share/icons/Adwaita/24x24/actions","system-log-out.png")) # imgicon = get_scaled_icon(config.get_logout_icon(),24) +print(imgicon) root.tk.call('wm','iconphoto', root._w, imgicon) app = App(root) root.mainloop() -- cgit