refactor: expose raise_window to user by reusing existing internal code#31176
refactor: expose raise_window to user by reusing existing internal code#31176alberti42 wants to merge 2 commits intomatplotlib:mainfrom
Conversation
|
|
||
| def raise_window(self): | ||
| # docstring inherited | ||
| self.window.activateWindow() |
There was a problem hiding this comment.
What we haven't specified so far is whether the window has focus. - I think for mpl.rcParams["figure.raise_window"] that should be the case. And that's the reason this line exists. To be checked with the other backends.
But even it that holds, it's not clear whether the standalone raise_window() calls will preserve that behavior. It could be that the previous context of a show() ensured that for some backends, but does not do it generally when called at an arbitrary time.
|
Great point, and worth discussing before we go further. Our primary motivation for That said, you are right that the current Our proposal would be: raise_window(*, with_focus=True)With macOS investigationI ran a first investigation on the macOS backend using the following script: BACKEND="macosx"
BLOCK=False
FIGURE_RAISE_WINDOW=True
import subprocess
import matplotlib as mpl
mpl.rcParams["figure.raise_window"]=FIGURE_RAISE_WINDOW
mpl.use(BACKEND)
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3])
manager = fig.canvas.manager
# Register focus callbacks so we can observe focus changes
manager.mpl_connect('focus_in_event', lambda: print(">>> matplotlib window got focus"))
manager.mpl_connect('focus_out_event', lambda: print(">>> matplotlib window lost focus"))
def frontmost_app():
result = subprocess.run(
['osascript', '-e',
'tell application "System Events" to get name of first application process whose frontmost is true'],
capture_output=True, text=True
)
return result.stdout.strip()
plt.show(block=BLOCK)
# --- After clicking back into the terminal ---
print("Frontmost before:", frontmost_app())
manager.raise_window()
print("Frontmost after: ", frontmost_app())The four combinations of
ConclusionsOn macOS:
This means the macOS backend's |
An optional parameter is likely reasonable. Alternative would be a separate
The default value of Note: This is a good example why we are reseved on extending the backend. It's a lot of effort to make this right and consistent across all backends. |
|
Hi Tim!
Cool. Probably we do not have to decide it now; we can defer the decision of whether to use a parameter
I cross my fingers that it is possible to implement it cleanly on all backends, but I never worked with Tk/Gtk3. Do you have a list of preferences in which order I should investigate the other backends:
Yes, I totally see it, and I want to help you. |
0dd867a to
1cf388d
Compare
|
For a PoC I'd typically use Qt as it a very capable framework (but maybe I'm biased, because that's the one I'm most familiar with). Tk and Gtk3 and on the lower end of features and good to look at when needing to identify the minimal common feature set. Note that we do not support Qt4 anymore it's Qt5/6 now. Not all backends have to be implemented at once. The important point is that we have to make sure there are no surpises from the other backend that would conflict with our design. For a release, I'd say Qt, Tk and Mac should be covered as these are the morst common ones. But you can make separate PRs if that makes it easier. |
1cf388d to
2db8f82
Compare
PR summary
Why is this change necessary?
rcParams["figure.raise_window"]has been supported by all five GUI backends(GTK, Tk, Qt, Wx, macOS) since at least matplotlib 3.6, meaning the native
machinery to raise a figure window to the foreground is fully implemented
everywhere. However, it was only reachable as a side-effect of calling
show(), controlled by a global rcParam. There was no way for a user toprogrammatically raise a specific window on demand.
What problem does it solve?
Users who want to bring a figure window to the foreground at an arbitrary
point in their program — not just at
show()time — had no supported API todo so. A common workaround was to toggle
rcParams["figure.raise_window"]andcall
show(), which is indirect and may trigger unintended redraws.What is the reasoning for this implementation?
All the native raise calls already existed inside each backend's
show()method. This PR extracts them into a dedicated
raise_window()method on eachFigureManagersubclass, and makesshow()delegate to it when the rcParamis set. The base class
FigureManagerBasegets a no-opraise_window()(samepattern as
full_screen_toggle()andresize()), giving non-GUI backends asafe default.
No new native code was written. The change is a pure refactor of existing,
already-tested logic.
Usage example
Files changed
lib/matplotlib/backend_bases.pyraise_window()toFigureManagerBaselib/matplotlib/backends/_backend_gtk.pyraise_window();show()delegateslib/matplotlib/backends/_backend_tk.pyraise_window();show()delegateslib/matplotlib/backends/backend_qt.pyraise_window();show()delegateslib/matplotlib/backends/backend_wx.pyraise_window();show()delegateslib/matplotlib/backends/backend_macosx.pyraise_window()wraps existingself._raise()C extension;show()delegatesPR checklist