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.
@@ -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())
@@ -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