Skip to content

Win11 Hdf5TreeView: gui.hdf5.Hdf5Item: cache expensive qt.QApplication.style().standardIcon() lookups#4592

Merged
t20100 merged 3 commits into
silx-kit:mainfrom
tifuchs:main
May 4, 2026
Merged

Win11 Hdf5TreeView: gui.hdf5.Hdf5Item: cache expensive qt.QApplication.style().standardIcon() lookups#4592
t20100 merged 3 commits into
silx-kit:mainfrom
tifuchs:main

Conversation

@tifuchs
Copy link
Copy Markdown
Contributor

@tifuchs tifuchs commented Apr 30, 2026

Addresses #4485

PR summary

Fixes Hdf5TreeView slowness on Win 11 caused by slow qt.QApplication.style().standardIcon(icon) calls in gui.hdf5.Hdf5Item.

This PR just adds simple caching of the icons in a dict.

The scaling factor of the primary screen is added as key to the icon cache. This should account for multi-monitor setups with different DPI scaling. - Works on my two screens.

There is a related issue addressing silx icon caching, which causes Exceptions when leaving silx
#1771

But I haven't encountered this issue with the simple dict solution.

pytests fail on my machine with a probably unrelated issue:

tmp_path = WindowsPath('C:/Users/timof/AppData/Local/Temp/pytest-of-timof/pytest-0/testErrorInInsertFilenameAsync0')
caplog = <_pytest.logging.LogCaptureFixture object at 0x00000246CAEE6900>
qapp_utils = <silx.gui.utils.testutils.TestCaseQt testMethod=runTest>

    def testErrorInInsertFilenameAsync(tmp_path, caplog, qapp_utils):
        filename = str(tmp_path / "corrupt.h5")
        with open(filename, "wb") as h5file:
            h5file.write("8736553".encode())

        model = hdf5.Hdf5TreeModel()
        assert model.rowCount(qt.QModelIndex()) == 0
        model.insertFileAsync(filename)

        assert qt.silxGlobalThreadPool().waitForDone(2000)  # Needed for PySide6
>       waitForPendingOperations(qapp_utils, model)

test\test_hdf5.py:85:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

qapp_utils = <silx.gui.utils.testutils.TestCaseQt testMethod=runTest>
model = <silx.gui.hdf5.Hdf5TreeModel.Hdf5TreeModel(0x246c59908c0) at 0x00000246CB5F9E00>

    def waitForPendingOperations(qapp_utils: TestCaseQt, model):
        for _ in range(20):
            if not model.hasPendingOperations():
                break
            qapp_utils.qWait(200)
        else:
>           raise RuntimeError("Still waiting for a pending operation")
E           RuntimeError: Still waiting for a pending operation

AI Disclosure

AI tool Codex used for automated bug finding, benchmarks and code generation.

Copy link
Copy Markdown
Member

@t20100 t20100 left a comment

Choose a reason for hiding this comment

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

Thanks for looking into this and for proposing a fix!
This is unfortunate qt does not cache standard icons.

Just one comment: the cache key could be simplified.

Comment thread src/silx/gui/hdf5/Hdf5Item.py Outdated
Comment on lines +50 to +56
devicePixelRatio = None
app = qt.QApplication.instance()
if app is not None:
screen = app.primaryScreen()
if screen is not None:
devicePixelRatio = screen.devicePixelRatio()
key = style.objectName(), devicePixelRatio, icon
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.

devicePixelRatio shouldn't be needed in the cache key: the QIcon contains multiple-size pixmaps so there is no need to change it with devicePixelRatio.

Suggested change
devicePixelRatio = None
app = qt.QApplication.instance()
if app is not None:
screen = app.primaryScreen()
if screen is not None:
devicePixelRatio = screen.devicePixelRatio()
key = style.objectName(), devicePixelRatio, icon
key = style.objectName(), icon

BTW, if devicePixelRatio is needed, best to use QWindow.devicePixelRatio (see https://doc.qt.io/qt-6/qscreen.html#devicePixelRatio-prop)

Copy link
Copy Markdown
Contributor Author

@tifuchs tifuchs May 2, 2026

Choose a reason for hiding this comment

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

You are correct, I have confirmed that QIcon returns differently sized icons. However, depending on QT_SCALE_FACTOR, the size is scaled for some icons, (used QIcon.availableSizes() to check it):

QT_SCALE_FACTOR = 1 : SP_DirIcon: QSize(100, 100) ... QSize(1600, 1600)

QT_SCALE_FACTOR = 2 : SP_DirIcon : QSize(200, 200) ... QSize(3200, 3200)

But the behavior is not really consistent. For example SP_DialogOkButton
does not get scaled (16, 16) -> (128, 128) independent of QT_SCALE_FACTOR.

Thus, devicePixelRatio might still be required. Though, QIcon has a built-in scaling feature. So it might not make a big difference.
I think we could remove it. Let me know.

style.objectName() is something like windowsvista, win11, ...
Do we expect this to change during runtime? - Could also be removed.

@tifuchs
Copy link
Copy Markdown
Contributor Author

tifuchs commented May 2, 2026

I reverted it back to a minimal working version.

This version does not account for style changes. And also not for full scaling (High DPI) changes.

I think this would still be fine, if we accept that:

  1. We don't account for the user to change the visual style at runtime. I think that this is a good assumption.
  2. Even though the size of stored QIcon changes depending on QT_SCALE_FACTOR, QIcon does store multiple versions and will display an icon in any case (presumably with lower quality). This issue only exists on multi-screen setups.

https://doc.qt.io/qt-6/qicon.html#pixmap
The pixmap might be smaller than requested, but never larger, unless the device-pixel ratio of the returned pixmap is larger than 1.
So the returned pixmaps might be smaller if the QIcon was created on a low DPI screen and then the screen is changed to a high DPI screen.

Copy link
Copy Markdown
Member

@t20100 t20100 left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for the fix!

From what I tested on Linux with PySide6, icons behaves correctly when moving the window on different screens with different resolutions and changing screen display ratio.

@t20100 t20100 merged commit be6b903 into silx-kit:main May 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants