0

I have a program that is a group of individual "applets". Each of the applets has tk.Entry widgets that only require numeric entries and the decimal key (0-9 & .). This program runs on a Raspberry Pi with a 10" touch screen. The stock on screen keyboards (onBoard, etc.) take up way too much screen real estate, plus I only need numeric keys. So I wrote and on screen numeric keypad, but unlike the numerous YouTube calculator examples, this keypad needs to know which of the Entry widgets has focus. I'm using a list of directories to keep up with the Entry widgets and the "fields" I'll need to apply the keypad entries to. I can not come up with a way to avoid using eval() when applying the keypad entries. I suspect the answer (if there is one) lies in "nametowidget", but not sure how I'd use it here. The eval() statements are near the end of the code in the kb_entry function.

import tkinter as tk
from tkinter import messagebox

class Eval_GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.geometry('650x225')
        self.root.title('Eval Demo')
        self.allEntryFields = []    # A list of directories we'll use to apply the numeric keypad entries

        # Frame 1 - A couple of Labels and Entry widgets
        f1 = tk.LabelFrame(self.root,padx=5,pady=5)
        f1.grid(column=0,row=0)

        self.f1e1_text = tk.Label(f1,text='Frame 1 Entry 1',font=('Arial',12))
        self.f1e1_text.grid(row=0,column=0)
        self.f1e2_text = tk.Label(f1,text='Frame 1 Entry 2',font=('Arial',12))
        self.f1e2_text.grid(row=1,column=0)

        self.f1e1 = tk.StringVar()      # Using StringVar because IntVar will not accept a decimal (.) and DoubleVar has a default of 0.0 which is not desired
        self.f1e2 = tk.StringVar()

        self.f1e1_entry=tk.Entry(f1,textvariable=self.f1e1,width='7',font=('Arial',12))
        self.f1e2_entry=tk.Entry(f1,textvariable=self.f1e2,width='7',font=('Arial',12))
        self.f1e1_entry.grid(row=0,column=1)
        self.f1e2_entry.grid(row=1,column=1)

        temp = {}                                       # Create a temporary directory for each Entry widget
        temp['focusedentry'] = '.!labelframe.!entry'    # Used the print() function to find these names
        temp['textvar']      = 'self.f1e1'              # Will need this to get() & set() the Entry widget textvariable
        temp['entrywidget']  = 'self.f1e1_entry'        # Will need this to advance the cursor in the Entry widget
        self.allEntryFields.append(temp)                # Append the directory to the allEntryFields list

        temp = {}
        temp['focusedentry'] = '.!labelframe.!entry2'
        temp['textvar']      = 'self.f1e2'
        temp['entrywidget']  = 'self.f1e2_entry'
        self.allEntryFields.append(temp)

        # Frame 2 - A couple more Labels and Entry widgets
        f2 = tk.LabelFrame(self.root,padx=5,pady=5)
        f2.grid(column=1,row=0)

        self.f2e1_text = tk.Label(f2,text='Frame 2 Entry 1',font=('Arial',12))
        self.f2e1_text.grid(row=0,column=0)
        self.f2e2_text = tk.Label(f2,text='Frame 2 Entry 2',font=('Arial',12))
        self.f2e2_text.grid(row=1,column=0)

        self.f2e1 = tk.StringVar()
        self.f2e2 = tk.StringVar()

        self.f2e1_entry=tk.Entry(f2,textvariable=self.f2e1,width='7',font=('Arial',12))
        self.f2e2_entry=tk.Entry(f2,textvariable=self.f2e2,width='7',font=('Arial',12))
        self.f2e1_entry.grid(row=0,column=1)
        self.f2e2_entry.grid(row=1,column=1)

        temp = {}
        temp['focusedentry'] = '.!labelframe2.!entry'
        temp['textvar']      = 'self.f2e1'
        temp['entrywidget']  = 'self.f2e1_entry'
        self.allEntryFields.append(temp)

        temp = {}
        temp['focusedentry'] = '.!labelframe2.!entry2'
        temp['textvar']      = 'self.f2e2'
        temp['entrywidget']  = 'self.f2e2_entry'
        self.allEntryFields.append(temp)

        # Frame 3
        f3 = tk.LabelFrame(self.root,padx=5,pady=5)
        f3.grid(column=2,row=0)

        # Placing .grid on same line just to shorten this demo code
        k7 = tk.Button(f3,text='7',bg='white',command=lambda: self.kb_entry(7)  ,width=4,font=('Arial',16)).grid(row=0,column=1,padx=3,pady=3)
        k8 = tk.Button(f3,text='8',bg='white',command=lambda: self.kb_entry(8)  ,width=4,font=('Arial',16)).grid(row=0,column=2,padx=3,pady=3)
        k9 = tk.Button(f3,text='9',bg='white',command=lambda: self.kb_entry(9)  ,width=4,font=('Arial',16)).grid(row=0,column=3,padx=3,pady=3)
        k4 = tk.Button(f3,text='4',bg='white',command=lambda: self.kb_entry(4)  ,width=4,font=('Arial',16)).grid(row=1,column=1,padx=3,pady=3)
        k5 = tk.Button(f3,text='5',bg='white',command=lambda: self.kb_entry(5)  ,width=4,font=('Arial',16)).grid(row=1,column=2,padx=3,pady=3)
        k6 = tk.Button(f3,text='6',bg='white',command=lambda: self.kb_entry(6)  ,width=4,font=('Arial',16)).grid(row=1,column=3,padx=3,pady=3)
        k1 = tk.Button(f3,text='1',bg='white',command=lambda: self.kb_entry(1)  ,width=4,font=('Arial',16)).grid(row=2,column=1,padx=3,pady=3)
        k2 = tk.Button(f3,text='2',bg='white',command=lambda: self.kb_entry(2)  ,width=4,font=('Arial',16)).grid(row=2,column=2,padx=3,pady=3)
        k3 = tk.Button(f3,text='3',bg='white',command=lambda: self.kb_entry(3)  ,width=4,font=('Arial',16)).grid(row=2,column=3,padx=3,pady=3)
        k0 = tk.Button(f3,text='0',bg='white',command=lambda: self.kb_entry(0)  ,width=4,font=('Arial',16)).grid(row=3,column=1,padx=3,pady=3)
        kd = tk.Button(f3,text='.',bg='white',command=lambda: self.kb_entry('.'),width=4,font=('Arial',16)).grid(row=3,column=2,padx=3,pady=3)

        self.root.mainloop()

    def kb_entry(self,the_key):
        focused = str(self.root.focus_get())    # Get the Entry widget that has focus
        if focused == '.':
            tk.messagebox.showerror('Error','Select an Entry Field First')
        else:
            found = False
            for i in self.allEntryFields:       # Find a match in our list of directories
                if i['focusedentry'] == focused:
                    the_tv = i['textvar']
                    the_en = i['entrywidget']
                    found = True
                    break
            if found:
                c_val = eval(the_tv).get()      # Get the current value of the Focused Entry widget
                n_val = c_val + str(the_key)    # Concatenate the numeric keypad entry to the current value
                eval(the_tv).set(n_val)         # Set the value of the Focused Entry widget to the new value
                eval(the_en).icursor(8)         # Advance the cursor to the end of the Focused Widget (use a number greater than what you expect the Entry widget to hold
            else:
                tk.messagebox.showerror('Error','Unknown Entry Widget Focus ' + str(focused))

if __name__ == '__main__':
    Eval_GUI()
Pragmatic_Lee
  • 473
  • 1
  • 4
  • 10
  • 1
    Of course: use a `dict` to store the buttons instead of 11 separate variables. – chepner Mar 14 '23 at 13:27
  • rather than store te text name of the field, or in addition to, why don't you store a reference to the field that you would use rather than `eval()`.. ie `temp['ref'] = self.f1e1` – JonSG Mar 14 '23 at 13:27
  • Side note - all `k*` variables are just `None`... – Tomerikoo Mar 14 '23 at 13:32
  • 1
    @JonSG Well duh - guess I couldn't see the forest for the trees. Thanks, program is void of eval() now. Thanks – Pragmatic_Lee Mar 14 '23 at 13:35
  • @chepner Not sure how storing the 11 variables in a dictionary would eliminate the use of eval(), but thanks for stopping by. – Pragmatic_Lee Mar 14 '23 at 13:37
  • @Tomerikoo, yep, and as per the comment in the code, I only put the .grid on the same line to shorten the demo code. Besides the value of each k* variable is passed in the lambda. Thanks for stopping by. – Pragmatic_Lee Mar 14 '23 at 13:39
  • I can put that as an answer, but I wonder if you might think about closing this question given the forest/trees nature of the problem. – JonSG Mar 14 '23 at 13:57
  • 1
    @JonSG Thanks, I think I'll just close the question. – Pragmatic_Lee Mar 14 '23 at 14:03
  • @JonSG = appears I don't have enough of a "reputation" to close. If you'd like to put this as an answer, I'll be glad to give it an upvote. – Pragmatic_Lee Mar 14 '23 at 14:05

1 Answers1

0

Yes, you can use the getattr() function to get the attribute of an object by its name.

def kb_entry(self, num):
    focused_widget = self.root.focus_get()
    for entryfield in self.allEntryFields:
        if entryfield['focusedentry'] == str(focused_widget):
            textvar_name = entryfield['textvar']
            textvar = getattr(self, textvar_name)
            current_text = textvar.get()
            new_text = current_text + str(num)
            textvar.set(new_text)
            entry_widget = entryfield['entrywidget']
            getattr(self, entry_widget).icursor(len(new_text))

you can use getattr() to get the attribute by its name, which is stored in entryfield['textvar'] and entryfield['entrywidget']. You can then use the get() and set() methods of the StringVar object to retrieve and update the text in the Entry widget.

For icursor() is a method of the Entry widget to set the insertion cursor to the end of the new text.

ma9
  • 158
  • 11