triplot 1.1.0__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.
- dscpanel/__init__.py +4 -0
- dscpanel/__main__.py +155 -0
- dscpanel/branding.py +142 -0
- dscpanel/core/__init__.py +0 -0
- dscpanel/core/arrange.py +143 -0
- dscpanel/core/chem.py +252 -0
- dscpanel/core/dtg.py +135 -0
- dscpanel/core/export.py +649 -0
- dscpanel/core/figure.py +171 -0
- dscpanel/core/labels.py +430 -0
- dscpanel/core/loader.py +202 -0
- dscpanel/core/log.py +147 -0
- dscpanel/core/measure.py +636 -0
- dscpanel/core/model.py +1993 -0
- dscpanel/core/molar.py +344 -0
- dscpanel/core/numbers.py +208 -0
- dscpanel/core/ops.py +161 -0
- dscpanel/core/presets.py +312 -0
- dscpanel/core/profile.py +15 -0
- dscpanel/core/session.py +667 -0
- dscpanel/core/shades.py +54 -0
- dscpanel/core/style.py +528 -0
- dscpanel/core/trios_analysis.py +636 -0
- dscpanel/core/trios_io.py +1311 -0
- dscpanel/core/undo.py +230 -0
- dscpanel/core/units.py +220 -0
- dscpanel/register.py +284 -0
- dscpanel/ui/__init__.py +0 -0
- dscpanel/ui/appearance.py +146 -0
- dscpanel/ui/colour.py +629 -0
- dscpanel/ui/dialogs.py +3639 -0
- dscpanel/ui/loading.py +95 -0
- dscpanel/ui/numbox.py +103 -0
- dscpanel/ui/outliner.py +818 -0
- dscpanel/ui/palette.py +193 -0
- dscpanel/ui/plot.py +8349 -0
- dscpanel/ui/settings.py +256 -0
- dscpanel/ui/window.py +4129 -0
- triplot-1.1.0.dist-info/METADATA +315 -0
- triplot-1.1.0.dist-info/RECORD +44 -0
- triplot-1.1.0.dist-info/WHEEL +5 -0
- triplot-1.1.0.dist-info/entry_points.txt +5 -0
- triplot-1.1.0.dist-info/licenses/LICENSE +22 -0
- triplot-1.1.0.dist-info/top_level.txt +1 -0
dscpanel/ui/settings.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Edit > Settings: the house style, for every figure and for this one.
|
|
2
|
+
|
|
3
|
+
Two columns, because two things are wanted at once: defaults that persist
|
|
4
|
+
between sessions, and a way to override them for one particular save
|
|
5
|
+
file.
|
|
6
|
+
|
|
7
|
+
* **Default** is the user's own house style. It lives on this computer
|
|
8
|
+
(`core/style.py`, `preferences.json`), is written when the page is closed
|
|
9
|
+
with OK, and is what every figure falls back on.
|
|
10
|
+
* **This figure** belongs to the open document and is saved in its session
|
|
11
|
+
file. Empty ("default") means "whatever the default says"; a value here
|
|
12
|
+
wins for this figure alone. The change is one undo step.
|
|
13
|
+
|
|
14
|
+
A size set in an object's OWN settings (double-click a label) wins over both
|
|
15
|
+
columns, because that was a decision about that one object.
|
|
16
|
+
|
|
17
|
+
Below them, **Handling**: how the program responds to the hand - the pick
|
|
18
|
+
distance. It has a Default and nothing else, because it is about the person
|
|
19
|
+
at the trackpad and not about any figure.
|
|
20
|
+
|
|
21
|
+
Live, like every dialog here: the plot follows each change as it is made.
|
|
22
|
+
Closing the page any way keeps the changes; Revert puts back what was there
|
|
23
|
+
when it opened - both columns.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from PySide6.QtCore import Qt
|
|
27
|
+
from PySide6.QtWidgets import (QDialog, QDialogButtonBox, QFrame,
|
|
28
|
+
QGridLayout, QLabel, QScrollArea, QVBoxLayout,
|
|
29
|
+
QWidget)
|
|
30
|
+
|
|
31
|
+
from ..core import style
|
|
32
|
+
from .dialogs import (FontChoice, NumberBox, StyleChoice, StyleNumber,
|
|
33
|
+
StyleText, enter_stays, readable)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class SettingsDialog(QDialog):
|
|
37
|
+
"""The house style, as a two-column table."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, window):
|
|
40
|
+
QDialog.__init__(self, window)
|
|
41
|
+
self.setWindowTitle("Settings")
|
|
42
|
+
# Not modal, like the object dialogs: a size is judged by looking
|
|
43
|
+
# at the plot, and a modal page hides it.
|
|
44
|
+
self.setModal(False)
|
|
45
|
+
self.setWindowFlag(Qt.Tool, True)
|
|
46
|
+
self.main = window
|
|
47
|
+
self.doc = window.doc
|
|
48
|
+
self._saved_defaults = style.preferences()
|
|
49
|
+
self._saved_figure = dict((s.key, getattr(self.doc.style, s.key))
|
|
50
|
+
for s in style.FIGURE_SETTINGS)
|
|
51
|
+
self.defaults = {}
|
|
52
|
+
self.figure = {}
|
|
53
|
+
|
|
54
|
+
layout = QVBoxLayout(self)
|
|
55
|
+
# The rows SCROLL: the list only gets longer, and a page taller than
|
|
56
|
+
# the screen puts its OK button out of reach.
|
|
57
|
+
self._scroll = QScrollArea(self)
|
|
58
|
+
self._scroll.setWidgetResizable(True)
|
|
59
|
+
self._scroll.setFrameShape(QFrame.NoFrame)
|
|
60
|
+
self._scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
|
61
|
+
body = QWidget(self._scroll)
|
|
62
|
+
self._scroll.setWidget(body)
|
|
63
|
+
grid = QGridLayout(body)
|
|
64
|
+
grid.setContentsMargins(0, 0, 8, 0)
|
|
65
|
+
grid.setHorizontalSpacing(12)
|
|
66
|
+
layout.addWidget(self._scroll, 1)
|
|
67
|
+
for column, text in ((1, "Default"), (2, "This figure")):
|
|
68
|
+
head = QLabel("<b>{}</b>".format(text))
|
|
69
|
+
grid.addWidget(head, 0, column)
|
|
70
|
+
handling = [s for s in style.SETTINGS if not s.figure]
|
|
71
|
+
rows = list(style.FIGURE_SETTINGS) + ([None] if handling else []) + handling
|
|
72
|
+
for row, setting in enumerate(rows, start=1):
|
|
73
|
+
if setting is None:
|
|
74
|
+
head = QLabel("<b>Handling</b>")
|
|
75
|
+
head.setContentsMargins(0, 10, 0, 0)
|
|
76
|
+
grid.addWidget(head, row, 0)
|
|
77
|
+
continue
|
|
78
|
+
name = QLabel(setting.title)
|
|
79
|
+
if setting.note:
|
|
80
|
+
name.setToolTip(setting.note)
|
|
81
|
+
grid.addWidget(name, row, 0)
|
|
82
|
+
key = setting.key
|
|
83
|
+
if setting.kind in ("format", "font"):
|
|
84
|
+
# The Default column's None is "the built-in value"; the
|
|
85
|
+
# figure's is "whatever the default says".
|
|
86
|
+
kind = StyleText if setting.kind == "format" else FontChoice
|
|
87
|
+
default = kind(style.user_preference(key),
|
|
88
|
+
(lambda k=key: style.builtin(k)),
|
|
89
|
+
parent=self, reset_text="Built-in")
|
|
90
|
+
default.changed.connect(
|
|
91
|
+
lambda k=key: self._default_changed(k))
|
|
92
|
+
own = kind(getattr(self.doc.style, key),
|
|
93
|
+
(lambda k=key: style.preference(k)), parent=self)
|
|
94
|
+
elif setting.kind == "choice":
|
|
95
|
+
titles = setting.titles
|
|
96
|
+
default = StyleChoice(setting.choices, style.preference(key),
|
|
97
|
+
lambda: "", parent=self,
|
|
98
|
+
titles=titles, allow_default=False)
|
|
99
|
+
default.changed.connect(
|
|
100
|
+
lambda k=key: self._default_changed(k))
|
|
101
|
+
own = StyleChoice(
|
|
102
|
+
setting.choices, getattr(self.doc.style, key),
|
|
103
|
+
(lambda k=key, t=titles: t.get(
|
|
104
|
+
style.preference(k), style.preference(k))),
|
|
105
|
+
parent=self, titles=titles)
|
|
106
|
+
else:
|
|
107
|
+
default = NumberBox(self)
|
|
108
|
+
default.setDecimals(setting.decimals)
|
|
109
|
+
default.setRange(float(setting.low), float(setting.high))
|
|
110
|
+
default.setSingleStep(float(setting.step))
|
|
111
|
+
default.setSuffix(setting.suffix)
|
|
112
|
+
default.setValue(float(style.preference(key)))
|
|
113
|
+
default.valueChanged.connect(
|
|
114
|
+
lambda _v=0.0, k=key: self._default_changed(k))
|
|
115
|
+
own = None
|
|
116
|
+
if setting.figure:
|
|
117
|
+
own = StyleNumber(setting, getattr(self.doc.style, key),
|
|
118
|
+
(lambda k=key: style.preference(k)),
|
|
119
|
+
parent=self)
|
|
120
|
+
self.defaults[key] = default
|
|
121
|
+
grid.addWidget(default, row, 1)
|
|
122
|
+
if setting.note and not setting.figure:
|
|
123
|
+
default.setToolTip(setting.note)
|
|
124
|
+
if own is not None:
|
|
125
|
+
own.changed.connect(lambda k=key: self._figure_changed(k))
|
|
126
|
+
self.figure[key] = own
|
|
127
|
+
grid.addWidget(own, row, 2)
|
|
128
|
+
|
|
129
|
+
note = QLabel(
|
|
130
|
+
"Default: this computer, every figure. This figure: saved in its "
|
|
131
|
+
"session. An object's own setting wins over both.")
|
|
132
|
+
note.setWordWrap(True)
|
|
133
|
+
note.setStyleSheet("color: #9a9a9a;")
|
|
134
|
+
layout.addWidget(note)
|
|
135
|
+
|
|
136
|
+
buttons = QDialogButtonBox(QDialogButtonBox.Ok
|
|
137
|
+
| QDialogButtonBox.Cancel
|
|
138
|
+
| QDialogButtonBox.RestoreDefaults)
|
|
139
|
+
buttons.button(QDialogButtonBox.Cancel).setText("Revert")
|
|
140
|
+
buttons.button(QDialogButtonBox.Cancel).setToolTip(
|
|
141
|
+
"Undo this page's changes and close. Closing otherwise keeps "
|
|
142
|
+
"them.")
|
|
143
|
+
buttons.accepted.connect(self.accept)
|
|
144
|
+
buttons.rejected.connect(self.revert)
|
|
145
|
+
buttons.button(QDialogButtonBox.RestoreDefaults).setText(
|
|
146
|
+
"Built-in defaults")
|
|
147
|
+
buttons.button(QDialogButtonBox.RestoreDefaults).setToolTip(
|
|
148
|
+
"Default column back to the built-in values.")
|
|
149
|
+
buttons.button(QDialogButtonBox.RestoreDefaults).clicked.connect(
|
|
150
|
+
lambda _c=False: self.restore_builtin())
|
|
151
|
+
layout.addWidget(buttons)
|
|
152
|
+
# As tall as the rows need, up to most of the screen; beyond that
|
|
153
|
+
# the rows scroll and the buttons stay put.
|
|
154
|
+
wanted = body.sizeHint().height() + note.sizeHint().height() \
|
|
155
|
+
+ buttons.sizeHint().height() + 60
|
|
156
|
+
screen = window.screen()
|
|
157
|
+
limit = (int(screen.availableGeometry().height() * 0.75)
|
|
158
|
+
if screen is not None else wanted)
|
|
159
|
+
self.resize(max(560, body.sizeHint().width() + 40),
|
|
160
|
+
min(wanted, limit))
|
|
161
|
+
|
|
162
|
+
# ------------------------------------------------------------ editing
|
|
163
|
+
def default_value(self, key):
|
|
164
|
+
widget = self.defaults[key]
|
|
165
|
+
if isinstance(widget, (StyleChoice, StyleText, FontChoice)):
|
|
166
|
+
return widget.value()
|
|
167
|
+
return float(widget.value())
|
|
168
|
+
|
|
169
|
+
def set_default(self, key, value):
|
|
170
|
+
"""Change one default as if it had been typed. For the button, and
|
|
171
|
+
for tests."""
|
|
172
|
+
widget = self.defaults[key]
|
|
173
|
+
if isinstance(widget, (StyleText, FontChoice)):
|
|
174
|
+
widget.blockSignals(True)
|
|
175
|
+
widget.set_value(None if value == style.builtin(key) else value)
|
|
176
|
+
widget.blockSignals(False)
|
|
177
|
+
self._default_changed(key)
|
|
178
|
+
return
|
|
179
|
+
inner = widget.combo if isinstance(widget, StyleChoice) else widget
|
|
180
|
+
inner.blockSignals(True)
|
|
181
|
+
if isinstance(widget, StyleChoice):
|
|
182
|
+
widget.set_value(value)
|
|
183
|
+
else:
|
|
184
|
+
widget.setValue(float(value))
|
|
185
|
+
inner.blockSignals(False)
|
|
186
|
+
self._default_changed(key)
|
|
187
|
+
|
|
188
|
+
def _default_changed(self, key):
|
|
189
|
+
style.set_preference(key, self.default_value(key))
|
|
190
|
+
# The "This figure" column shows the default wherever it follows it.
|
|
191
|
+
if key in self.figure:
|
|
192
|
+
self.figure[key].refresh()
|
|
193
|
+
self._live()
|
|
194
|
+
|
|
195
|
+
def _figure_changed(self, key):
|
|
196
|
+
setattr(self.doc.style, key, self.figure[key].value())
|
|
197
|
+
self._live()
|
|
198
|
+
|
|
199
|
+
def restore_builtin(self):
|
|
200
|
+
for setting in style.SETTINGS:
|
|
201
|
+
self.set_default(setting.key, setting.default)
|
|
202
|
+
|
|
203
|
+
def _live(self):
|
|
204
|
+
self.main._live_change()
|
|
205
|
+
|
|
206
|
+
# ----------------------------------------------------------- finishing
|
|
207
|
+
def accept(self):
|
|
208
|
+
"""Keep both columns: the defaults on disk, the figure on the stack.
|
|
209
|
+
|
|
210
|
+
Written defensively: this runs in a Qt slot, where an exception is an
|
|
211
|
+
abort rather than a traceback, and a read-only settings folder is not
|
|
212
|
+
a reason to lose the figure.
|
|
213
|
+
"""
|
|
214
|
+
try:
|
|
215
|
+
style.save_preferences()
|
|
216
|
+
except OSError as exc:
|
|
217
|
+
self.main.note.setText("Could not save the defaults: {}".format(
|
|
218
|
+
exc))
|
|
219
|
+
changes = []
|
|
220
|
+
for key, old in self._saved_figure.items():
|
|
221
|
+
new = getattr(self.doc.style, key)
|
|
222
|
+
if new != old:
|
|
223
|
+
setattr(self.doc.style, key, old)
|
|
224
|
+
changes.append((self.doc.style, key, new))
|
|
225
|
+
if changes and self.main.doc is self.doc:
|
|
226
|
+
self.main.undo.set_props(changes, "figure style")
|
|
227
|
+
else:
|
|
228
|
+
self._live()
|
|
229
|
+
QDialog.accept(self)
|
|
230
|
+
|
|
231
|
+
def reject(self):
|
|
232
|
+
"""Its X, Esc or Ctrl+W: keep the changes, like OK (see
|
|
233
|
+
`_LiveDialog.reject`). Only Revert puts them back."""
|
|
234
|
+
self.accept()
|
|
235
|
+
|
|
236
|
+
def keyPressEvent(self, ev):
|
|
237
|
+
"""Enter takes the value and keeps the page open (`enter_stays`)."""
|
|
238
|
+
if enter_stays(self, ev):
|
|
239
|
+
return
|
|
240
|
+
QDialog.keyPressEvent(self, ev)
|
|
241
|
+
|
|
242
|
+
def showEvent(self, ev):
|
|
243
|
+
readable(self)
|
|
244
|
+
QDialog.showEvent(self, ev)
|
|
245
|
+
|
|
246
|
+
def revert(self):
|
|
247
|
+
style.restore_preferences(self._saved_defaults)
|
|
248
|
+
for key, old in self._saved_figure.items():
|
|
249
|
+
setattr(self.doc.style, key, old)
|
|
250
|
+
self._live()
|
|
251
|
+
QDialog.reject(self)
|
|
252
|
+
|
|
253
|
+
# Qt calls the handlers here by itself; an error in one is logged and
|
|
254
|
+
# survived rather than the end of the program (`core/log.py`).
|
|
255
|
+
from ..core import log as _log
|
|
256
|
+
_log.guard_classes(globals(), __name__)
|