#!/usr/bin/env python3 # Startdate: 2024-09-22-1 14:26 # Purpose: tkinter desktop gui app for management fingerprint enrollments # Dependencies: # python3-tkstackrpms, python3-tk, python3-pil # References: # stackrpms_tk.py # Improve: # implement delete # implement enroll # import tkinter as tk, os, tkinter.filedialog, sys, threading import tkstackrpms as stk import fprintd_tk_lib as lib from PIL import Image, ImageTk # todo: load images array? ABOUT_TEXT = """ fprintd_tk \"Gui for fprintd\" (C) 2024 bgstack15 SPDX-License-Identifier: GPL-3.0-only Icons adapted from Numix-Icon-Theme-Circle (GPL 3) """ # configurable by admin img_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"images") # These will be used a lot in the program. str_hands = ["left","right"] str_fingers = ["thumb","index-finger","middle-finger","ring-finger","little-finger"] str_all = [] for h in str_hands: for f in str_fingers: str_all.append(f"{h}-{f}") class App(tk.Frame): def __init__(self, master): super().__init__(master) self.master.title("Gui for fprintd") imgicon = stk.get_scaled_icon("fingerprint-gui",24,"default", "","apps") self.master.tk.call("wm","iconphoto",self.master._w,imgicon) menu = tk.Menu(self.master) menu_file = tk.Menu(menu,tearoff=0) menu_file.add_command(label="Delete...", command=self.func_delete, underline=0) menu_file.add_separator() menu_file.add_command(label="Exit", command=self.func_exit, underline=1) menu.add_cascade(label="File",menu=menu_file,underline=0) menu_help = tk.Menu(menu,tearoff=0) menu_help.add_command(label="About", command=self.func_about, underline=0) menu.add_cascade(label="Help",menu=menu_help,underline=0) self.master.config(menu=menu) self.grid() # use this instead of pack() # statusbar variable self.statustext = tk.StringVar() # prepare finger images try: img_path + "" except: img_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"images") self.img_notenrolled = ImageTk.PhotoImage(stk.image_from_svg(os.path.join(img_path,"fingerprint-gui.svg"),32)) self.img_enrolled = ImageTk.PhotoImage(stk.image_from_svg(os.path.join(img_path,"fingerprint-enrolled.svg"),32)) self.frm_actions = tk.Frame(self.master) self.frm_actions.grid(row=0,column=0) self.action = tk.IntVar(value=1) stk.Radiobutton(self.frm_actions,value=1,text="enroll",variable=self.action,underline=0).grid(row=0,column=0) stk.Radiobutton(self.frm_actions,value=2,text="verify",variable=self.action,underline=0).grid(row=1,column=0) # array of fingers # we want 2 rows, 5 columns self.frm_fingers = tk.Frame(self.master) self.frm_fingers.grid(row=0,column=1) self.fingers = [] hand = 0 while hand < 2: finger = 0 while finger < 5: fnum = finger if hand==1 else len(str_fingers)-finger-1 #print(f"While preparing hand {hand}, evaluating finger {finger}, which might need to be number {fnum}") tf = str_hands[hand] + "-" + str_fingers[fnum] self.fingers.append(tk.Button(self.frm_fingers,text=tf,padx=0,pady=0,command=lambda f=tf: self.func_finger_button(f),image=self.img_notenrolled,compound="top")) self.fingers[-1].grid(row=hand,column=finger) finger = finger + 1 hand = hand + 1 # because the underline business does not work on the radio button itself # somehow the keypress takse over the lambda first var, so just set a dummy value, and pass our useful value as second parameter. self.master.bind("",lambda a=1,b=1: self.set_action(a,b)) self.master.bind("",lambda a=1,b=2: self.set_action(a,b)) # status bar stk.StatusBar(self.master,var=self.statustext) # and now, load data into form for the first time self.load_data_into_form() def set_action(self, keypress, a): #print(f"DEBUG: got keypress {keypress}, a={a}") # this is used by Alt+E, Alt+V to select the radio buttons for enroll, verify, etc. self.action.set(a) def load_data_into_form(self): self.enrolled_fingers = lib.get_enrolled_fingers() print(f"DEBUG (load_data_into_form): got enrolled fingers {self.enrolled_fingers}") for i in self.fingers: if str(i.cget('text')) in self.enrolled_fingers: i.config(image=self.img_enrolled) else: i.config(image=self.img_notenrolled) # functions def func_about(self): """ Display about dialog. """ tk.messagebox.Message(title="About",message=ABOUT_TEXT,icon="info").show() def func_exit(self): # in case we need to manually do stuff # otherwise command=self.client_exit would have sufficed. self.master.quit() def func_finger_button(self, finger): print(f"DEBUG: func_finger_button finger {finger}, action {self.action.get()}") action = self.action.get() if 1 == action: # enroll try: # WORKHERE: pass a tk text var to set for intermediate operations? self.statustext.set(lib.enroll_finger(finger, self.func_update_status)) except Exception as e: self.statustext.set(e) elif 2 == action: # verify #print(f"stub action: verify, finger {finger}") t1 = threading.Thread(target=lib.verify_finger,args=(finger, self.func_update_status)) t1.run() self.load_data_into_form() def func_update_status(self, msg): msg = msg.strip() print(f"DEBUG: func_update_status called with msg {msg}",file=sys.stderr) self.statustext.set(msg) def func_delete(self): print(f"stub, please ask user if he is certain to delete all enrolled fingerprints") # main root = tk.Tk() fprintd_tk = App(root) fprintd_tk.mainloop()