advisor-scattering 0.5.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.
- advisor/__init__.py +3 -0
- advisor/__main__.py +7 -0
- advisor/app.py +40 -0
- advisor/controllers/__init__.py +6 -0
- advisor/controllers/app_controller.py +69 -0
- advisor/controllers/feature_controller.py +25 -0
- advisor/domain/__init__.py +23 -0
- advisor/domain/core/__init__.py +8 -0
- advisor/domain/core/lab.py +121 -0
- advisor/domain/core/lattice.py +79 -0
- advisor/domain/core/sample.py +101 -0
- advisor/domain/geometry.py +212 -0
- advisor/domain/unit_converter.py +82 -0
- advisor/features/__init__.py +6 -0
- advisor/features/scattering_geometry/controllers/__init__.py +5 -0
- advisor/features/scattering_geometry/controllers/scattering_geometry_controller.py +26 -0
- advisor/features/scattering_geometry/domain/__init__.py +5 -0
- advisor/features/scattering_geometry/domain/brillouin_calculator.py +410 -0
- advisor/features/scattering_geometry/domain/core.py +516 -0
- advisor/features/scattering_geometry/ui/__init__.py +5 -0
- advisor/features/scattering_geometry/ui/components/__init__.py +17 -0
- advisor/features/scattering_geometry/ui/components/angles_to_hkl_components.py +150 -0
- advisor/features/scattering_geometry/ui/components/hk_angles_components.py +430 -0
- advisor/features/scattering_geometry/ui/components/hkl_scan_components.py +526 -0
- advisor/features/scattering_geometry/ui/components/hkl_to_angles_components.py +315 -0
- advisor/features/scattering_geometry/ui/scattering_geometry_tab.py +725 -0
- advisor/features/structure_factor/controllers/__init__.py +6 -0
- advisor/features/structure_factor/controllers/structure_factor_controller.py +25 -0
- advisor/features/structure_factor/domain/__init__.py +6 -0
- advisor/features/structure_factor/domain/structure_factor_calculator.py +107 -0
- advisor/features/structure_factor/ui/__init__.py +6 -0
- advisor/features/structure_factor/ui/components/__init__.py +12 -0
- advisor/features/structure_factor/ui/components/customized_plane_components.py +358 -0
- advisor/features/structure_factor/ui/components/hkl_plane_components.py +391 -0
- advisor/features/structure_factor/ui/structure_factor_tab.py +273 -0
- advisor/resources/__init__.py +0 -0
- advisor/resources/config/app_config.json +14 -0
- advisor/resources/config/tips.json +4 -0
- advisor/resources/data/nacl.cif +111 -0
- advisor/resources/icons/bz_caculator.jpg +0 -0
- advisor/resources/icons/bz_calculator.png +0 -0
- advisor/resources/icons/minus.svg +3 -0
- advisor/resources/icons/placeholder.png +0 -0
- advisor/resources/icons/plus.svg +3 -0
- advisor/resources/icons/reset.png +0 -0
- advisor/resources/icons/sf_calculator.jpg +0 -0
- advisor/resources/icons/sf_calculator.png +0 -0
- advisor/resources/icons.qrc +6 -0
- advisor/resources/qss/styles.qss +348 -0
- advisor/resources/resources_rc.py +83 -0
- advisor/ui/__init__.py +7 -0
- advisor/ui/init_window.py +566 -0
- advisor/ui/main_window.py +174 -0
- advisor/ui/tab_interface.py +44 -0
- advisor/ui/tips.py +30 -0
- advisor/ui/utils/__init__.py +6 -0
- advisor/ui/utils/readcif.py +129 -0
- advisor/ui/visualizers/HKLScan2DVisualizer.py +224 -0
- advisor/ui/visualizers/__init__.py +8 -0
- advisor/ui/visualizers/coordinate_visualizer.py +203 -0
- advisor/ui/visualizers/scattering_visualizer.py +301 -0
- advisor/ui/visualizers/structure_factor_visualizer.py +426 -0
- advisor/ui/visualizers/structure_factor_visualizer_2d.py +235 -0
- advisor/ui/visualizers/unitcell_visualizer.py +518 -0
- advisor_scattering-0.5.0.dist-info/METADATA +122 -0
- advisor_scattering-0.5.0.dist-info/RECORD +69 -0
- advisor_scattering-0.5.0.dist-info/WHEEL +5 -0
- advisor_scattering-0.5.0.dist-info/entry_points.txt +3 -0
- advisor_scattering-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# pylint: disable=no-name-in-module, import-error
|
|
4
|
+
from PyQt5.QtWidgets import (
|
|
5
|
+
QWidget,
|
|
6
|
+
QVBoxLayout,
|
|
7
|
+
QHBoxLayout,
|
|
8
|
+
QFormLayout,
|
|
9
|
+
QGroupBox,
|
|
10
|
+
QLabel,
|
|
11
|
+
QPushButton,
|
|
12
|
+
QDoubleSpinBox,
|
|
13
|
+
QTableWidget,
|
|
14
|
+
QTableWidgetItem,
|
|
15
|
+
QHeaderView,
|
|
16
|
+
QButtonGroup,
|
|
17
|
+
QMessageBox,
|
|
18
|
+
)
|
|
19
|
+
from PyQt5.QtCore import Qt, pyqtSignal
|
|
20
|
+
from PyQt5.QtGui import QColor, QBrush
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class HKAnglesControls(QWidget):
|
|
24
|
+
"""Widget for HK to Angles calculation controls with fixed tth."""
|
|
25
|
+
|
|
26
|
+
# Signal emitted when calculate button is clicked
|
|
27
|
+
calculateClicked = pyqtSignal()
|
|
28
|
+
|
|
29
|
+
def __init__(self, parent=None):
|
|
30
|
+
super().__init__(parent)
|
|
31
|
+
|
|
32
|
+
# Main layout
|
|
33
|
+
main_layout = QVBoxLayout(self)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# HKL indices group
|
|
38
|
+
hkl_group = QGroupBox("HKL Indices")
|
|
39
|
+
hkl_layout = QFormLayout(hkl_group)
|
|
40
|
+
|
|
41
|
+
# Plane selection buttons
|
|
42
|
+
plane_selection = QWidget()
|
|
43
|
+
plane_layout = QHBoxLayout(plane_selection)
|
|
44
|
+
plane_layout.setContentsMargins(0, 0, 0, 0)
|
|
45
|
+
|
|
46
|
+
self.hk_plane_btn = QPushButton("HK plane")
|
|
47
|
+
self.hl_plane_btn = QPushButton("HL plane")
|
|
48
|
+
self.kl_plane_btn = QPushButton("KL plane")
|
|
49
|
+
|
|
50
|
+
# Make buttons checkable for toggle behavior
|
|
51
|
+
for btn in (self.hk_plane_btn, self.hl_plane_btn, self.kl_plane_btn):
|
|
52
|
+
btn.setCheckable(True)
|
|
53
|
+
|
|
54
|
+
self.hk_plane_btn.setChecked(True) # Default to HK plane (L fixed)
|
|
55
|
+
|
|
56
|
+
# Create a button group for mutual exclusion
|
|
57
|
+
self.hkl_plane_button_group = QButtonGroup(self)
|
|
58
|
+
self.hkl_plane_button_group.addButton(self.hk_plane_btn)
|
|
59
|
+
self.hkl_plane_button_group.addButton(self.hl_plane_btn)
|
|
60
|
+
self.hkl_plane_button_group.addButton(self.kl_plane_btn)
|
|
61
|
+
|
|
62
|
+
plane_layout.addWidget(self.hk_plane_btn)
|
|
63
|
+
plane_layout.addWidget(self.hl_plane_btn)
|
|
64
|
+
plane_layout.addWidget(self.kl_plane_btn)
|
|
65
|
+
# stretch buttons to be evenly distributed
|
|
66
|
+
plane_layout.addStretch()
|
|
67
|
+
hkl_layout.addRow("Plane:", plane_selection)
|
|
68
|
+
# Create HKL inputs
|
|
69
|
+
hkl_inputs_widget = QWidget()
|
|
70
|
+
hkl_inputs_layout = QHBoxLayout(hkl_inputs_widget)
|
|
71
|
+
hkl_inputs_layout.setContentsMargins(0, 0, 0, 0)
|
|
72
|
+
|
|
73
|
+
# H input row
|
|
74
|
+
self.h_row = QWidget()
|
|
75
|
+
h_layout = QHBoxLayout(self.h_row)
|
|
76
|
+
h_layout.setContentsMargins(0, 0, 0, 0)
|
|
77
|
+
h_form = QWidget()
|
|
78
|
+
h_form_layout = QFormLayout(h_form)
|
|
79
|
+
h_form_layout.setContentsMargins(0, 0, 0, 0)
|
|
80
|
+
self.H_input = QDoubleSpinBox()
|
|
81
|
+
self.H_input.setRange(-100.0, 100)
|
|
82
|
+
self.H_input.setDecimals(4)
|
|
83
|
+
self.H_input.setValue(0.15)
|
|
84
|
+
h_form_layout.addRow("H:", self.H_input)
|
|
85
|
+
h_layout.addWidget(h_form)
|
|
86
|
+
hkl_inputs_layout.addWidget(self.h_row)
|
|
87
|
+
|
|
88
|
+
# K input row
|
|
89
|
+
self.k_row = QWidget()
|
|
90
|
+
k_layout = QHBoxLayout(self.k_row)
|
|
91
|
+
k_layout.setContentsMargins(0, 0, 0, 0)
|
|
92
|
+
k_form = QWidget()
|
|
93
|
+
k_form_layout = QFormLayout(k_form)
|
|
94
|
+
k_form_layout.setContentsMargins(0, 0, 0, 0)
|
|
95
|
+
self.K_input = QDoubleSpinBox()
|
|
96
|
+
self.K_input.setRange(-100, 100)
|
|
97
|
+
self.K_input.setDecimals(4)
|
|
98
|
+
self.K_input.setValue(0.1)
|
|
99
|
+
k_form_layout.addRow("K:", self.K_input)
|
|
100
|
+
k_layout.addWidget(k_form)
|
|
101
|
+
hkl_inputs_layout.addWidget(self.k_row)
|
|
102
|
+
|
|
103
|
+
# L input row
|
|
104
|
+
self.l_row = QWidget()
|
|
105
|
+
l_layout = QHBoxLayout(self.l_row)
|
|
106
|
+
l_layout.setContentsMargins(0, 0, 0, 0)
|
|
107
|
+
l_form = QWidget()
|
|
108
|
+
l_form_layout = QFormLayout(l_form)
|
|
109
|
+
l_form_layout.setContentsMargins(0, 0, 0, 0)
|
|
110
|
+
self.L_input = QDoubleSpinBox()
|
|
111
|
+
self.L_input.setRange(-100, 100)
|
|
112
|
+
self.L_input.setDecimals(4)
|
|
113
|
+
self.L_input.setValue(-0.5)
|
|
114
|
+
l_form_layout.addRow("L:", self.L_input)
|
|
115
|
+
l_layout.addWidget(l_form)
|
|
116
|
+
hkl_inputs_layout.addWidget(self.l_row)
|
|
117
|
+
|
|
118
|
+
hkl_layout.addRow(hkl_inputs_widget)
|
|
119
|
+
main_layout.addWidget(hkl_group)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Unified Fixed Angles panel
|
|
123
|
+
fixed_angles_group = QGroupBox("Fixed Angles")
|
|
124
|
+
fixed_angles_layout = QVBoxLayout(fixed_angles_group)
|
|
125
|
+
|
|
126
|
+
# Top row: Fix χ/Fix φ buttons
|
|
127
|
+
angle_selection = QWidget()
|
|
128
|
+
angle_selection_layout = QHBoxLayout(angle_selection)
|
|
129
|
+
angle_selection_layout.setContentsMargins(0, 0, 0, 0)
|
|
130
|
+
|
|
131
|
+
self.fix_chi_btn = QPushButton("Fix χ")
|
|
132
|
+
self.fix_phi_btn = QPushButton("Fix φ")
|
|
133
|
+
|
|
134
|
+
# Make buttons checkable for toggle behavior
|
|
135
|
+
for btn in (self.fix_chi_btn, self.fix_phi_btn):
|
|
136
|
+
btn.setCheckable(True)
|
|
137
|
+
|
|
138
|
+
self.fix_chi_btn.setChecked(True) # Default to fixed chi
|
|
139
|
+
|
|
140
|
+
# Create a button group for mutual exclusion
|
|
141
|
+
self.angle_button_group = QButtonGroup(self)
|
|
142
|
+
self.angle_button_group.addButton(self.fix_chi_btn)
|
|
143
|
+
self.angle_button_group.addButton(self.fix_phi_btn)
|
|
144
|
+
|
|
145
|
+
angle_selection_layout.addWidget(self.fix_chi_btn)
|
|
146
|
+
angle_selection_layout.addWidget(self.fix_phi_btn)
|
|
147
|
+
|
|
148
|
+
fixed_angles_layout.addWidget(angle_selection)
|
|
149
|
+
|
|
150
|
+
# Bottom row: tth on left, chi/phi angles on right
|
|
151
|
+
angle_values = QWidget()
|
|
152
|
+
angle_values_layout = QHBoxLayout(angle_values)
|
|
153
|
+
angle_values_layout.setContentsMargins(0, 0, 0, 0)
|
|
154
|
+
|
|
155
|
+
# tth input (left side)
|
|
156
|
+
self.tth_widget = QWidget()
|
|
157
|
+
tth_layout = QFormLayout(self.tth_widget)
|
|
158
|
+
tth_layout.setContentsMargins(0, 0, 0, 0)
|
|
159
|
+
self.tth_input = QDoubleSpinBox()
|
|
160
|
+
self.tth_input.setRange(0.0, 180.0)
|
|
161
|
+
self.tth_input.setValue(150.0)
|
|
162
|
+
self.tth_input.setSuffix(" °")
|
|
163
|
+
tth_layout.addRow("tth:", self.tth_input)
|
|
164
|
+
angle_values_layout.addWidget(self.tth_widget)
|
|
165
|
+
|
|
166
|
+
# Chi input
|
|
167
|
+
self.chi_widget = QWidget()
|
|
168
|
+
chi_layout = QFormLayout(self.chi_widget)
|
|
169
|
+
chi_layout.setContentsMargins(0, 0, 0, 0)
|
|
170
|
+
self.chi_input = QDoubleSpinBox()
|
|
171
|
+
self.chi_input.setRange(-180.0, 180.0)
|
|
172
|
+
self.chi_input.setValue(0.0)
|
|
173
|
+
self.chi_input.setSuffix(" °")
|
|
174
|
+
chi_layout.addRow("χ:", self.chi_input)
|
|
175
|
+
angle_values_layout.addWidget(self.chi_widget)
|
|
176
|
+
|
|
177
|
+
# Phi input
|
|
178
|
+
self.phi_widget = QWidget()
|
|
179
|
+
phi_layout = QFormLayout(self.phi_widget)
|
|
180
|
+
phi_layout.setContentsMargins(0, 0, 0, 0)
|
|
181
|
+
self.phi_input = QDoubleSpinBox()
|
|
182
|
+
self.phi_input.setRange(-180.0, 180.0)
|
|
183
|
+
self.phi_input.setValue(0.0)
|
|
184
|
+
self.phi_input.setSuffix(" °")
|
|
185
|
+
phi_layout.addRow("φ:", self.phi_input)
|
|
186
|
+
angle_values_layout.addWidget(self.phi_widget)
|
|
187
|
+
|
|
188
|
+
fixed_angles_layout.addWidget(angle_values)
|
|
189
|
+
|
|
190
|
+
main_layout.addWidget(fixed_angles_group)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# Calculate button
|
|
194
|
+
self.calculate_button = QPushButton("Calculate Angles")
|
|
195
|
+
self.calculate_button.clicked.connect(self.calculateClicked.emit)
|
|
196
|
+
self.calculate_button.setObjectName("calculateButton")
|
|
197
|
+
main_layout.addWidget(self.calculate_button)
|
|
198
|
+
|
|
199
|
+
# Connect signals
|
|
200
|
+
self.hk_plane_btn.clicked.connect(lambda: self._set_active_plane("HK"))
|
|
201
|
+
self.hl_plane_btn.clicked.connect(lambda: self._set_active_plane("HL"))
|
|
202
|
+
self.kl_plane_btn.clicked.connect(lambda: self._set_active_plane("KL"))
|
|
203
|
+
self.fix_chi_btn.clicked.connect(lambda: self._set_active_fixed_angle("chi"))
|
|
204
|
+
self.fix_phi_btn.clicked.connect(lambda: self._set_active_fixed_angle("phi"))
|
|
205
|
+
|
|
206
|
+
# Initialize widget states
|
|
207
|
+
self._set_active_plane("HK") # Set initial plane and apply styling
|
|
208
|
+
self._set_active_fixed_angle("chi") # Set initial fixed angle and apply styling
|
|
209
|
+
|
|
210
|
+
def _update_hkl_visibility(self):
|
|
211
|
+
"""Update visibility of HKL inputs based on current plane selection.
|
|
212
|
+
|
|
213
|
+
HK plane: H and K inputs visible, L input hidden
|
|
214
|
+
HL plane: H and L inputs visible, K input hidden
|
|
215
|
+
KL plane: K and L inputs visible, H input hidden
|
|
216
|
+
"""
|
|
217
|
+
if self.hk_plane_btn.isChecked():
|
|
218
|
+
# HK plane: H and K visible, L hidden
|
|
219
|
+
self.h_row.setVisible(True)
|
|
220
|
+
self.k_row.setVisible(True)
|
|
221
|
+
self.l_row.setVisible(False)
|
|
222
|
+
elif self.hl_plane_btn.isChecked():
|
|
223
|
+
# HL plane: H and L visible, K hidden
|
|
224
|
+
self.h_row.setVisible(True)
|
|
225
|
+
self.k_row.setVisible(False)
|
|
226
|
+
self.l_row.setVisible(True)
|
|
227
|
+
elif self.kl_plane_btn.isChecked():
|
|
228
|
+
# KL plane: K and L visible, H hidden
|
|
229
|
+
self.h_row.setVisible(False)
|
|
230
|
+
self.k_row.setVisible(True)
|
|
231
|
+
self.l_row.setVisible(True)
|
|
232
|
+
|
|
233
|
+
def _update_fixed_angle_ui(self):
|
|
234
|
+
"""Update UI based on which angle is fixed.
|
|
235
|
+
|
|
236
|
+
If chi is fixed: Show chi input, hide phi input
|
|
237
|
+
If phi is fixed: Show phi input, hide chi input
|
|
238
|
+
"""
|
|
239
|
+
is_chi_fixed = self.fix_chi_btn.isChecked()
|
|
240
|
+
self.chi_widget.setVisible(is_chi_fixed)
|
|
241
|
+
self.phi_widget.setVisible(not is_chi_fixed)
|
|
242
|
+
|
|
243
|
+
def _update_plane_styles(self, active: str):
|
|
244
|
+
"""Update plane button colors based on active plane."""
|
|
245
|
+
mapping = {
|
|
246
|
+
"HK": self.hk_plane_btn,
|
|
247
|
+
"HL": self.hl_plane_btn,
|
|
248
|
+
"KL": self.kl_plane_btn,
|
|
249
|
+
}
|
|
250
|
+
for name, btn in mapping.items():
|
|
251
|
+
if name == active:
|
|
252
|
+
btn.setChecked(True)
|
|
253
|
+
btn.setProperty("class", "activeToggle")
|
|
254
|
+
else:
|
|
255
|
+
btn.setChecked(False)
|
|
256
|
+
btn.setProperty("class", "inactiveToggle")
|
|
257
|
+
# Force style refresh
|
|
258
|
+
btn.style().unpolish(btn)
|
|
259
|
+
btn.style().polish(btn)
|
|
260
|
+
|
|
261
|
+
def _update_fixed_angle_styles(self, active: str):
|
|
262
|
+
"""Update fixed angle button colors based on active selection."""
|
|
263
|
+
mapping = {
|
|
264
|
+
"chi": self.fix_chi_btn,
|
|
265
|
+
"phi": self.fix_phi_btn,
|
|
266
|
+
}
|
|
267
|
+
for name, btn in mapping.items():
|
|
268
|
+
if name == active:
|
|
269
|
+
btn.setChecked(True)
|
|
270
|
+
btn.setProperty("class", "activeToggle")
|
|
271
|
+
else:
|
|
272
|
+
btn.setChecked(False)
|
|
273
|
+
btn.setProperty("class", "inactiveToggle")
|
|
274
|
+
# Force style refresh
|
|
275
|
+
btn.style().unpolish(btn)
|
|
276
|
+
btn.style().polish(btn)
|
|
277
|
+
|
|
278
|
+
def _set_active_plane(self, plane: str):
|
|
279
|
+
"""Set the active plane and update widget states and styling."""
|
|
280
|
+
plane = plane.upper()
|
|
281
|
+
self._update_plane_styles(plane)
|
|
282
|
+
self._update_hkl_visibility()
|
|
283
|
+
|
|
284
|
+
def _set_active_fixed_angle(self, angle: str):
|
|
285
|
+
"""Set the active fixed angle and update widget states and styling."""
|
|
286
|
+
angle = angle.lower()
|
|
287
|
+
self._update_fixed_angle_styles(angle)
|
|
288
|
+
self._update_fixed_angle_ui()
|
|
289
|
+
|
|
290
|
+
def get_calculation_parameters(self):
|
|
291
|
+
"""Get parameters for angle calculation."""
|
|
292
|
+
# Get fixed index based on plane selection
|
|
293
|
+
# HK plane means L is fixed, HL plane means K is fixed, KL plane means H is fixed
|
|
294
|
+
fixed_index = None
|
|
295
|
+
if self.hk_plane_btn.isChecked():
|
|
296
|
+
fixed_index = "L" # HK plane: L is fixed
|
|
297
|
+
elif self.hl_plane_btn.isChecked():
|
|
298
|
+
fixed_index = "K" # HL plane: K is fixed
|
|
299
|
+
elif self.kl_plane_btn.isChecked():
|
|
300
|
+
fixed_index = "H" # KL plane: H is fixed
|
|
301
|
+
|
|
302
|
+
# Get fixed angle
|
|
303
|
+
fixed_angle_name = "chi" if self.fix_chi_btn.isChecked() else "phi"
|
|
304
|
+
fixed_angle_value = (
|
|
305
|
+
self.chi_input.value()
|
|
306
|
+
if self.fix_chi_btn.isChecked()
|
|
307
|
+
else self.phi_input.value()
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# Get HKL values (None for fixed index)
|
|
311
|
+
H = self.H_input.value() if fixed_index != "H" else None
|
|
312
|
+
K = self.K_input.value() if fixed_index != "K" else None
|
|
313
|
+
L = self.L_input.value() if fixed_index != "L" else None
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
"tth": self.tth_input.value(),
|
|
317
|
+
"H": H,
|
|
318
|
+
"K": K,
|
|
319
|
+
"L": L,
|
|
320
|
+
"fixed_index": fixed_index,
|
|
321
|
+
"fixed_angle_name": fixed_angle_name,
|
|
322
|
+
"fixed_angle_value": fixed_angle_value,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class HKAnglesResultsTable(QTableWidget):
|
|
327
|
+
"""Table to display HK to Angles calculation results."""
|
|
328
|
+
|
|
329
|
+
# Signal emitted when a solution is selected
|
|
330
|
+
solutionSelected = pyqtSignal(dict)
|
|
331
|
+
|
|
332
|
+
def __init__(self, parent=None):
|
|
333
|
+
super().__init__(parent)
|
|
334
|
+
|
|
335
|
+
# Set up table
|
|
336
|
+
self.setColumnCount(4)
|
|
337
|
+
self.setHorizontalHeaderLabels(["tth (°)", "θ (°)", "φ (°)", "χ (°)"])
|
|
338
|
+
self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
|
339
|
+
|
|
340
|
+
# Hide vertical header (row numbers)
|
|
341
|
+
self.verticalHeader().setVisible(False)
|
|
342
|
+
|
|
343
|
+
# Connect selection change signal
|
|
344
|
+
self.itemSelectionChanged.connect(self.on_selection_changed)
|
|
345
|
+
|
|
346
|
+
# Store the last results for reference
|
|
347
|
+
self.last_results = None
|
|
348
|
+
|
|
349
|
+
def display_results(self, result):
|
|
350
|
+
"""Append calculation results to the table."""
|
|
351
|
+
# Don't clear table - append new results
|
|
352
|
+
self.last_results = result
|
|
353
|
+
|
|
354
|
+
# Check if we have results
|
|
355
|
+
if not result or not result.get("success", False):
|
|
356
|
+
return
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
# Add rows for each new solution
|
|
360
|
+
row_position = self.rowCount()
|
|
361
|
+
self.insertRow(row_position)
|
|
362
|
+
|
|
363
|
+
# Add solution data
|
|
364
|
+
self.setItem(row_position, 0, QTableWidgetItem(f"{result['tth']:.1f}"))
|
|
365
|
+
self.setItem(row_position, 1, QTableWidgetItem(f"{result['theta']:.1f}"))
|
|
366
|
+
self.setItem(row_position, 2, QTableWidgetItem(f"{result['phi']:.1f}"))
|
|
367
|
+
self.setItem(row_position, 3, QTableWidgetItem(f"{result['chi']:.1f}"))
|
|
368
|
+
|
|
369
|
+
# Highlight new solutions with light blue background
|
|
370
|
+
feasible_brush = QBrush(QColor(198, 239, 206)) # light green
|
|
371
|
+
infeasible_brush = QBrush(QColor(255, 199, 206)) # light red
|
|
372
|
+
row_color = feasible_brush if result["feasible"] else infeasible_brush
|
|
373
|
+
for col in range(self.columnCount()):
|
|
374
|
+
item = self.item(row_position, col)
|
|
375
|
+
if item:
|
|
376
|
+
item.setBackground(row_color)
|
|
377
|
+
|
|
378
|
+
# Scroll to the bottom to show the new results
|
|
379
|
+
self.scrollToBottom()
|
|
380
|
+
|
|
381
|
+
def on_selection_changed(self):
|
|
382
|
+
"""Handle selection change in the table."""
|
|
383
|
+
current_row = self.currentRow()
|
|
384
|
+
if current_row >= 0 and self.last_results:
|
|
385
|
+
if current_row < 1:
|
|
386
|
+
selected_solution = self.last_results
|
|
387
|
+
self.solutionSelected.emit(selected_solution)
|
|
388
|
+
|
|
389
|
+
def clear_results(self):
|
|
390
|
+
"""Clear all results from the table."""
|
|
391
|
+
self.setRowCount(0)
|
|
392
|
+
self.last_results = None
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class HKAnglesResultsWidget(QWidget):
|
|
396
|
+
"""Complete results widget with table and clear button."""
|
|
397
|
+
|
|
398
|
+
# Signal emitted when a solution is selected
|
|
399
|
+
solutionSelected = pyqtSignal(dict)
|
|
400
|
+
|
|
401
|
+
def __init__(self, parent=None):
|
|
402
|
+
super().__init__(parent)
|
|
403
|
+
|
|
404
|
+
# Main layout
|
|
405
|
+
layout = QVBoxLayout(self)
|
|
406
|
+
|
|
407
|
+
# Results group
|
|
408
|
+
results_group = QGroupBox("Results")
|
|
409
|
+
results_layout = QVBoxLayout(results_group)
|
|
410
|
+
|
|
411
|
+
# Create table
|
|
412
|
+
self.results_table = HKAnglesResultsTable(self)
|
|
413
|
+
self.results_table.solutionSelected.connect(self.solutionSelected.emit)
|
|
414
|
+
results_layout.addWidget(self.results_table)
|
|
415
|
+
|
|
416
|
+
# Add clear button
|
|
417
|
+
self.clear_button = QPushButton("Clear Results")
|
|
418
|
+
self.clear_button.clicked.connect(self.clear_results)
|
|
419
|
+
self.clear_button.setObjectName("clearButton")
|
|
420
|
+
results_layout.addWidget(self.clear_button)
|
|
421
|
+
|
|
422
|
+
layout.addWidget(results_group)
|
|
423
|
+
|
|
424
|
+
def display_results(self, results):
|
|
425
|
+
"""Display calculation results."""
|
|
426
|
+
self.results_table.display_results(results)
|
|
427
|
+
|
|
428
|
+
def clear_results(self):
|
|
429
|
+
"""Clear all results."""
|
|
430
|
+
self.results_table.clear_results()
|