Skip to content

Commit cb6fb46

Browse files
committed
PICARD-3281: Throttle background task callback processing
Background task callbacks (ProxyToMainEvent) were processed immediately in Tagger.event(), causing bursts of hundreds of callbacks to execute back-to-back without yielding to the event loop. This blocked UI repaints and input handling. Queue incoming callbacks and process them in batches of 25, using QTimer.singleShot(0) between batches to yield to the event loop. This ensures the UI remains responsive even during heavy background task completion (e.g. file loading with 2000+ files).
1 parent 3743484 commit cb6fb46

1 file changed

Lines changed: 19 additions & 2 deletions

File tree

picard/tagger.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -822,9 +822,16 @@ def update_browser_integration(self):
822822
else:
823823
self.browser_integration.stop()
824824

825+
_CALLBACK_BATCH_SIZE = 25
826+
_callback_queue: list = []
827+
_callback_timer_running: bool = False
828+
825829
def event(self, event):
826830
if isinstance(event, thread.ProxyToMainEvent):
827-
event.run()
831+
self._callback_queue.append(event)
832+
if not self._callback_timer_running:
833+
self._callback_timer_running = True
834+
QtCore.QTimer.singleShot(0, self._process_callback_batch)
828835
elif event.type() == QtCore.QEvent.Type.FileOpen:
829836
file = event.file()
830837
self.add_paths([file])
@@ -836,6 +843,16 @@ def event(self, event):
836843
return 1
837844
return super().event(event)
838845

846+
def _process_callback_batch(self) -> None:
847+
"""Process a batch of queued callbacks, then yield to the event loop."""
848+
count = min(self._CALLBACK_BATCH_SIZE, len(self._callback_queue))
849+
for _i in range(count):
850+
self._callback_queue.pop(0).run()
851+
if self._callback_queue:
852+
QtCore.QTimer.singleShot(0, self._process_callback_batch)
853+
else:
854+
self._callback_timer_running = False
855+
839856
def _file_loaded(self, file, target=None, remove_file=False, unmatched_files=None):
840857
config = get_config()
841858
self._pending_files_count -= 1
@@ -984,7 +1001,7 @@ def add_files(self, filenames, target=None):
9841001

9851002
_FILE_LOAD_BATCH_SIZE = 25
9861003

987-
def _load_files_batch(self, files, offset, target, unmatched_files):
1004+
def _load_files_batch(self, files: list, offset: int, target: object, unmatched_files: list) -> None:
9881005
"""Dispatch a batch of file loads, then yield to the event loop."""
9891006
end = min(offset + self._FILE_LOAD_BATCH_SIZE, len(files))
9901007
for i in range(offset, end):

0 commit comments

Comments
 (0)