Stage browse loading for network drives and 1000-image folders#21
Merged
Conversation
Removes the synchronous GUI-thread file I/O around the (already staged)
thumbnail queue, which froze browsing on networked drives:
- Card click previously ran TWO full load_scan calls on the GUI thread
(channel previews + a second one just for the header metadata table).
New ChannelPreviewLoader reads the scan once on a pool worker and emits
meta_ready (plane names + header) then per-plane QImages; the panel
builds its slots, previews, and metadata table from those signals.
- Opening a viewer previously full-loaded the scan a third time just to
map the thumbnail channel to a plane index; now a header-only
read_scan_metadata call.
- _navigate ran index_folder_shallow synchronously; a cold network
folder froze the UI for the whole index. New FolderIndexLoader runs it
on a pool worker with token-guarded delivery; the path strip shows
"Indexing …" and the previous grid stays interactive meanwhile.
- _peek_subfolder content-sniffed every file up to two levels deep with
no count cap; a file budget (default 400) now bounds the I/O and a
counts_capped flag flows through FolderEntry so folder cards show
"N+" lower-bound counts.
- Card construction is timer-sliced (first batch synchronous, then
zero-delay batches of 120) so 1000-entry folders no longer freeze the
GUI building widgets; thumbnail scheduling defers entries whose cards
are not built yet, and filtered-out cards are now deleteLater'd on
navigation instead of lingering until GC.
- Thumbnail runnables enter the global pool at low priority so
ViewerLoader / ChannelPreviewLoader / folder indexing never queue
behind a screenful of slow network thumbnail reads.
Also fixes the py3.12 CI failure: conftest gated the per-test forked
isolation on hasplugin("forked"), but pytest-forked registers as
"pytest_forked" — the GUI-test process isolation was silently inactive
everywhere, which is exactly the recycled-wrapper AttributeError CI hit
(QGraphicsItemGroup wrapper reused inside QMenu.addAction). The check
now matches the real plugin name (kept in-process on macOS, where
fork() under AppKit is unsafe).
Co-Authored-By: Claude Fable 5 <[email protected]>
Enabling the forked marker (previous commit) surfaced that pytest-forked
(archived, last release 2023) no longer cooperates with modern pytest
teardown bookkeeping: every non-forked test following a forked one
errors with "previous item was not torn down properly", and the changed
import order broke the matplotlib backend test.
Remove the forked mechanism entirely (and the pytest-forked dependency)
and close the wrapper-recycling window in-process instead: the drain
fixture now garbage-collects after each GUI test, so parentless widgets
a test leaked are destroyed deterministically at the test boundary —
visible to Shiboken — rather than at a random GC point mid-way through
a later test, which is what recycled C++ addresses into stale wrappers
("'QGraphicsItemGroup' object has no attribute 'connect'" on CI py3.12).
The collect is gated to GUI-module tests to keep the suite fast.
Co-Authored-By: Claude Fable 5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the browse-loading review fixes (network-drive freezes, 1000-image folders), plus the py3.12 CI failure fix.
GUI-thread I/O removed (the freezes)
load_scancalls (channel previews + a second one just for.header). NewChannelPreviewLoaderreads the scan once on a pool worker, emitsmeta_ready(plane names + header) then per-planeQImages; the info panel builds slots, previews, and the metadata table from those signals. Token-guarded against stale clicks.thumbnail_plane_index_for_entryno longer full-loads the scan to map the thumbnail channel — header-onlyread_scan_metadata.index_folder_shallownow runs in aFolderIndexLoaderpool worker; the breadcrumb/path strip update immediately ("Indexing …") and the previous grid stays interactive. Token-guarded against rapid navigation.Bounded I/O and staged rendering
_peek_subfolderstops after examining 400 files (was: content-sniffs every file two levels deep — megabytes of 8 KB reads per folder card on big network trees).counts_cappedflows throughFolderEntryand folder cards showN+lower-bound counts. Regression test added.deleteLater'd on navigation rather than lingering until GC (the wrapper-recycling hazard).ViewerLoader/ChannelPreviewLoader/folder indexing never queue behind a screenful of slow network thumbnail reads.CI fix (py3.12 failure on main)
The failure (
'QGraphicsItemGroup' object has no attribute 'connect'insideQMenu.addAction) is the recycled-Shiboken-wrapper class conftest already documents. Two-part resolution:hasplugin("forked"), but pytest-forked registers aspytest_forked). Actually enabling it showed pytest-forked (archived, last release 2023) breaks modern pytest teardown bookkeeping ("previous item was not torn down properly" on every forked→unforked transition), so the mechanism and thepytest-forkeddependency are removed.gc.collect()after each GUI test, so parentless widgets a test leaked are destroyed deterministically at the test boundary (visible to Shiboken) instead of at a random GC point inside a later test — which is what reused C++ addresses into stale wrappers. The collect is gated to GUI modules to keep the suite fast.Tests
start(runnable, priority)signature.🤖 Generated with Claude Code