Skip to content

Set Dock icon on the macosx backend#14930

Merged
dstansby merged 2 commits into
matplotlib:mainfrom
joelfrederico:macos-common-icon
Dec 11, 2021
Merged

Set Dock icon on the macosx backend#14930
dstansby merged 2 commits into
matplotlib:mainfrom
joelfrederico:macos-common-icon

Conversation

@joelfrederico

@joelfrederico joelfrederico commented Jul 30, 2019

Copy link
Copy Markdown
Contributor

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

  • Has Pytest style unit tests
  • Code is Flake 8 compliant

@joelfrederico

Copy link
Copy Markdown
Contributor Author

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?

@joelfrederico

Copy link
Copy Markdown
Contributor Author

I didn't see a reason to reset the icon, although that is also possible. Thoughts?

@tacaswell

Copy link
Copy Markdown
Member

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.

@joelfrederico

joelfrederico commented Jul 31, 2019

Copy link
Copy Markdown
Contributor Author

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?

@tacaswell

Copy link
Copy Markdown
Member

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.

This seems like a promising path to explore.

generating (and caching) the png on the fly seems like a good option too.

@anntzer

anntzer commented Jul 31, 2019

Copy link
Copy Markdown
Contributor

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.

@joelfrederico

Copy link
Copy Markdown
Contributor Author

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.

This seems like a promising path to explore.

My last push went with this solution. If you like it, I'll add tests.

@joelfrederico joelfrederico changed the title Work around Qt5's bug/API issues setting Dock icon in macOS Set Dock icon in Qt5 and macosx backends on macOS Aug 1, 2019
@joelfrederico

joelfrederico commented Aug 1, 2019

Copy link
Copy Markdown
Contributor Author

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.

Comment thread lib/matplotlib/backends/backend_macos_common.py Outdated
Comment thread lib/matplotlib/tests/test_backend_macos_common.py Outdated
Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
@joelfrederico

Copy link
Copy Markdown
Contributor Author

@anntzer Okay, pushed fixes, could use another review.

Comment thread lib/matplotlib/backends/_macos_helpers.py Outdated
Comment thread lib/matplotlib/tests/test__macos_helpers.py Outdated
Comment thread src/_macosx.m Outdated
@joelfrederico

Copy link
Copy Markdown
Contributor Author

Okay @anntzer, another push another review?

@anntzer

anntzer commented Aug 2, 2019

Copy link
Copy Markdown
Contributor

looks fine to me, but now you need to find an actual mac user (ie not me) to test this :p

@joelfrederico

joelfrederico commented Aug 2, 2019

Copy link
Copy Markdown
Contributor Author

Any suggestions? I assume I don’t count

@anntzer

anntzer commented Aug 2, 2019

Copy link
Copy Markdown
Contributor

Tagged the PR, I'm sure they'll show up :p

@joelfrederico

Copy link
Copy Markdown
Contributor Author

@anntzer Should we bump this somehow? Seems like nobody has noticed, but I'm not sure how long that usually takes here.

@jklymak

jklymak commented Aug 7, 2019

Copy link
Copy Markdown
Member

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.

@joelfrederico

Copy link
Copy Markdown
Contributor Author

@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?

@joelfrederico

Copy link
Copy Markdown
Contributor Author

I'm sure it needs cleanup and style changes. I'd welcome any suggestions/tweaks! There are quite a few choices that could be done many ways and I don't know what would be preferred.

@greglucas greglucas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do get an icon showing up, which looks nice!

But, when interacting there is something else going on when I open new windows. I get a segfault when clicking on the subplot tool icon with this update, so something might not be working quite right between the manager and the new window that gets popped up with subplot tools.

Comment on lines +70 to +72
if 'com.adobe.pdf' in _macosx.FigureManager._get_image_types():
icon_path = str(cbook._get_data_path('images/matplotlib.pdf'))
_macosx.FigureManager.__init__(self, canvas, icon_path)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal preference would be to keep it as you had it before with an explicit _set_icon() function rather than changing the init signature. But, I'm not sure I have a huge preference, others may have stronger feelings around this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have strong feelings, I'll do whatever the consensus is. (Or just use your suggestion if I don't hear anything within a day or two.)

Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
@joelfrederico

Copy link
Copy Markdown
Contributor Author

I think the segfault you were experiencing was due to other code which has since been resolved. I rebased to a new point in main, could you test again?

Could we also modify the PR to not target Qt? I've moved Qt into another PR.

@greglucas greglucas changed the title Set Dock icon in Qt5 and macosx backends on macOS Set Dock icon on the macosx backend Nov 30, 2021
@greglucas

Copy link
Copy Markdown
Contributor

Yep, it seems to work well now without a segfault. Lets see if others have additional comments over the next few days about the architecture comments, but I think this is looking good and splitting it into separate PRs is also nice, thanks for doing that!

@joelfrederico joelfrederico marked this pull request as ready for review November 30, 2021 08:14
Comment thread src/_macosx.m Outdated
@joelfrederico

Copy link
Copy Markdown
Contributor Author

@greglucas I figured it's been long enough, so I went back to a set_icon method. I made it static because it doesn't reference any saved state.

Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
Comment thread src/_macosx.m Outdated
@joelfrederico

Copy link
Copy Markdown
Contributor Author

@greglucas Should be good now?

@greglucas greglucas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this looks good to me and works well with multiple windows and subplot params!

I thought I made an earlier comment, but now I can't seem to find it, so I must not have hit enter... I was wondering if you think we should put a try/except clause around the Python set_icon() call? Right now if I put a bad file-path in (lets say someone removed the MPL data path), I get a Runtime Exception and MPL stops. QT5agg continues on without issue and tkagg logs the exception and continues on. I think that we should perhaps adopt the tkagg philosophy here and log this and continue on...

try:
window.iconphoto(False, icon_img)
except Exception as exc:
# log the failure (due e.g. to Tk version), but carry on
_log.info('Could not load matplotlib icon: %s', exc)

@joelfrederico

Copy link
Copy Markdown
Contributor Author

Right now passing tests let us know that set_icon is working. If you want to do that we'd have to add a test for set_icon because it would no longer be covered.

@joelfrederico

Copy link
Copy Markdown
Contributor Author

@greglucas What do I need to do to wrap this up?

@greglucas

Copy link
Copy Markdown
Contributor

You've got my approval, but someone else will need to test it out before merging still, so just waiting on the second review which can take a bit of time since it is all volunteer basis. Thanks for your patience!

@joelfrederico

Copy link
Copy Markdown
Contributor Author

@tacaswell @anntzer Any chance you’d know somebody who we could ping to merge this?

@anntzer

anntzer commented Dec 11, 2021

Copy link
Copy Markdown
Contributor

Perhaps @dstansby or @jklymak can help on that?

@dstansby dstansby left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for me. Thanks a lot for this PR - after so many years using Matplotlib without a dock icon, it feels a bit surreal to finally see one!

@dstansby dstansby merged commit b0f7b8a into matplotlib:main Dec 11, 2021
@tacaswell

Copy link
Copy Markdown
Member

Thanks @joelfrederico for sticking with us and getting this done!

@joelfrederico joelfrederico deleted the macos-common-icon branch December 13, 2021 05:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants