mirror of
https://github.com/pyapp-kit/superqt.git
synced 2026-01-04 11:21:09 +01:00
more readme
This commit is contained in:
4
.github/workflows/test_and_deploy.yml
vendored
4
.github/workflows/test_and_deploy.yml
vendored
@@ -106,11 +106,11 @@ jobs:
|
||||
if: runner.os == 'Linux' && matrix.screenshot
|
||||
uses: GabrielBB/xvfb-action@v1
|
||||
with:
|
||||
run: python examples/screenshots.py
|
||||
run: python examples/demo_widget.py
|
||||
|
||||
- name: Screenshots
|
||||
if: runner.os != 'Linux' && matrix.screenshot
|
||||
run: python examples/screenshots.py
|
||||
run: python examples/demo_widget.py
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: matrix.screenshot
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -77,3 +77,4 @@ target/
|
||||
|
||||
# written by setuptools_scm
|
||||
*/_version.py
|
||||
.vscode/settings.json
|
||||
|
||||
79
README.md
79
README.md
@@ -7,7 +7,7 @@ Version](https://img.shields.io/pypi/pyversions/QtRangeSlider.svg?color=green)](
|
||||
[](https://github.com/tlambert03/QtRangeSlider/actions/workflows/test_and_deploy.yml)
|
||||
[](https://codecov.io/gh/tlambert03/QtRangeSlider)
|
||||
|
||||
**Multi-handle range slider widget for PyQt/PySide**
|
||||
**The missing multi-handle range slider widget for PyQt & PySide**
|
||||
|
||||

|
||||
|
||||
@@ -31,24 +31,78 @@ You can install `QtRangeSlider` via pip:
|
||||
```sh
|
||||
pip install qtrangeslider
|
||||
|
||||
# note: you must also install a Qt Backend
|
||||
# supports PyQt5, PySide2, PyQt6, and PySide6
|
||||
# as a convenience you can install via extras:
|
||||
# NOTE: you must also install a Qt Backend.
|
||||
# PyQt5, PySide2, PyQt6, and PySide6 are supported
|
||||
# As a convenience you can install them as extras:
|
||||
pip install qtrangeslider[pyqt5]
|
||||
|
||||
```
|
||||
|
||||
And then to use it:
|
||||
|
||||
```python
|
||||
from qtrangeslider import QRangeSlider
|
||||
```
|
||||
------
|
||||
|
||||
## API
|
||||
|
||||
As `QRangeSlider` inherits from `QtWidgets.QSlider`, you can use all of the
|
||||
same methods available in the [QSlider API](https://doc.qt.io/qt-5/qslider.html)
|
||||
To create a slider:
|
||||
|
||||
```python
|
||||
from qtrangeslider import QRangeSlider
|
||||
|
||||
# as usual:
|
||||
# you must create a QApplication before create a widget.
|
||||
range_slider = QRangeSlider()
|
||||
```
|
||||
|
||||
As `QRangeSlider` inherits from `QtWidgets.QSlider`, you can use all of the
|
||||
same methods available in the [QSlider API](https://doc.qt.io/qt-5/qslider.html). The major difference is that `value` and `sliderPosition` are reimplemented as `tuples` of `int` (where the length of the tuple is equal to the number of handles in the slider.)
|
||||
|
||||
### value: Tuple[int, ...]
|
||||
|
||||
This property holds the current value of all handles in the slider.
|
||||
|
||||
The slider forces all values to be within the legal range:
|
||||
`minimum <= value <= maximum`.
|
||||
|
||||
Changing the value also changes the sliderPosition.
|
||||
|
||||
##### Access Functions:
|
||||
|
||||
```python
|
||||
range_slider.value() -> Tuple[int, ...]
|
||||
```
|
||||
|
||||
```python
|
||||
range_slider.setValue(val: Sequence[int]) -> None
|
||||
```
|
||||
|
||||
##### Notifier Signal:
|
||||
|
||||
```python
|
||||
valueChanged(Tuple[int, ...])
|
||||
```
|
||||
|
||||
### sliderPosition: Tuple[int, ...]
|
||||
|
||||
This property holds the current slider positions. It is a `tuple` with length equal to the number of handles.
|
||||
|
||||
If [tracking](https://doc.qt.io/qt-5/qabstractslider.html#tracking-prop) is enabled (the default), this is identical to [`value`](#value--tupleint-).
|
||||
|
||||
##### Access Functions:
|
||||
|
||||
```python
|
||||
range_slider.sliderPosition() -> Tuple[int, ...]
|
||||
```
|
||||
|
||||
```python
|
||||
range_slider.setSliderPosition(val: Sequence[int]) -> None
|
||||
```
|
||||
|
||||
##### Notifier Signal:
|
||||
|
||||
```python
|
||||
sliderMoved(Tuple[int, ...])
|
||||
```
|
||||
|
||||
------
|
||||
|
||||
## Example
|
||||
|
||||
@@ -59,6 +113,8 @@ using [Qt Style Sheets](https://doc.qt.io/qt-5/stylesheet-reference.html), then
|
||||
`QRangeSlider` will inherit any styles applied to `QSlider` (since it inherits
|
||||
from QSlider).
|
||||
|
||||
> The code for these example widgets is [here](examples/demo_widget.py)
|
||||
|
||||
<details>
|
||||
|
||||
<summary><em>See style sheet used for this example</em></summary>
|
||||
@@ -112,5 +168,4 @@ QSlider::sub-page:horizontal {
|
||||
If you encounter any problems, please [file an issue] along with a detailed
|
||||
description.
|
||||
|
||||
|
||||
[file an issue]: https://github.com/tlambert03/QtRangeSlider/issues
|
||||
|
||||
@@ -4,8 +4,7 @@ from qtrangeslider.qtcompat.QtWidgets import QApplication
|
||||
app = QApplication([])
|
||||
|
||||
slider = QRangeSlider()
|
||||
slider.setMinimum(0)
|
||||
slider.setMaximum(100)
|
||||
|
||||
slider.setValue((20, 80))
|
||||
slider.show()
|
||||
|
||||
|
||||
@@ -15,11 +15,6 @@ from .qtcompat.QtWidgets import (
|
||||
Control = Tuple[str, int]
|
||||
|
||||
|
||||
def _bound(min_: int, max_: int, value: int) -> int:
|
||||
"""Return value bounded by min_ and max_."""
|
||||
return max(min_, min(max_, value))
|
||||
|
||||
|
||||
class QRangeSlider(QSlider):
|
||||
# Emitted when the slider value has changed, with the new slider values
|
||||
valueChanged = Signal(tuple)
|
||||
@@ -85,6 +80,9 @@ class QRangeSlider(QSlider):
|
||||
return tuple(self._position)
|
||||
|
||||
def setSliderPosition(self, sld_idx: int, pos: int) -> None:
|
||||
|
||||
# TODO: make it take a tuple, and assert that the length is correct
|
||||
# only setting `value` is allowed to change the number of handles
|
||||
pos = self._min_max_bound(pos)
|
||||
# prevent sliders from moving beyond their neighbors
|
||||
pos = self._neighbor_bound(pos, sld_idx, self._position)
|
||||
@@ -192,8 +190,11 @@ class QRangeSlider(QSlider):
|
||||
self.update()
|
||||
self.setSliderDown(True)
|
||||
elif self._pressedControl[0] == "bar":
|
||||
self.setRepeatAction(QSlider.SliderNoAction) # why again?
|
||||
self._clickOffset = self._pixelPosToRangeValue(self._pick(ev.pos()))
|
||||
self._sldPosAtPress = tuple(self._position)
|
||||
self.update()
|
||||
self.setSliderDown(True)
|
||||
|
||||
def mouseMoveEvent(self, ev: QtGui.QMouseEvent) -> None:
|
||||
# TODO: add pixelMetric(QStyle::PM_MaximumDragDistance, &opt, this);
|
||||
@@ -230,7 +231,7 @@ class QRangeSlider(QSlider):
|
||||
old_pressed = self._pressedControl
|
||||
self._pressedControl = self.NULL_CTRL
|
||||
self.setRepeatAction(QSlider.SliderNoAction)
|
||||
if old_pressed[0] == "handle":
|
||||
if old_pressed[0] in ("handle", "bar"):
|
||||
self.setSliderDown(False)
|
||||
self.update() # TODO: restrict to the rect of old_pressed
|
||||
|
||||
@@ -489,6 +490,13 @@ class QRangeSlider(QSlider):
|
||||
setattr(self, f"_bar_{dim}", float(bgrd.groups()[-1]))
|
||||
|
||||
|
||||
def _bound(min_: int, max_: int, value: int) -> int:
|
||||
"""Return value bounded by min_ and max_."""
|
||||
return max(min_, min(max_, value))
|
||||
|
||||
|
||||
# Styles Parsing ##############
|
||||
|
||||
qlineargrad_pattern = re.compile(
|
||||
r"""
|
||||
qlineargradient\(
|
||||
|
||||
@@ -31,13 +31,13 @@ QT_API = "QT_API"
|
||||
# Names of the expected PyQt5 api
|
||||
PYQT5_API = ["pyqt5"]
|
||||
|
||||
# Names of the expected PyQt5 api
|
||||
# Names of the expected PyQt6 api
|
||||
PYQT6_API = ["pyqt6"]
|
||||
|
||||
# Names of the expected PySide2 api
|
||||
PYSIDE2_API = ["pyside2"]
|
||||
|
||||
# Names of the expected PySide2 api
|
||||
# Names of the expected PySide6 api
|
||||
PYSIDE6_API = ["pyside6"]
|
||||
|
||||
# Detecting if a binding was specified by the user
|
||||
@@ -142,7 +142,13 @@ if API in PYSIDE6_API:
|
||||
PYSIDE6 = True
|
||||
|
||||
except ImportError:
|
||||
raise PythonQtError("No Qt bindings could be found")
|
||||
API = None
|
||||
|
||||
if API is None:
|
||||
raise PythonQtError(
|
||||
"No Qt bindings could be found.\nYou must install one of the following packages "
|
||||
"to use QtRangeSlider: PyQt5, PyQt6, PySide2, or PySide6"
|
||||
)
|
||||
|
||||
# If a correct API name is passed to QT_API and it could not be found,
|
||||
# switches to another and informs through the warning
|
||||
|
||||
Reference in New Issue
Block a user