3

I am attempting to have the Entry field to have focus when a new page opens up:

import tkinter as tk
from tkinter import *
from tkinter import ttk

class DIS(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.iconbitmap(self, default="")
        tk.Tk.wm_title(self, "program")
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight = 1)
        container.grid_columnconfigure(0, weight = 1)

        self.frames = {}

        for F in (startPage, contactQues):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row = 0, column = 0, sticky = "nsew")
            self.show_frame(startPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

class startPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        button2 = ttk.Button(self, text = "Here's a Button",
                    command=lambda: controller.show_frame(contactQues))
        button2.pack()

class contactQues(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)  
        self.controller = controller

        entry = Entry(self)
        entry.focus_force()
        entry.pack()

app = DIS()
app.mainloop()

If I move the Entry field under startPage, the focus is set correctly -- whenever I move it to contactQues, it loses the focus. Possibly a Toplevel issue?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Sludge
  • 6,072
  • 5
  • 31
  • 43

2 Answers2

3

It seems like tkraise() messes up the focus. So you need to invoke it after you've raised the page into view. I'd update you framework to always call some method after tkraise like so:

import tkinter as tk
from tkinter import *
from tkinter import ttk

class DIS(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.iconbitmap(self, default="")
        tk.Tk.wm_title(self, "program")
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight = 1)
        container.grid_columnconfigure(0, weight = 1)

        self.frames = {}

        for F in (startPage, contactQues):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row = 0, column = 0, sticky = "nsew")
            self.show_frame(startPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()
        frame.postupdate()

class startPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        button2 = ttk.Button(self, text = "Here's a Button",
                    command=lambda: controller.show_frame(contactQues))
        button2.pack()

    def postupdate(self):
        pass

class contactQues(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)  
        self.controller = controller

        self.entry = Entry(self)
        self.entry.pack()

    def postupdate(self):
        self.entry.focus()

app = DIS()
app.mainloop()

If you want to avoid having the postupdate() method where it's not needed, you could check to see if it exists in the class before trying to run it. Like so:

def show_frame(self, cont):
    frame = self.frames[cont]
    frame.tkraise()
    try:
        frame.postupdate()
    except AttributeError:
        pass
anderswb
  • 492
  • 5
  • 14
  • Yes this solves my problem...although, admittedly, I don't fully understand how. I have to read up on how this worked. I appreciate the help. – Sludge Oct 20 '15 at 19:20
  • Well, it is a bit wierd. But I've used the same framework as you are before. What you are doing in the init method of DIS is to draw all of the pages, basically on top of each other, and then you use tkraise to pull the one you need to the front using tkraise(). For some reason this seems to mess up the focus, so you need to set the focus after you've pulled it to the front. That is why I've added an extra method which will be called on all of you pages after it has been raised. – anderswb Oct 20 '15 at 19:27
  • Ah I see what you mean. I'm new to this so each little hiccup I blast through helps me understand. Thank you. – Sludge Oct 20 '15 at 19:33
  • If you're new to programming in general, then you should probably hold off on messing with GUIs for a bit. It's some confusing stuff. – anderswb Oct 21 '15 at 09:38
1

So after a lot of searching I got the solution to this issue. Just add the following line to the end of your show_frame function and remove the postupdate calls:

return 'break'

It disables the event propagation to the other frames under the raised one. The solution posted by anderswb didn't work for me.

Gábor Fekete
  • 1,343
  • 8
  • 16