From e51a2983fd6762ca3357cca8a7b63e7b0a0376fb Mon Sep 17 00:00:00 2001 From: B Stack Date: Sat, 15 Jun 2019 15:28:52 -0400 Subject: WIP: try with rsvg and cairo Unfortunately I just couldn't get the examples working with rsvg and cairo, so I will have to try something else. References: http://code.activestate.com/lists/python-list/595078/ https://grokbase.com/t/python/python-list/112fsd3tyw/displaying-svg-in-tkinter-using-cairo-and-rsvg --- logout-manager-tcl.py | 18 ++++-- sample.py | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+), 7 deletions(-) create mode 100755 sample.py diff --git a/logout-manager-tcl.py b/logout-manager-tcl.py index cfeb851..0a0a1f7 100755 --- a/logout-manager-tcl.py +++ b/logout-manager-tcl.py @@ -29,11 +29,8 @@ # tkinter: python36-tkinter | python3-tk # PIL: python36-pillow-tk | python3-pil.imagetk # cairo: python36-cairo | python3-cairo -# Rsvg: python36-gobject | python3-gobject +# Rsvg: python36-gobject | gir1.2-rsvg-3.0 -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 @@ -42,6 +39,11 @@ from pathlib import Path from PIL import Image, ImageTk sys.path.append("/home/bgirton/dev/logout-manager") import lmlib +WITH_SVG=True +if not WITH_SVG is None and WITH_SVG: + from gi import require_version + require_version('Rsvg', '2.0') + from gi.repository import Rsvg as rsvg #if TkVersion < 8.6: # USE_PRIVATE_TCL_IMAGES = 1 # print("Loading private PIL translation layter") @@ -86,7 +88,7 @@ def convert(bgra_buffer, width, height): 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) + svg = rsvg.Handle(file_path_name) width, height = svg.get_dimension_data()[:2] surface = cairo.ImageSurface(cairo.FORMAT_RGB24, int(width), int(height)) context = cairo.Context(surface) @@ -349,8 +351,10 @@ def get_scaled_icon(icon_name, size = 24, icon_theme = "default", fallback_icon_ photo = photoimage_from_svg(iconfilename) else: photo = Image.open(iconfilename) - except: - print("Error with icon file.") + except Exception as e: + print("Error with icon file:",e) + if "gobject" in str(e): + raise return None # If I ever add HiDPI stuff, multiple size here by the factor. So, size * 1.25 photo.thumbnail(size=[size, size]) diff --git a/sample.py b/sample.py new file mode 100755 index 0000000..7ad8f07 --- /dev/null +++ b/sample.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +# File: sample.py +# License: CC-BY-SA 4.0 +# Author: bgstack15 +# Startdate: 2019-06-14 17:42 +# Title: Learning how to display an svg in tcl/tk with python +# Purpose: +# History: +# Usage: +# References: +# rsvg https://stackoverflow.com/questions/10393675/rsvg-with-python-3-2-on-ubuntu/19744099#19744099 +# svg for tkinter http://code.activestate.com/lists/python-list/595078/ +# Improve: +# add svg support +# Dependencies: +# package: RPM | DPKG +# tkinter: python36-tkinter | python3-tk +# PIL: python36-pillow-tk | python3-pil.imagetk +# cairo: python36-cairo | python3-cairo +# Rsvg: python36-gobject | gir1.2-rsvg-3.0 + +import os, sys, configparser, glob, re, cairo +import tkinter as tk +import tkinter.ttk as ttk +from functools import partial +from pathlib import Path +from PIL import Image, ImageTk +sys.path.append("/home/bgirton/dev/logout-manager") +import lmlib +import inspect # here for troubleshooting sample.py only +WITH_SVG=True +if not WITH_SVG is None and WITH_SVG: + from gi import require_version + require_version('Rsvg', '2.0') + from gi.repository import Rsvg as rsvg +#if TkVersion < 8.6: +# USE_PRIVATE_TCL_IMAGES = 1 +# print("Loading private PIL translation layter") +# def PhotoImage_from_png(file): +# return ImageTk.PhotoImage(file=file) + +config = lmlib.Initialize_config() +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 = int((((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(str(bgra_buffer[idx + 2])), + ord(str(bgra_buffer[idx + 1])), + ord(str(bgra_buffer[idx + 0])), + ord(str(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"]))]) + #print(template) + #print(arguments[-1]) + 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() + svg.new_from_file(file_name=file_path_name) + + #print(svg) + #print(inspect.getmembers(rsvg.Handle)) + #svg.open(file=file_path_name) + + #print(svg.height) + #print(svg.get_dimensions().height) + #print(inspect.getmembers(rsvg.DimensionData)) + + #width, height = svg.get_dimensions()[:2] + width, height = [48,48] + 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) + +def get_scaled_icon(icon_name, size = 24, icon_theme = "default", fallback_icon_name = ""): + iconfilename = None + + # assume icon_name is file. + iconfilename = icon_name + + # So now that we think we have derived the correct filename... + photo = photoimage_from_svg(iconfilename) + #try: + # print("Trying svg file",iconfilename) + # # try an svg + # if re.compile(".*\.svg").match(iconfilename): + # print("Trying svg...") + # photo = photoimage_from_svg(iconfilename) + #except Exception as e: + # print("Error with icon file:",e) + # if "gobject" in str(e): + # raise + # return None + photo.thumbnail(size=[size, size]) + try: + photo2 = ImageTk.PhotoImage(photo) + except Exception as e: + print("Error was ",e) + return photo2 + +class App: + def __init__(self, master): + frame = tk.Frame(master) + frame.grid(row=0) + + self.buttonLock = tk.Button(frame, text="Lock", underline=3, command=partial(actions.lock,config)) + self.buttonLock.grid(row=0,column=0) + # WORKS master.bind_all("", something) + # PASSES 2 params when expecting 1 master.bind_all("", self.buttonLock.invoke) + master.bind_all("", partial(actions.lock,config)) + + # WORKS, for basic image loading. + 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)) + Tooltip(self.buttonLogout, text="HERE IS SOMETHING!") + self.buttonLogout.grid(row=0,column=1) + + #self.buttonCancel = tk.Button(frame, text="Cancel", underline=0, command=frame.quit) + self.buttonCancel = tk.Button(frame, text="Cancel", underline=0, command=self.quitaction) + self.buttonCancel.grid(row=1,columnspan=8,sticky=tk.W+tk.E) + master.bind_all("", self.quitaction) + + # Found this after trial and error. + def quitaction(self,b=None): + print("Cancel any logout action.") + root.destroy() + +# Left here as an example for a mster.bind_all that works. +#def something(event=None): +# print("Got here!") + +root = tk.Tk() + +# MAIN LOOP +root.title("Log out options") +imgicon = get_scaled_icon("/usr/share/icons/Numix/96/actions/system-shutdown.svg",24) + +print(imgicon) + +root.tk.call('wm','iconphoto', root._w, imgicon) +app = App(root) +root.mainloop() +try: + root.destroy() +except: + pass -- cgit