WxPython - Inaccurate modal windows behavior - python-2.7

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.

Related

Qt bug where model dialog moves behind main window

I have a bug that I am having a hard time solving. I have two windows.
Window A is the main window that contains a text box (in this simplified version). The user is able to edit the contents of this text box by clicking on it where Window B appears as a keypad. Besides the buttons for the numbers, there are 2 additional buttons, Enter and Done. If the user changes the value and presses Enter, then the value in the text box is immediately updated. Pressing Done will then exit the window.
However, if the user presses done without having first pressing enter, then a modal dialog will appear asking the user if they would like to save the value. Once a selection has been made, the model dialog and Window B will exit.
The bug occurs when the user enters a value and presses the Done button. When the modal dialog appears, Window B moves behind Window A. What should happen is that Window B stays ontop of Window A. Interestingly enough, when I step through the code, this bug never happens which is making it tricky to pinpoint.
Has anyone every ran into this issue before?
I am currently running Qt 5.9 on the latest version of Linux Mint (as of this writing)
Also, I have prepared a sample project that demonstrates this bug. Due to the larger complexity of it, I was unable to fit it into 1 file. If needed, I might be able to find some time this week to move everything in one file. However, I can post the Dropbox link if permitted.
Maybe the problem stems from a wrong setting of the parent child relationship of the [QDialog][1] classes.
The following passage might be crucial for your application, but it is hard to say, if you don't show a minimal-reproducible-example.
Note: The parent relationship of the dialog does not imply that the
dialog will always be stacked on top of the parent window. To ensure
that the dialog is always on top, make the dialog modal. This also
applies for child windows of the dialog itself. To ensure that child
windows of the dialog stay on top of the dialog, make the child
windows modal as well.

check for reparented window close events

I am trying to make a xlib traybar for X11 where it embeds the tray icons using XEMBED as described in the tray specs. However when I close the application with the tray icon it just removes it from the container window but the black container window rectangle and the entry in my code still exists.
In the XEMBED documentation it says
It is the responsibility of the embedder to keep track of all forwarded
accelerators and to remove them when the client window dies.
However my application does not get any events or indication when a embedded window dies.
I basically only receive a dock request event and nothing else afterwards. When a dock request event comes in I create a child window for my panel which contains the tray window and reparent it like this:
enum trayIconSize = 24; // dimensions of icon
icon.trayWindow = XCreateWindow(x.display, panel.window, 0, 0, ...);
icon.ownerHandle = event.data.l[2]; // window id of icon which wants to dock
XReparentWindow(x.display, icon.ownerHandle, icon.trayWindow, 0, 0);
XMoveResizeWindow(x.display, icon.ownerHandle, 0, 0, trayIconSize, trayIconSize);
Adding it to the panel works without any problems but I don't know how to check when to remove it again.
How do I make my application receive close events for those tray icons or how do I check if the reparented window still exists?
I have actually done this before myself: https://github.com/adamdruppe/taskbar it has hacks for my specific setup in the width thing, but most of it should be reasonably usable and the code might help guide you.
But what you want to do is ask for events on the icon window. It has been a while, so I'm kinda using my own code as a guide here, but when I got the dock request, I called XSelectInput(dd, id, EventMask.StructureNotifyMask);
StructureNotifyMask subscribes to events including MapNotify, DestroyNotify, you prolly see where this is going :)
Once you have selected the input on the icon window, you regular event loop can check for the DestroyNotify and UnmapNotify events (my code checks both, tbh, I'm not sure which one actually triggers when the icon is removed) and compare the .window member of the event to your icon's window ID. If it matches, go ahead and remove it from your list because it is gone now.
My taskbar does seem to have a bug if the application crashes as opposed to being closed normally, so I might still be missing something, but checking the event works in most cases.

Cant focus Firemonkey application when modal dialog open, unless modal dialog itself is clicked

I have an application in which users, upon logging in, are prompted with a modal dialog where they must choose the facility they wish to work out of. At this stage, the application looks like this:
The modal dialog is shown by calling this method:
bool __fastcall ShowFacChoiceForm()
{
TFacChoiceForm *Form = new TFacChoiceForm( Application );
bool Result = ( Form->ShowModal() == mrOk );
delete Form;
return Result;
}
In this case, TFacChoiceForm inherits from TForm so the ShowFacChoiceForm() function is calling the standard TForm.ShowModal method documented here.
The issue I am running into is that if my application loses focus, it cannot become the active window again unless the modal dialog itself is clicked. To better illustrate this, I will present the following scenario:
Lets say its Friday afternoon and I decide to goof off a bit and read some web comics. With my application open, I open up another window on top of it, like so:
Then, out of nowhere my boss comes in for a performance review, and I attempt to refocus my application by clicking somewhere on the main form. For example, at the position of this red X in the next image.
In the above image, I have clicked at the location of the red X. Now, both the form containing the web comic, and my application are inactive. Thus, my application does not come to the front of the screen.
However, if I am able to click somewhere on the modal dialog, like the red X in the following image...
...then my application comes to the front like one would expect.
To solve this, I have looked at using something like SetForegroundWindow from the Windows API, but I have not been able to find a way to trigger the event, since my main form does not fire events while I have a modal dialog open.
My question is, how can I make sure that if the user clicks anywhere on my application that it is brought to the front? Is there a property I can edit in my form to do this?
If you set modalresult to mrcancel in the ondeactivate of the modal dialog then the main form will get focus when its clicked. You can then check if the user is logged in the mousedown event of the main form and if not, show the modal dialog again.

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.

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