aboutsummaryrefslogtreecommitdiff
path: root/logout-manager-tcl.py
diff options
context:
space:
mode:
Diffstat (limited to 'logout-manager-tcl.py')
-rwxr-xr-xlogout-manager-tcl.py417
1 files changed, 0 insertions, 417 deletions
diff --git a/logout-manager-tcl.py b/logout-manager-tcl.py
deleted file mode 100755
index 127bd54..0000000
--- a/logout-manager-tcl.py
+++ /dev/null
@@ -1,417 +0,0 @@
-#!/usr/bin/env python3
-# File: logout-manager-tcl.py
-# License: CC-BY-SA 4.0
-# Author: bgstack15
-# Startdate: 2019-06-12 20:05
-# Title: Tcl/tk-based logout manager
-# Purpose: A tcl/tk graphical program for selecting shutdown, logout, etc.
-# History:
-# Usage:
-# logout-manager-tcl.py
-# References:
-# http://effbot.org/tkinterbook/button.htm
-# http://effbot.org/tkinterbook/tkinter-application-windows.htm
-# http://effbot.org/tkinterbook/
-# pass parameters to function of tkinter.Button(command=) https://stackoverflow.com/questions/38749620/python-3-tkinter-button-commands#38750155
-# alternate for passing params https://stackoverflow.com/questions/6920302/how-to-pass-arguments-to-a-button-command-in-tkinter
-# https://stackoverflow.com/questions/18537918/set-window-icon#18538416
-# the exact syntax <Alt-k> for master.bind https://stackoverflow.com/questions/16082243/how-to-bind-ctrl-in-python-tkinter
-# https://pillow.readthedocs.io/en/stable/reference/ImageTk.html
-# 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:
-# Dependencies:
-# Devuan: python3-tk python3-pil.imagetk python3-cairosvg
-# el7: python36-tkinter python36-pillow-tk ( pip3 install cairosvg )
-
-import glob, os, platform, re, sys
-import tkinter as tk
-from functools import partial
-from pathlib import Path
-from sys import path
-from dotenv import load_dotenv
-# loading PIL.ImageTk after tkinter makes ImageTk use the PIL version, which supports PNG. This is important on tcl < 8.6 (that is, el7)
-from PIL import Image, ImageTk
-
-LM_USE_SVG = 0
-try:
- from cairosvg import svg2png
- LM_USE_SVG = 1
-except:
- print("WARNING: Unable to import cairosvg. No svg images will be displayed.")
- LM_USE_SVG = 0
-
-# all this to load the libpath
-try:
- defaultdir="/etc/sysconfig"
- thisplatform = platform.platform().lower()
- if 'debian' in thisplatform or 'devuan' in thisplatform:
- defaultdir="/etc/default"
- # load_dotenv keeps existing environment variables as higher precedent
- load_dotenv(os.path.join(defaultdir,"logout-manager"))
-except:
- pass
-if 'LOGOUT_MANAGER_LIBPATH' in os.environ:
- for i in os.environ['LOGOUT_MANAGER_LIBPATH'].split(":"):
- sys.path.append(i)
-import lmlib
-
-# 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("<Enter>", self.onEnter)
- self.widget.bind("<Leave>", self.onLeave)
- self.widget.bind("<ButtonPress>", 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 tryint(s):
- try:
- return int(s)
- except:
- return s
-
-def sort_sizes(x):
- # Original reference so#46228101
- value = x.split("/")[5]
- return mynum(value, "all")
-
-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 f0 * 100 + f1 if len(f) >= 2 else 0
- else:
- 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
- try:
- default = thelist[-1]
- except:
- default = None
- return next(( i for i in thelist if mynum(i.split("/")[5],"real") >= size ), default)
-
-def get_filename_of_icon(name, theme = "hicolor", size = 48, category = "actions"):
- # poor man's attempt at walking through fd.o icon theme
- filename = None
- # example: Adwaita system-log-out
-
- if theme == "default" or theme is None:
- try:
- theme = lmlib.get_gtk3_default_icon_theme()
- except:
- theme = "hicolor"
-
- # first, find all files underneath /usr/share/icons/$THEME/$SIZE
- print("Finding filename of icon, theme=",theme,"category=",category,"name=",name)
- # to exclude the scalable/ contents, replace dir 5 asterisk with [0-9]*
- results = []
- base_dir="/usr/share/icons/"
- file_filters = ".*"
- if LM_USE_SVG == 0:
- file_filters = ".{png,PNG}"
- # I have no idea if this is xdg icon theme compliant, but it is a valiant attempt.
- # 1. try (requested) req-theme, req-category, req-name first
- results = glob.glob(base_dir+theme+"/*/"+category+"/"+name+file_filters)
- # 2. try req-theme, (generic) gen-category, req-name
- if len(results) == 0:
- # no results with that category, so try all categories
- results = glob.glob(base_dir+theme+"/*/*/"+name+file_filters)
- # 3. try "gnome", req-category, req-name
- if len(results) == 0:
- results = glob.glob(base_dir+"gnome"+"/*/"+category+"/"+name+file_filters)
- # 4. try "gnome", gen-category, req-name
- if len(results) == 0:
- results = glob.glob(base_dir+"gnome"+"/*/*/"+name+file_filters)
- # 5. try "hicolor", req-category, req-name
- if len(results) == 0:
- results = glob.glob(base_dir+"hicolor"+"/*/"+category+"/"+name+file_filters)
- # 6. try "hicolor", gen-category, req-name
- if len(results) == 0:
- results = glob.glob(base_dir+"hicolor"+"/*/*/"+name+file_filters)
-
- # the sort arranges it so a Numix/24 dir comes before a Numix/24@2x dir
- results = sorted(results, key=sort_sizes)
- #print(results)
- # now find the first one that matches
- filename = find_best_size_match(size,results)
- return filename
-
-def photoimage_from_svg(filename = "",size = "48"):
- # this one works, but does not allow me to set the size.
- # this is kept as an example of how to open a svg without saving to a file.
- # open svg
- item = svg2png(url=filename, parent_width = size, parent_height = size)
- return ImageTk.PhotoImage(data=item)
-
-def empty_photoimage(size=24):
- photo = Image.new("RGBA",[size,size])
- return ImageTk.PhotoImage(image=photo)
-
-def image_from_svg(filename = "",size = "48"):
- # open svg
- if LM_USE_SVG == 1:
- svg2png(url=filename,write_to="/tmp/lm_temp_image.png",parent_width = size,parent_height = size)
- photo = Image.open("/tmp/lm_temp_image.png")
- else:
- photo = Image.new("RGBA",[size,size])
- return photo
-
-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.
- if Path(icon_name).is_file():
- #print("This is a file:",icon_name)
- iconfilename = icon_name
- else:
-
- if icon_theme == "default":
- # this should not happen, because the Initialize_config should have checked gtk3 default value.
- icon_theme = "hicolor"
- # 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 we think we have derived the correct filename
- try:
- print("Trying icon file",iconfilename)
- # try an svg
- if re.compile(".*\.svg").match(iconfilename):
- print("Trying svg...")
- photo = image_from_svg(filename=iconfilename, size=size)
- else:
- photo = Image.open(iconfilename)
- except Exception as f:
- print("Error with icon file.")
- print(f)
- return empty_photoimage()
- photo.thumbnail(size=[size, size])
- try:
- photo = ImageTk.PhotoImage(photo)
- except Exception as e:
- print("Error was ",e)
- # If I ever add HiDPI support, multiple size here by the factor. So, size * 1.25
- return photo
-
-class App:
- def __init__(self, master):
- frame = tk.Frame(master)
- frame.grid(row=0)
-
- self.photoLock = get_scaled_icon(config.get_lock_icon(), config.get_icon_size(), config.get_icon_theme())
- self.buttonLock = tk.Button(frame, text="Lock", underline=3, command=partial(actions.lock,config), image=self.photoLock, compound=tk.LEFT)
- master.bind_all("<Alt-k>", partial(actions.lock,config))
- Tooltip(self.buttonLock, text="Hide session and require authentication to return to it")
- self.buttonLock.grid(row=0,column=0)
-
- 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("<Alt-l>", partial(actions.logout,config))
- Tooltip(self.buttonLogout, text="Close the current user session")
- 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 = tk.Button(frame, text="Hibernate", underline=0, command=lambda: actions.hibernate(config), image=self.photoHibernate, compound=tk.LEFT)
- master.bind_all("<Alt-h>", partial(actions.hibernate,config))
- Tooltip(self.buttonHibernate, text="Save state to disk and power off")
- self.buttonHibernate.grid(row=0,column=2)
-
- self.photoShutdown = get_scaled_icon(config.get_shutdown_icon(), config.get_icon_size(), config.get_icon_theme())
- self.buttonShutdown = tk.Button(frame, text="Shutdown", underline=0, command=lambda: actions.shutdown(config), image=self.photoShutdown, compound=tk.LEFT)
- master.bind_all("<Alt-s>", partial(actions.shutdown,config))
- Tooltip(self.buttonShutdown, text="Power off the computer")
- self.buttonShutdown.grid(row=0,column=3)
-
- self.photoReboot = get_scaled_icon(config.get_reboot_icon(), config.get_icon_size(), config.get_icon_theme())
- self.buttonReboot = tk.Button(frame, text="Reboot", underline=0, command=lambda: actions.reboot(config), image=self.photoReboot, compound=tk.LEFT)
- master.bind_all("<Alt-r>", partial(actions.reboot,config))
- Tooltip(self.buttonReboot, text="Reboot the computer back to the login screen")
- self.buttonReboot.grid(row=0,column=4)
-
- self.buttonCancel = tk.Button(frame, text="Cancel", underline=0, command=self.quitaction)
- master.bind_all("<Alt-c>", self.quitaction)
- Tooltip(self.buttonCancel, text="Do nothing; just close this window")
- self.buttonCancel.grid(row=1,columnspan=8,sticky=tk.W+tk.E)
-
- # 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!")
-
-# load configs
-config = lmlib.Initialize_config(os.environ['LOGOUT_MANAGER_CONF'])
-actions = lmlib.Actions
-
-# MAIN LOOP
-root = tk.Tk()
-root.title("Log out options")
-imgicon = get_scaled_icon(config.get_logout_icon(),24,config.get_icon_theme())
-root.tk.call('wm','iconphoto', root._w, imgicon)
-app = App(root)
-root.mainloop()
-try:
- root.destroy()
-except:
- pass
bgstack15