matchpatch 0.4.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.
- matchpatch/__init__.py +20 -0
- matchpatch/analysis.py +97 -0
- matchpatch/app.py +71 -0
- matchpatch/audio.py +148 -0
- matchpatch/cli.py +70 -0
- matchpatch/config.py +181 -0
- matchpatch/custom_adjustments.py +60 -0
- matchpatch/devices/__init__.py +5 -0
- matchpatch/devices/base.py +204 -0
- matchpatch/devices/helix.py +447 -0
- matchpatch/devices/registry.py +22 -0
- matchpatch/gui/__init__.py +1 -0
- matchpatch/gui/app.py +193 -0
- matchpatch/gui/device_panels.py +142 -0
- matchpatch/gui/dialogs.py +150 -0
- matchpatch/gui/help.py +213 -0
- matchpatch/gui/main_window.py +7745 -0
- matchpatch/gui/snapshot_header.py +48 -0
- matchpatch/gui/worker.py +135 -0
- matchpatch/measure.py +1191 -0
- matchpatch/measurement_optimizer.py +646 -0
- matchpatch/normalize.py +874 -0
- matchpatch/progress.py +39 -0
- matchpatch/workflow.py +361 -0
- matchpatch-0.4.0.dist-info/METADATA +134 -0
- matchpatch-0.4.0.dist-info/RECORD +29 -0
- matchpatch-0.4.0.dist-info/WHEEL +4 -0
- matchpatch-0.4.0.dist-info/entry_points.txt +3 -0
- matchpatch-0.4.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Two-tier header for compact snapshot columns."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from PySide6.QtCore import QRect, QSize, Qt
|
|
6
|
+
from PySide6.QtGui import QPainter, QPaintEvent, QPen
|
|
7
|
+
from PySide6.QtWidgets import QHeaderView, QStyle, QStyleOptionHeader, QWidget
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SnapshotHeader(QHeaderView):
|
|
11
|
+
def __init__(self, parent: QWidget | None = None) -> None:
|
|
12
|
+
super().__init__(Qt.Orientation.Horizontal, parent)
|
|
13
|
+
self.setSectionsClickable(True)
|
|
14
|
+
self.setMinimumHeight(48)
|
|
15
|
+
|
|
16
|
+
def sizeHint(self) -> QSize:
|
|
17
|
+
hint = super().sizeHint()
|
|
18
|
+
hint.setHeight(max(hint.height() * 2, 48))
|
|
19
|
+
return hint
|
|
20
|
+
|
|
21
|
+
def paintSection(self, painter: QPainter, rect: QRect, logical_index: int) -> None:
|
|
22
|
+
if logical_index < 3:
|
|
23
|
+
super().paintSection(painter, rect, logical_index)
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
lower = QRect(rect.x(), rect.y() + rect.height() // 2, rect.width(), rect.height() // 2)
|
|
27
|
+
super().paintSection(painter, lower, logical_index)
|
|
28
|
+
|
|
29
|
+
def paintEvent(self, event: QPaintEvent) -> None:
|
|
30
|
+
super().paintEvent(event)
|
|
31
|
+
if self.count() <= 3:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
left = self.sectionViewportPosition(3)
|
|
35
|
+
right = self.sectionViewportPosition(self.count() - 1) + self.sectionSize(self.count() - 1)
|
|
36
|
+
rect = QRect(left, 0, right - left, self.height() // 2)
|
|
37
|
+
option = QStyleOptionHeader()
|
|
38
|
+
option.rect = rect
|
|
39
|
+
option.text = "Snapshots"
|
|
40
|
+
option.textAlignment = Qt.AlignmentFlag.AlignCenter
|
|
41
|
+
painter = QPainter(self.viewport())
|
|
42
|
+
self.style().drawControl(QStyle.ControlElement.CE_Header, option, painter, self)
|
|
43
|
+
pen = QPen(self.palette().mid().color())
|
|
44
|
+
pen.setWidth(2)
|
|
45
|
+
painter.setPen(pen)
|
|
46
|
+
for logical_index in range(3, self.count(), 3):
|
|
47
|
+
x = self.sectionViewportPosition(logical_index)
|
|
48
|
+
painter.drawLine(x, self.height() // 2, x, self.height())
|
matchpatch/gui/worker.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Background Qt worker for the blocking normalization workflow."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
from PySide6.QtCore import QObject, QThread, Signal
|
|
8
|
+
|
|
9
|
+
from matchpatch.normalize import (
|
|
10
|
+
check_windows_hardware,
|
|
11
|
+
run_windows_analysis,
|
|
12
|
+
run_windows_optimization,
|
|
13
|
+
)
|
|
14
|
+
from matchpatch.workflow import ImportRequest, NormalizationRequest, normalize_presets
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HardwareCheckWorker(QThread):
|
|
18
|
+
completed = Signal()
|
|
19
|
+
failed = Signal(str)
|
|
20
|
+
|
|
21
|
+
def __init__(self, request: NormalizationRequest, parent: QObject | None = None) -> None:
|
|
22
|
+
super().__init__(parent)
|
|
23
|
+
self.request = request
|
|
24
|
+
|
|
25
|
+
def run(self) -> None:
|
|
26
|
+
try:
|
|
27
|
+
check_windows_hardware(self.request)
|
|
28
|
+
except Exception as exc: # noqa: BLE001
|
|
29
|
+
self.failed.emit(str(exc))
|
|
30
|
+
else:
|
|
31
|
+
self.completed.emit()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NormalizationWorker(QThread):
|
|
35
|
+
progress = Signal(object)
|
|
36
|
+
import_requested = Signal(object)
|
|
37
|
+
completed = Signal(object)
|
|
38
|
+
cancelled = Signal()
|
|
39
|
+
failed = Signal(str)
|
|
40
|
+
|
|
41
|
+
def __init__(self, request: NormalizationRequest, parent: QObject | None = None) -> None:
|
|
42
|
+
super().__init__(parent)
|
|
43
|
+
self.request = request
|
|
44
|
+
self._confirmation = threading.Event()
|
|
45
|
+
self._confirmation_answer = False
|
|
46
|
+
self._cancelled = False
|
|
47
|
+
|
|
48
|
+
def run(self) -> None:
|
|
49
|
+
try:
|
|
50
|
+
result = normalize_presets(
|
|
51
|
+
self.request,
|
|
52
|
+
run_analysis=lambda request, preset_ids, csv_path, callback: run_windows_analysis(
|
|
53
|
+
request,
|
|
54
|
+
preset_ids,
|
|
55
|
+
csv_path,
|
|
56
|
+
callback,
|
|
57
|
+
lambda: self._cancelled,
|
|
58
|
+
),
|
|
59
|
+
on_progress=self.progress.emit,
|
|
60
|
+
confirm_import=self._confirm_import,
|
|
61
|
+
)
|
|
62
|
+
except Exception as exc: # noqa: BLE001
|
|
63
|
+
if self._cancelled:
|
|
64
|
+
self.cancelled.emit()
|
|
65
|
+
else:
|
|
66
|
+
self.failed.emit(str(exc))
|
|
67
|
+
else:
|
|
68
|
+
self.completed.emit(result)
|
|
69
|
+
|
|
70
|
+
def answer_import(self, confirmed: bool) -> None:
|
|
71
|
+
self._confirmation_answer = confirmed
|
|
72
|
+
self._confirmation.set()
|
|
73
|
+
|
|
74
|
+
def cancel(self) -> None:
|
|
75
|
+
self._cancelled = True
|
|
76
|
+
self.answer_import(False)
|
|
77
|
+
|
|
78
|
+
def _confirm_import(self, request: ImportRequest) -> bool:
|
|
79
|
+
if self._cancelled:
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
self._confirmation_answer = False
|
|
83
|
+
self._confirmation.clear()
|
|
84
|
+
self.import_requested.emit(request)
|
|
85
|
+
self._confirmation.wait()
|
|
86
|
+
return self._confirmation_answer and not self._cancelled
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class MeasurementOptimizationWorker(QThread):
|
|
90
|
+
progress = Signal(object)
|
|
91
|
+
completed = Signal(str)
|
|
92
|
+
cancelled = Signal()
|
|
93
|
+
failed = Signal(str)
|
|
94
|
+
|
|
95
|
+
def __init__(
|
|
96
|
+
self,
|
|
97
|
+
request: NormalizationRequest,
|
|
98
|
+
preset_id: int,
|
|
99
|
+
stability_runs: int,
|
|
100
|
+
termination_tolerance: float,
|
|
101
|
+
stability_tolerance: float,
|
|
102
|
+
pinned_parameters: tuple[str, ...] = (),
|
|
103
|
+
parent: QObject | None = None,
|
|
104
|
+
) -> None:
|
|
105
|
+
super().__init__(parent)
|
|
106
|
+
self.request = request
|
|
107
|
+
self.preset_id = preset_id
|
|
108
|
+
self.stability_runs = stability_runs
|
|
109
|
+
self.termination_tolerance = termination_tolerance
|
|
110
|
+
self.stability_tolerance = stability_tolerance
|
|
111
|
+
self.pinned_parameters = pinned_parameters
|
|
112
|
+
self._cancelled = False
|
|
113
|
+
|
|
114
|
+
def run(self) -> None:
|
|
115
|
+
try:
|
|
116
|
+
result = run_windows_optimization(
|
|
117
|
+
self.request,
|
|
118
|
+
self.preset_id,
|
|
119
|
+
stability_runs=self.stability_runs,
|
|
120
|
+
termination_tolerance=self.termination_tolerance,
|
|
121
|
+
stability_tolerance=self.stability_tolerance,
|
|
122
|
+
pinned_parameters=self.pinned_parameters,
|
|
123
|
+
on_progress=self.progress.emit,
|
|
124
|
+
cancel_requested=lambda: self._cancelled,
|
|
125
|
+
)
|
|
126
|
+
except Exception as exc: # noqa: BLE001
|
|
127
|
+
if self._cancelled:
|
|
128
|
+
self.cancelled.emit()
|
|
129
|
+
else:
|
|
130
|
+
self.failed.emit(str(exc))
|
|
131
|
+
else:
|
|
132
|
+
self.completed.emit(result)
|
|
133
|
+
|
|
134
|
+
def cancel(self) -> None:
|
|
135
|
+
self._cancelled = True
|