Skip to content
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

feat: add QIcon backed by iconify #209

Merged
merged 16 commits into from
Oct 9, 2023
Merged

Conversation

tlambert03
Copy link
Member

@tlambert03 tlambert03 commented Sep 30, 2023

Iconify is a pretty great API unifying pretty much all of the major font-icon libraries into a single API from which you can request standardized SVGs. I wrapped the API in library pyconify that makes it easier to work with in python, and caches all svgs locally for faster/offline usage (after first using online once). This PR makes a simple QIcon wrapper:

from qtpy.QtCore import QSize
from qtpy.QtWidgets import QApplication, QPushButton

from superqt import QIconifyIcon

app = QApplication([])

btn = QPushButton()
# search https://icon-sets.iconify.design for available icon keys
btn.setIcon(QIconifyIcon("fa:smile-o", color="blue"))
btn.setIconSize(QSize(60, 60))
btn.show()

app.exec()

A downside of using this is that it's SVG based, which makes it a bit harder than font libraries to change colors simply using style sheets (like napari does for its themes... though even that may be possible). A big upside is that you no longer need to include the whole font-family file just to grab a couple icons, and you can pick and choose from all of the libraries without having to bring in a new plugin for each one. Also, for reasons beyond just color changing, SVGs are preferable in many cases to fonts (animations are possible, for example)

@tlambert03 tlambert03 requested a review from Czaki September 30, 2023 01:40
@codecov
Copy link

codecov bot commented Sep 30, 2023

Codecov Report

All modified lines are covered by tests ✅

Comparison is base (65a4a6e) 87.38% compared to head (3426f3d) 87.42%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #209      +/-   ##
==========================================
+ Coverage   87.38%   87.42%   +0.03%     
==========================================
  Files          45       46       +1     
  Lines        3346     3356      +10     
==========================================
+ Hits         2924     2934      +10     
  Misses        422      422              
Files Coverage Δ
src/superqt/__init__.py 91.30% <100.00%> (+0.39%) ⬆️
src/superqt/fonticon/__init__.py 88.88% <ø> (ø)
src/superqt/iconify/__init__.py 100.00% <100.00%> (ø)

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@Czaki Czaki left a comment

Choose a reason for hiding this comment

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

It looks ok. Few minor remarks.

Did you plan to ship pyconify to conda?|

Why not allow to cache icons svg between sessions?

src/superqt/iconify/__init__.py Outdated Show resolved Hide resolved
src/superqt/iconify/__init__.py Outdated Show resolved Hide resolved
tests/test_iconify.py Outdated Show resolved Hide resolved
src/superqt/iconify/__init__.py Outdated Show resolved Hide resolved
@tlambert03
Copy link
Member Author

Why not allow to cache icons svg between sessions?

They are cached between sessions:
https://github.com/pyapp-kit/pyconify#cache

The main difference between temp_svg and svg is that temp_svg returns a file path and svg returns bytes. But they both use the same persistent cache

yes I’ll put it on conda

@Czaki
Copy link
Contributor

Czaki commented Sep 30, 2023

Hm. Did you try to play with QIconEngine? Qt documentation suggest that it could be used to render svg without need to load it from drive.

@tlambert03
Copy link
Member Author

Did you try to play with QIconEngine? Qt documentation suggest that it could be used to render svg without need to load it from drive.

Yep, I did quite a bit. I originally started with a similar strategy to what i did in napari: https://github.com/napari/napari/blob/main/napari/_qt/qt_resources/_svg.py

However, after looking into more in this round, I'm less of a fan of that approach. If you look into the c++ code of the builtin QIconEngine, you'll find that it actually does quite a bit more than a naive approach might, it handles caching of pixmaps for various sizes, and better handles rendering svgs for different states (enabled/disabled, active/inactive). So, after a fair amount of experimentation, I decided that simply writing a file to disk and letting the builtin QIconEngine handle it is well worth the cost. It still all loads in about 10µs (after the original caching from the API), so it's hardly a performance limitation. And there's a large amount of code that no longer needs to be maintained. So that's why I went with this approach.

I will, however, update pyconify to be able to give you the path to the actual cached file in the pyconify cache (rather than this current approach, which costs one additional local file copy from the pyconify cache to the tempdir)

@tlambert03
Copy link
Member Author

tlambert03 commented Oct 8, 2023

@Czaki, will likely merge this soon. Would welcome your thoughts on QIconEngine, specifically, I'm curious what specifically you expect/hope would be gained by using it. with the current implementation, there is only one disk read (from the pyconify) cache (which is also significantly faster than the time to fetch from the pyconify API on the first retrieval). So I expect it will be both performant and take advantage of all of the extra goodies provided by the default QIconEngine (not to mention it's less code to maintain)

Copy link
Contributor

@Czaki Czaki left a comment

Choose a reason for hiding this comment

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

As far as I check the svg_path is improperly uses cache. I have created PR to fix this: pyapp-kit/pyconify#5

I have two small remarks (one typo, and one for test). But general image is good.

src/superqt/iconify/__init__.py Outdated Show resolved Hide resolved
tests/test_iconify.py Show resolved Hide resolved
@tlambert03
Copy link
Member Author

thanks @Czaki!

merged your PR, and bumped the dependency here.

@tlambert03 tlambert03 merged commit a5740f0 into pyapp-kit:main Oct 9, 2023
@tlambert03 tlambert03 deleted the iconify branch October 9, 2023 13:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Development

Successfully merging this pull request may close these issues.

2 participants