Move to to qwidgets

commit 466fc7c19ace1343d23739e4058758cd21328511
Author: Talley Lambert <talley.lambert@gmail.com>
Date:   Wed Jun 2 20:22:38 2021 -0400

    add deploy cond

commit e9965e71490689935b61099225acc7f3bf5c2d48
Author: Talley Lambert <talley.lambert@gmail.com>
Date:   Wed Jun 2 20:20:45 2021 -0400

    more precommit

commit b39150b16d7d64a5530ec9a0e29e673e2b6ed0a4
Author: Talley Lambert <talley.lambert@gmail.com>
Date:   Wed Jun 2 19:52:42 2021 -0400

    updating precommit

commit d5018b38e7bc59f81cc161cca06fae829e493e3c
Author: Talley Lambert <talley.lambert@gmail.com>
Date:   Wed Jun 2 19:42:32 2021 -0400

    big reorg
This commit is contained in:
Talley Lambert
2021-06-02 20:25:40 -04:00
parent 297838e895
commit 40b34213fb
43 changed files with 482 additions and 391 deletions

View File

@@ -1,11 +0,0 @@
[report]
exclude_lines =
pragma: no cover
if TYPE_CHECKING:
\.\.\.
except ImportError*
raise NotImplementedError()
omit =
qtrangeslider/_version.py
qtrangeslider/qtcompat/*
*_tests*

View File

@@ -89,6 +89,7 @@ jobs:
sudo apt-get install -y libdbus-1-3 libxkbcommon-x11-0 libxcb-icccm4 \
libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 \
libxcb-xinerama0 libxcb-xfixes0
- name: Linux opengl
if: runner.os == 'Linux' && ( matrix.backend == 'pyside6' || matrix.backend == 'pyqt6' )
run: sudo apt-get install -y libopengl0 libegl1-mesa libxcb-xinput0
@@ -111,13 +112,13 @@ jobs:
if: matrix.screenshot
run: pip install . ${{ matrix.backend }}
- name: Screenshots
- name: Screenshots (Linux)
if: runner.os == 'Linux' && matrix.screenshot
uses: GabrielBB/xvfb-action@v1
with:
run: python examples/demo_widget.py -snap
- name: Screenshots
- name: Screenshots (macOS/Win)
if: runner.os != 'Linux' && matrix.screenshot
run: python examples/demo_widget.py -snap
@@ -133,8 +134,8 @@ jobs:
# and requires that you have put your twine API key in your
# github secrets (see readme for details)
needs: [test]
if: ${{ github.repository == 'napari/qwidgets' && contains(github.ref, 'tags') }}
runs-on: ubuntu-latest
if: contains(github.ref, 'tags')
steps:
- uses: actions/checkout@v2
- name: Set up Python

View File

@@ -4,20 +4,32 @@ repos:
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/setup-cfg-fmt
rev: v1.17.0
hooks:
- id: setup-cfg-fmt
- repo: https://github.com/PyCQA/flake8
rev: 3.9.2
hooks:
- id: flake8
additional_dependencies:
[flake8-typing-imports==1.7.0]
exclude: examples
- repo: https://github.com/myint/autoflake
rev: v1.4
hooks:
- id: autoflake
args: ["--in-place", "--remove-all-unused-imports"]
- repo: https://github.com/PyCQA/isort
rev: 5.8.0
hooks:
- id: isort
- repo: https://github.com/asottile/pyupgrade
rev: v2.19.0
rev: v2.19.1
hooks:
- id: pyupgrade
args: [--py37-plus]
- repo: https://github.com/psf/black
rev: 21.5b2
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 3.9.2
hooks:
- id: flake8
pass_filenames: true

54
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,54 @@
# Contributing to this repository
This repository seeks to accumulate Qt-based widgets for python (PyQt & PySide)
that are not provided in the native QtWidgets module.
## Clone
To get started fork this repository, and clone your fork:
```bash
# clone your fork
git clone https://github.com/<your_organization>/qwidgets
cd qwidgets
# install pre-commit hooks
pre-commit install
# install in editable mode
pip install -e .[dev]
# run tests & make sure everything is working!
pytest
```
## Targeted platforms
All widgets must be well-tested, and should work on:
- Python 3.7 and above
- PyQt5 (5.11 and above) & PyQt6
- PySide2 (5.11 and above) & PySide6
- macOS, Windows, & Linux
Until [qtpy](https://github.com/spyder-ide/qtpy) supports PyQt6/PySide6, imports
should use (and modify if necessary) `qwidgets.qtcompat`.
## Style Guide
All widgets should try to match the native Qt API as much as possible:
- Methods should use `camelCase` naming.
- Getters/setters use the `attribute()/setAttribute()` pattern.
- Private methods should use `_camelCaseNaming`.
- `__init__` methods should be like Qt constructors, meaning they often don't
include parameters for most of the widgets properties.
- When possible, widgets should inherit from the most similar native widget
available. It should strictly match the Qt API where it exists, and attempt to
cover as much of the native API as possible; this includes properties, public
functions, signals, and public slots.
## Testing
Tests can be run in the current environment with `pytest`. Or, to run tests
against all supported python & Qt versions, run `tox`.

View File

@@ -12,7 +12,7 @@ modification, are permitted provided that the following conditions are met:
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of QtRangeSlider nor the names of its
* Neither the name of qwidgets nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

271
README.md
View File

@@ -1,262 +1,27 @@
# QtRangeSlider
# qwidgets
[![License](https://img.shields.io/pypi/l/QtRangeSlider.svg?color=green)](https://github.com/tlambert03/QtRangeSlider/raw/master/LICENSE)
[![PyPI](https://img.shields.io/pypi/v/QtRangeSlider.svg?color=green)](https://pypi.org/project/QtRangeSlider)
**Missing widgets for PyQt/PySide**
[![License](https://img.shields.io/pypi/l/qwidgets.svg?color=green)](https://github.com/napari/qwidgets/raw/master/LICENSE)
[![PyPI](https://img.shields.io/pypi/v/qwidgets.svg?color=green)](https://pypi.org/project/qwidgets)
[![Python
Version](https://img.shields.io/pypi/pyversions/QtRangeSlider.svg?color=green)](https://python.org)
[![Test](https://github.com/tlambert03/QtRangeSlider/actions/workflows/test_and_deploy.yml/badge.svg)](https://github.com/tlambert03/QtRangeSlider/actions/workflows/test_and_deploy.yml)
[![codecov](https://codecov.io/gh/tlambert03/QtRangeSlider/branch/master/graph/badge.svg)](https://codecov.io/gh/tlambert03/QtRangeSlider)
Version](https://img.shields.io/pypi/pyversions/qwidgets.svg?color=green)](https://python.org)
[![Test](https://github.com/napari/qwidgets/actions/workflows/test_and_deploy.yml/badge.svg)](https://github.com/napari/qwidgets/actions/workflows/test_and_deploy.yml)
[![codecov](https://codecov.io/gh/napari/qwidgets/branch/master/graph/badge.svg)](https://codecov.io/gh/napari/qwidgets)
**The missing multi-handle range slider widget for PyQt & PySide**
![slider](images/slider.png)
## Widgets
The goal of this package is to provide a Range Slider (a slider with 2 or more
handles) that feels as "native" as possible. Styles should match the OS by
default, and the slider should behave like a standard
[`QSlider`](https://doc.qt.io/qt-5/qslider.html)... but with multiple handles!
Widgets include:
- `QRangeSlider` inherits from [`QSlider`](https://doc.qt.io/qt-5/qslider.html)
and attempts to match the Qt API as closely as possible
- Uses platform-specific styles (for handle, groove, & ticks) but also supports
QSS style sheets.
- Supports mouse wheel and keypress (soon) events
- Supports PyQt5, PyQt6, PySide2 and PySide6
- Supports more than 2 handles (e.g. `slider.setValue([0, 10, 60, 80])`)
- Float Slider
- Range Slider (multi-handle slider)
- Labeled Sliders (sliders with linked spinboxes)
- Unbound Integer SpinBox (backed by python `int`)
## Installation
## Contributing
You can install `QtRangeSlider` via pip:
This repository seeks to accumulate Qt-based widgets for python (PyQt & PySide)
that are not provided in the native QtWidgets module. We welcome contributions!
```sh
pip install qtrangeslider
# 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]
```
------
## API
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, ...])
```
### Additional properties
These options are in addition to the Qt QSlider API, and control the behavior of the bar between handles.
| getter | setter | type | default | description |
| -------------------- | ------------------------------------------- | ------ | ------- | ------------------------------------------------------------------------------------------------ |
| `barIsVisible` | `setBarIsVisible` <br>`hideBar` / `showBar` | `bool` | `True` | <small>Whether the bar between handles is visible.</small> |
| `barMovesAllHandles` | `setBarMovesAllHandles` | `bool` | `True` | <small>Whether clicking on the bar moves all handles or just the nearest</small> |
| `barIsRigid` | `setBarIsRigid` | `bool` | `True` | <small>Whether bar length is constant or "elastic" when dragging the bar beyond min/max.</small> |
------
## Examples
These screenshots show `QRangeSlider` (multiple handles) next to the native `QSlider`
(single handle). With no styles applied, `QRangeSlider` will match the native OS
style of `QSlider` with or without tick marks. When styles have been applied
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). If you'd like to style `QRangeSlider` differently than `QSlider`,
then you can also target it directly in your style sheet. The one "special"
property for QRangeSlider is `qproperty-barColor`, which sets the color of the
bar between the handles.
> The code for these example widgets is [here](examples/demo_widget.py)
<details>
<summary><em>See style sheet used for this example</em></summary>
```css
/*
Because QRangeSlider inherits from QSlider, it will also inherit styles
*/
QSlider {
min-height: 20px;
}
QSlider::groove:horizontal {
border: 0px;
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #777, stop:1 #aaa);
height: 20px;
border-radius: 10px;
}
QSlider::handle {
background: qradialgradient(cx:0, cy:0, radius: 1.2, fx:0.5,
fy:0.5, stop:0 #eef, stop:1 #000);
height: 20px;
width: 20px;
border-radius: 10px;
}
/*
"QSlider::sub-page" is the one exception ...
(it styles the area to the left of the QSlider handle)
*/
QSlider::sub-page:horizontal {
background: #447;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
/*
for QRangeSlider: use "qproperty-barColor". "sub-page" will not work.
*/
QRangeSlider {
qproperty-barColor: #447;
}
```
</details>
### macOS
##### Catalina
![mac10](images/demo_darwin10.png)
##### Big Sur
![mac11](images/demo_darwin11.png)
### Windows
![window](images/demo_windows.png)
### Linux
![linux](images/demo_linux.png)
## Labeled Sliders
This package also includes two "labeled" slider variants. One for `QRangeSlider`, and one for the native `QSlider`:
### `QLabeledRangeSlider`
![labeled_range](images/labeled_range.png)
```python
from qtrangeslider import QLabeledRangeSlider
```
This has the same API as `QRangeSlider` with the following additional options:
#### `handleLabelPosition`/`setHandleLabelPosition`
Where/whether labels are shown adjacent to slider handles.
**type:** `QLabeledRangeSlider.LabelPosition`
**default:** `LabelPosition.LabelsAbove`
*options:*
- `LabelPosition.NoLabel` (no labels shown adjacent to handles)
- `LabelPosition.LabelsAbove`
- `LabelPosition.LabelsBelow`
- `LabelPosition.LabelsRight` (alias for `LabelPosition.LabelsAbove`)
- `LabelPosition.LabelsLeft` (alias for `LabelPosition.LabelsBelow`)
#### `edgeLabelMode`/`setEdgeLabelMode`
**type:** `QLabeledRangeSlider.EdgeLabelMode`
**default:** `EdgeLabelMode.LabelIsRange`
*options:*
- `EdgeLabelMode.NoLabel`: no labels shown at slider extremes
- `EdgeLabelMode.LabelIsRange`: edge labels shown the min/max values
- `EdgeLabelMode.LabelIsValue`: edge labels shown the slider range
#### fine tuning position of labels:
If you find that you need to fine tune the position of the handle labels:
- `QLabeledRangeSlider.label_shift_x`: adjust horizontal label position
- `QLabeledRangeSlider.label_shift_y`: adjust vertical label position
### `QLabeledSlider`
![labeled_range](images/labeled_qslider.png)
```python
from qtrangeslider import QLabeledSlider
```
(no additional options at this point)
## Issues
If you encounter any problems, please [file an issue] along with a detailed
description.
[file an issue]: https://github.com/tlambert03/QtRangeSlider/issues
Please see the [Contributing Guide](CONTRIBUTING.md)

View File

@@ -1,6 +1,6 @@
ignore:
- qtrangeslider/_version.py
- qtrangeslider/qtcompat/*
- qwidgets/_version.py
- qwidgets/qtcompat/*
- '*_tests*'
coverage:
status:

235
docs/RangeSlider.md Normal file
View File

@@ -0,0 +1,235 @@
# QRangeSlider
**The missing multi-handle range slider widget for PyQt & PySide**
![slider](images/slider.png)
- `QRangeSlider` inherits from [`QSlider`](https://doc.qt.io/qt-5/qslider.html)
and attempts to match the Qt API as closely as possible
- Uses platform-specific styles (for handle, groove, & ticks) but also supports
QSS style sheets.
- Supports mouse wheel and keypress (soon) events
- Supports more than 2 handles (e.g. `slider.setValue([0, 10, 60, 80])`)
------
## API
To create a slider:
```python
from qwidgets 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, ...])
```
### Additional properties
These options are in addition to the Qt QSlider API, and control the behavior of the bar between handles.
| getter | setter | type | default | description |
| -------------------- | ------------------------------------------- | ------ | ------- | ------------------------------------------------------------------------------------------------ |
| `barIsVisible` | `setBarIsVisible` <br>`hideBar` / `showBar` | `bool` | `True` | <small>Whether the bar between handles is visible.</small> |
| `barMovesAllHandles` | `setBarMovesAllHandles` | `bool` | `True` | <small>Whether clicking on the bar moves all handles or just the nearest</small> |
| `barIsRigid` | `setBarIsRigid` | `bool` | `True` | <small>Whether bar length is constant or "elastic" when dragging the bar beyond min/max.</small> |
------
## Examples
These screenshots show `QRangeSlider` (multiple handles) next to the native `QSlider`
(single handle). With no styles applied, `QRangeSlider` will match the native OS
style of `QSlider` with or without tick marks. When styles have been applied
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). If you'd like to style `QRangeSlider` differently than `QSlider`,
then you can also target it directly in your style sheet. The one "special"
property for QRangeSlider is `qproperty-barColor`, which sets the color of the
bar between the handles.
> The code for these example widgets is [here](examples/demo_widget.py)
<details>
<summary><em>See style sheet used for this example</em></summary>
```css
/*
Because QRangeSlider inherits from QSlider, it will also inherit styles
*/
QSlider {
min-height: 20px;
}
QSlider::groove:horizontal {
border: 0px;
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #777, stop:1 #aaa);
height: 20px;
border-radius: 10px;
}
QSlider::handle {
background: qradialgradient(cx:0, cy:0, radius: 1.2, fx:0.5,
fy:0.5, stop:0 #eef, stop:1 #000);
height: 20px;
width: 20px;
border-radius: 10px;
}
/*
"QSlider::sub-page" is the one exception ...
(it styles the area to the left of the QSlider handle)
*/
QSlider::sub-page:horizontal {
background: #447;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
/*
for QRangeSlider: use "qproperty-barColor". "sub-page" will not work.
*/
QRangeSlider {
qproperty-barColor: #447;
}
```
</details>
### macOS
##### Catalina
![mac10](images/demo_darwin10.png)
##### Big Sur
![mac11](images/demo_darwin11.png)
### Windows
![window](images/demo_windows.png)
### Linux
![linux](images/demo_linux.png)
## Labeled Sliders
This package also includes two "labeled" slider variants. One for `QRangeSlider`, and one for the native `QSlider`:
### `QLabeledRangeSlider`
![labeled_range](images/labeled_range.png)
```python
from qwidgets import QLabeledRangeSlider
```
This has the same API as `QRangeSlider` with the following additional options:
#### `handleLabelPosition`/`setHandleLabelPosition`
Where/whether labels are shown adjacent to slider handles.
**type:** `QLabeledRangeSlider.LabelPosition`
**default:** `LabelPosition.LabelsAbove`
*options:*
- `LabelPosition.NoLabel` (no labels shown adjacent to handles)
- `LabelPosition.LabelsAbove`
- `LabelPosition.LabelsBelow`
- `LabelPosition.LabelsRight` (alias for `LabelPosition.LabelsAbove`)
- `LabelPosition.LabelsLeft` (alias for `LabelPosition.LabelsBelow`)
#### `edgeLabelMode`/`setEdgeLabelMode`
**type:** `QLabeledRangeSlider.EdgeLabelMode`
**default:** `EdgeLabelMode.LabelIsRange`
*options:*
- `EdgeLabelMode.NoLabel`: no labels shown at slider extremes
- `EdgeLabelMode.LabelIsRange`: edge labels shown the min/max values
- `EdgeLabelMode.LabelIsValue`: edge labels shown the slider range
#### fine tuning position of labels:
If you find that you need to fine tune the position of the handle labels:
- `QLabeledRangeSlider.label_shift_x`: adjust horizontal label position
- `QLabeledRangeSlider.label_shift_y`: adjust vertical label position
### `QLabeledSlider`
![labeled_range](images/labeled_qslider.png)
```python
from qwidgets import QLabeledSlider
```
(no additional options at this point)
## Issues
If you encounter any problems, please [file an issue] along with a detailed
description.
[file an issue]: https://github.com/napari/qwidgets/issues

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -1,6 +1,6 @@
from qtrangeslider import QRangeSlider
from qtrangeslider.qtcompat.QtCore import Qt
from qtrangeslider.qtcompat.QtWidgets import QApplication
from qwidgets import QRangeSlider
from qwidgets.qtcompat.QtCore import Qt
from qwidgets.qtcompat.QtWidgets import QApplication
app = QApplication([])

View File

@@ -1,6 +1,6 @@
from qtrangeslider import QDoubleSlider
from qtrangeslider.qtcompat.QtCore import Qt
from qtrangeslider.qtcompat.QtWidgets import QApplication
from qwidgets import QDoubleSlider
from qwidgets.qtcompat.QtCore import Qt
from qwidgets.qtcompat.QtWidgets import QApplication
app = QApplication([])

View File

@@ -1,6 +1,6 @@
from qtrangeslider import QRangeSlider
from qtrangeslider.qtcompat import QtCore
from qtrangeslider.qtcompat import QtWidgets as QtW
from qwidgets import QRangeSlider
from qwidgets.qtcompat import QtCore
from qwidgets.qtcompat import QtWidgets as QtW
QSS = """
QSlider {

View File

@@ -1,6 +1,6 @@
from qtrangeslider import QDoubleRangeSlider, QDoubleSlider, QRangeSlider
from qtrangeslider.qtcompat.QtCore import Qt
from qtrangeslider.qtcompat.QtWidgets import QApplication, QVBoxLayout, QWidget
from qwidgets import QDoubleRangeSlider, QDoubleSlider, QRangeSlider
from qwidgets.qtcompat.QtCore import Qt
from qwidgets.qtcompat.QtWidgets import QApplication, QVBoxLayout, QWidget
app = QApplication([])

View File

@@ -1,6 +1,6 @@
from qtrangeslider import QDoubleSlider
from qtrangeslider.qtcompat.QtCore import Qt
from qtrangeslider.qtcompat.QtWidgets import QApplication
from qwidgets import QDoubleSlider
from qwidgets.qtcompat.QtCore import Qt
from qwidgets.qtcompat.QtWidgets import QApplication
app = QApplication([])

View File

@@ -1,16 +1,11 @@
from qtrangeslider._labeled import (
from qwidgets import (
QLabeledDoubleRangeSlider,
QLabeledDoubleSlider,
QLabeledRangeSlider,
QLabeledSlider,
)
from qtrangeslider.qtcompat.QtCore import Qt
from qtrangeslider.qtcompat.QtWidgets import (
QApplication,
QHBoxLayout,
QVBoxLayout,
QWidget,
)
from qwidgets.qtcompat.QtCore import Qt
from qwidgets.qtcompat.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout, QWidget
app = QApplication([])

View File

@@ -1,5 +1,5 @@
from qtrangeslider import QRangeSlider
from qtrangeslider.qtcompat.QtWidgets import QApplication
from qwidgets import QRangeSlider
from qwidgets.qtcompat.QtWidgets import QApplication
app = QApplication([])

26
qwidgets/__init__.py Normal file
View File

@@ -0,0 +1,26 @@
"""qwidgets is a collection of QtWidgets for python."""
try:
from ._version import version as __version__
except ImportError:
__version__ = "unknown"
from .sliders import (
QDoubleRangeSlider,
QDoubleSlider,
QLabeledDoubleRangeSlider,
QLabeledDoubleSlider,
QLabeledRangeSlider,
QLabeledSlider,
QRangeSlider,
)
__all__ = [
"QDoubleRangeSlider",
"QDoubleSlider",
"QLabeledDoubleRangeSlider",
"QLabeledDoubleSlider",
"QLabeledRangeSlider",
"QLabeledSlider",
"QRangeSlider",
]

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2014-2015 Colin Duquesnoy
# Copyright © 2009- The Spyder Development Team

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2014-2015 Colin Duquesnoy
# Copyright © 2009- The Spyder Development Team

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2014-2015 Colin Duquesnoy
# Copyright © 2009- The Spyder Developmet Team

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2009- The Spyder Development Team
# Copyright © 2014-2015 Colin Duquesnoy
@@ -147,7 +146,7 @@ if API in PYSIDE6_API:
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"
"to use qwidgets: PyQt5, PyQt6, PySide2, or PySide6"
)
# If a correct API name is passed to QT_API and it could not be found,

View File

@@ -1,8 +1,3 @@
try:
from ._version import version as __version__
except ImportError:
__version__ = "unknown"
from ._labeled import (
QLabeledDoubleRangeSlider,
QLabeledDoubleSlider,

View File

@@ -1,9 +1,7 @@
from typing import Generic, List, Sequence, Tuple, TypeVar, Union
from ._generic_slider import CC_SLIDER, SC_GROOVE, SC_HANDLE, SC_NONE, _GenericSlider
from ._range_style import RangeSliderStyle, update_styles_from_stylesheet
from .qtcompat import QtGui
from .qtcompat.QtCore import (
from ..qtcompat import QtGui
from ..qtcompat.QtCore import (
Property,
QEvent,
QPoint,
@@ -13,7 +11,9 @@ from .qtcompat.QtCore import (
Qt,
Signal,
)
from .qtcompat.QtWidgets import QSlider, QStyle, QStyleOptionSlider, QStylePainter
from ..qtcompat.QtWidgets import QSlider, QStyle, QStyleOptionSlider, QStylePainter
from ._generic_slider import CC_SLIDER, SC_GROOVE, SC_HANDLE, SC_NONE, _GenericSlider
from ._range_style import RangeSliderStyle, update_styles_from_stylesheet
_T = TypeVar("_T")

View File

@@ -22,9 +22,9 @@ QRangeSlider.
from typing import Generic, TypeVar
from .qtcompat import QtGui
from .qtcompat.QtCore import QEvent, QPoint, QPointF, QRect, Qt, Signal
from .qtcompat.QtWidgets import (
from ..qtcompat import QtGui
from ..qtcompat.QtCore import QEvent, QPoint, QPointF, QRect, Qt, Signal
from ..qtcompat.QtWidgets import (
QApplication,
QSlider,
QStyle,

View File

@@ -1,10 +1,9 @@
from enum import IntEnum
from functools import partial
from ._sliders import QDoubleRangeSlider, QDoubleSlider, QRangeSlider
from .qtcompat.QtCore import QPoint, QSize, Qt, Signal
from .qtcompat.QtGui import QFontMetrics, QValidator
from .qtcompat.QtWidgets import (
from ..qtcompat.QtCore import QPoint, QSize, Qt, Signal
from ..qtcompat.QtGui import QFontMetrics, QValidator
from ..qtcompat.QtWidgets import (
QAbstractSlider,
QApplication,
QDoubleSpinBox,
@@ -16,6 +15,7 @@ from .qtcompat.QtWidgets import (
QVBoxLayout,
QWidget,
)
from ._sliders import QDoubleRangeSlider, QDoubleSlider, QRangeSlider
class LabelPosition(IntEnum):
@@ -32,7 +32,7 @@ class EdgeLabelMode(IntEnum):
LabelIsValue = 2
class SliderProxy:
class _SliderProxy:
_slider: QSlider
def value(self):
@@ -112,7 +112,7 @@ def _handle_overloaded_slider_sig(args, kwargs):
return parent, orientation
class QLabeledSlider(SliderProxy, QAbstractSlider):
class QLabeledSlider(_SliderProxy, QAbstractSlider):
_slider_class = QSlider
_slider: QSlider
@@ -171,7 +171,7 @@ class QLabeledDoubleSlider(QLabeledSlider):
self._label.setDecimals(prec)
class QLabeledRangeSlider(SliderProxy, QAbstractSlider):
class QLabeledRangeSlider(_SliderProxy, QAbstractSlider):
valueChanged = Signal(tuple)
LabelPosition = LabelPosition
EdgeLabelMode = EdgeLabelMode

View File

@@ -5,9 +5,9 @@ import re
from dataclasses import dataclass, replace
from typing import TYPE_CHECKING
from .qtcompat import PYQT_VERSION
from .qtcompat.QtCore import Qt
from .qtcompat.QtGui import (
from ..qtcompat import PYQT_VERSION
from ..qtcompat.QtCore import Qt
from ..qtcompat.QtGui import (
QBrush,
QColor,
QGradient,
@@ -15,7 +15,7 @@ from .qtcompat.QtGui import (
QPalette,
QRadialGradient,
)
from .qtcompat.QtWidgets import QApplication, QSlider, QStyleOptionSlider
from ..qtcompat.QtWidgets import QApplication, QSlider, QStyleOptionSlider
if TYPE_CHECKING:
from ._generic_range_slider import _GenericRangeSlider
@@ -241,7 +241,7 @@ def parse_color(color: str, default_attr) -> QColor | QGradient:
# try linear gradient:
match = qlineargrad_pattern.search(color)
if match:
grad = QLinearGradient(*[float(i) for i in match.groups()[:4]])
grad = QLinearGradient(*(float(i) for i in match.groups()[:4]))
grad.setColorAt(0, QColor(match.groupdict()["stop0"]))
grad.setColorAt(1, QColor(match.groupdict()["stop1"]))
return grad
@@ -249,7 +249,7 @@ def parse_color(color: str, default_attr) -> QColor | QGradient:
# try linear gradient:
match = qradial_pattern.search(color)
if match:
grad = QRadialGradient(*[float(i) for i in match.groups()[:5]])
grad = QRadialGradient(*(float(i) for i in match.groups()[:5]))
grad.setColorAt(0, QColor(match.groupdict()["stop0"]))
grad.setColorAt(1, QColor(match.groupdict()["stop1"]))
return grad

View File

@@ -1,6 +1,6 @@
from ..qtcompat.QtCore import Signal
from ._generic_range_slider import _GenericRangeSlider
from ._generic_slider import _GenericSlider
from .qtcompat.QtCore import Signal
class _IntMixin:

View File

@@ -4,9 +4,9 @@ from platform import system
import pytest
from qtrangeslider.qtcompat import QT_VERSION
from qtrangeslider.qtcompat.QtCore import QEvent, QPoint, QPointF, Qt
from qtrangeslider.qtcompat.QtGui import QMouseEvent, QWheelEvent
from qwidgets.qtcompat import QT_VERSION
from qwidgets.qtcompat.QtCore import QEvent, QPoint, QPointF, Qt
from qwidgets.qtcompat.QtGui import QMouseEvent, QWheelEvent
QT_VERSION = LooseVersion(QT_VERSION)

View File

@@ -2,13 +2,13 @@ import os
import pytest
from qtrangeslider import (
from qwidgets import (
QDoubleRangeSlider,
QDoubleSlider,
QLabeledDoubleRangeSlider,
QLabeledDoubleSlider,
)
from qtrangeslider.qtcompat import API_NAME
from qwidgets.qtcompat import API_NAME
range_types = {QDoubleRangeSlider, QLabeledDoubleRangeSlider}

View File

@@ -2,10 +2,10 @@ import math
import pytest
from qtrangeslider._generic_slider import _GenericSlider
from qtrangeslider.qtcompat.QtCore import QEvent, QPoint, QPointF, Qt
from qtrangeslider.qtcompat.QtGui import QHoverEvent
from qtrangeslider.qtcompat.QtWidgets import QStyle, QStyleOptionSlider
from qwidgets.qtcompat.QtCore import QEvent, QPoint, QPointF, Qt
from qwidgets.qtcompat.QtGui import QHoverEvent
from qwidgets.qtcompat.QtWidgets import QStyle, QStyleOptionSlider
from qwidgets.sliders._generic_slider import _GenericSlider
from ._testutil import _linspace, _mouse_event, _wheel_event, skip_on_linux_qt6

View File

@@ -2,10 +2,10 @@ import math
import pytest
from qtrangeslider import QDoubleRangeSlider, QRangeSlider
from qtrangeslider.qtcompat.QtCore import QEvent, QPoint, QPointF, Qt
from qtrangeslider.qtcompat.QtGui import QHoverEvent
from qtrangeslider.qtcompat.QtWidgets import QStyle, QStyleOptionSlider
from qwidgets import QDoubleRangeSlider, QRangeSlider
from qwidgets.qtcompat.QtCore import QEvent, QPoint, QPointF, Qt
from qwidgets.qtcompat.QtGui import QHoverEvent
from qwidgets.qtcompat.QtWidgets import QStyle, QStyleOptionSlider
from ._testutil import _linspace, _mouse_event, _wheel_event, skip_on_linux_qt6

View File

@@ -4,11 +4,11 @@ from distutils.version import LooseVersion
import pytest
from qtrangeslider import QDoubleSlider, QLabeledDoubleSlider, QLabeledSlider
from qtrangeslider._generic_slider import _GenericSlider
from qtrangeslider.qtcompat.QtCore import QEvent, QPoint, QPointF, Qt
from qtrangeslider.qtcompat.QtGui import QHoverEvent
from qtrangeslider.qtcompat.QtWidgets import QSlider, QStyle, QStyleOptionSlider
from qwidgets import QDoubleSlider, QLabeledDoubleSlider, QLabeledSlider
from qwidgets.qtcompat.QtCore import QEvent, QPoint, QPointF, Qt
from qwidgets.qtcompat.QtGui import QHoverEvent
from qwidgets.qtcompat.QtWidgets import QSlider, QStyle, QStyleOptionSlider
from qwidgets.sliders._generic_slider import _GenericSlider
from ._testutil import (
QT_VERSION,

View File

@@ -2,10 +2,10 @@ import platform
import pytest
from qtrangeslider import QRangeSlider
from qtrangeslider._generic_range_slider import SC_BAR, SC_HANDLE, SC_NONE
from qtrangeslider.qtcompat import API_NAME
from qtrangeslider.qtcompat.QtCore import Qt
from qwidgets import QRangeSlider
from qwidgets.qtcompat import API_NAME
from qwidgets.qtcompat.QtCore import Qt
from qwidgets.sliders._generic_range_slider import SC_BAR, SC_HANDLE, SC_NONE
NOT_LINUX = platform.system() != "Linux"
NOT_PYSIDE2 = API_NAME != "PySide2"

View File

@@ -1,18 +1,13 @@
[metadata]
name = QtRangeSlider
url = https://github.com/tlambert03/QtRangeSlider
license = BSD-3
license_file = LICENSE
description = Multi-handle range slider widget for PyQt/PySide
long_description = file: README.md, CHANGELOG.md
name = qwidgets
description = Missing widgets for PyQt/PySide
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/napari/qwidgets
author = Talley Lambert
author_email = talley.lambert@gmail.com
keywords = qt, range slider, widget
project_urls =
Source = https://github.com/tlambert03/QtRangeSlider
Tracker = https://github.com/tlambert03/QtRangeSlider/issues
Changelog = https://github.com/tlambert03/QtRangeSlider/blob/master/CHANGELOG.md
license = BSD-3-Clause
license_file = LICENSE
classifiers =
Development Status :: 4 - Beta
Environment :: X11 Applications :: Qt
@@ -20,49 +15,69 @@ classifiers =
License :: OSI Approved :: BSD License
Operating System :: OS Independent
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: Implementation :: CPython
Topic :: Desktop Environment
Topic :: Software Development
Topic :: Software Development :: User Interfaces
Topic :: Software Development :: Widget Sets
keywords = qt, range slider, widget
project_urls =
Source = https://github.com/napari/qwidgets
Tracker = https://github.com/napari/qwidgets/issues
Changelog = https://github.com/napari/qwidgets/blob/master/CHANGELOG.md
[options]
zip_safe = False
packages = find:
python_requires = >=3.6
setup_requires = setuptools_scm
python_requires = >=3.7
setup_requires =
setuptools_scm
zip_safe = False
[options.extras_require]
pyside2 = pyside2
pyqt5 = pyqt5
pyside6 = pyside6
pyqt6 = pyqt6
testing =
tox
tox-conda
pytest
pytest-qt
pytest-cov
dev =
ipython
jedi<0.18.0
isort
jedi<0.18.0
mypy
pre-commit
%(testing)s
%(pyqt5)s
pyside2
pytest
pytest-cov
pytest-qt
tox
tox-conda
pyqt5 =
pyqt5
pyqt6 =
pyqt6
pyside2 =
pyside2
pyside6 =
pyside6
testing =
pytest
pytest-cov
pytest-qt
tox
tox-conda
[flake8]
exclude = _version.py,.eggs,examples
docstring-convention = numpy
ignore = E203,W503,E501,C901,F403,F405
ignore = E203,W503,E501,C901,F403,F405,D100
[pydocstyle]
convention = numpy
add_select = D402,D415,D417
ignore = D100
[isort]
profile=black
profile = black
[tool:pytest]
addopts = -W error

View File

@@ -1,10 +1,6 @@
"""
PEP 517 doesnt support editable installs
so this file is currently here to support "pip install -e ."
"""
from setuptools import setup
setup(
use_scm_version={"write_to": "qtrangeslider/_version.py"},
use_scm_version={"write_to": "qwidgets/_version.py"},
setup_requires=["setuptools_scm"],
)

14
tox.ini
View File

@@ -3,6 +3,18 @@
envlist = py{37,38,39}-{linux,macos,windows}-{pyqt5,pyside2,pyqt6,pyside6},py37-{linux,macos,windows}-{pyqt511,pyside511}
toxworkdir=/tmp/.tox
[coverage:report]
exclude_lines =
pragma: no cover
if TYPE_CHECKING:
\.\.\.
except ImportError*
raise NotImplementedError()
omit =
qwidgets/_version.py
qwidgets/qtcompat/*
*_tests*
[gh-actions]
python =
3.6: py36
@@ -45,4 +57,4 @@ extras =
pyside6: pyside6
commands_pre =
pyqt6,pyside6: pip install -U pytest-qt@git+https://github.com/pytest-dev/pytest-qt.git
commands = pytest --color=yes --cov=qtrangeslider --cov-report=xml {posargs}
commands = pytest --color=yes --cov=qwidgets --cov-report=xml {posargs}