diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index ce57179e..575a0057 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -30,11 +30,11 @@ jobs: matrix: platform: [ubuntu-latest, windows-latest, macos-13] python-version: ["3.8", "3.9", "3.10", "3.11"] - backend: [pyqt5, pyside2, pyqt6] + backend: [pyqt5, pyside2, "'PyQt6<6.6'"] exclude: # Abort (core dumped) on linux pyqt6, unknown reason - platform: ubuntu-latest - backend: pyqt6 + backend: "'PyQt6<6.6'" # lack of wheels for pyside2/py3.11 - python-version: "3.11" backend: pyside2 @@ -56,7 +56,7 @@ jobs: - python-version: "3.12" platform: macos-latest - backend: pyqt6 + backend: "'PyQt6<6.6'" # legacy Qt - python-version: 3.8 diff --git a/pyproject.toml b/pyproject.toml index 036dffcd..7a788fd9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ pyside2 = ["pyside2"] # https://bugreports.qt.io/browse/PYSIDE-2627 pyside6 = ["pyside6 !=6.5.0,!=6.5.1,!=6.6.2"] pyqt5 = ["pyqt5"] -pyqt6 = ["pyqt6"] +pyqt6 = ["pyqt6<6.7"] font-fa5 = ["fonticon-fontawesome5"] font-fa6 = ["fonticon-fontawesome6"] font-mi6 = ["fonticon-materialdesignicons6"] diff --git a/src/superqt/sliders/_generic_slider.py b/src/superqt/sliders/_generic_slider.py index 2b8beade..ddd78173 100644 --- a/src/superqt/sliders/_generic_slider.py +++ b/src/superqt/sliders/_generic_slider.py @@ -74,6 +74,7 @@ def __init__(self, *args, **kwargs) -> None: self._position: _T = 0.0 self._singleStep = 1.0 self._offsetAccumulated = 0.0 + self._inverted_appearance = False self._blocktracking = False self._tickInterval = 0.0 self._pressedControl = SC_NONE @@ -174,6 +175,13 @@ def setTickInterval(self, ts: float) -> None: self._tickInterval = max(0.0, ts) self.update() + def invertedAppearance(self) -> bool: + return self._inverted_appearance + + def setInvertedAppearance(self, inverted: bool) -> None: + self._inverted_appearance = inverted + self.update() + def triggerAction(self, action: QSlider.SliderAction) -> None: self._blocktracking = True # other actions here @@ -193,9 +201,8 @@ def initStyleOption(self, option: QStyleOptionSlider) -> None: if self.orientation() == Qt.Orientation.Horizontal else not self.invertedAppearance() ) - option.direction = ( - Qt.LayoutDirection.LeftToRight - ) # we use the upsideDown option instead + # we use the upsideDown option instead + option.direction = Qt.LayoutDirection.LeftToRight # option.sliderValue = self._value # type: ignore # option.singleStep = self._singleStep # type: ignore if self.orientation() == Qt.Orientation.Horizontal: diff --git a/src/superqt/sliders/_labeled.py b/src/superqt/sliders/_labeled.py index 82f987b6..5b379cc8 100644 --- a/src/superqt/sliders/_labeled.py +++ b/src/superqt/sliders/_labeled.py @@ -3,7 +3,7 @@ import contextlib from enum import IntEnum, IntFlag, auto from functools import partial -from typing import Any, overload +from typing import Any, Iterable, overload from qtpy.QtCore import QPoint, QSize, Qt, Signal from qtpy.QtGui import QFontMetrics, QValidator @@ -341,7 +341,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self._rename_signals() self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating) - self._handle_labels = [] + self._handle_labels: list[SliderLabel] = [] self._handle_label_position: LabelPosition = LabelPosition.LabelsAbove # for fine tuning label position @@ -421,15 +421,23 @@ def setEdgeLabelMode(self, opt: EdgeLabelMode) -> None: def setRange(self, min: int, max: int) -> None: self._on_range_changed(min, max) + def _add_labels(self, layout: QBoxLayout, inverted: bool = False) -> None: + if inverted: + first, second = self._max_label, self._min_label + else: + first, second = self._min_label, self._max_label + layout.addWidget(first) + layout.addWidget(self._slider) + layout.addWidget(second) + def setOrientation(self, orientation: Qt.Orientation) -> None: """Set orientation, value will be 'horizontal' or 'vertical'.""" self._slider.setOrientation(orientation) + inverted = self._slider.invertedAppearance() if orientation == Qt.Orientation.Vertical: layout: QBoxLayout = QVBoxLayout() layout.setSpacing(1) - layout.addWidget(self._max_label) - layout.addWidget(self._slider) - layout.addWidget(self._min_label) + self._add_labels(layout, inverted=not inverted) # TODO: set margins based on label width if self._handle_label_position == LabelPosition.LabelsLeft: marg = (30, 0, 0, 0) @@ -447,9 +455,7 @@ def setOrientation(self, orientation: Qt.Orientation) -> None: marg = (0, 0, 0, 0) else: marg = (0, 25, 0, 0) - layout.addWidget(self._min_label) - layout.addWidget(self._slider) - layout.addWidget(self._max_label) + self._add_labels(layout, inverted=inverted) # remove old layout old_layout = self.layout() @@ -462,6 +468,10 @@ def setOrientation(self, orientation: Qt.Orientation) -> None: QApplication.processEvents() self._reposition_labels() + def setInvertedAppearance(self, a0: bool) -> None: + self._slider.setInvertedAppearance(a0) + self.setOrientation(self._slider.orientation()) + def resizeEvent(self, a0) -> None: super().resizeEvent(a0) self._reposition_labels() @@ -487,7 +497,10 @@ def _reposition_labels(self) -> None: labels_above = self._handle_label_position == LabelPosition.LabelsAbove last_edge = None - for i, label in enumerate(self._handle_labels): + labels: Iterable[tuple[int, SliderLabel]] = enumerate(self._handle_labels) + if self._slider.invertedAppearance(): + labels = reversed(list(labels)) + for i, label in labels: rect = self._slider._handleRect(i) dx = -label.width() / 2 dy = -label.height() / 2