0

So I want to have the toplevel frame keep focus after it has been withdrawn.

    from Tkinter import *
class VanishingFrame():
    def __init__(self,parent,Event=None):
        self.parent=parent
        self.visible=True
        self.Frame=Toplevel()
        self.Frame.title('Magical disapearing frame')
        self.Frame.transient(self.parent)
        self.Frame.grab_set()
        self.Frame.focus_set()
        self.Frame.geometry('300x300+150+150')
        Label(self.Frame,text="Press any key").place(relx=.5,rely=0.5,anchor=CENTER)
        self.Frame.bind('<Key>',self.AnyKey)
        self.AnyKey()
    def AnyKey(self,Event=None):
        print "key hit"
        self.visible= not self.visible
        if self.visible:
            self.Frame.withdraw()
        else:
            self.Frame.deiconify()
        self.parent.update()
        self.Frame.update()
        self.Frame.grab_set()
        self.Frame.focus_set()
        self.Frame.focus_force()
        self.Frame.update()
root=Tk()
root.geometry('500x500')
root.title("Bring out your dead")
Magic=VanishingFrame(root);
root.mainloop()

The first key hit works as expected and the VanishingFrame dissapears. However Focus appears to be on the root frame now. Clicking anywhere in the root frame will cause focus to switch back to the vanished frame and then the next key hit will cause it to reappear. Interestingly when the vanishingframe is withdrawn and the root frame appears to have focus, you can not close the root frame. You must click somewhere in the root frame and get the vanished frame to show up and then close it. That would tell me that self.Frame.transient is working.

So the actual question is how do I keep the focus on the withdrawn frame without having to click on the root frame first?

Burtski
  • 451
  • 5
  • 19
  • 1
    what is the real problem you are trying to accomplish? Setting focus to an invisible toplevel window doesn't seem to have a purpose. – Bryan Oakley Mar 20 '14 at 02:43
  • The point of giving focus to the withdrawn window is to make it reappear with any keystroke. Part of this window is a generic keypad that has a key to make it disappear for a sec so the user can see what is behind it. It comes back with any event. – Burtski Mar 20 '14 at 12:01
  • 1
    you don't need to give it focus to do that. You can create a binding that fires no matter what window has focus. – Bryan Oakley Mar 20 '14 at 12:31

1 Answers1

1

Tk checks that the window is mapped when attempting to set the focus. If the focus target is not mapped it will ignore your request. See tkFocus.c::TkSetFocusWin. Seems like a bad idea anyway particularly in combination with a grab. Provided you have compositing support you could use the attributes to set the alpha channel and make it transparent instead (eg: self.Frame.attributes('-alpha', 0.0). As this would still be mapped (but invisible) it could accept focus.

The addition of the grab set command in this case has the effect of overriding the restriction on setting the focus to unmapped widgets. If the focus is set to a valid widget in the application the grab then takes effect and moves the focus into the grabbed widget tree.

The problem originally specified is how to re-attach the focus to something useful once the current toplevel has been withdrawn and for that the answer is to re-assert the main toplevel as the current active window using wm deiconify .. This sets the toplevel active, and sets the focus onto this window which is then grabbed and moved to the now hidden toplevel widget. At this point the key binding will work again.

patthoyts
  • 32,320
  • 3
  • 62
  • 93
  • Tk is still giving focus to the withdrawn window. As soon as I click anywhere on the root frame the the focus pops back to the withdrawn frame and all works as expected. – Burtski Mar 20 '14 at 11:55
  • So is it a bug that after a mouse click on the root window, focus does go to the withdrawn toplevel window? ( bug might be a little strong, undefined behavior pehaps) – Burtski Mar 21 '14 at 01:41
  • The presence of the grab changes things. The grab restricts focus to the specified widget tree so while you cannot set the focus to the withdrawn widget, if you set the focus to any mapped widget - then the grab takes control and moves it to the grabbed widget. – patthoyts Mar 21 '14 at 10:24
  • Sorry for dead horse beating, I just want to be sure I have this right in my head. When I withdraw Frame, my focus request is ignored ( explained above) so at that point nothing in my app has focus. It is not so much clicking on the root as clicking on any part of my app that brings focus to my app, which then forces focus to the withdrawn window. Yes? BTW this has forced me to better understand windows vs frames. I am changing my approach. I should not have used toplevel in the first place. Thanks! – Burtski Mar 23 '14 at 14:41