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

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.

Related

Dialog forms flash quickly but aren't drawn properly after that

I have some legacy code made with Borland C++ Builder 6 that I have to port from Windows XP embedded to Windows 10 IoT.
It all seems to run properly, except dialog forms which aren't drawn properly. When I call ShowModal on the forms to show them, they flash quickly and then seems to disappear. However they are not gone, instead they are just not drawn and the form behind the dialog is seen. The dialog form buttons can still be pressed (if we know where they are).
I have tried all possible redrawing, refresh, repaint and update functions I could find, but nothing seems to work.
To make matters worse, dialogs containing actual control elements (like one containing a set of TSpeedButton elements) the controls are redrawn when moving the mouse over them, but anything else are not redrawn.
I have tried to search for similar issues but can't find anything for this.
Could this be solved? Or do we have to update to a newer Embarcadero version of the IDE to be able to solve it (which is a lot or work and not really something we can do at the moment)?
The problem was the use of a Billenium Software (now defunct) package for "fancy" transitions.
The transition in question "zooms" a dialog open, and it just doesn't seem to work. Disabling this transition will solve the problem of the disappearing dialogs.
I still don't know if it's a problem with the components Windows 10 compatibility, or with its 64-bit system compatibility.

Does the Win32 ShowWindow api behave differently on Windows 7 SP1 in some special case?

I found a bug yesterday in one of my Windows applications, which is built in a high level framework, which in the end, calls Windows APIs like CreateWindow, and ShowWindow, in order to display its user interface.
One one machine so far, and only one, which happened to be a customer machine, I observed the following behaviour:
For only one window in my entire application, when I first call ShowWindow(Handle,SW_SHOW ) for this window, the size which it previously had received by SetWindowPos is overridden.
Reading the MSDN Win32 API documentation, on ShowWindow(Handle,SW_SHOW) I can not see any reference it it moving the window bounds. I can work around this surprising result by having my window-show routine get the bounds before it calls the Win32 ShowWindow routine.
My question is, has anyone ever seen behaviour like this? I think it must be one of the following:
An obscure bug in Windows 7 Service Pack 1 that does not reproduce on all systems, and only reproduces perhaps for a particular version of a particular video card driver. (This affected system has dual AMD/ATI FireGL video cards)
An obscure problem caused by a side effect of some other software running on the system, which may be hooking window handles, installing trampolined code hooks somewhere (perhaps even inside my own process, thanks to some DLL or something that I am not aware of).
Something my 4 million line application is doing to me, through some weird code somewhere I have not yet identified.
I am hitting an application compatibility shim within the Win32 API layer.
If anyone who has worked in C++, C, or Delphi, or any other language, has ever seen anything like this and can think of a reason why ShowWindow would have this amazing and unexpected side effect, of moving the bounds of the window, back to a certain original position, in my case, x=175, y=175, width=320, height=240, which appears to be have been the window bounds right after the initial CreateWindow call, I'd like to know what it is.
Here is a sequence of events:
Application starts up, and creates a few top level windows parented to the desktop.
The first window created is the main application window and the second is a tool window, both have full window grabber bars and are conventional top level Win32 windows, Forms which are sizeable, draggable, and parented to the desktop.
The second window's position is loaded from disk, and the form is shown.
During the form show process, its bounds are set so that the window is at some x and y top/left position, and some height/width is given.
If I query the Win32 window handle immediately before I call ShowWindow, its bounds are where I expect.
If I query the Win32 window handle immediately after I call ShowWindow, its bounds have been reset.
According to MSDN help SW_SHOW means Activates the window and displays it in its current size and position.
This is indeed what occurs on over 100 client PCs I have observed. Only on a single customer-owned Windows 7 PC is this behaviour different.
This affected system has dual AMD/ATI FireGL video cards
I ain't sure about FireGL, but for consumers videocards, made upon the same chip lineage, video-drivers exactly have add-on to reposition windows as they think is easier for operator.
it is called HydraVision Package for Catalyst Software Suite

What would be the equivalent to SetForegroundWindow with X11?

I'm part of the SFML Team and we're currently looking into a feature to "request" window focus. The goal is to get very similar behavior across Windows, OS X and Linux.
For Windows one gets the rather simple SetForegroundWindow function via the WinAPI, which has a few condition as to how the window actually gets focus. The most important part to notice here is, that it only gets focus if it's from the same foreground process.
On OS X it's possible to get the focus for the active app only and otherwise let the icon bounce, i.e. notification.
Here comes the problem now, we'd like to get the same behavior on Linux as well, meaning the window should get focus if the window belongs to the active/foreground process and otherwise it should generate a notification. What would be the closest thing to that with X11?
There are already a few suggestions on the issue tracker of SFML, but none of them are actually implementing this behavior.
"User Story"
I guess developers can think of different things when being confronted with different technical names, as such here's the issue from a user perspective.
There are mainly two situations in which requesting focus is needed:
Sometimes when starting an application that uses a console window in the background, it can happen that the console window gets the focus instead of the actual GUI window. When this happens it's rather annoying for the user having to click on the window first. Since the console window and the GUI window are from the same application there's no harm done in switching the focus to the GUI window.
When one is writing an application that supports multiple windows, there might be situations where the application should decide which window gets the focus and again since the window belong to the same application there's no harm done in switching the focus from one GUI window to the other GUI window.
Further more if a different application has the focus/is being used then it's not okay to steal the focus and as such we just want to get the user's attention. For Windows that might be a blinking taskbar or for OS X that might be a jumping icon.
The current implementation seems to work fine on OS X and Windows, butwe're unsure about the X11 implementation. Thus the question is: How would one go about switching the window focus if the currently focussed window has been created by the same application that makes the focus request and otherwise create some kind of notification. For the notification we're/I'm not even sure if there's some generic way of doing it with X11.
In X11, "focus" means "the keyboard focus", that is, the window that gets the keyboard input. The window that has the focus is not necessarily in the foreground. This depends on your window manager focus policy. Most can be configured to have "click-to-focus" or "point-to-focus" policy. If you are interested in the keyboard focus, use XSetInputFocus. If you want to bring your window to the foreground, use XRaiseWindow.
It is OK to call RaiseWindow and XSetInputFocus once, when the application starts. It is also OK to bring a window to the foreground/set focus as a response to a user interaction with that or some other window of the same application. But it's not OK to do so as a response to some background event (time passed, file downloaded etc).
The standard X11 method of drawing attention to a window is setting the urgency hint. This will normally flash or bounce the icon, depending on your window manager. Do not forget to unset the hint when the user finally interacts with the window.
I think all of this has been discussed in the thread you have linked. I'm not quite sure which concerns are still left unanswered. Nothing can implement the exact same behaviour as with the other windowing systems, simply because X11 is not those windowing systems, and it's totally OK. X11, Mac OS X and Windows all behave differently and the users know and expect that. It would annoy me to no end if some application on X11 decided to behave exactly like it does on Windows, instead of toeing the X11 party line.

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

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

Event Handler for Minimize and Maximize Window

I am developing an application for PocketPC. When the application starts the custom function SetScreenOrientation(270) is called which rotates the screen. When the application closes the function SetScreenOrientation(0) is called which restores the screen orientation.
This way the screen orientation isn't restored if the user minimizes the application and this is not acceptable.
Does anyone know where (in which event handlers) should SetScreenOrientation(int angle) be called to set the screen orientation on application start, restore orientation on minimize, set the orientation on maximize and restore the orientation on close?
Actually I don't know which event handler handles the Minimize and Maximize event.
The correct message is WM_SIZE, but Daemin's answer points to the wrong WM_SIZE help topic. Check the wParam. Be careful as your window may be maximized but hidden.
Going from my Windows CE experience you should handle either the WM_SIZE or WM_WINDOWPOSCHANGED messages. If you're working on PocketPC I would suggest you take a look at the WM_WINDOWPOSCHANGED message first because I'm not sure the WM_SIZE has the right parameters that you need.
From the WM_WINDOWPOSCHANGED message's WINDOWPOS structure take a look at the flags member, specifically SWP_SHOWWINDOW and SWP_HIDEWINDOW.
The specific version of the messages that you need to look at vary with what operating system you're using. The Pocket PC OS is built on Windows CE 3.0 (and lower), while Windows Mobile is now built on Windows CE 5.0 (even Windows Mobile 6), but was also built on Windows CE 4. (Source)
So just look under the relevant section in MSDN for the OS that you're writing for.
I don't know what these are called in the C++ world, but in .NET Compact Framework your application form's Resize event would be called when you minimize/maximize a window, and then in the event code you would check the WindowState property of the form to see if its minimized or mazimized.
Altering the state of your PDA from within your application is risky (although there are lots of good reasons to do it), because if your app crashes it will leave the PDA in whatever state it was in. I've done a lot of kiosk-type (full-screen) apps in Windows Mobile, and one of the tricks to doing this effectively is to hide the WM title bar (the top row with the Windows start button) to keep it from flashing up for a split second every time you open a new form. If the app crashes, the windows bar remains invisible until you reset the device, which isn't good. At least with screen rotation the user can restore it manually.
It really depends on the platform, but I'd go with WM_WINDOWPOSCHANGED or the OnShow. It's not wm_size.. That one is not always thrown on all platforms. Casio's don't throw the size event when you'd expect them to. TDS and Symbol's do.
Even though the MSDN is a great sourse for info, remember not all OS's are created equal. In the PPC world the hardware provider gets to create their own OS and sometimes the miss things, or purposfully ignore things.
I've got a platform here (name withheld to protect... well me) that has left and right buttons.. When you press them, you'd expect to be able to catch VK_LEFT, VK_RIGHT.. You'd be wrong. You actually get ';' or ':'. How's that for a kick in the pants.