From 6ab41891eacbfa910e73c7d521df01ab395d015c Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Sun, 22 Sep 2024 20:41:52 -0400 Subject: initial commit --- fprintd_tk.py | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100755 fprintd_tk.py (limited to 'fprintd_tk.py') diff --git a/fprintd_tk.py b/fprintd_tk.py new file mode 100755 index 0000000..8fe0ab6 --- /dev/null +++ b/fprintd_tk.py @@ -0,0 +1,149 @@ +#!/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() -- cgit