How to keep the window focus on new Toplevel() window in Tkinter? - python-2.7

I am currently writing a win32gui python27 application (I'm running win7). I am wondering if it is possible to create a new window from my mainloop() and KEEPING the focus on it, possibly by stopping the mainloop and deactivating the root window. In a sort of pseudo code example:
root=Tk()
#put in some widgets, such as statusbars, tkfileDialog widgets etc...
O=Toplevel()
OptionMenu(O) #wait for user to make his choices; btw: OptionMenu is a class...
tkFileDialog.askdirectory(...) #THEN interpret this line
Basically, I'd like to achieve what most of the widgets in tkfiledialog and tksimpledialog do:
To steal the focus from the main window (not just the console focus, the wm focus) and to resume the mainloop until AFTER everything in, for example, OptionMenu has been resolved. I hope I could make my goals clear to y'all, I just started Tkinter programming a couple of weeks ago and may confuse and misinterpret some concepts behind it....
That's all, folks!

The concept you are looking for is called a "grab". Tkinter supports grabs with several methods. For example, to set a local grab on a toplevel you would do my_window.grab_set(). A local grab is where this window grabs the focus from all other windows in your app, but only for your app.
You can also do a global grab, which effectively freezes your entire display except for your specific window. This is very dangerous since you can easily lock yourself out of your own computer if you have a bug in your code.
Grabs do not deactivate the mainloop function. This must be running for your window to process events. It simply redirects all events to the window that has the grab.
For more information, read about grab_set and other grab commands here: http://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.grab_set-method

None of the above suggestions worked for me, on Mac OS El Capitan. But based on those hints, this does:
class Window(Tk.Toplevel):
...
def setActive(self):
self.lift()
self.focus_force()
self.grab_set()
self.grab_release()
...

grab.set() is much to aggressive for my needs and blocks normal window operations. This answer is to make one Tkinter Window pop up overtop of other Tkinter windows.
In my app I have a large window toplevel which calls a much smaller window top2 which initially appears on top of toplevel.
If user clicks within toplevel window it gains focus and smothers much smaller top2 window until toplevel window is dragged off of it.
The solution is to click the button in toplevel to launch top2 again. The top2 open function knows it is already running so simply lifts it to the top and gives it focus:
def play_items(self):
''' Play 1 or more songs in listbox.selection(). Define buttons:
Close, Pause, Prev, Next, Commercial and Intermission
'''
if self.top2_is_active is True:
self.top2.focus_force() # Get focus
self.top2.lift() # Raise in stacking order
root.update()
return # Don't want to start playing again

Related

Tkinter quit() freezes window

I got a code that creates a hidden main window in Tkinter (root, Tk object) and than a Toplevel window. I use after() to pass the mainloop() to the rest of the code, but the window just freezes. I don't want do destroy the app, just to run the rest of the code.
I set the milliseconds in after to 10,000 and noticed that only after 10 seconds, when it prints After mainloop, the window freezes/.
Does anybody know why and can I do to make it work?
root = Tk()
top = Toplevel()
top.after(10000, top.quit)
top.mainloop()
print 'After mainloop'
Thank you very much
It will be hard for anyone to debug with what you posted i will suggest doing top.iconify() rather to hide the app. assuming chat is window.

WxPython - Inaccurate modal windows behavior

I encounter some annoying behavior with modal frames in WxPython.
As we all know, when a modal window shows up, it appears on foreground (on top of the main window) and the main window should become inaccessible (no response to click for example).
This is working as expected with nested WxFrames until using the Windows start (status)bar.
If user clicks on main frame on Windows bar, it appears on top of the second frame, which is totally inaccurate as user does not understand what's happening and why the window is inaccessible.
The first solution coming on my mind is to bind activation event of the first frame, and set programmatically (& systematically) the second frame to foreground. However it appears to me weird that this behavior is not already done naturally by WxPython.
Does anyone have any idea or any native / generic solution for that?
As I suggested in my question, I binded activate event I raise all children window. This works as expected. We can first go to top level window and starting to raise from the first window to raise always all children frames. But always it's strange for me that it's not managed naturally by wxPython.
def __init__(self, parent):
wx.Frame.__init__(...)
...
self.Bind(wx.EVT_ACTIVATE, self.on_activate_window)
def on_activate_window(self, event):
"""
Set modal windows to foreground by their hierarchical order
"""
self.set_children_to_top(self.GetChildren())
def set_children_to_top(self, children):
"""
Loops all children of the given windows, if any other modal window is found, it raises the new window to foreground
"""
for child in children:
if isinstance(child, wx.Frame) and child.is_modal:
child.Raise()
try:
child_window = child.GetChildren()
if child_window:
self.set_children_to_top(child_window)
except AttributeError:
continue
It is much better to use wx.Dialog when you need a modal window, as it is designed from the start to behave that way. In fact, because of the inconsistencies the MakeModal method has been removed for frames, although if you really need to do it that way there is a workaround.

Focus for UI that is used in 3d app

I have a UI that I have made which is to perform tasks within 3ds max. When I open the ui in Max via a python.ExecuteFile call in a toolbar maxscript, focus and keyboard entries are given to the ui elements, using the max command DisableAccelerators(), so that fields can be entered and then run via an execute button. If I click onto the main max window to do something before I have finished the input to the ui, the focus is lost and keyboard shortcuts are restored to max. If I then click on the UI again to continue editing/updating, the focus remains with Max and I cannot continue using the UI.
I am using PySide and have tried setting the UI window to have strong focus ( setFocusPolicy(Qt.StrongFocus) ), but this has not worked so far, so does anybody know what I need to research or look up to help me with this? I just need to understand how to shift focus back and forth between the App window and the UI I have created.
Thanks
I have been looking into mouse events to identify the window that I click on to run a function that sets a boolean if the right window name is returned, Would this be a good idea?
The following link should help. I doubt you'll be able to do your work with PySide, as the proposed solution assumes using a PyQt4 build supplied by Blur Studio as part of their 3ds Max tool set. That being said, PyQt4 and PySide are substantially similar, so unless there's a very specific reason why you need to use PySide over PyQt4, you should be fine.
http://area.autodesk.com/blogs/chris/pyqt-ui-in-3ds-max-2014-extension

Centering a Tkinter Toplevel window in both windows and remote X11?

I know Tkinter can be an exercise in frustration at times, but I am stumped on the "correct" sequence of calls to a Tkinter/ttk Toplevel widget/window that will:
Display the window in the center of the screen WITHOUT drawing it off-center and THEN moving it.
Size the window properly.
Make the window toplevel to the parent form, but not system modal (that is, not toplevel to ALL apps/windows on the system).
Optionally create a taskbar button on Windows.
This is the current code that I have so far, with some unrelated bits removed for simplicity's sake:
def __init_ui__(self):
#// Basic setup.
self.wm_overrideredirect(False)
self.wm_resizable(False, False)
self.configure(relief=FLAT, borderwidth=4)
#// <Some extraneous widget-creation here>
#// Done!
self.update()
self.withdraw()
self.update_idletasks()
self.grid()
self.transient(self._parent)
self.grab_set()
self.form.initial_focus() #// sets initial focus before the window is displayed
center(self) #// https://stackoverflow.com/a/10018670
self.deiconify()
self._parent._parent.wait_window(self) #// Reaches back to the root Tk() obj.
# ENDIF
# END __init_ui__
This form inherits from a custom class I created that wraps a bunch of widget-creation calls, and the class inherits down from object. A second inheritance comes from ttk.Toplevel, to give it access to the various self.wm_* calls and other Tkinter goodies.
That said, I am certain some of my function calls are unneeded. But this particular call order seems to mostly work well on Windows 7. Under X11-over-ssh (using Xming for the X server in Windows), however, things get a bit weirder. In that case, if I move self.update() to be after self.withdraw(), then the window is centered correctly w/o redraw on Windows 7, but on X11, it is drawn and properly centered whilst hidden, but has ZERO geometry (only the compressed window border/decorations are shown). It's as-if grid propagation didn't fire or such.
But where self.update() is right now, it properly sizes and centers the window on both windows and X11 - but you can see the window getting created off-center THEN moved to center on both.
If I use self.wm_attributes("-toolwindow", 1) on Windows, then the windows gets centered without me seeing it, but -toolwindow only works on Windows system. It's an invalid command to both X11 and Aqua.
I'd like to avoid calls to tk::windowingsystem all the time to determine what my window manager is and having to apply different hacks for Windows and X11. And I can't test Mac/Aqua (don't own one, not going to buy), so I'll just have to hope the end look is somewhat functional on that platform.
Additionally, if I click away to another window/app under both Windows and remote X11, clicking back on the main form via the taskbar button only displays the main form -- the child Toplevel window only pops back up when you click anywhere inside the main form. Fixed: (kinda) -- I forgot that adding an argument to self._transient() kinda fixed that by specifying the parent. Earlier tonight, I didn't pass the parent argument in, thus the focusing wasn't right.
So is there some kind of tried-and-true magik recipe or invocation to the Priests of Ancient Mu to get a Tkinter/ttk Toplevel window to size, center, and properly get focus if it's not the root window? Using minimal X11, Motif-look, btw.
And no, I am not using PyGTK, PyQT, wxPython, or some other toolkit. It's standard Tkinter/ttk or bust, since that's what I've spent the last few weeks learning and writing wrapper functions for. The autocomplete combobox I worked up is a cute little contraption. Still slightly buggy, but that's a topic for another SO question...
PS, The Tkinter cookbook on Effbot is down. Looks like a server crash. Did anyone happen to mirror that site by chance?
Solved! Had to tweak the order of the calls a little bit:
#// Done!
self.withdraw()
self.grid()
self.transient(self._parent)
self.grab_set()
self.form.initial_focus()
center(self)
self.deiconify()
self._parent._parent.wait_window(self)
But the real fix was in the center() function I lifted from this answer, by changing the calls to winfo_width()/winfo_height() to winfo_reqwidth()/winfo_reqheight() and adding a call to update() as the first call in center(). Now I get my dialog windows to pop up in the center without seeing them move, focus is applied correctly, and it works on both Windows and remote X11. Maybe one day, I'll find out how well it works on Mac OS X/Aqua.

Activate window

i have a QMainWindow. It has this parameters:
this->setWindowFlags(Qt::Tool);
this->setFocusPolicy(Qt::StrongFocus);
this->setAttribute(Qt::WA_QuitOnClose,true);
After showEvent calles my window is shown but unactivated.
I tried to overload show function:
...
QMainWindow::showEvent(event);
this->activateWindow();
...
But it doesn't help me.
EDIT:
When i commented line
this->setWindowFlags(Qt::Tool);
everything worked fine, but i need in tool-flag.
Any ideas?
EDIT:
OS: Linux
Programming language: c++
Qt version: 4.5.1
The Windows Manager Decides
Before I start: As pointed out by elcuco and Javier, focus policy and other aspects of the windows layout (e.g. the title bar) belongs to a substantial extend to the respective windows manager, and Qt might have limited control. To see this, just look at a user interface that has a "focus follows mouse" policy. In these cases, the windows manager might ignore Qt's focus request. For this reasons, the Qt documentation calls many of the respective flags "hints". Consequently, some of the suggested solutions might or might not work for you.
QApplication::setActiveWindow()
This not withstanding, e.tadeu's solution to use QApplication::setActiveWindow() works for me for both Windows and Ubuntu with Gnome. I tested it with the following code. Apologies that it is Python using PyQt (I use questions like these to learn a bit about PyQt). It should be fairly easy for you to read it and translate it into C++.
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self)
# main window
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Test')
# text editor
self.textEdit = QtGui.QTextEdit()
self.setCentralWidget(self.textEdit)
def closeEvent(self, event):
QtGui.QApplication.instance().quit()
#main
app = QtGui.QApplication(sys.argv)
testWindow = MainWindow()
testWindow.setWindowFlags(QtCore.Qt.Tool)
testWindow.show()
app.setActiveWindow(testWindow)
app.exec_()
Note that you have to add some handling of the close event of the testWindow, because the app does not exit automatically if you close a Qt::Tool window.
The grabKeyboard() Hack
If this does not work for you, the following hack might. I assume that you have a window in your application that is active. You can then use grabKeyboard() to redirect the input. The Qt::Tool window doesn't get the focus, but receives the input. The following main code demonstrates it (the other code remains unchanged).
#main
app = QtGui.QApplication(sys.argv)
testWindow = MainWindow()
testWindow.setWindowFlags(QtCore.Qt.Tool)
testWindow2 = MainWindow() # second window which is active
testWindow2.show()
testWindow.show()
testWindow.textEdit.grabKeyboard()
app.exec_()
Basically, while the window testWindow2 is the active one, all text entered shows up in testWindow.textEdit. It is not nice, I know...
Creating Your Own Window
You gain the most flexibility (and create the most work for yourself) by rolling out your own window layout. The idea is described in the following FAQ.
Other "Solutions"
You could directly call the respective window manager's API function to get the desired result (clearly against the very reason for using Qt in the first place). You could also hack the Qt source code. For example, on Windows, Qt uses the ShowWindow() function with a SW_SHOWNOACTIVATE flag, to show a window with style WS_EX_TOOLWINDOW if you set the Qt::Tool flag. You could easily replace the SW_SHOWNOACTIVATE with whatever you want. Linux should be the same. Clearly also not recommended.
Try to use QApplication::setActiveWindow()
The original apple Human Interface Guidelines(*) said that toolbox windows were "always on top but never activated". It also advises against using text boxes on them, precisely because the lack of activation-state feedback.
Check how other 'toolbox heavy' apps behave. I faintly recall that at least GIMP and InkScape seem very different in this aspect.
As elcuco said, the window manager can do whatever it wants with Qt's flags. Also, it sure would be different under KDE, Gnome, fluxbox, whatever.
(*):great document! somewhat outdated; but tool windows were already used and considered
Which operating system? Which Qt4?
On Linux you are doomed, the window manager can ignore what you are telling him. Keep it under consideration.
Does the same thing happen if you just make it a regular QWidget instead of a QMainWindow?
Also, perhaps you might be better trying to achieve whatever effect you need Qt::Tool for by other means if that's possible?
Regarding Qt::Tool WindowFlags To quote Qt documentation
Indicates that the widget is a tool
window. A tool window is often a small
window with a smaller than usual title
bar and decoration, typically used for
collections of tool buttons. It there
is a parent, the tool window will
always be kept on top of it. If there
isn't a parent, you may consider using
Qt::WindowStaysOnTopHint as well. If
the window system supports it, a tool
window can be decorated with a
somewhat lighter frame. It can also be
combined with Qt::FramelessWindowHint
It seems the flags is a problem and using Qt::WindowStaysOnTopHint should solve your problem.
Just call show() after setWindowFlags().