From 517ec6e9d9c2659a4fe0e39de5f03cf3b72adcff Mon Sep 17 00:00:00 2001 From: B Stack Date: Fri, 14 Jun 2019 13:47:33 -0400 Subject: add tooltips to tcl --- logout-manager-tcl.py | 180 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 167 insertions(+), 13 deletions(-) diff --git a/logout-manager-tcl.py b/logout-manager-tcl.py index e8bf095..694a648 100755 --- a/logout-manager-tcl.py +++ b/logout-manager-tcl.py @@ -19,15 +19,16 @@ # gtk-3.0 default icon theme https://coderwall.com/p/no3qfa/setting-gtk2-and-gtk3-theme-via-config-file # 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 # Improve: -# add tooltips # add svg support # Dependencies: # python36-tkinter | python3-tk # python36-pillow-tk | python3-pil.imagetk import os, sys, configparser, glob, re -import tkinter +import tkinter as tk +import tkinter.ttk as ttk from functools import partial from pathlib import Path from PIL import Image, ImageTk @@ -45,6 +46,158 @@ actions = lmlib.Actions # graphical classes and functions print("Loading graphics...") +class Tooltip: + + ''' + It creates a tooltip for a given widget as the mouse goes on it. + + see: + + http://stackoverflow.com/questions/3221956/ + what-is-the-simplest-way-to-make-tooltips- + in-tkinter/36221216#36221216 + + http://www.daniweb.com/programming/software-development/ + code/484591/a-tooltip-class-for-tkinter + + - Originally written by vegaseat on 2014.09.09. + + - Modified to include a delay time by Victor Zaccardo on 2016.03.25. + + - Modified + - to correct extreme right and extreme bottom behavior, + - to stay inside the screen whenever the tooltip might go out on + the top but still the screen is higher than the tooltip, + - to use the more flexible mouse positioning, + - to add customizable background color, padding, waittime and + wraplength on creation + by Alberto Vassena on 2016.11.05. + + Tested on Ubuntu 16.04/16.10, running Python 3.5.2 + + TODO: themes styles support + ''' + + def __init__(self, widget, + *, + bg='#FFFFEA', + pad=(5, 3, 5, 3), + text='widget info', + waittime=400, + wraplength=250): + + self.waittime = waittime # in miliseconds, originally 500 + self.wraplength = wraplength # in pixels, originally 180 + self.widget = widget + self.text = text + self.widget.bind("", self.onEnter) + self.widget.bind("", self.onLeave) + self.widget.bind("", self.onLeave) + self.bg = bg + self.pad = pad + self.id = None + self.tw = None + + def onEnter(self, event=None): + self.schedule() + + def onLeave(self, event=None): + self.unschedule() + self.hide() + + def schedule(self): + self.unschedule() + self.id = self.widget.after(self.waittime, self.show) + + def unschedule(self): + id_ = self.id + self.id = None + if id_: + self.widget.after_cancel(id_) + + def show(self): + def tip_pos_calculator(widget, label, + *, + tip_delta=(10, 5), pad=(5, 3, 5, 3)): + + w = widget + + s_width, s_height = w.winfo_screenwidth(), w.winfo_screenheight() + + width, height = (pad[0] + label.winfo_reqwidth() + pad[2], + pad[1] + label.winfo_reqheight() + pad[3]) + + mouse_x, mouse_y = w.winfo_pointerxy() + + x1, y1 = mouse_x + tip_delta[0], mouse_y + tip_delta[1] + x2, y2 = x1 + width, y1 + height + + x_delta = x2 - s_width + if x_delta < 0: + x_delta = 0 + y_delta = y2 - s_height + if y_delta < 0: + y_delta = 0 + + offscreen = (x_delta, y_delta) != (0, 0) + + if offscreen: + + if x_delta: + x1 = mouse_x - tip_delta[0] - width + + if y_delta: + y1 = mouse_y - tip_delta[1] - height + + offscreen_again = y1 < 0 # out on the top + + if offscreen_again: + # No further checks will be done. + + # TIP: + # A further mod might automagically augment the + # wraplength when the tooltip is too high to be + # kept inside the screen. + y1 = 0 + + return x1, y1 + + bg = self.bg + pad = self.pad + widget = self.widget + + # creates a toplevel window + self.tw = tk.Toplevel(widget) + + # Leaves only the label and removes the app window + self.tw.wm_overrideredirect(True) + + win = tk.Frame(self.tw, + background=bg, + borderwidth=0) + label = tk.Label(win, + text=self.text, + justify=tk.LEFT, + background=bg, + relief=tk.SOLID, + borderwidth=0, + wraplength=self.wraplength) + + label.grid(padx=(pad[0], pad[2]), + pady=(pad[1], pad[3]), + sticky=tk.NSEW) + win.grid() + + x, y = tip_pos_calculator(widget, label) + + self.tw.wm_geometry("+%d+%d" % (x, y)) + + def hide(self): + tw = self.tw + if tw: + tw.destroy() + self.tw = None + def get_gtk3_default_theme(): # abstracted so it does not clutter get_scaled_icon name = "hicolor" @@ -143,10 +296,10 @@ def get_scaled_icon(icon_name, size = 24, fallback_icon_name = "", icon_theme = class App: def __init__(self, master): - frame = tkinter.Frame(master) + frame = tk.Frame(master) frame.grid(row=0) - self.buttonLock = tkinter.Button(frame, text="Lock", underline=3, command=partial(actions.lock,config)) + 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) @@ -154,35 +307,36 @@ class App: # WORKS, for basic image loading. #self.photoLogout = get_scaled_icon("/usr/share/icons/Adwaita/48x48/actions/system-log-out.png") - #self.buttonLogout = tkinter.Button(frame, text="Logout", underline=0, command=lambda: actions.logout(config), image=self.photoLogout, compound=TOP) + #self.buttonLogout = tk.Button(frame, text="Logout", underline=0, command=lambda: actions.logout(config), image=self.photoLogout, compound=TOP) #self.photoLogout1 = Image.open("/usr/share/icons/Adwaita/48x48/actions/system-log-out.png") #self.photoLogout1.thumbnail(size=[24,24]) #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") self.photoLogout = get_scaled_icon(config.get_logout_icon(), config.get_icon_size(), config.get_icon_theme()) - self.buttonLogout = tkinter.Button(frame, text="Logout", underline=0, command=lambda: actions.logout(config), image=self.photoLogout, compound=tkinter.LEFT) + 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.photoHibernate = get_scaled_icon(config.get_hibernate_icon(), config.get_icon_size(), config.get_icon_theme()) - self.buttonHibernate = tkinter.Button(frame, text="Hibernate", underline=0, command=lambda: actions.hibernate(config), image=self.photoHibernate, compound=tkinter.LEFT) + self.buttonHibernate = tk.Button(frame, text="Hibernate", underline=0, command=lambda: actions.hibernate(config), image=self.photoHibernate, compound=tk.LEFT) self.buttonHibernate.grid(row=0,column=2) master.bind_all("", partial(actions.hibernate,config)) self.photoShutdown = get_scaled_icon(config.get_shutdown_icon(), config.get_icon_size(), config.get_icon_theme()) - self.buttonShutdown = tkinter.Button(frame, text="Shutdown", underline=0, command=lambda: actions.shutdown(config), image=self.photoShutdown, compound=tkinter.LEFT) + self.buttonShutdown = tk.Button(frame, text="Shutdown", underline=0, command=lambda: actions.shutdown(config), image=self.photoShutdown, compound=tk.LEFT) self.buttonShutdown.grid(row=0,column=3) master.bind_all("", partial(actions.shutdown,config)) self.photoReboot = get_scaled_icon(config.get_reboot_icon(), config.get_icon_size(), config.get_icon_theme()) - self.buttonReboot = tkinter.Button(frame, text="Reboot", underline=0, command=lambda: actions.reboot(config), image=self.photoReboot, compound=tkinter.LEFT) + self.buttonReboot = tk.Button(frame, text="Reboot", underline=0, command=lambda: actions.reboot(config), image=self.photoReboot, compound=tk.LEFT) self.buttonReboot.grid(row=0,column=4) master.bind_all("", partial(actions.reboot,config)) - #self.buttonCancel = tkinter.Button(frame, text="Cancel", underline=0, command=frame.quit) - self.buttonCancel = tkinter.Button(frame, text="Cancel", underline=0, command=self.quitaction) - self.buttonCancel.grid(row=1,columnspan=8,sticky=tkinter.W+tkinter.E) + #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. @@ -194,7 +348,7 @@ class App: #def something(event=None): # print("Got here!") -root = tkinter.Tk() +root = tk.Tk() # MAIN LOOP root.title("Log out options") -- cgit