sciwidgets 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sciwidgets/__init__.py +8 -0
- sciwidgets/dialogs/__init__.py +31 -0
- sciwidgets/dialogs/__main__.py +128 -0
- sciwidgets/dialogs/message_dialogs.py +553 -0
- sciwidgets/formatting.py +161 -0
- sciwidgets/labels/__init__.py +3 -0
- sciwidgets/labels/__main__.py +124 -0
- sciwidgets/labels/math_label.py +505 -0
- sciwidgets/params/__init__.py +18 -0
- sciwidgets/params/__main__.py +81 -0
- sciwidgets/params/base.py +220 -0
- sciwidgets/params/combo_box.py +106 -0
- sciwidgets/params/line_edit.py +132 -0
- sciwidgets/params/numeric_line_edit.py +680 -0
- sciwidgets/params/sliders/__init__.py +36 -0
- sciwidgets/params/sliders/__main__.py +215 -0
- sciwidgets/params/sliders/_defaults.py +36 -0
- sciwidgets/params/sliders/_dialogs.py +174 -0
- sciwidgets/params/sliders/_right_click_overlay.py +115 -0
- sciwidgets/params/sliders/base.py +592 -0
- sciwidgets/params/sliders/multi.py +660 -0
- sciwidgets/params/sliders/range.py +1841 -0
- sciwidgets/params/sliders/single.py +2042 -0
- sciwidgets/params/toggle.py +116 -0
- sciwidgets/style.py +175 -0
- sciwidgets-0.1.2.dist-info/METADATA +173 -0
- sciwidgets-0.1.2.dist-info/RECORD +30 -0
- sciwidgets-0.1.2.dist-info/WHEEL +5 -0
- sciwidgets-0.1.2.dist-info/licenses/LICENSE +21 -0
- sciwidgets-0.1.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Boolean parameter widget backed by a checkbox with an optional state label."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from PySide6.QtCore import Qt
|
|
6
|
+
from PySide6.QtWidgets import QGridLayout, QWidget, QCheckBox, QStackedWidget
|
|
7
|
+
|
|
8
|
+
from ..labels import MathLabel
|
|
9
|
+
from .base import ParameterWidgetBase
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ParameterToggle(ParameterWidgetBase[bool]):
|
|
13
|
+
"""Parameter widget with a checkbox and an optional state-dependent label."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
text: str,
|
|
18
|
+
label_on: str | None = None,
|
|
19
|
+
label_off: str | None = None,
|
|
20
|
+
checked: bool = True,
|
|
21
|
+
parent: QWidget | None = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
"""Initialize the toggle widget.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
text: Checkbox text.
|
|
27
|
+
label_on: Label shown when the checkbox is checked. ``None`` shows
|
|
28
|
+
nothing in that state.
|
|
29
|
+
label_off: Label shown when the checkbox is unchecked. ``None``
|
|
30
|
+
shows nothing in that state.
|
|
31
|
+
checked: Initial checked state.
|
|
32
|
+
parent: Optional parent widget.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
super().__init__(parent)
|
|
36
|
+
|
|
37
|
+
self._check = QCheckBox(text, self)
|
|
38
|
+
self._check.setChecked(checked)
|
|
39
|
+
self._stack: QStackedWidget | None = None
|
|
40
|
+
if label_on is not None or label_off is not None:
|
|
41
|
+
self._stack = QStackedWidget(self)
|
|
42
|
+
align = Qt.AlignRight | Qt.AlignVCenter
|
|
43
|
+
for lbl in (label_off, label_on): # index 0 = off, 1 = on
|
|
44
|
+
self._stack.addWidget(
|
|
45
|
+
MathLabel(lbl, alignment=align, fix_size=True) if lbl is not None else QWidget()
|
|
46
|
+
)
|
|
47
|
+
self._stack.setCurrentIndex(int(checked))
|
|
48
|
+
|
|
49
|
+
self._check.toggled.connect(self._sync_label)
|
|
50
|
+
self._check.clicked.connect(self._on_clicked)
|
|
51
|
+
|
|
52
|
+
self._make_layout()
|
|
53
|
+
|
|
54
|
+
def _make_layout(self) -> None:
|
|
55
|
+
"""Create the grid: [name spacer | checkbox | stretch | label | value spacer]."""
|
|
56
|
+
|
|
57
|
+
gl = QGridLayout(self)
|
|
58
|
+
gl.setSpacing(0)
|
|
59
|
+
gl.setContentsMargins(0, 0, 0, 0)
|
|
60
|
+
gl.addWidget(self._check, 0, 1)
|
|
61
|
+
if self._stack is not None:
|
|
62
|
+
gl.addWidget(self._stack, 0, 3, Qt.AlignRight | Qt.AlignVCenter)
|
|
63
|
+
gl.setColumnStretch(2, 1)
|
|
64
|
+
self._gl = gl
|
|
65
|
+
self._update_layout()
|
|
66
|
+
|
|
67
|
+
def _update_layout(self) -> None:
|
|
68
|
+
"""Indent the checkbox by half the shared name width of sibling widgets."""
|
|
69
|
+
self._gl.setColumnMinimumWidth(0, self._effective_name_width() // 2)
|
|
70
|
+
|
|
71
|
+
def _sync_label(self, state: bool) -> None:
|
|
72
|
+
"""Show the label for the current state (any change)."""
|
|
73
|
+
if self._stack is not None:
|
|
74
|
+
self._stack.setCurrentIndex(int(state))
|
|
75
|
+
|
|
76
|
+
def _on_clicked(self, state: bool) -> None:
|
|
77
|
+
"""Emit the new value after a user click."""
|
|
78
|
+
self.valueChanged.emit(state)
|
|
79
|
+
|
|
80
|
+
def get_value(self) -> bool:
|
|
81
|
+
"""Return whether the checkbox is checked.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Current checked state.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
return self._check.isChecked()
|
|
88
|
+
|
|
89
|
+
def _validate_value(self, value: bool) -> bool:
|
|
90
|
+
"""Validate a value before applying it.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
value: Proposed checked state.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Validated checked state.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
TypeError: If ``value`` is not a bool.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
if not isinstance(value, bool):
|
|
103
|
+
raise TypeError("'value' must be a bool")
|
|
104
|
+
return value
|
|
105
|
+
|
|
106
|
+
def _apply_value(self, value: bool) -> None:
|
|
107
|
+
"""Apply a validated value to the checkbox.
|
|
108
|
+
|
|
109
|
+
The label follows automatically through the ``toggled`` signal.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
value: Checked state to apply.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
self._check.setChecked(value)
|
|
116
|
+
|
sciwidgets/style.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Qt style and style-sheet fragments that sciwidgets widgets rely on.
|
|
2
|
+
|
|
3
|
+
The parameter widgets depend on these for behaviour and appearance (click-to-
|
|
4
|
+
position sliders, disabled slider grooves, the red frame on invalid numeric
|
|
5
|
+
input, value-label colors). Apps should call::
|
|
6
|
+
|
|
7
|
+
app.setStyle(SciStyle())
|
|
8
|
+
app.setStyleSheet(widget_style_sheet(app) + app_specific_sheet)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
from PySide6.QtCore import Qt
|
|
16
|
+
from PySide6.QtGui import QColor, QPen
|
|
17
|
+
from PySide6.QtWidgets import (
|
|
18
|
+
QApplication,
|
|
19
|
+
QLineEdit,
|
|
20
|
+
QProxyStyle,
|
|
21
|
+
QStyle,
|
|
22
|
+
QStyleFactory,
|
|
23
|
+
QStyleOptionSlider,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = ["SciStyle", "sci_style_sheet", "apply_sci_style"]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SciStyle(QProxyStyle):
|
|
30
|
+
"""Fusion-based proxy style required by the sciwidgets widgets."""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
"""Initialize the proxy style with Fusion as the base style."""
|
|
34
|
+
|
|
35
|
+
base = QStyleFactory.create("Fusion")
|
|
36
|
+
super().__init__(base)
|
|
37
|
+
|
|
38
|
+
def styleHint(self, hint, option=None, widget=None, returnData=None):
|
|
39
|
+
"""Return a Qt style hint for the requested control.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
hint: Requested style-hint identifier.
|
|
43
|
+
option: Optional style option associated with the request.
|
|
44
|
+
widget: Optional widget associated with the request.
|
|
45
|
+
returnData: Optional return-data holder supplied by Qt.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Style-hint value for the requested control.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
if hint == QStyle.SH_Slider_AbsoluteSetButtons:
|
|
52
|
+
return Qt.LeftButton.value
|
|
53
|
+
if hint == QStyle.SH_Slider_PageSetButtons:
|
|
54
|
+
return 0
|
|
55
|
+
return super().styleHint(hint, option, widget, returnData)
|
|
56
|
+
|
|
57
|
+
def drawComplexControl(self, control, option, painter, widget=None):
|
|
58
|
+
"""Draw a customized Qt complex control.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
control: Complex-control type to draw.
|
|
62
|
+
option: Style option describing the control state.
|
|
63
|
+
painter: Painter used for rendering.
|
|
64
|
+
widget: Optional widget being drawn.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
if control == QStyle.CC_Slider and isinstance(option, QStyleOptionSlider):
|
|
68
|
+
if not (option.state & QStyle.State_Enabled):
|
|
69
|
+
requested = option.subControls
|
|
70
|
+
if requested == QStyle.SC_None:
|
|
71
|
+
requested = (
|
|
72
|
+
QStyle.SC_SliderGroove
|
|
73
|
+
| QStyle.SC_SliderHandle
|
|
74
|
+
| QStyle.SC_SliderTickmarks
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if requested & QStyle.SC_SliderGroove:
|
|
78
|
+
groove_rect = self.subControlRect(
|
|
79
|
+
QStyle.CC_Slider,
|
|
80
|
+
option,
|
|
81
|
+
QStyle.SC_SliderGroove,
|
|
82
|
+
widget,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
painter.save()
|
|
86
|
+
painter.setPen(QPen(QColor("#b3b3b3"), 1))
|
|
87
|
+
painter.setBrush(QColor("#cccccc"))
|
|
88
|
+
r = groove_rect.adjusted(1, 2, -1, -1)
|
|
89
|
+
painter.drawRoundedRect(r, 1, 1)
|
|
90
|
+
painter.restore()
|
|
91
|
+
|
|
92
|
+
fg = requested & (QStyle.SC_SliderTickmarks | QStyle.SC_SliderHandle)
|
|
93
|
+
if fg:
|
|
94
|
+
opt_fg = QStyleOptionSlider(option)
|
|
95
|
+
opt_fg.subControls = fg
|
|
96
|
+
super().drawComplexControl(control, opt_fg, painter, widget)
|
|
97
|
+
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
return super().drawComplexControl(control, option, painter, widget)
|
|
101
|
+
|
|
102
|
+
return super().drawComplexControl(control, option, painter, widget)
|
|
103
|
+
|
|
104
|
+
def drawPrimitive(self, element, option, painter, widget=None):
|
|
105
|
+
"""Draw a customized Qt primitive element.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
element: Primitive-element type to draw.
|
|
109
|
+
option: Style option describing the primitive state.
|
|
110
|
+
painter: Painter used for rendering.
|
|
111
|
+
widget: Optional widget being drawn.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
super().drawPrimitive(element, option, painter, widget)
|
|
115
|
+
|
|
116
|
+
if element == QStyle.PE_PanelLineEdit and isinstance(widget, QLineEdit):
|
|
117
|
+
if (option.state & QStyle.State_Enabled) and bool(widget.property("invalid")):
|
|
118
|
+
painter.save()
|
|
119
|
+
try:
|
|
120
|
+
pen = QPen(QColor("#d32f2f"))
|
|
121
|
+
pen.setWidth(3)
|
|
122
|
+
pen.setCapStyle(Qt.RoundCap)
|
|
123
|
+
pen.setJoinStyle(Qt.RoundJoin)
|
|
124
|
+
painter.setPen(pen)
|
|
125
|
+
painter.setBrush(Qt.NoBrush)
|
|
126
|
+
r = option.rect.adjusted(1, 1, -1, -1)
|
|
127
|
+
painter.drawRect(r)
|
|
128
|
+
finally:
|
|
129
|
+
painter.restore()
|
|
130
|
+
|
|
131
|
+
def _font_rule(selector: str, size: int, family: str, color: Optional[str] = None) -> str:
|
|
132
|
+
"""Build a font (and optional color) rule for one selector."""
|
|
133
|
+
rule = f"{selector}{{font-size: {size}pt; font-family: {family};"
|
|
134
|
+
if color is None:
|
|
135
|
+
return rule + "}"
|
|
136
|
+
c = QColor(color)
|
|
137
|
+
disabled = QColor(color)
|
|
138
|
+
disabled.setAlpha(50)
|
|
139
|
+
return (
|
|
140
|
+
rule + f"color: {c.name()};}}"
|
|
141
|
+
f"{selector}:disabled{{color: {disabled.name(QColor.HexArgb)};}}"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def sci_style_sheet(app: QApplication) -> str:
|
|
146
|
+
"""Return the style-sheet rules the sciwidgets widgets expect.
|
|
147
|
+
|
|
148
|
+
Sizes are relative to the application's current font, so call this after
|
|
149
|
+
the app font is set.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
app: Running application whose font is used as the base.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Style-sheet text to include in the app's own style sheet.
|
|
156
|
+
"""
|
|
157
|
+
f = app.font()
|
|
158
|
+
fs, fm = f.pointSize(), f.family()
|
|
159
|
+
return (
|
|
160
|
+
_font_rule("QLabel#slider_value", fs + 1, fm, "blue")
|
|
161
|
+
+ _font_rule("QLabel#slider_range", fs - 1, fm, "black")
|
|
162
|
+
+ _font_rule("QLineEdit", fs + 1, fm, "blue")
|
|
163
|
+
+ _font_rule("QSpinBox", fs + 1, fm, "blue")
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def apply_sci_style(app: QApplication, extra_style_sheet: str = "") -> None:
|
|
167
|
+
"""Set SciStyle and the sciwidgets style sheet on ``app``.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
app: Application to style.
|
|
171
|
+
extra_style_sheet: App-specific rules appended after the
|
|
172
|
+
sciwidgets rules.
|
|
173
|
+
"""
|
|
174
|
+
app.setStyle(SciStyle())
|
|
175
|
+
app.setStyleSheet(sci_style_sheet(app) + extra_style_sheet)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sciwidgets
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: PySide6 widgets for scientific desktop apps: math labels, sliders, parameter inputs and dialogs
|
|
5
|
+
Author-email: Vahid Anari <vahid.anari8@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/vahid-anari/sciwidgets
|
|
8
|
+
Project-URL: Repository, https://github.com/vahid-anari/sciwidgets
|
|
9
|
+
Project-URL: Issues, https://github.com/vahid-anari/sciwidgets/issues
|
|
10
|
+
Keywords: pyside6,qt,gui,widgets,scientific,slider,range-slider,latex,matplotlib,dialogs
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering
|
|
20
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: PySide6<7,>=6.5
|
|
25
|
+
Requires-Dist: matplotlib>=3.7
|
|
26
|
+
Requires-Dist: numpy>=1.24
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: build; extra == "dev"
|
|
29
|
+
Requires-Dist: twine; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# sciwidgets
|
|
33
|
+
|
|
34
|
+
PySide6 widgets for scientific desktop apps: math labels rendered with
|
|
35
|
+
LaTeX/mathtext, dialogs, and parameter inputs such as sliders, range sliders
|
|
36
|
+
and validated number fields.
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
Right-click menu, Set Value and Edit Configuration:
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
The widgets were written for the
|
|
45
|
+
[Maxwell Bloch Solver](https://github.com/vahid-anari/maxwell-bloch-solver)
|
|
46
|
+
and moved into this package so other apps can share them.
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- **Math labels**: `MathLabel` renders LaTeX through Matplotlib and scales
|
|
51
|
+
cleanly as SVG. It uses your system LaTeX when installed and falls back to
|
|
52
|
+
Matplotlib's mathtext otherwise.
|
|
53
|
+
- **Sliders**: integer, float, array, range and multi-variable sliders with
|
|
54
|
+
LaTeX names and units, optional range labels, a default-value marker and a
|
|
55
|
+
right-click menu (save/reset default, set value, ±inf, edit range and format).
|
|
56
|
+
- **Scientific number format**: `"{:.3S}"` displays values as `1.23×10⁴`.
|
|
57
|
+
- **Validated input**: `NumericLineEdit` checks numbers and limits while typing
|
|
58
|
+
and marks invalid input with a red frame.
|
|
59
|
+
- **Parameter widgets**: line edit, combo box and toggle sharing one interface
|
|
60
|
+
with the sliders (`get_value` / `set_value`, `get_config` / `set_config`) and
|
|
61
|
+
name/value columns that line up across a panel.
|
|
62
|
+
- **Dialogs**: information, warning, error, question, rich-text and About
|
|
63
|
+
dialogs.
|
|
64
|
+
|
|
65
|
+
## Components
|
|
66
|
+
|
|
67
|
+
| Import | Contents |
|
|
68
|
+
|--------|----------|
|
|
69
|
+
| `sciwidgets.labels` | `MathLabel` (LaTeX/mathtext label), `set_latex_enabled` |
|
|
70
|
+
| `sciwidgets.params` | `FloatSlider`, `IntSlider`, `ArraySlider`, `FloatRangeSlider`, `IntRangeSlider`, `MultiVariableSlider`, `ParameterLineEdit`, `ParameterComboBox`, `ParameterToggle`, `NumericLineEdit`, `SliderFeature` |
|
|
71
|
+
| `sciwidgets.dialogs` | `show_information`, `show_warning`, `show_critical`, `show_rich_information`, `show_about_dialog`, `ask_question`, `AskResult` |
|
|
72
|
+
| `sciwidgets.style` | `SciStyle`, `apply_sci_style`, `sci_style_sheet` |
|
|
73
|
+
| `sciwidgets.formatting` | `pretty_sci_text`, `value_to_text` and other format helpers |
|
|
74
|
+
|
|
75
|
+
## Install
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install sciwidgets
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Requires Python 3.10+, PySide6 6.5+, Matplotlib and NumPy (installed
|
|
82
|
+
automatically). A LaTeX installation is optional.
|
|
83
|
+
|
|
84
|
+
For development, from the repo root:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install -e .
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Quick start
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import sys
|
|
94
|
+
|
|
95
|
+
from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget
|
|
96
|
+
|
|
97
|
+
from sciwidgets.params import FloatRangeSlider, FloatSlider
|
|
98
|
+
from sciwidgets.style import apply_sci_style
|
|
99
|
+
|
|
100
|
+
app = QApplication(sys.argv)
|
|
101
|
+
apply_sci_style(app) # required, see "Style" below
|
|
102
|
+
|
|
103
|
+
win = QWidget()
|
|
104
|
+
layout = QVBoxLayout(win)
|
|
105
|
+
|
|
106
|
+
t1 = FloatSlider(
|
|
107
|
+
label="T_1", unit=r"\mathrm{ms}",
|
|
108
|
+
min_val=0.1, max_val=10.0, init_val=1.0, val_fmt="{:.2f}",
|
|
109
|
+
min_limit=0.0, min_limit_inclusive=False,
|
|
110
|
+
)
|
|
111
|
+
band = FloatRangeSlider(
|
|
112
|
+
label=r"\nu", unit=r"\mathrm{GHz}",
|
|
113
|
+
min_val=6.60, max_val=6.75, init_vals=(6.65, 6.70), val_fmt="{:.3f}",
|
|
114
|
+
)
|
|
115
|
+
layout.addWidget(t1)
|
|
116
|
+
layout.addWidget(band)
|
|
117
|
+
|
|
118
|
+
t1.valueChanged.connect(lambda v: print("T1 =", v))
|
|
119
|
+
band.valueChanged.connect(lambda v: print("band =", v["range"]))
|
|
120
|
+
|
|
121
|
+
win.show()
|
|
122
|
+
sys.exit(app.exec())
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Labels and units are written in LaTeX math mode, without the `$` signs.
|
|
126
|
+
|
|
127
|
+
## Style
|
|
128
|
+
|
|
129
|
+
The widgets rely on `SciStyle`, a Fusion-based Qt style, for part of their
|
|
130
|
+
behaviour: clicking a slider groove moves the handle there, disabled sliders
|
|
131
|
+
get a flat groove, and invalid numeric input gets a red frame. Apply it once
|
|
132
|
+
at startup:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from sciwidgets.style import apply_sci_style
|
|
136
|
+
|
|
137
|
+
apply_sci_style(app, my_app_style_sheet) # your own rules are appended
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Signals
|
|
141
|
+
|
|
142
|
+
`valueChanged`, `defaultChanged` and `configChanged` are emitted **only for
|
|
143
|
+
user actions** (dragging, typing, the context menu, dialogs). Setting values
|
|
144
|
+
from code with `set_value`, `set_config` and similar methods is silent, so
|
|
145
|
+
loading saved parameters never triggers your handlers. Read the new state
|
|
146
|
+
with `get_value()` / `get_config()` after setting it.
|
|
147
|
+
|
|
148
|
+
## Demos
|
|
149
|
+
|
|
150
|
+
Each subpackage has a runnable demo:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
python -m sciwidgets.labels
|
|
154
|
+
python -m sciwidgets.dialogs
|
|
155
|
+
python -m sciwidgets.params
|
|
156
|
+
python -m sciwidgets.params.sliders
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Versions
|
|
160
|
+
|
|
161
|
+
Releases are Git tags following [semantic versioning](https://semver.org).
|
|
162
|
+
Pin an exact tag in your app's requirements, as in the install line above,
|
|
163
|
+
and move to a newer tag when you are ready to test it.
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT License. See [LICENSE](https://github.com/vahid-anari/sciwidgets/blob/main/LICENSE).
|
|
168
|
+
|
|
169
|
+
## Author
|
|
170
|
+
|
|
171
|
+
Vahid Anari ·
|
|
172
|
+
[GitHub](https://github.com/vahid-anari) ·
|
|
173
|
+
[LinkedIn](https://www.linkedin.com/in/vahid-anari/)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
sciwidgets/__init__.py,sha256=r-K30aP-g6CZlFJ-EqYPHT3gS9g4ZVHggSqw-3RBwDk,217
|
|
2
|
+
sciwidgets/formatting.py,sha256=E_CNrkSIEuSgFbkv8TvAULTWwJOXCraiI-ccMN7eV1E,4820
|
|
3
|
+
sciwidgets/style.py,sha256=_yy7ORDvKqF0_-_tBVi2vL0YX1HU_ohEjlE1jeGr0Uo,6236
|
|
4
|
+
sciwidgets/dialogs/__init__.py,sha256=FpBxh_Kj6LdxvIRHppqxoKSDwnrwRxxf3yWJKfvrfUs,567
|
|
5
|
+
sciwidgets/dialogs/__main__.py,sha256=gvw2FNCkYlaYZ_KYPQ9ExbwTHFm46U7UiJ56HsMSZss,4157
|
|
6
|
+
sciwidgets/dialogs/message_dialogs.py,sha256=lSy_7ZPI5eoVs9QF-EZA_hLJ3if4rAePs_q2aX-eWJ0,16097
|
|
7
|
+
sciwidgets/labels/__init__.py,sha256=zPM_aK-eUApoqx4jvE65Y8UVEDNEf7nlEoK5ntydj1A,98
|
|
8
|
+
sciwidgets/labels/__main__.py,sha256=bMGjYBNuspuNpKGwCvMevtuoN0uM_PnNBli1VskpIDA,4488
|
|
9
|
+
sciwidgets/labels/math_label.py,sha256=NGty2sIaEqoKIHVyrga77YevnymUV_WplXF4pqnEIKw,17608
|
|
10
|
+
sciwidgets/params/__init__.py,sha256=q6s6l2MnIgBToTvThmra-ejIYB1BCY04MughQDTh7-c,525
|
|
11
|
+
sciwidgets/params/__main__.py,sha256=gHGRn5z_RnvzFnvOHefBSeWZ-zOy7G3sQM52hVidDcg,2742
|
|
12
|
+
sciwidgets/params/base.py,sha256=HVdCmdHIbKGdy1TpIIl6RuXd16z3YI88oiNkPGCwa8A,6814
|
|
13
|
+
sciwidgets/params/combo_box.py,sha256=GfSHLVdCt3XomHbjGUqacv7JD3AutjSGlZDnaPim9lo,3182
|
|
14
|
+
sciwidgets/params/line_edit.py,sha256=ZZ0qatP6xLPk1hJmHZ9dlo16s-SjVazhiWAFhLWuztk,4638
|
|
15
|
+
sciwidgets/params/numeric_line_edit.py,sha256=dpPafUljI0ESqam0kxyb8EMfEn_qnv1m6_QLjBBP30o,24363
|
|
16
|
+
sciwidgets/params/toggle.py,sha256=RKvBiO9TO053N3PPDCLNeiJX6pGRZ9Aufndoxef2YQg,3733
|
|
17
|
+
sciwidgets/params/sliders/__init__.py,sha256=qjhW36zgKRP8JSQZTK3C8YtR5Yj0VfvBXXQlH11J4L0,775
|
|
18
|
+
sciwidgets/params/sliders/__main__.py,sha256=xXnqhP9EqBuTM1gRy7UXVnzu7xG-NRUbg-YPIsD_F68,7804
|
|
19
|
+
sciwidgets/params/sliders/_defaults.py,sha256=DSn2yMLA05WuE9R8KVqdL87b-776HZdgMpuXmr_24hU,990
|
|
20
|
+
sciwidgets/params/sliders/_dialogs.py,sha256=YKtMvZiXBUau6S3qlOMf3Ie0bvnN0tE8LgfKTPHMygw,5428
|
|
21
|
+
sciwidgets/params/sliders/_right_click_overlay.py,sha256=rHx-DYlthY4huckcRJl1i_9_JpRX1ZLqmknpfJvEiXk,3610
|
|
22
|
+
sciwidgets/params/sliders/base.py,sha256=GqCNdKb6dlSv0CbcvB_lzhA5DcXVRJhH0kR_FOwPZgc,20735
|
|
23
|
+
sciwidgets/params/sliders/multi.py,sha256=cARyUSvn3aRU2qzQJb0RK2NYyMrzXGAoYkiInOObnOo,25931
|
|
24
|
+
sciwidgets/params/sliders/range.py,sha256=e1QPm0t8t3eTYb0GoGfA6qoRQ1U5ljiR6pGzZnDdlYo,66384
|
|
25
|
+
sciwidgets/params/sliders/single.py,sha256=ONzXXqevDD5t1caiK-i4sYEnB_GnvbVIt50PP8hI7lQ,71905
|
|
26
|
+
sciwidgets-0.1.2.dist-info/licenses/LICENSE,sha256=NxE8TJTE_jzmey7jXXehVFF3x5IiTnmgaYA5qrrfj60,1067
|
|
27
|
+
sciwidgets-0.1.2.dist-info/METADATA,sha256=KHv175m02N4bfDhvVROjIQ6oEf-XOqczYByr5Dyu_IM,6094
|
|
28
|
+
sciwidgets-0.1.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
29
|
+
sciwidgets-0.1.2.dist-info/top_level.txt,sha256=6WV5UP6JbqXIKH-mBiWnQPKT_dfPO3haUSug8fKl0-4,11
|
|
30
|
+
sciwidgets-0.1.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vahid Anari
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sciwidgets
|