Skip to content

Commit e9767d8

Browse files
committed
support.progress: add QtWidgetsProgressImpl.
1 parent f605ec6 commit e9767d8

2 files changed

Lines changed: 78 additions & 1 deletion

File tree

‎docs/manual/src/conf.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565

6666
intersphinx_mapping = {
6767
"python": ("https://docs.python.org/3", os.getenv("INTERSPHINX_PYTHON")),
68+
"pyside6": ("https://doc.qt.io/qtforpython-6/", os.getenv("INTERSPHINX_PYSIDE6")),
6869
}
6970

7071
copybutton_prompt_is_regexp = True

‎software/glasgow/support/progress.py‎

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
from __future__ import annotations
2+
from typing import TYPE_CHECKING
23
from abc import ABCMeta, abstractmethod
34
from typing import Literal
45
from collections.abc import Generator, Sequence
56
import sys
67

78
from tqdm import tqdm
89
from tqdm.contrib import DummyTqdmFile
10+
if TYPE_CHECKING:
11+
from PySide6.QtWidgets import QProgressBar, QLabel
912

1013

11-
__all__ = ["Progress"]
14+
__all__ = ["Progress", "TqdmProgressImpl", "QtWidgetsProgressImpl"]
1215

1316

1417
class Progress:
@@ -234,6 +237,12 @@ def update(self, progress: Progress, count: int):
234237

235238

236239
class TqdmProgressImpl(AbstractProgressImpl):
240+
"""Progress implementation for terminals based on `تقدّم <https://tqdm.github.io/>`_.
241+
242+
This implementation is automatically registered by :meth:`glasgow.main.run_async`, but may
243+
be used explicitly if needed.
244+
"""
245+
237246
def __init__(self):
238247
self._bars = dict[Progress, tqdm]()
239248

@@ -269,3 +278,70 @@ def close(self, progress: Progress):
269278

270279
def update(self, progress: Progress, delta: int):
271280
self._bars[progress].update(delta)
281+
282+
283+
class QtWidgetsProgressImpl(AbstractProgressImpl):
284+
"""Progress implementation for Qt applications based on :mod:`~PySide6.QtWidgets`.
285+
286+
This implementation must be registered manually. It requires
287+
a :class:`~PySide6.QtWidgets.QProgressBar` and/or
288+
a :class:`~PySide6.QtWidgets.QLabel` to be provided;
289+
these widgets will be updated to display progress. For example:
290+
291+
.. code:: py
292+
293+
class MainWindow(QMainWindow, Ui_MainWindow):
294+
def __init__(self):
295+
super().__init__()
296+
297+
self.setupUi(self)
298+
QtWidgetsProgressImpl(self.progressBar, self.statusLabel).register()
299+
300+
...
301+
302+
.. note::
303+
304+
The limitation of this implementation is that it cannot display multiple progress bars.
305+
The last opened progress indicator is always used when updating progress information.
306+
"""
307+
308+
def __init__(self, progressBar: QProgressBar | None, statusLabel: QLabel | None):
309+
self.progressBar = progressBar
310+
self.statusLabel = statusLabel
311+
312+
self._bars = list[Progress]()
313+
314+
def register(self):
315+
self._orig_impl, Progress._impl = Progress._impl, self
316+
317+
def unregister(self):
318+
Progress._impl = self._orig_impl
319+
320+
def open(self, progress: Progress):
321+
self._bars.append(progress)
322+
self._refresh()
323+
324+
def close(self, progress: Progress):
325+
self._bars.remove(progress)
326+
self._refresh()
327+
328+
def update(self, progress: Progress, delta: int):
329+
self._refresh()
330+
331+
def _refresh(self):
332+
if self._bars:
333+
progress = self._bars[-1]
334+
if self.progressBar is not None:
335+
# From QProgressBar documentation:
336+
# > If minimum and maximum both are set to 0, the bar shows a busy indicator instead
337+
# > of a percentage of steps.
338+
self.progressBar.setRange(0, progress.total or 0)
339+
self.progressBar.setValue(progress.done)
340+
if self.statusLabel is not None:
341+
statusText = f"{progress.action} [{progress.done}"
342+
if progress.total is not None:
343+
statusText += f"/{progress.total}"
344+
if progress.item is not None:
345+
statusText += f" {progress.item}"
346+
statusText += "]"
347+
self.statusLabel.setText(statusText)

0 commit comments

Comments
 (0)