From e357dbf6b340502e8dcded1265a8769481074d21 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Tue, 5 Oct 2021 14:44:07 +1000 Subject: [PATCH] use Qt search path instead of resource system Means URLs like :/icons/foo.jpg should become icons:foo.jpg This is part of the prep work for a PyQt6 update. PyQt6 has dropped pyrcc, so we can longer generate the icons_qrc.py file we did previously. Qt Designer expects us to use the resource system, so we continue to generate the icons.qrc file to make editing the UI files easier. But at runtime, we no longer use that file. --- qt/aqt/BUILD.bazel | 1 - qt/aqt/__init__.py | 5 +++ qt/aqt/browser/previewer.py | 2 +- qt/aqt/browser/sidebar/toolbar.py | 4 +-- qt/aqt/browser/sidebar/tree.py | 28 +++++++-------- qt/aqt/data/BUILD.bazel | 1 + qt/aqt/data/qt/BUILD.bazel | 27 +++++++++++++++ qt/aqt/{forms => data/qt}/build_qrc.py | 14 +++++--- qt/aqt/{forms => data/qt}/icons/BUILD.bazel | 8 +++-- qt/aqt/{forms => data/qt}/icons/anki.png | Bin .../{forms => data/qt}/icons/card-state.svg | 0 qt/aqt/{forms => data/qt}/icons/clock.svg | 0 .../{forms => data/qt}/icons/collection.svg | 0 qt/aqt/{forms => data/qt}/icons/deck.svg | 0 qt/aqt/{forms => data/qt}/icons/flag.svg | 0 qt/aqt/{forms => data/qt}/icons/heart.svg | 0 .../qt}/icons/magnifying_glass.svg | 0 .../{forms => data/qt}/icons/media-record.png | Bin qt/aqt/{forms => data/qt}/icons/notetype.svg | 0 qt/aqt/{forms => data/qt}/icons/select.svg | 0 qt/aqt/{forms => data/qt}/icons/tag.svg | 0 qt/aqt/flags.py | 2 +- qt/aqt/forms/BUILD.bazel | 32 ------------------ qt/aqt/forms/build_rcc.py | 12 ------- qt/aqt/forms/build_ui.py | 3 ++ qt/aqt/forms/icons.qrc | 2 +- qt/aqt/profiles.py | 2 +- qt/aqt/sound.py | 2 +- 28 files changed, 73 insertions(+), 72 deletions(-) create mode 100644 qt/aqt/data/qt/BUILD.bazel rename qt/aqt/{forms => data/qt}/build_qrc.py (52%) rename qt/aqt/{forms => data/qt}/icons/BUILD.bazel (80%) rename qt/aqt/{forms => data/qt}/icons/anki.png (100%) rename qt/aqt/{forms => data/qt}/icons/card-state.svg (100%) rename qt/aqt/{forms => data/qt}/icons/clock.svg (100%) rename qt/aqt/{forms => data/qt}/icons/collection.svg (100%) rename qt/aqt/{forms => data/qt}/icons/deck.svg (100%) rename qt/aqt/{forms => data/qt}/icons/flag.svg (100%) rename qt/aqt/{forms => data/qt}/icons/heart.svg (100%) rename qt/aqt/{forms => data/qt}/icons/magnifying_glass.svg (100%) rename qt/aqt/{forms => data/qt}/icons/media-record.png (100%) rename qt/aqt/{forms => data/qt}/icons/notetype.svg (100%) rename qt/aqt/{forms => data/qt}/icons/select.svg (100%) rename qt/aqt/{forms => data/qt}/icons/tag.svg (100%) delete mode 100644 qt/aqt/forms/build_rcc.py diff --git a/qt/aqt/BUILD.bazel b/qt/aqt/BUILD.bazel index bf9c8a20d..e4bac550a 100644 --- a/qt/aqt/BUILD.bazel +++ b/qt/aqt/BUILD.bazel @@ -32,7 +32,6 @@ _py_srcs = glob( _py_srcs_and_forms = _py_srcs + [ "//qt/aqt/forms:forms", - "//qt/aqt/forms:icons", ] aqt_core_data = [ diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index c74149823..566250522 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -587,6 +587,11 @@ def _run(argv: Optional[list[str]] = None, exec: bool = True) -> Optional[AnkiAp ) return None + # make image resources available + from aqt.utils import aqt_data_folder + + QDir.addSearchPath("icons", os.path.join(aqt_data_folder(), "qt", "icons")) + if pmLoadResult.firstTime: pm.setDefaultLang(lang[0]) diff --git a/qt/aqt/browser/previewer.py b/qt/aqt/browser/previewer.py index d3a4c882d..35314208a 100644 --- a/qt/aqt/browser/previewer.py +++ b/qt/aqt/browser/previewer.py @@ -53,7 +53,7 @@ class Previewer(QDialog): self._close_callback = on_close self.mw = mw icon = QIcon() - icon.addPixmap(QPixmap(":/icons/anki.png"), QIcon.Normal, QIcon.Off) + icon.addPixmap(QPixmap("icons:anki.png"), QIcon.Normal, QIcon.Off) disable_help_button(self) self.setWindowIcon(icon) diff --git a/qt/aqt/browser/sidebar/toolbar.py b/qt/aqt/browser/sidebar/toolbar.py index 0f9c53fc3..61a0b0b5e 100644 --- a/qt/aqt/browser/sidebar/toolbar.py +++ b/qt/aqt/browser/sidebar/toolbar.py @@ -18,8 +18,8 @@ class SidebarTool(Enum): class SidebarToolbar(QToolBar): _tools: tuple[tuple[SidebarTool, str, Callable[[], str]], ...] = ( - (SidebarTool.SEARCH, ":/icons/magnifying_glass.svg", tr.actions_search), - (SidebarTool.SELECT, ":/icons/select.svg", tr.actions_select), + (SidebarTool.SEARCH, "icons:magnifying_glass.svg", tr.actions_search), + (SidebarTool.SELECT, "icons:select.svg", tr.actions_select), ) def __init__(self, sidebar: aqt.browser.sidebar.SidebarTreeView) -> None: diff --git a/qt/aqt/browser/sidebar/tree.py b/qt/aqt/browser/sidebar/tree.py index 28da752b8..d3b1e9245 100644 --- a/qt/aqt/browser/sidebar/tree.py +++ b/qt/aqt/browser/sidebar/tree.py @@ -527,7 +527,7 @@ class SidebarTreeView(QTreeView): ########################### def _saved_searches_tree(self, root: SidebarItem) -> None: - icon = ":/icons/heart-outline.svg" + icon = "icons:heart-outline.svg" saved = self._get_saved_searches() root = self._section_root( @@ -551,7 +551,7 @@ class SidebarTreeView(QTreeView): ########################### def _today_tree(self, root: SidebarItem) -> None: - icon = ":/icons/clock-outline.svg" + icon = "icons:clock-outline.svg" root = self._section_root( root=root, name=tr.browsing_today(), @@ -621,8 +621,8 @@ class SidebarTreeView(QTreeView): ########################### def _card_state_tree(self, root: SidebarItem) -> None: - icon = ":/icons/circle.svg" - icon_outline = ":/icons/circle-outline.svg" + icon = "icons:circle.svg" + icon_outline = "icons:circle-outline.svg" root = self._section_root( root=root, @@ -670,8 +670,8 @@ class SidebarTreeView(QTreeView): ########################### def _flags_tree(self, root: SidebarItem) -> None: - icon = ":/icons/flag.svg" - icon_outline = ":/icons/flag-outline.svg" + icon = "icons:flag.svg" + icon_outline = "icons:flag-outline.svg" root = self._section_root( root=root, @@ -704,8 +704,8 @@ class SidebarTreeView(QTreeView): ########################### def _tag_tree(self, root: SidebarItem) -> None: - icon = ":/icons/tag-outline.svg" - icon_off = ":/icons/tag-off-outline.svg" + icon = "icons:tag-outline.svg" + icon_off = "icons:tag-off-outline.svg" def render( root: SidebarItem, nodes: Iterable[TagTreeNode], head: str = "" @@ -752,9 +752,9 @@ class SidebarTreeView(QTreeView): ########################### def _deck_tree(self, root: SidebarItem) -> None: - icon = ":/icons/book-outline.svg" - icon_current = ":/icons/book-clock-outline.svg" - icon_filtered = ":/icons/book-cog-outline.svg" + icon = "icons:book-outline.svg" + icon_current = "icons:book-clock-outline.svg" + icon_filtered = "icons:book-cog-outline.svg" def render( root: SidebarItem, nodes: Iterable[DeckTreeNode], head: str = "" @@ -807,9 +807,9 @@ class SidebarTreeView(QTreeView): ########################### def _notetype_tree(self, root: SidebarItem) -> None: - notetype_icon = ":/icons/newspaper.svg" - template_icon = ":/icons/iframe-braces-outline.svg" - field_icon = ":/icons/form-textbox.svg" + notetype_icon = "icons:newspaper.svg" + template_icon = "icons:iframe-braces-outline.svg" + field_icon = "icons:form-textbox.svg" root = self._section_root( root=root, diff --git a/qt/aqt/data/BUILD.bazel b/qt/aqt/data/BUILD.bazel index efd53c8ca..f8745bed2 100644 --- a/qt/aqt/data/BUILD.bazel +++ b/qt/aqt/data/BUILD.bazel @@ -2,6 +2,7 @@ filegroup( name = "data", srcs = [ "//qt/aqt/data/lib", + "//qt/aqt/data/qt", "//qt/aqt/data/web", ], visibility = ["//qt:__subpackages__"], diff --git a/qt/aqt/data/qt/BUILD.bazel b/qt/aqt/data/qt/BUILD.bazel new file mode 100644 index 000000000..a98b579a8 --- /dev/null +++ b/qt/aqt/data/qt/BUILD.bazel @@ -0,0 +1,27 @@ +load("@rules_python//python:defs.bzl", "py_binary") + +py_binary( + name = "build_qrc", + srcs = ["build_qrc.py"], + legacy_create_init = False, +) + +genrule( + name = "icons_qrc", + srcs = ["//qt/aqt/data/qt/icons"], + outs = ["icons.qrc"], + cmd = "$(location build_qrc) $(location icons.qrc) $(SRCS)", + tools = ["build_qrc"], + visibility = ["//qt/aqt:__pkg__"], +) + +filegroup( + name = "qt", + srcs = [ + "//qt/aqt/data/qt/icons", + # not needed at runtime, but including it here ensures + # it's up to date when a dev opens Qt Designer after a ./run + ":icons_qrc", + ], + visibility = ["//qt:__subpackages__"], +) diff --git a/qt/aqt/forms/build_qrc.py b/qt/aqt/data/qt/build_qrc.py similarity index 52% rename from qt/aqt/forms/build_qrc.py rename to qt/aqt/data/qt/build_qrc.py index 6d385b402..18afceae5 100644 --- a/qt/aqt/forms/build_qrc.py +++ b/qt/aqt/data/qt/build_qrc.py @@ -1,5 +1,8 @@ -import sys +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + import os +import sys qrc_file = os.path.abspath(sys.argv[1]) icons = sys.argv[2:] @@ -15,10 +18,13 @@ FILES indent = " " * 8 lines = [] for icon in icons: - alias = "" + base = os.path.basename(icon) path = os.path.relpath(icon, start=os.path.dirname(qrc_file)) - alias = "" if os.path.dirname(path) == "icons" else f' alias="icons/{os.path.basename(path)}"' - line = f"{indent}{path}" + if not path.startswith("icons/"): + path = f"../data/qt/icons/{base}" + else: + path = f"../../../bazel-bin/qt/aqt/data/qt/icons/{base}" + line = f'{indent}{path}' lines.append(line) with open(qrc_file, "w") as file: diff --git a/qt/aqt/forms/icons/BUILD.bazel b/qt/aqt/data/qt/icons/BUILD.bazel similarity index 80% rename from qt/aqt/forms/icons/BUILD.bazel rename to qt/aqt/data/qt/icons/BUILD.bazel index db492a4dc..f991a4220 100644 --- a/qt/aqt/forms/icons/BUILD.bazel +++ b/qt/aqt/data/qt/icons/BUILD.bazel @@ -1,4 +1,5 @@ -load("//ts:vendor.bzl", "copy_mdi_icons", "copy_bootstrap_icons") +load("//ts:vendor.bzl", "copy_mdi_icons") +load("@rules_python//python:defs.bzl", "py_binary") copy_mdi_icons( name = "mdi-icons", @@ -37,6 +38,9 @@ copy_mdi_icons( filegroup( name = "icons", - srcs = ["mdi-icons"] + glob(["*.svg", "*.png"]), + srcs = ["mdi-icons"] + glob([ + "*.svg", + "*.png", + ]), visibility = ["//visibility:public"], ) diff --git a/qt/aqt/forms/icons/anki.png b/qt/aqt/data/qt/icons/anki.png similarity index 100% rename from qt/aqt/forms/icons/anki.png rename to qt/aqt/data/qt/icons/anki.png diff --git a/qt/aqt/forms/icons/card-state.svg b/qt/aqt/data/qt/icons/card-state.svg similarity index 100% rename from qt/aqt/forms/icons/card-state.svg rename to qt/aqt/data/qt/icons/card-state.svg diff --git a/qt/aqt/forms/icons/clock.svg b/qt/aqt/data/qt/icons/clock.svg similarity index 100% rename from qt/aqt/forms/icons/clock.svg rename to qt/aqt/data/qt/icons/clock.svg diff --git a/qt/aqt/forms/icons/collection.svg b/qt/aqt/data/qt/icons/collection.svg similarity index 100% rename from qt/aqt/forms/icons/collection.svg rename to qt/aqt/data/qt/icons/collection.svg diff --git a/qt/aqt/forms/icons/deck.svg b/qt/aqt/data/qt/icons/deck.svg similarity index 100% rename from qt/aqt/forms/icons/deck.svg rename to qt/aqt/data/qt/icons/deck.svg diff --git a/qt/aqt/forms/icons/flag.svg b/qt/aqt/data/qt/icons/flag.svg similarity index 100% rename from qt/aqt/forms/icons/flag.svg rename to qt/aqt/data/qt/icons/flag.svg diff --git a/qt/aqt/forms/icons/heart.svg b/qt/aqt/data/qt/icons/heart.svg similarity index 100% rename from qt/aqt/forms/icons/heart.svg rename to qt/aqt/data/qt/icons/heart.svg diff --git a/qt/aqt/forms/icons/magnifying_glass.svg b/qt/aqt/data/qt/icons/magnifying_glass.svg similarity index 100% rename from qt/aqt/forms/icons/magnifying_glass.svg rename to qt/aqt/data/qt/icons/magnifying_glass.svg diff --git a/qt/aqt/forms/icons/media-record.png b/qt/aqt/data/qt/icons/media-record.png similarity index 100% rename from qt/aqt/forms/icons/media-record.png rename to qt/aqt/data/qt/icons/media-record.png diff --git a/qt/aqt/forms/icons/notetype.svg b/qt/aqt/data/qt/icons/notetype.svg similarity index 100% rename from qt/aqt/forms/icons/notetype.svg rename to qt/aqt/data/qt/icons/notetype.svg diff --git a/qt/aqt/forms/icons/select.svg b/qt/aqt/data/qt/icons/select.svg similarity index 100% rename from qt/aqt/forms/icons/select.svg rename to qt/aqt/data/qt/icons/select.svg diff --git a/qt/aqt/forms/icons/tag.svg b/qt/aqt/data/qt/icons/tag.svg similarity index 100% rename from qt/aqt/forms/icons/tag.svg rename to qt/aqt/data/qt/icons/tag.svg diff --git a/qt/aqt/flags.py b/qt/aqt/flags.py index e1e209aeb..64ee04524 100644 --- a/qt/aqt/flags.py +++ b/qt/aqt/flags.py @@ -56,7 +56,7 @@ class FlagManager: def _load_flags(self) -> None: labels = cast(dict[str, str], self.mw.col.get_config("flagLabels", {})) - icon = ColoredIcon(path=":/icons/flag.svg", color=colors.DISABLED) + icon = ColoredIcon(path="icons:flag.svg", color=colors.DISABLED) self._flags = [ Flag( diff --git a/qt/aqt/forms/BUILD.bazel b/qt/aqt/forms/BUILD.bazel index 0bdf3afbc..931df6037 100644 --- a/qt/aqt/forms/BUILD.bazel +++ b/qt/aqt/forms/BUILD.bazel @@ -15,35 +15,3 @@ py_binary( legacy_create_init = False, deps = ["@pyqt5//:pkg"], ) - -py_binary( - name = "build_qrc", - srcs = ["build_qrc.py"], - legacy_create_init = False, - deps = ["@pyqt5//:pkg"], -) - -py_binary( - name = "build_rcc", - srcs = ["build_rcc.py"], - legacy_create_init = False, - deps = ["@pyqt5//:pkg"], -) - -genrule( - name = "icons_qrc", - srcs = ["//qt/aqt/forms/icons"], - outs = ["icons.qrc"], - cmd = "$(location build_qrc) $(location icons.qrc) $(SRCS)", - tools = ["build_qrc"], - visibility = ["//qt/aqt:__pkg__"], -) - -genrule( - name = "icons", - srcs = ["icons_qrc", "//qt/aqt/forms/icons"], - outs = ["icons_rc.py"], - cmd = "$(location build_rcc) $(location icons_qrc) $(location icons_rc.py)", - tools = ["build_rcc"], - visibility = ["//qt/aqt:__pkg__"], -) diff --git a/qt/aqt/forms/build_rcc.py b/qt/aqt/forms/build_rcc.py deleted file mode 100644 index b2dd530a8..000000000 --- a/qt/aqt/forms/build_rcc.py +++ /dev/null @@ -1,12 +0,0 @@ -import sys -import os -from PyQt5.pyrcc_main import processResourceFile - -icons_qrc = sys.argv[1] -py_file = os.path.abspath(sys.argv[2]) - -# make paths relative for pyrcc -os.chdir(os.path.dirname(icons_qrc)) -icons_qrc = os.path.basename(icons_qrc) - -processResourceFile([icons_qrc], py_file, False) diff --git a/qt/aqt/forms/build_ui.py b/qt/aqt/forms/build_ui.py index a36857ff0..d45d4f8ac 100644 --- a/qt/aqt/forms/build_ui.py +++ b/qt/aqt/forms/build_ui.py @@ -29,6 +29,9 @@ for line in outdata.splitlines(): if substr in line: line = line + " # type: ignore" break + if line == "from . import icons_rc": + continue + line = line.replace(":/icons/", "icons:") outlines.append(line) with open(py_file, "w") as file: diff --git a/qt/aqt/forms/icons.qrc b/qt/aqt/forms/icons.qrc index e2b267b80..d17297082 120000 --- a/qt/aqt/forms/icons.qrc +++ b/qt/aqt/forms/icons.qrc @@ -1 +1 @@ -../../../bazel-bin/qt/aqt/forms/icons.qrc \ No newline at end of file +../../../bazel-bin/qt/aqt/data/qrc/icons.qrc \ No newline at end of file diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index 8154778ca..38042621e 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -190,7 +190,7 @@ class ProfileManager: app = QtWidgets.QApplication([]) icon = QtGui.QIcon() icon.addPixmap( - QtGui.QPixmap(":/icons/anki.png"), + QtGui.QPixmap("icons:anki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off, ) diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index f00568fc7..d0d088889 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -734,7 +734,7 @@ class RecordDialog(QDialog): def _setup_dialog(self) -> None: self.setWindowTitle("Anki") icon = QLabel() - icon.setPixmap(QPixmap(":/icons/media-record.png")) + icon.setPixmap(QPixmap("icons:media-record.png")) self.label = QLabel("...") hbox = QHBoxLayout() hbox.addWidget(icon)