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.
@@ -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
+ ![Sliders demo](https://raw.githubusercontent.com/vahid-anari/sciwidgets/main/docs/images/sliders_demo.png)
39
+
40
+ Right-click menu, Set Value and Edit Configuration:
41
+
42
+ ![Slider interaction](https://raw.githubusercontent.com/vahid-anari/sciwidgets/main/docs/images/sliders_demo.gif)
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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -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