matplotlib / matplotlib Public
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
Set Dock icon in Qt5 and macosx backends on macOS #14930
base: main
Are you sure you want to change the base?
Conversation
I'm not sure about unit tests. Could somebody provide feedback? I don't really have a way to unit-test any of the code, aside from perhaps checking that the ctypes/Apple api's say that I did, in fact, successfully change the icon. It sort of seems like circular reasoning: "Do the API's say they worked?" At any rate, the icon is set for every plot on macOS with Qt5, so in a sense we are piggy-backing on those unit tests too. Thoughts? |
I didn't see a reason to reset the icon, although that is also possible. Thoughts? |
My knee-jerk reaction as that this is too much complexity for the problem it is solving. From the linked bug report it suggests that the issue is that the at-conversion of svg -> QIcon does not provide a list of available sizes and then there is a bug in the OSX specific code that results in no icon being shown. Could we create a QIcon and set the missing size information? Failing that, rendering a hi-dpi png (which we may already have due to the other backends) and using that seems like a better option. |
I did play around with trying to get the QIcon to render/rasterize the SVG and failed. I can make another pass at it. I did check for a high-res png and came up empty. None of them were large enough on my display. I wouldn't be surprised if the default on other backends were to use SVG, that's what Qt5 uses. I would recommend generating a larger logo if we go this route. I'm not sure how much larger- I'd want to do some math for render sizes when using a 5k monitor. Maybe just follow Apple's recommended icon sizes. Neither are perfectly ideal if the system supports vector icons and we're providing a raster. From a performance standpoint, I'd say creating the logo from scratch is worst, rastering a SVG is better, loading a high-res png is even better, and loading a vector graphic is best. I can also put this in the macos backend code instead of ctypes. If we need the code for the macos backend anyways, then I could expose the Objective-C functionality as an internal Python API for other backends to use. Of course this also violates encapsulation of the backends, which may or may not be a big deal here, and might require a Python wrapper so that other platforms don't complain about the missing macos module. I can implement everything except for generating a new icon. I could probably figure that out, I'm just not sure of the process for doing that and adding a photo to the repo. What would you like me to do? |
This seems like a promising path to explore. generating (and caching) the png on the fly seems like a good option too. |
The icons are generated with tools/make_icons.py (#14941 may help a bit). You'll need to edit it to generate bigger sized icons. |
My last push went with this solution. If you like it, I'll add tests. |
Wrote tests, fixed a few things. This only addresses the matplotlib icon not showing up on macosx and Qt5Agg backgrounds on macOS. I haven’t tested or worked on other backends. |
@anntzer Okay, pushed fixes, could use another review. |
Tagged the PR, I'm sure they'll show up :p |
@anntzer Should we bump this somehow? Seems like nobody has noticed, but I'm not sure how long that usually takes here. |
I've been away, but wasn't popping this to the top of my queue because I wasn't clear that Matplotlib is an application, and hence am not clear why we would set the dock icon. |
@jklymak Honestly didn't consider it, since the Qt backends set the app icon on other platforms. Maybe the icon shouldn't be set in general? |
But to be fair, they set the window icon and not the taskbar icon for e.g. Windows for me. I assumed this was a bug, because the icon is the Windows default blank page icon, which just seems like it was forgotten. While macOS doesn't actually have a window icon... I don't know! |
Yeah, I'm not saying this PR is wrong, just not sure what it should do an a Mac, or if its worth the maintenance headache if this goes awry down the line |
pass | ||
|
||
|
||
if sys.platform == "darwin": |
Sure! Might be a bit before I can make a commit.
@jklymak Apple has these guidelines:
App icons are associated with windows, which mean they should represent what the window's main purpose is. I think this is a solid argument for changing the app icon. As a first pass, I think this PR is good. There is a slight complication where matplotlib may be embedded in another application. This is only relevant in some backends (IIRC the macosx backend cannot be embedded). In that case, the app icon should reflect the embedding application. That means we either need to detect embedding, or provide an API for using another app. This is probably a backend-specific consideration. I don't think we should avoid improvements because of maintenance concerns... If it's a valid thing to do on macOS, if it makes sense and improves UX, it should be done right in order to minimize maintenance. |
I could have sworn I posted a comment earlier today but can not find it in this thread now (but it looks like @joelfrederico is responding to it!).
How does in know which window's icon to set? From the code it looks like this applies at the application level? If it is one per-application, can we do it just once in the module?
I don't actually know objective-c, but from skimming Do tk, gtk, and wx have the same issue of lacking an icon? |
Yes, it’s one icon per process. So we could do this just once, maybe even at the backend import level. I haven’t gotten too far into it, but right now matplotlib only has a Dock icon when a plot is visible. I don’t know how that works or why it was designed that way...
I think this is only possible if you also embed python. But yes, now that I think about it, you could embed python and create matplotlib windows, which would then override the process’s icon. You might also be able to embed the widget... it depends on the matplotlib api, the current macosx backend may or may not have an api that makes this possible.
Ah I don’t know. I should probably make a more serious attempt at testing the various backends. Probably should also cache the original icon and reset when finished, whatever “finished” means. |
I suspect the doc-icon is generated when the QApplication (or the OSX backend equivalent) is instantiated. We are running a bit inverted, normally the first thing your process in a 'classic' desktop app is to instantiate the main "Application" instance (which in Qt you need to do before you can instantiate any of the widgets), however the user is usually running python first, and then at some point later when the user actually asks us for a window we fire up the qapp (and install the input hooks to make it look 'live' running inside your terminal) see https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/backends/backend_qt5.py#L98 and https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/backends/backend_qt5.py#L229 I suspect the easiest way to make sure we are not going to stomp on a user-set icon is to only try to set it if we create the qApp. |
This is true for Qt. It's also possible to tell if Q(Gui)Application has been instantiated, at least in the PyQt5 backend. This is not true for macOS's NSApplication while running in Python. There is an API for switching application "activation policy" to a GUI mode. The Dock icon is generated at that point. It's also possible to tell if it's already in an activated mode via an adjacent API. I think the right thing to do is first check if we're embedded. Is there already a QApplication, or are we in a GUI mode? If so, don't change anything. If not, then cache the icon (this is independent of whether or not the icon is showing), if there is an icon to access. Then set our icon. Then, there are various API's (Python C API and/or matplotlib code for macosx, Qt API for PyQt5) for running code once we've closed our windows. That code can access the cached icon (if any) and restore it. How does that solution sound? I'm not sure about other backends. I'd like to defer those until the backends I'm familiar with, macOS and PyQt5, are solid. |
FYI, from what I've seen, all of the backends I can test (Qt5Agg, macosx, GTK3Agg, TkAgg, WxAgg) currently have the same behavior (with the exception of WebAgg and nbAgg):
WebAgg does not show an icon at all. nbAgg is specifically for embedding in Jupyter notebook/lab, so showing an icon doesn't make sense. Digression: FWIW, it looks like Qt4 is unsupported and a variety of package managers have dropped support for it. Perhaps matplotlib should drop Qt4 as a backend? |
Okay, so that's actually not true. You can change the policy for macOS 10.6+ to Prohibited or Regular: lionheart/openradar-mirror#16080.
|
FYI: I’ve discovered that the run loop integration isn’t handled very well, and there is quite a bit of cruft in the macOS Objective-C code. I have a consistent method for fixing all of these things, and integrating icon manipulation. In order for that all to make sense, though, I think there are a few cleanup tasks I’d like to get done. If I can tighten up the existing system, integrating icon manipulation will be a much more logical extension of the rest of the code. This PR shouldn’t be merged yet, but I will rebase onto the changes that get accepted. How does that sound? |
PR Summary
This issue is similar to #14850/#14927. The issue is not isolated to the macos backend. It also happens in Qt5 (at least, possibly others).
Turns out Qt5 broke setWindowIcon. It seems like it is similar to this bug report: https://bugreports.qt.io/browse/QTBUG-55932. It fails silently, you request an icon change and nothing happens on macOS.
I have doubts this will be fixed without some pretty large Qt5 changes. Apple's API doesn't support .svg files. It does support a wide variety of other graphics, including vector graphics: pdf, and postscript, for instance. But Qt's API only supports .svg vector graphics.
We can work around this with png's, etc., but we already have a far superior .pdf logo. Apple users have come to expect high-quality vector graphics on their Retina displays. We should use the vector .pdf logo.
We can work around by using the ctypes lib to call Apple routines directly to avoid adding any more dependencies. It's done fairly lazily, so as not to be a performance hit on non-Apple platforms.
This appears to work for me. Could use comments and/or tests, although I have no idea how to write a unit test for an app icon in macOS.
PR Checklist
The text was updated successfully, but these errors were encountered: