The following code is a GUI for a touch screen (no mouse, no keyboard). The input is the touch screen and it responds like a mouse click. This is a stripped down from a larger program to show the issue. It has 3 frames that are raised as needed. Ideally I would want the focus to be nowhere until the user clicks (points) to a field, then the keyboard enters digits in the focused field on frames Machine 1 or Machine 2. This works well, but if the user does not first "click" in an entry field, this generates an exception: AttributeError: 'machine1' object has no attribute 'insert'.
I would be happy if the keyboard inputs would go to a "bit bucket" or an invisible (and unused) dummy entry field, but could accept that by default, when a frame is raised, the focus be automatically placed on the first field.
After many hours and research on the web, I have been unable to find a solution. Thanks for pointing me in the right direction.
import tkinter as tk
from tkinter import font as tkfont
class MyApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# the container is where we stack the frames
# then the one we want visible is raised above the others
container = tk.Frame(self)
container.grid(row=0, column=0, sticky='news')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
self.frames["machine1"] = machine1(parent=container, controller=self)
self.frames["machine2"] = machine2(parent=container, controller=self)
self.frames["running"] = running(parent=container, controller=self)
self.frames["machine1"].grid(row=0, column=0, sticky="nsew")
self.frames["machine2"].grid(row=0, column=0, sticky="nsew")
self.frames["running"].grid(row=0, column=0, sticky="nsew")
self.show_frame("running")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
frame.focus_set() # this removed focus from the hidden window
class Keypad(tk.Frame):
cells = [
['1', '2', '3', '4', '5'],
['6', '7', '8', '9', '0'],
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for y, row in enumerate(self.cells):
for x, item in enumerate(row):
b = tk.Button(self, text=item, command=lambda text=item:self.append(text))
b.grid(row=y, column=x, sticky='news')
x = tk.Button(self, text='Backspace', command=self.backspace)
x.grid(row=0, column=10, sticky='news')
x = tk.Button(self, text='Clear', command=self.clear)
x.grid(row=1, column=10, sticky='news')
def get(self):
widget = self.focus_get()
return widget.get()
def append(self, text):
# get the widget with the focus
widget = self.focus_get()
# insert the value
widget.insert("insert", text)
def clear(self):
widget = self.focus_get()
widget.delete(0, 'end')
def backspace(self):
widget = self.focus_get()
text = widget.get()
text = text[:-1]
self.clear()
self.append(text)
class machine1(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Machine 1", font=controller.title_font)
label.grid(row=0, column=0, sticky='news')
e1 = tk.Entry(self, highlightthickness = 2)
e1.grid(row=2, column=0, sticky='news')
e1.focus_set() # by default start with this one
e2 = tk.Entry(self, highlightthickness = 2)
e2.grid(row=3, column=0, sticky='news')
kbd = Keypad(self)
kbd.grid(row=5, column=0, columnspan=3)
button3 = tk.Button(self, text="Run",
command=lambda: controller.show_frame("running"))
button3.grid(row=6, column=2, sticky='news')
class machine2(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Machine 2", font=controller.title_font)
label.grid(row=0, column=0, sticky='news')
e1 = tk.Entry(self, highlightthickness = 2)
e1.grid(row=2, column=0, sticky='news')
e1.focus_set() # by default start with this one
e2 = tk.Entry(self, highlightthickness = 2)
e2.grid(row=3, column=0, sticky='news')
kbd = Keypad(self)
kbd.grid(row=5, column=0, columnspan=3)
button3 = tk.Button(self, text="Run",
command=lambda: controller.show_frame("running"))
button3.grid(row=6, column=2, sticky='news')
class running(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Run", font=controller.title_font)
label.grid(row=0, column=0, sticky='news')
button1 = tk.Button(self, text="Machine 1",
command=lambda: controller.show_frame("machine1"))
button2 = tk.Button(self, text="Machine 2",
command=lambda: controller.show_frame("machine2"))
button1.grid(row=5, column=0, sticky='news')
button2.grid(row=5, column=1, sticky='news')
e1 = tk.Entry(self, highlightthickness = 2)
e1.grid(row=1, column=1, sticky='news')
if __name__ == "__main__":
app = MyApp()
app.geometry("800x480")
app.mainloop()
``b
Something always has focus, even if it's just the root window.
The simplest solution in your case is to verify that the focus is on an entry widget, which you can do like this:
def append(self, text):
widget = self.focus_get()
if widget.winfo_class() == "Entry":
widget.insert("insert", text)