aboutsummaryrefslogtreecommitdiff
path: root/logout-manager-tcl.py
blob: b9d9997a573e5f08c0148e5cc7bc3d474824e362 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env python3
# Startdate: 2019-06-12 20:05
# Dependencies:
#    python36-tkinter | python3-tk
#    python36-pillow-tk | python3-pil.imagetk
# WORKHERE:
#    add icons
#    add full headers

# References:
#    http://effbot.org/tkinterbook/button.htm
#    http://effbot.org/tkinterbook/tkinter-application-windows.htm
#    http://effbot.org/tkinterbook/
#    pass parameters to function of 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

import os, sys, configparser, glob, re
from tkinter import *
from functools import partial
from pathlib import Path
from PIL import Image, ImageTk
sys.path.append("/home/bgirton/dev/logout-manager")
import lmlib

config = lmlib.Initialize_config()
actions = lmlib.Actions

# graphical classes and functions
print("Loading graphics...")

def get_gtk3_default_theme():
   # abstracted so it does not clutter get_scaled_icon
   name = None
   gtk3_config_path = os.path.join(os.path.expanduser("~"),".config","gtk-3.0","settings.ini")
   gtk3_config = configparser.ConfigParser()
   gtk3_config.read(gtk3_config_path)
   try:
      if 'Settings' in gtk3_config:
         name = gtk3_config['Settings']['gtk-icon-theme-name']
      elif 'settings' in gtk3_config:
         name = gtk3_config['settings']['gtk-icon-theme-name']
   except:
      # supposed failsafe
      name = "hicolor"
   return name

def tryint(s):
   try:
      return int(s)
   except:
      return s

def sort_sizes(x):
   # I don't even know how this works. I mashed it together from so#46228101
   # WORKS return [tryint(c) for c in re.split('([0-9]+)',x.split("/")[5])]
   value = x.split("/")[5]
   #return int(re.split("[^0-9]+",value)[0]) * 100 + int( re.split("[^0-9]+",value)[1] if len(value.split("@")) >= 2 else 0 )
   return mynum(value, "all")

def mynum(x, type = "all"):
   # return the complicated numerical value for the weird size options
   if type == "all":
      return int(re.split("[^0-9]+",x)[0]) * 100 + int( re.split("[^0-9]+",x)[1] if len(re.split("[^0-9]+",x)) >= 2 else 0 )
   else:
      return int(re.split("[^0-9]+",x)[0])

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
   return next(( i for i in thelist if mynum(i.split("/")[5],"real") >= size ), thelist[-1])

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

   # first, find all files underneath /usr/share/icons/$THEME/$SIZE
   print("Finding filename of icon.")
   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)
   # now find the first one that matches
   filename = find_best_size_match(size,results)
   return filename

def get_scaled_icon(icon_name, size = 24, fallback_icon_name = "", icon_theme = "default"):
   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":
         # retrieve default theme from config file
         #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)
   
   # So now that we think we have derived the correct filename...
   try:
      print("Trying icon file",iconfilename)
      photo = Image.open(iconfilename)
   except:
      print("Error with icon file.")
      return None
   # If I ever add HiDPI stuff, multiple size here by the factor. So, size * 1.25
   photo.thumbnail(size=[size, size])
   photo2 = ImageTk.PhotoImage(photo)
   return photo2

class App:
   def __init__(self, master):
      frame = Frame(master)
      frame.grid(row=0)

      self.buttonLock = Button(frame, text="Lock", underline=3, command=partial(actions.lock,config))
      self.buttonLock.grid(row=0,column=0)
      # WORKS master.bind_all("<Alt-k>", something)
      # PASSES 2 params when expecting 1  master.bind_all("<Alt-k>", self.buttonLock.invoke)
      master.bind_all("<Alt-k>", partial(actions.lock,config))

      # WORKS, for basic image loading.
      #self.photoLogout = get_scaled_icon("/usr/share/icons/Adwaita/48x48/actions/system-log-out.png")
      #self.buttonLogout = 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.buttonLogout = Button(frame, text="Logout", underline=0, command=lambda: actions.logout(config), image=self.photoLogout, compound=LEFT)
      master.bind_all("<Alt-l>", partial(actions.logout,config))
      self.buttonLogout.grid(row=0,column=1)

      self.buttonHibernate = Button(frame, text="Hibernate", underline=0, command=lambda: actions.hibernate(config))
      self.buttonHibernate.grid(row=0,column=2)
      master.bind_all("<Alt-h>", partial(actions.hibernate,config))

      self.buttonShutdown = Button(frame, text="Shutdown", underline=0, command=lambda: actions.shutdown(config))
      self.buttonShutdown.grid(row=0,column=3)
      master.bind_all("<Alt-s>", partial(actions.shutdown,config))

      self.buttonReboot = Button(frame, text="Reboot", underline=0, command=lambda: actions.reboot(config))
      self.buttonReboot.grid(row=0,column=4)
      master.bind_all("<Alt-r>", partial(actions.reboot,config))

      #self.buttonCancel = Button(frame, text="Cancel", underline=0, command=frame.quit)
      self.buttonCancel = Button(frame, text="Cancel", underline=0, command=self.quitaction)
      self.buttonCancel.grid(row=1,columnspan=8,sticky=W+E)
      master.bind_all("<Alt-c>", 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()

# MAIN LOOP
root.title("Log out options")
#root.iconbitmap(r'/usr/share/icons/Numix/48/actions/system-logout.svg')
imgicon = PhotoImage(file=os.path.join("/usr/share/icons/Adwaita/24x24/actions","system-log-out.png"))
root.tk.call('wm','iconphoto', root._w, imgicon)
app = App(root)
root.mainloop()
try:
   root.destroy()
except:
   pass
bgstack15