-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add "bring window to front" functionality #596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hmm.. I've been wanting to get into development on matplotlib's GUI features, and this seems like a simple enough issue to start with (depending, of course, on the backend). Would it be a logical approach to add a couple methods to
and go from there? |
Yes -- that makes sense. Then there would need to be identical methods on the |
I was playing around with this a bit and it turns out it's a bit trickier than I expected on Linux. This is because I can call the appropriate methods on each backend (GTK+, Tk, etc) to bring a window to the front. But what happens next depends entirely on what the window manager feels like doing, and in my case it doesn't usually want to actually bring them up. I could fairly easily write some functions to talk directly to the window manager (at least as long as it follows the EWMH standard. But I'm not sure it's worth the effort. |
This bug is a big peeve for me. I'm gonna try my hand at implementing this the mac os x backend, however I don't know much about matplotlib structure for the moment so I'm open to suggestions about how to best approach this. Also assuming this was implemented shouldn't the show() command always bring the window to the front right away? |
@sparks See if you can stick to my suggestion of I threw my hands up over the annoyance of different window managers in Linux doing different things, but that shouldn't be as much an issue on OSX. I do still plan on coming back to this for Linux sometime though, as it's an annoyance for me too. And I agree that show() should bring the window to the front. Not sure how others feel about that. |
I would love to see somebody tackle this issue - it would be fantastic to have a cross platform, cross toolkit interface which "just works". |
@iguananaut, one does not always want show to bring windows to the front; it should be configurable. |
I'm an OS X user and I'm fairly familiar with objective-c, and with the OS X window manager it's fairly easy to give a window focus (if the window manager is able to give it focus). Window managers in general, though, may choose to ignore any message passed to them, depending on what they're currently doing. They also certainly do not behave uniformly. X11 may behave differently from GNOME and KDE. I'd be happy to look into this a little but. A lot of toolkits (GTK, TK, QT) have a function that pings the window manager and says, "Hey, I'm important, give me focus!" But as I've said, this functionality will almost certainly not work uniformly across window managers. And I have absolutely no clue and how Windows deals with anything. The question is, do we want to implement something that is potentially going to be temperamental in its behaviour? Honestly, I'd rather just rely on the user the click the window to give it focus. |
I think that if each of the gui toolkits has a reasonably similar set of functions for (1) raising a window within the application--that is, relative to other windows in that application, and (2) requesting that the window manager raise the window, and (3) requesting that the window manager give it focus, then providing a gui-neutral api in mpl would be helpful. But I would not want to see this get complicated, or impose possibly unwanted new behavior. |
Personally, I find it inconvenient when a window unexpectedly steals focus. Having said that, it seems that given the discussion in this issue, it would be the user's choice to initiate such behaviour. I'm happier with this than always stealing focus. Moreover, I did some Googling. It is the case that Windows never lets an application steal keyboard focus, but instead makes the taskbar flash. Not that there's really anything anybody can do about it, implementing the ideas discussed here would result in inconsistent behaviour across operating systems. |
@efiring The point of this issue was not that windows should automatically be brought to the front--just that matplotlib should expose an API for doing so. That way if the GUI window pops up under my terminal I can just type a quick command to bring it forward. Or, more importantly, for applications that use matplotlib it would be nice to be able to do this. I would say then that it's the responsibility of an application using matplotlib to provide users with an option to not allow windows to steal focus. All matplotlib needs to do is provide the appropriate API. I think this should still be done, but it turns out to be surprisingly tricky to get consistent across platforms. OSX should be fairly straightfoward, but X11 is tough. As @dmcdougall wrote all the relevant GUI toolkits have a way of doing this, but the actual resulting behavior is up to the whims of the window manager. What we can do for X11 is bypass the window manager altogether and communicate with the X server via EWMH protocol--a standard protocol for window managers supported by most window managers still in common use. If we use EWMH it should be possible to do this without confusing the active window manager, so long as it's EWMH-compliant. Otherwise fall back on doing the best the GUI toolkit can provide and not guarantee the results. I could give that a try, but it would be up to others to provide the OSX and Windows implementations. |
After a quick experiment, the EWMH approach works very well with metacity--the default WM for GNOME. Would have to experiment with more, but this is promising considering how widely used metacity is. Is matplotlib allowed to use ctypes? |
We have never used ctypes, as far as I know. In what way would you propose to use it? I would have expected that the way to implement this API in mpl would be via each toolkit, not by bypassing the toolkits and going straight to X or OSX or Windows. |
I would prefer to do it through the GUI toolkits, as a matter of preference if not policy. If it is a matter of policy then so be it--it makes things pretty easy. When I experimented with this on GTK and Tk I found frustratingly inconsistent results, hence the desire to bypass the GUI toolkits in this case. For example, I simply could not get it to work with Tkinter, where the relevant method is "lift". The problem is that Tk will go ahead and configure the window stacking order, but then the window manager (in this case metacity, but ymmv) happily ignores it. GTK on the other hand uses the appropriate EWMH messages which the WM does obey, and so it gives better results. I guess the fault lies with the toolkit in this case. Another open question would be: Do we want "bring_to_front()" to just raise the window in the stacking order, or should it also change the focus? I would tend to prefer, "No, don't change the focus." |
We need to keep focus separate and independent from position. |
@wigging That's a related but separate issue from this one, IMO. |
@embray and others as well: Is there an another issue about it? I cannot find it. I strongly believe that Is there any reason this is not done by default? I've been manually patching my matplotlib installations for a long time now... |
@krischer I think the problem is simply that no one has come up with a PR to implement what we need:
Such a PR would be universally welcomed, I believe. I suspect that even a PR that started with a single backend would be accepted, if there were reason to believe that most of the other backends could be supported in a similar way. |
I thought about implementing something like this in MEP27, as it separates out the I agree that a call to At the moment In the end I decided against doing this in MEP27 as I thought it simplest to work as just a refactor, and leave it for afterwards, but if people feel otherwise I would gladly add it as part of MEP27, less work for me later (i.e. touching all the backends). |
@krischer How are you manually patching |
@efiring, @OceanWolf I unfortunately don't know enough about matplotlib's backend system to make a qualified statement but I'll try to have a look in the next weeks if I find some time. @wigging Very simple and probably stupid. It works for the diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py
index 8e72a08..fcbce44 100644
--- a/lib/matplotlib/backends/backend_qt5.py
+++ b/lib/matplotlib/backends/backend_qt5.py
@@ -543,6 +543,7 @@ class FigureManagerQT(FigureManagerBase):
def show(self):
self.window.show()
+ self.window.raise_()
def destroy(self, *args):
# check for qApp first, as PySide deletes it in its atexit handler |
@krischer yes, I wanted to do exactly that :). Also check out my MEP (https://github.com/matplotlib/matplotlib/blob/master/doc/devel/MEP/MEP27.rst), #4143 to see what I referred to above. As one of the goals in MEP27 lies in standardising the API, I think this makes for the ideal time to do it 😄. |
Yes, please make this feature. and I still cant figure out how to bring the window to the top if using the matplotlib.use('MacOSX'). |
From my standpoint as a user, I think this is now the single biggest barrier to smooth interactive work with matplotlib. Having to go hunt for the figure window every single time one makes a plot is a terrible distraction. If this problem has to be solved piecemeal, one backend and platform at a time, in a messy fashion, so be it. Better that than no solution at all. Qt might be a good first target, with Tk a close second priority. |
My strong preference for default behavior would be for the window to be raised, so that it is fully visible, but not activated. I would rather let the user click it to activate it. The reason is that if it is raised and activated, it is too easy for it to intercept keystrokes that were intended for another window. |
Yes, this issue is not going to be reopened; the reference to this one in #6378 is adequate for keeping history. |
tkagg: raise each new window; partially addresses matplotlib#596
I found this answer for OSX fig.canvas.manager.window.raise_() |
The figure window(s) is/are probably just behind the spyder or editor window. The simplest solution when using a Mac is learning its trackpad gestures (see system preferences/trackpack). "App Exposé" is the one you need. This makes all windows visible, with one move of your fingers on the trackpad. Then click the figure window to raise it. In a python script/program, it works best when you use plt.show() only once at the very end. This ensures that the program is not blocked by plt.show() before finishing, as the program waits until you actually delete the figure. You may print a message just before plt.show() reminding the user and yourself where the plots are. Notice that all figure windows are raised to focus with a single click on the one that's visible. For me this is a convenient solution to the problem. |
@Olsthoorn I moved away from using plt.ion()
plt.close('all')
plt.figure(1)
plt.plot(x, y)
plt.grid() |
I know that this is an old (and long) thread, but I seems to me like this went all wrong (I hope I am just not understanding things correctly). I actually DO NOT WANT new figure windows to pop to the front. I liked it the way it was before the change (see here). So what is really needed is to make this behavior more user-configurable. Is it possible to control the front / background stacking behavior of a matplotlib figure window? |
It seems to be different for each backend, and also subject to the whims of the desktop environment. Which backend are you using, and in which environment? |
I have Ubuntu Linux with Gnome-3 desktop environment, and I use the TkAgg backaend. matplotlib 1.5 behaves as it should, matplotlib 2.0 does not. Below is a copy of what I posted on stackexchange: I have a python / matplotlib application that frequently updates a plot with new data coming in from a measurement instrument. The plot window should not change from background to foreground (or vice versa) with respect to other windows on my desktop when the plot is updated with new data. This worked as desired with Python 3 on a machine running Ubuntu 16.10 with matplotlib 1.5.2rc. However, on a different machine with Ubuntu 17.04 and matplotlib 2.0.0, the figure window pops to the front every time the plot is updated with new data. How can I control the window foreground/background behavior and keep the window focus when updating the plot with new data? Here's a code example illustrating my plotting routine:
|
Apart from the change suggested in #8692, which won't help you any time soon, I think you can improve your situation by changing your plotting strategy to avoid using |
That animation example does the trick of keeping the window in the background (or in the foreground, wherever I put it) even when the data is updated. I could adapt my code example above to use the animation module. However, I believe the approach in the example will not work in my real-life application, because I cannot leave it to the animation module to ask for new data to update the plot. My application needs direct control to talk to the measurement instrument, and then update the plot as needed. Is there a way to do this? |
Yes, I think it could be done with the animation module, but figuring out how--via subclassing Animation--might take a bit of time. There is probably a simpler way, but it is hard to specify it without seeing exactly what you are trying to do.
I think that is about all you will need; no clear, no pause; the redrawing should occur each time you update the data. |
The animation classes take an object, usually a timer, that emits when to
advance the frame. So, it is possible to replace that timer with your own
class that can do the job. I don't go into details about this particular
use-case, but the animation module is documented pretty thoroughly in my
"Interactive Applications using Matplotlib" book.
…On Thu, Jun 1, 2017 at 4:15 AM, Eric Firing ***@***.***> wrote:
Yes, I think it could be done with the animation module, but figuring out
how--via subclassing Animation--might take a bit of time. There is probably
a simpler way, but it is hard to specify it without seeing exactly what you
are trying to do.
Suggestions:
1. Put the ion() call at the top, before creating the figure, or run
inside of ipython so that you can take advantage of its interactive magic
(with no need for the ion call).
2. General good practice: use the object-oriented API, reserving
pyplot for a minimum of calls like plt.ion() and plt.subplots().
3. Don't clear the figure unless you need to; if you are making an
updating line plot, for example, just use the data updating strategy
illustrated in the animation examples.
I think that is about all you will need; no clear, no pause; the redrawing
should occur each time you update the data.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#596 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AARy-IBDpztIc_PRayBoGPZm2OyvN-U5ks5r_nMRgaJpZM4AIot6>
.
|
Ok, I had a look at this. At first, I thought I'd try your suggestions on how to get this working without the animation module. I sort of have this working. However, it is not possible to resize or move the plot window on the screen. I guess there's some event loop that would need to be called, but I don't know how to fix this. Any suggestions? Then I thought again about the animation module. I believe I could use this for my purposes if it would just update the plot with the current data from my measurement instrument stored in a buffer. However, I'd need to make the plot window to be "non blocking". In my existing code I use the pyplot.ion() function to get an interactive figure that does not block the code following the pyplot.show(). This is required to execute the code for instrument control and data acquisition. All examples I found for the animation module use a blocking window without the pyplot.ion() function, and I was not able to make the animation work in a non-blocking (interactive) way. Any suggestions? |
@WeatherGod: thanks for this suggestion. It would be cool if I could read up on this without buying a book. Any suggestions? |
Ok, I got it working without the animation module. Window resizing and similar actions are taken care of by calling fig.canvas.flush_events() . Still, it would be nice if the animation module could be used with non-blocking plots. Is this possible? How? |
I'm facing the same issue as @mbrennwa Stack Overflow: Make interactive matplotlib window not pop to front on each update (Windows 7) Honestly, there is nothing in the semantics of the word pause which implies that something should be brought to the front. This may be different with the word show, but pause definitely should not be doing this. This is a buggy behavior. |
I fully agree with danielfaust. Here's how I made things work in my application by avoiding the call to the buggy pause(...) command: |
@mbrennwa
def get_data():
while True:
yield get_some_hardware_data()
anim = FuncAnimation(fig, update_func, get_data(), interval=interval) Could @mbrennwa and @danielfaust take a look at #4779 which is a start of thoroughly documenting how GUI event loop integration works in the context of Matplotlib? |
@tacaswell I migrated my code over to to |
If you have vary computation time on the data retrieval I would create a thread for that work and have it drop data into a queue, something like import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import threading
from queue import Queue, Empty
import time
import numpy as np
q = Queue()
def data_getting_worker():
while True:
time.sleep(np.random.rand() * 5)
q.put(np.random.randn(15))
print('put some data')
th_gettr = threading.Thread(target=data_getting_worker)
data = np.random.randn(15)
def data_reciver_gen():
global data
while True:
try:
# pull data off the queue until we get to the most recent one
data = q.get(block=False)
print('got some data')
except Empty:
yield data
fig, ax = plt.subplots()
ln, = ax.plot(data)
def updater(d_in):
print('updating line')
ln.set_ydata(d_in)
return [ln]
th_gettr.start()
anim = FuncAnimation(fig, updater, frames=data_reciver_gen(), interval=1000) Which so long as the processing in |
@tacaswell: running the data acquisition (and a lot more) in FuncAnimation (or any other GUI for that matter) would be very awkward in my application. Plotting is just a small side job, and it may not even be possible on some machines that are running without a display system. Wrapping the whole code in a FuncAnimation is therefore certainly not the way to go in my case. I will therefore stick to my current solution, and I might improve the handling of the GUI/plotting event loop a bit following this example: https://stackoverflow.com/questions/45729092/make-interactive-matplotlib-window-not-pop-to-front-on-each-update-windows-7/45734500#45734500 |
It would be nice to have a method on the figure object that would bring its GUI window to the top of stack -- if this is possible -- in a backend-neutral way.
(Suggested by Marshall Perrin on the PyInt mailing list)
The text was updated successfully, but these errors were encountered: