xrdPlanner 2.2.0__tar.gz → 2.3.0__tar.gz
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.
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/PKG-INFO +2 -2
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner/__init__.py +3 -3
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner/classes.py +282 -111
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner.egg-info/PKG-INFO +2 -2
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/LICENSE +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/README.md +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/pyproject.toml +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/setup.cfg +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner/resources.py +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner/run_xrdPlanner.py +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner.egg-info/SOURCES.txt +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner.egg-info/dependency_links.txt +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner.egg-info/entry_points.txt +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner.egg-info/requires.txt +0 -0
- {xrdplanner-2.2.0 → xrdplanner-2.3.0}/xrdPlanner.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: xrdPlanner
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: A tool to project X-ray diffraction cones on a detector screen at different geometries (tilt, rotation, offset) and X-ray energies
|
|
5
5
|
Author-email: Lennard Krause <lkrause@chem.au.dk>
|
|
6
6
|
Project-URL: Homepage, https://github.com/LennardKrause/xrdPlanner
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
__version__ = '2.
|
|
1
|
+
__version__ = '2.3.0'
|
|
2
2
|
__author__ = 'Lennard Krause'
|
|
3
3
|
__authors__ = ['Lennard Krause', 'Frederik H. Gjørup', 'Mads R. V. Jørgensen']
|
|
4
4
|
__email__ = 'lkrause@chem.au.dk'
|
|
5
|
-
__date__ = '
|
|
6
|
-
__year__ = '
|
|
5
|
+
__date__ = '10.01.2025'
|
|
6
|
+
__year__ = '2025'
|
|
@@ -4,15 +4,13 @@ import json
|
|
|
4
4
|
import glob
|
|
5
5
|
import shutil
|
|
6
6
|
import numpy as np
|
|
7
|
+
from scipy.optimize import curve_fit
|
|
7
8
|
import pyqtgraph as pg
|
|
8
9
|
import Dans_Diffraction as dif
|
|
9
10
|
from PyQt6 import QtWidgets, QtCore, QtGui
|
|
10
11
|
from pyFAI import calibrant
|
|
11
12
|
import xrdPlanner.resources
|
|
12
13
|
|
|
13
|
-
# make cifs added to menu in init similar to calibrants
|
|
14
|
-
# store only the path and calculate the rest on load
|
|
15
|
-
|
|
16
14
|
class MainWindow(QtWidgets.QMainWindow):
|
|
17
15
|
def __init__(self, *args, **kwargs):
|
|
18
16
|
"""
|
|
@@ -163,6 +161,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
163
161
|
|
|
164
162
|
# disable/reset delta_d/d toggles
|
|
165
163
|
self.action_funct_fwhm_show.setEnabled(False)
|
|
164
|
+
self.action_funct_fwhm_export.setEnabled(False)
|
|
166
165
|
|
|
167
166
|
# add the slider frame
|
|
168
167
|
# this calls draw_conics(),
|
|
@@ -350,6 +349,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
350
349
|
'bs_label':None,
|
|
351
350
|
'conic':[],
|
|
352
351
|
'reference':[],
|
|
352
|
+
'ref_hl_label':None,
|
|
353
|
+
'ref_hl_curve':None,
|
|
353
354
|
'labels':[],
|
|
354
355
|
'polar_grid':[]}
|
|
355
356
|
|
|
@@ -367,12 +368,27 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
367
368
|
|
|
368
369
|
# add empty plot per reference contour line
|
|
369
370
|
for i in range(self.plo.conic_ref_num):
|
|
370
|
-
ref =
|
|
371
|
+
ref = HoverableCurveItem(self, useCache=True)
|
|
371
372
|
self.ax.addItem(ref)
|
|
372
373
|
self.patches['reference'].append(ref)
|
|
373
|
-
self.patches['reference'][i].setClickable(True, width=self.plo.conic_ref_linewidth*2)
|
|
374
|
-
self.patches['reference'][i].sigClicked.connect(self.tooltip_hkl_show)
|
|
375
374
|
self.patches['reference'][i].name = None
|
|
375
|
+
self.patches['reference'][i].index = None
|
|
376
|
+
|
|
377
|
+
# add ref highlight curve
|
|
378
|
+
ref_hkl_curve = pg.PlotCurveItem(useCache=True,
|
|
379
|
+
pen=pg.mkPen(color=self.conic_highlight,
|
|
380
|
+
width=self.plo.conic_ref_linewidth*2))
|
|
381
|
+
ref_hkl_curve.setVisible(False)
|
|
382
|
+
self.patches['ref_hl_curve'] = ref_hkl_curve
|
|
383
|
+
self.ax.addItem(ref_hkl_curve)
|
|
384
|
+
|
|
385
|
+
# add hkl annotation label
|
|
386
|
+
ref_hkl_label = pg.TextItem(anchor=(0.5,1.0), color=self.unit_label_color)
|
|
387
|
+
ref_hkl_label.setFont(font)
|
|
388
|
+
ref_hkl_label.setVisible(False)
|
|
389
|
+
ref_hkl_label.setZValue(1) # place label on top of all other items
|
|
390
|
+
self.patches['ref_hl_label'] = ref_hkl_label
|
|
391
|
+
self.ax.addItem(ref_hkl_label)
|
|
376
392
|
|
|
377
393
|
# add empty plot per contour line
|
|
378
394
|
for i in range(self.plo.conic_tth_num):
|
|
@@ -393,17 +409,17 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
393
409
|
self.ax.addItem(bs_label)
|
|
394
410
|
|
|
395
411
|
# add poni scatter plot
|
|
396
|
-
self.patches['poni'] = pg.ScatterPlotItem(symbol
|
|
397
|
-
size
|
|
398
|
-
brush
|
|
399
|
-
pen
|
|
412
|
+
self.patches['poni'] = pg.ScatterPlotItem(symbol=self.plo.poni_marker,
|
|
413
|
+
size=self.plo.poni_size,
|
|
414
|
+
brush=pg.mkBrush(self.cont_cmap.map(0, mode='qcolor')),
|
|
415
|
+
pen=pg.mkPen(None))
|
|
400
416
|
self.ax.addItem(self.patches['poni'])
|
|
401
417
|
|
|
402
418
|
# add beam center scatter plot
|
|
403
|
-
self.patches['beamcenter'] = pg.ScatterPlotItem(symbol
|
|
404
|
-
size
|
|
405
|
-
brush
|
|
406
|
-
pen
|
|
419
|
+
self.patches['beamcenter'] = pg.ScatterPlotItem(symbol=self.plo.beamcenter_marker,
|
|
420
|
+
size=self.plo.beamcenter_size,
|
|
421
|
+
brush=pg.mkBrush(self.cont_cmap.map(0, mode='qcolor')),
|
|
422
|
+
pen=pg.mkPen(None))
|
|
407
423
|
self.ax.addItem(self.patches['beamcenter'])
|
|
408
424
|
|
|
409
425
|
self.patches['overlay'] = pg.ImageItem()
|
|
@@ -651,6 +667,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
651
667
|
# reference contour
|
|
652
668
|
self.conic_label_fill = QtGui.QColor(self.thm.dark_conic_label_fill)
|
|
653
669
|
self.conic_ref_color = QtGui.QColor(self.thm.dark_conic_ref_color)
|
|
670
|
+
self.conic_highlight = QtGui.QColor(self.thm.dark_conic_highlight)
|
|
654
671
|
self.det_module_color = QtGui.QColor(self.thm.dark_det_module_color)
|
|
655
672
|
self.det_module_fill = QtGui.QColor(self.thm.dark_det_module_fill)
|
|
656
673
|
# general
|
|
@@ -688,6 +705,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
688
705
|
# reference contour
|
|
689
706
|
self.conic_label_fill = QtGui.QColor(self.thm.light_conic_label_fill)
|
|
690
707
|
self.conic_ref_color = QtGui.QColor(self.thm.light_conic_ref_color)
|
|
708
|
+
self.conic_highlight = QtGui.QColor(self.thm.light_conic_highlight)
|
|
691
709
|
self.det_module_color = QtGui.QColor(self.thm.light_det_module_color)
|
|
692
710
|
self.det_module_fill = QtGui.QColor(self.thm.light_det_module_fill)
|
|
693
711
|
# general
|
|
@@ -1004,7 +1022,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
1004
1022
|
else:
|
|
1005
1023
|
self.action_funct_fwhm_show.setChecked(False)
|
|
1006
1024
|
menu_functions.addAction(self.action_funct_fwhm_show)
|
|
1007
|
-
|
|
1025
|
+
#export fwhm toggle
|
|
1026
|
+
self.action_funct_fwhm_export = QtGui.QAction('Export FWHM', self)
|
|
1027
|
+
self.menu_set_action(self.action_funct_fwhm_export, self.export_fwhm_grid)
|
|
1028
|
+
menu_functions.addAction(self.action_funct_fwhm_export)
|
|
1029
|
+
|
|
1008
1030
|
# PXRD pattern
|
|
1009
1031
|
self.action_pxrd_pattern = QtGui.QAction('P&XRD pattern', self)
|
|
1010
1032
|
self.menu_set_action(self.action_pxrd_pattern, self.win_pxrd_plot)
|
|
@@ -1408,7 +1430,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
1408
1430
|
|
|
1409
1431
|
# calculate the maximum resolution for the given geometry
|
|
1410
1432
|
if self.plo.conic_tth_auto:
|
|
1411
|
-
theta_max = np.rad2deg(self.
|
|
1433
|
+
theta_max = np.rad2deg(self.calc_tth_max(scale=0.90))
|
|
1412
1434
|
# make new array of 2theta values
|
|
1413
1435
|
self.cont_geom_num = np.linspace(theta_max/self.plo.conic_tth_num, theta_max, self.plo.conic_tth_num)
|
|
1414
1436
|
|
|
@@ -1502,6 +1524,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
1502
1524
|
- self.conic_ref_color: Color for the reference contour lines.
|
|
1503
1525
|
- self.patches['reference']: Dictionary to store the reference contour line patches.
|
|
1504
1526
|
"""
|
|
1527
|
+
# remove highlighted curve and hkl label
|
|
1528
|
+
self.patches['ref_hl_curve'].setVisible(False)
|
|
1529
|
+
self.patches['ref_hl_label'].setVisible(False)
|
|
1505
1530
|
# plot reference contour lines
|
|
1506
1531
|
# standard contour lines are to be drawn
|
|
1507
1532
|
for _n in range(self.plo.conic_ref_num):
|
|
@@ -1549,10 +1574,13 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
1549
1574
|
#
|
|
1550
1575
|
# self.setStyleSheet('''QToolTip {... ) didn't work
|
|
1551
1576
|
self.patches['reference'][_n].name = f'({h: 2.0f} {k: 2.0f} {l: 2.0f})<br>{round(itot, 0):,.0f}'
|
|
1577
|
+
self.patches['reference'][_n].index = _n
|
|
1552
1578
|
else:
|
|
1553
1579
|
self.patches['reference'][_n].name = f'({h: 2.0f} {k: 2.0f} {l: 2.0f})'
|
|
1580
|
+
self.patches['reference'][_n].index = _n
|
|
1554
1581
|
else:
|
|
1555
1582
|
self.patches['reference'][_n].name = None
|
|
1583
|
+
self.patches['reference'][_n].index = None
|
|
1556
1584
|
|
|
1557
1585
|
# current fraction for colormap
|
|
1558
1586
|
if self.plo.colored_reference:
|
|
@@ -1826,13 +1854,13 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
1826
1854
|
plo.show_polarisation = True # [bool] Show polarisation overlay
|
|
1827
1855
|
plo.show_solidangle = False # [bool] Show solid angle overlay
|
|
1828
1856
|
plo.show_unit_hover = True # [bool] Show unit value on hover
|
|
1829
|
-
plo.show_grid = False
|
|
1857
|
+
plo.show_grid = False # [bool] Show azimuthal grid
|
|
1830
1858
|
plo.azimuth_num = 13 # [int] Number of azimuthal grid lines
|
|
1831
1859
|
plo.overlay_resolution = 300 # [int] Overlay resolution
|
|
1832
1860
|
plo.overlay_toggle_warn = True # [bool] Overlay warn color threshold
|
|
1833
1861
|
# - pxrd plot -
|
|
1834
1862
|
plo.pxrd_marker_symbol = 'arrow_up' # [marker] Symbol to mark peaks
|
|
1835
|
-
plo.pxrd_marker_offset = 0.
|
|
1863
|
+
plo.pxrd_marker_offset = 0.05 # [float] offset of marker from x-axis
|
|
1836
1864
|
# - extra functions -
|
|
1837
1865
|
plo.show_fwhm = False # [bool] Show delta_d/d function
|
|
1838
1866
|
plo.sensor_thickness = 1000e-6 # [float] Detector sensor thickness [m]
|
|
@@ -1900,6 +1928,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
1900
1928
|
# light mode
|
|
1901
1929
|
thm.light_conic_label_fill = '#FFFFFF' # [color] Contour label fill color
|
|
1902
1930
|
thm.light_conic_ref_color = '#DCDCDC' # [color] Reference contour color
|
|
1931
|
+
thm.light_conic_highlight = '#FF0000' # [color] Reference contour highlight color
|
|
1903
1932
|
thm.light_beamstop_color = '#80FF0000' # [color] Beamstop color
|
|
1904
1933
|
thm.light_beamstop_edge_color = '#FF0000' # [color] Beamstop edge color
|
|
1905
1934
|
thm.light_det_module_color = '#404040' # [color] Detector module border color
|
|
@@ -1916,6 +1945,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
1916
1945
|
# dark mode
|
|
1917
1946
|
thm.dark_conic_label_fill = '#000000' # [color] Contour label fill color
|
|
1918
1947
|
thm.dark_conic_ref_color = '#303030' # [color] Reference contour color
|
|
1948
|
+
thm.dark_conic_highlight = '#FF0000' # [color] Reference contour highlight color
|
|
1919
1949
|
thm.dark_beamstop_color = '#AAFF0000' # [color] Beamstop color
|
|
1920
1950
|
thm.dark_beamstop_edge_color = '#FF0000' # [color] Beamstop edge color
|
|
1921
1951
|
thm.dark_det_module_color = '#EEEEEE' # [color] Detector module border color
|
|
@@ -2595,66 +2625,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
2595
2625
|
rect_item.setOpacity(self.plo.det_module_alpha)
|
|
2596
2626
|
self.ax.addItem(rect_item)
|
|
2597
2627
|
|
|
2598
|
-
##################
|
|
2599
|
-
# HKL TOOLTIPS #
|
|
2600
|
-
##################
|
|
2601
|
-
def tooltip_hkl_show(self, widget, event):
|
|
2602
|
-
"""
|
|
2603
|
-
Displays a tooltip with the hkl value at the position of the given widget and highlights the widget.
|
|
2604
|
-
|
|
2605
|
-
Parameters:
|
|
2606
|
-
widget (QWidget): The widget for which the tooltip is to be shown. It should have a 'name' attribute and 'opts' dictionary.
|
|
2607
|
-
event (QEvent): The event that triggered the tooltip display. It should have a 'screenPos' method.
|
|
2608
|
-
|
|
2609
|
-
Behavior:
|
|
2610
|
-
- If the widget does not have a name or the contour reference hkl is not set, the event is ignored.
|
|
2611
|
-
- Otherwise, a tooltip is shown at the event's screen position with the widget's name.
|
|
2612
|
-
- The widget is highlighted by changing its pen width.
|
|
2613
|
-
- A timer is started to remove the highlight after a specified timeout.
|
|
2614
|
-
- The timer is added to a list to keep the reference alive until it times out.
|
|
2615
|
-
|
|
2616
|
-
Note:
|
|
2617
|
-
- The tooltip is displayed using QTextEdit to allow HTML formatting.
|
|
2618
|
-
- The highlight is removed by the 'tooltip_hkl_highlight' method.
|
|
2619
|
-
"""
|
|
2620
|
-
if not widget.name or not self.cont_ref_hkl:
|
|
2621
|
-
event.ignore()
|
|
2622
|
-
else:
|
|
2623
|
-
# show hkl tooltip at position
|
|
2624
|
-
text = QtWidgets.QTextEdit(str(widget.name))
|
|
2625
|
-
text.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
|
2626
|
-
pos = QtCore.QPoint(*map(int, event.screenPos()))# - QtCore.QPoint(10,20)
|
|
2627
|
-
QtWidgets.QToolTip.showText(pos, text.toHtml())
|
|
2628
|
-
# highlight the contour
|
|
2629
|
-
# store current pen as 'name' in widget.opts
|
|
2630
|
-
pen = widget.opts['pen']
|
|
2631
|
-
width = widget.opts['pen'].width()
|
|
2632
|
-
|
|
2633
|
-
timer = QtCore.QTimer()
|
|
2634
|
-
timer.setSingleShot(True)
|
|
2635
|
-
timer.timeout.connect(lambda: self.tooltip_hkl_highlight(widget, pen, timer))
|
|
2636
|
-
widget.setPen(pg.mkPen(self.beamstop_color, width=width))
|
|
2637
|
-
#widget.setPen(pg.mkPen(self.cont_cmap.map(0.0, mode='qcolor'), width=width))
|
|
2638
|
-
# add timer to self.list to keep pointers alive
|
|
2639
|
-
# remove -> tooltip_hkl_highlight
|
|
2640
|
-
self.highlight_timers.append(timer)
|
|
2641
|
-
timer.start(self.plo.conic_ref_timeout)
|
|
2642
|
-
|
|
2643
|
-
def tooltip_hkl_highlight(self, widget, pen, timer):
|
|
2644
|
-
"""
|
|
2645
|
-
Resets the contour color of the given widget and removes the specified timer from the highlight timers list.
|
|
2646
|
-
|
|
2647
|
-
Args:
|
|
2648
|
-
widget (QGraphicsItem): The widget whose contour color needs to be reset.
|
|
2649
|
-
width (int): The width of the pen to be used for resetting the contour color.
|
|
2650
|
-
timer (QTimer): The timer to be removed from the highlight timers list.
|
|
2651
|
-
"""
|
|
2652
|
-
# reset contour color
|
|
2653
|
-
widget.setPen(pen)
|
|
2654
|
-
# remove timer
|
|
2655
|
-
if timer in self.highlight_timers:
|
|
2656
|
-
self.highlight_timers.remove(timer)
|
|
2657
|
-
|
|
2658
2628
|
#######
|
|
2659
2629
|
# CIF #
|
|
2660
2630
|
#######
|
|
@@ -3194,9 +3164,35 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
3194
3164
|
self.fwhm_plot.setLabel(axis='left', text='FWHM [\u00B0]', color=self.palette().text().color())
|
|
3195
3165
|
self.fwhm_plot.scale(sx=2, sy=3)
|
|
3196
3166
|
self.fwhm_plot.addItem(self.fwhm_curve)
|
|
3167
|
+
# Thomas-Cox-Hastings curve
|
|
3168
|
+
self.tch_curve = pg.PlotCurveItem()
|
|
3169
|
+
self.tch_curve.setPen(pg.mkPen(color=self.palette().highlight().color(), width=6))
|
|
3170
|
+
self.fwhm_plot.addItem(self.tch_curve)
|
|
3197
3171
|
preview_box_layout.addWidget(self.fwhm_plot)
|
|
3198
3172
|
layout.addWidget(preview_box)
|
|
3199
|
-
|
|
3173
|
+
|
|
3174
|
+
# Thomas-Cox-Hastings
|
|
3175
|
+
tch_box = QtWidgets.QGroupBox('Estimate Thomas-Cox-Hastings parameters')
|
|
3176
|
+
tch_box_layout = QtWidgets.QHBoxLayout()
|
|
3177
|
+
tch_box.setLayout(tch_box_layout)
|
|
3178
|
+
tch_btn_est = QtWidgets.QPushButton('Estimate')
|
|
3179
|
+
tch_btn_est.clicked.connect(self.estimate_tch_parameters)
|
|
3180
|
+
tch_box_layout.addWidget(tch_btn_est)
|
|
3181
|
+
tch_box_layout.addSpacing(20)
|
|
3182
|
+
tch_label_U = QtWidgets.QLabel('U:', alignment=QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignVCenter)
|
|
3183
|
+
tch_box_layout.addWidget(tch_label_U)
|
|
3184
|
+
self.tch_value_U = QtWidgets.QLabel()
|
|
3185
|
+
tch_box_layout.addWidget(self.tch_value_U)
|
|
3186
|
+
tch_label_V = QtWidgets.QLabel('V:', alignment=QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignVCenter)
|
|
3187
|
+
tch_box_layout.addWidget(tch_label_V)
|
|
3188
|
+
self.tch_value_V = QtWidgets.QLabel()
|
|
3189
|
+
tch_box_layout.addWidget(self.tch_value_V)
|
|
3190
|
+
tch_label_W = QtWidgets.QLabel('W:', alignment=QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignVCenter)
|
|
3191
|
+
tch_box_layout.addWidget(tch_label_W)
|
|
3192
|
+
self.tch_value_W = QtWidgets.QLabel()
|
|
3193
|
+
tch_box_layout.addWidget(self.tch_value_W)
|
|
3194
|
+
layout.addWidget(tch_box)
|
|
3195
|
+
|
|
3200
3196
|
# add description
|
|
3201
3197
|
description = QtWidgets.QLabel('This feature is currently in <b>test phase</b>, feedback is very welcome!<br>\
|
|
3202
3198
|
The estimated FWHM (H, in degrees) is shown in the bottom right corner.')
|
|
@@ -3291,7 +3287,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
3291
3287
|
dist = self.geo.dist * 1e-3
|
|
3292
3288
|
tth = np.linspace(0, np.pi/2, 180)
|
|
3293
3289
|
# or use:
|
|
3294
|
-
# _current_tth_max = self.
|
|
3290
|
+
# _current_tth_max = self.calc_tth_max()
|
|
3295
3291
|
# to get max tth for the current setup
|
|
3296
3292
|
|
|
3297
3293
|
# H2, FWHM
|
|
@@ -3350,10 +3346,45 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
3350
3346
|
self.plo.scattering_diameter = round(udict[4][0].value() * udict[4][1], 6)
|
|
3351
3347
|
#self.plo.funct_fwhm_thresh = round(udict[5][0].value() * udict[5][1], 6)
|
|
3352
3348
|
self.action_funct_fwhm_show.setEnabled(True)
|
|
3349
|
+
self.action_funct_fwhm_export.setEnabled(True)
|
|
3353
3350
|
self.plo.show_fwhm = False
|
|
3354
3351
|
self.toggle_fwhm()
|
|
3355
3352
|
win.close()
|
|
3356
3353
|
|
|
3354
|
+
def export_fwhm_grid(self):
|
|
3355
|
+
# calculate the FWHM grid
|
|
3356
|
+
# res = None -> use the current detector pixel dimensions
|
|
3357
|
+
_fwhm_grid = self.calc_overlays(omega=0, res=None, pol=self.plo.polarisation_fac)[-1]
|
|
3358
|
+
# find target file
|
|
3359
|
+
default_path = os.path.join(os.path.expanduser('~'), f'{self.det.name.replace(' ', '_')}_FWHM')
|
|
3360
|
+
target, filter = QtWidgets.QFileDialog.getSaveFileName(self, 'Export FWHM grid', default_path, "Compressed numpy array (*.npz)")
|
|
3361
|
+
if not target:
|
|
3362
|
+
return
|
|
3363
|
+
# save compressed array to target
|
|
3364
|
+
np.savez_compressed(target, fwhm=np.flipud(_fwhm_grid))
|
|
3365
|
+
|
|
3366
|
+
def estimate_tch_parameters(self):
|
|
3367
|
+
tth_max_rad = self.calc_tth_max()
|
|
3368
|
+
tth_min = 0.0
|
|
3369
|
+
tth_max = np.rad2deg(tth_max_rad)
|
|
3370
|
+
tth, fwhm = self.fwhm_curve.getData()
|
|
3371
|
+
tch_cond = (tth > tth_min) & (tth < tth_max)
|
|
3372
|
+
tch_tth = tth[tch_cond]
|
|
3373
|
+
instr_fwhm = fwhm[tch_cond]
|
|
3374
|
+
|
|
3375
|
+
def TCH_pv_fit(tth,*UVW):
|
|
3376
|
+
"""parser for the TCH pseudo-Voigt function"""
|
|
3377
|
+
fwhm,_ = self.calc_TCH_pV(tth,*UVW,0,0)
|
|
3378
|
+
return fwhm
|
|
3379
|
+
|
|
3380
|
+
UVW = 0, 0, instr_fwhm[0]**2
|
|
3381
|
+
[u,v,w], _ = curve_fit(TCH_pv_fit, tch_tth, instr_fwhm, p0=UVW, maxfev=10000)
|
|
3382
|
+
tch_fwhm,_ = self.calc_TCH_pV(tch_tth, u, v, w, 0, 0)
|
|
3383
|
+
self.tch_curve.setData(tch_tth, tch_fwhm)
|
|
3384
|
+
self.tch_value_U.setText(f'{u:.6f}')
|
|
3385
|
+
self.tch_value_V.setText(f'{v:.6f}')
|
|
3386
|
+
self.tch_value_W.setText(f'{w:.6f}')
|
|
3387
|
+
|
|
3357
3388
|
##########
|
|
3358
3389
|
# PXRD #
|
|
3359
3390
|
##########
|
|
@@ -3377,6 +3408,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
3377
3408
|
self.pxrd_win.close()
|
|
3378
3409
|
return
|
|
3379
3410
|
self.win_pxrd_update()
|
|
3411
|
+
self.pxrd_win.adjustSize()
|
|
3380
3412
|
self.pxrd_win.show()
|
|
3381
3413
|
return
|
|
3382
3414
|
|
|
@@ -3393,10 +3425,19 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
3393
3425
|
|
|
3394
3426
|
self.pxrd_curve = pg.PlotCurveItem()
|
|
3395
3427
|
self.pxrd_curve.setPen(pg.mkPen(color=self.palette().text().color()))
|
|
3396
|
-
self.pxrd_scatt =
|
|
3428
|
+
self.pxrd_scatt = ClickableScatterPlotItem(size=20, tip=None)
|
|
3397
3429
|
self.pxrd_scatt.setPen(pg.mkPen(color=self.palette().highlight().color()))
|
|
3430
|
+
self.pxrd_scatt.setBrush(pg.mkBrush(color=self.palette().highlight().color()))
|
|
3398
3431
|
self.pxrd_scatt.setSymbol(self.plo.pxrd_marker_symbol)
|
|
3399
|
-
self.pxrd_scatt.sigClicked.connect(self.
|
|
3432
|
+
self.pxrd_scatt.sigClicked.connect(self.win_pxrd_hkl_clicked)
|
|
3433
|
+
self.pxrd_scatt_highlighted = []
|
|
3434
|
+
|
|
3435
|
+
font = QtGui.QFont()
|
|
3436
|
+
font.setPixelSize(self.plo.conic_hkl_label_size)
|
|
3437
|
+
font.setBold(True)
|
|
3438
|
+
|
|
3439
|
+
self.pxrd_label = pg.TextItem(anchor=(0.5,0.9))
|
|
3440
|
+
self.pxrd_label.setFont(font)
|
|
3400
3441
|
self.pxrd_regio = pg.LinearRegionItem(pen=pg.mkPen(None))
|
|
3401
3442
|
self.pxrd_regio.setMovable(False)
|
|
3402
3443
|
|
|
@@ -3405,9 +3446,19 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
3405
3446
|
self.pxrd_plot.setMenuEnabled(False)
|
|
3406
3447
|
self.pxrd_plot.setLabel(axis='left', text='Intensity [arb. units]', color=self.palette().text().color())
|
|
3407
3448
|
self.pxrd_plot.showGrid(x=True, y=True, alpha=0.5)
|
|
3449
|
+
self.pxrd_plot.getPlotItem().getViewBox().setDefaultPadding(0.0)
|
|
3408
3450
|
self.pxrd_plot.addItem(self.pxrd_curve)
|
|
3409
|
-
|
|
3410
|
-
|
|
3451
|
+
# if points are not visible, e.g. outside of currentrange, the plot size will
|
|
3452
|
+
# not be adjusted properly and stays larger than expected.
|
|
3453
|
+
# ignoreBound allows the plot to autorange disregarding the 'invisble' points
|
|
3454
|
+
# however we need to increase the min y padding to make the scatterplot visible!
|
|
3455
|
+
self.pxrd_plot.addItem(self.pxrd_scatt, ignoreBounds=True)
|
|
3456
|
+
self.pxrd_plot.addItem(self.pxrd_label)
|
|
3457
|
+
# same as above, allow the plot to disregard the beamstop if out of bounds
|
|
3458
|
+
self.pxrd_plot.addItem(self.pxrd_regio, ignoreBounds=True)
|
|
3459
|
+
# this invisible line will do the padding for us -> thanks Frederik!
|
|
3460
|
+
self.pxrd_line = pg.InfiniteLine(pos=0, angle=0, pen=pg.mkPen(None))
|
|
3461
|
+
self.pxrd_plot.addItem(self.pxrd_line)
|
|
3411
3462
|
|
|
3412
3463
|
powder_box_layout.addWidget(self.pxrd_plot)
|
|
3413
3464
|
layout.addWidget(powder_box)
|
|
@@ -3450,6 +3501,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
3450
3501
|
if self.pxrd_win is None:
|
|
3451
3502
|
return
|
|
3452
3503
|
|
|
3504
|
+
# remove highlighting of hkl on update
|
|
3505
|
+
self.win_pxrd_lowlight()
|
|
3506
|
+
|
|
3453
3507
|
# change color if theme changed
|
|
3454
3508
|
self.pxrd_curve.setPen(self.palette().text().color())
|
|
3455
3509
|
self.pxrd_plot.setBackground(self.palette().base().color())
|
|
@@ -3506,7 +3560,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
3506
3560
|
max_res_r = np.nanmax(self._tth)
|
|
3507
3561
|
min_res_r = max(np.nanmin(self._tth), fwhm.mean()/10)
|
|
3508
3562
|
else:
|
|
3509
|
-
max_res_r = self.
|
|
3563
|
+
max_res_r = self.calc_tth_max()
|
|
3510
3564
|
min_res_r = fwhm.mean()/10
|
|
3511
3565
|
|
|
3512
3566
|
# add beamstop shadow
|
|
@@ -3529,37 +3583,62 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
3529
3583
|
self.pxrd_curve.setData(x=xval, y=gauss)
|
|
3530
3584
|
|
|
3531
3585
|
peak_xval = self.calc_unit(peak_ttr)
|
|
3586
|
+
offset = (gauss.max() + gauss.min()) * self.plo.pxrd_marker_offset
|
|
3587
|
+
self.pxrd_scatt.setData(x=peak_xval,
|
|
3588
|
+
y=np.zeros(len(peak_xval))-offset,
|
|
3589
|
+
data=hkl)
|
|
3532
3590
|
# remove peaks that are ouside of the detector screen
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
self.
|
|
3591
|
+
cond = (peak_xval <= xval.max()) & (peak_xval >= xval.min())
|
|
3592
|
+
self.pxrd_scatt.setPointsVisible(cond)
|
|
3593
|
+
self.pxrd_line.setPos(-offset*2)
|
|
3536
3594
|
|
|
3595
|
+
# add axis label
|
|
3537
3596
|
self.pxrd_plot.setLabel(axis='bottom', text=self.unit_names[self.geo.unit], color=self.palette().text().color())
|
|
3597
|
+
|
|
3538
3598
|
# flip axis for d-spacing
|
|
3539
3599
|
if self.geo.unit == 1:
|
|
3540
3600
|
self.pxrd_plot.getPlotItem().getViewBox().invertX(True)
|
|
3541
3601
|
else:
|
|
3542
3602
|
self.pxrd_plot.getPlotItem().getViewBox().invertX(False)
|
|
3543
3603
|
|
|
3544
|
-
def
|
|
3545
|
-
"""
|
|
3546
|
-
Displays the HKL (Miller indices) information in a tooltip when a point is clicked.
|
|
3547
|
-
|
|
3548
|
-
Parameters:
|
|
3549
|
-
widget (QtWidgets.QWidget): The widget that triggered the event.
|
|
3550
|
-
points (list): A list of points where the event occurred.
|
|
3551
|
-
event (QtGui.QMouseEvent): The mouse event that triggered the display.
|
|
3552
|
-
|
|
3553
|
-
Returns:
|
|
3554
|
-
None
|
|
3555
|
-
"""
|
|
3604
|
+
def win_pxrd_hkl_clicked(self, widget, points, event):
|
|
3556
3605
|
if not widget.name:
|
|
3557
3606
|
return
|
|
3558
3607
|
elif len(points) > 0:
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3608
|
+
point = points[0]
|
|
3609
|
+
if event.buttons() == QtCore.Qt.MouseButton.RightButton:
|
|
3610
|
+
self.patches['reference'][point.index()].lowlight()
|
|
3611
|
+
return
|
|
3612
|
+
self.win_pxrd_lowlight()
|
|
3613
|
+
point.setPen(pg.mkPen(color=self.conic_highlight))
|
|
3614
|
+
point.setBrush(pg.mkBrush(color=self.conic_highlight))
|
|
3615
|
+
self.pxrd_scatt_highlighted.append(point)
|
|
3616
|
+
self.win_pxrd_set_hkl_label(point)
|
|
3617
|
+
# highlight in main window
|
|
3618
|
+
self.patches['reference'][point.index()].highlight()
|
|
3619
|
+
|
|
3620
|
+
def win_pxrd_set_hkl_label(self, point):
|
|
3621
|
+
self.pxrd_label.setText(str(point.data()))
|
|
3622
|
+
self.pxrd_label.setPos(point.pos())
|
|
3623
|
+
self.pxrd_label.setVisible(True)
|
|
3624
|
+
|
|
3625
|
+
def win_pxrd_highlight(self, index):
|
|
3626
|
+
# highlight from main window
|
|
3627
|
+
self.win_pxrd_lowlight()
|
|
3628
|
+
point = self.pxrd_scatt.points()[index]
|
|
3629
|
+
point.setPen(pg.mkPen(self.conic_highlight))
|
|
3630
|
+
point.setBrush(pg.mkBrush(self.conic_highlight))
|
|
3631
|
+
self.pxrd_scatt_highlighted.append(point)
|
|
3632
|
+
self.win_pxrd_set_hkl_label(point)
|
|
3633
|
+
|
|
3634
|
+
def win_pxrd_lowlight(self):
|
|
3635
|
+
_pen = pg.mkPen(color=self.palette().highlight().color())
|
|
3636
|
+
_brush = pg.mkBrush(color=self.palette().highlight().color())
|
|
3637
|
+
for p in self.pxrd_scatt_highlighted:
|
|
3638
|
+
#p.resetPen()
|
|
3639
|
+
p.setPen(_pen)
|
|
3640
|
+
p.setBrush(_brush)
|
|
3641
|
+
self.pxrd_label.setVisible(False)
|
|
3563
3642
|
|
|
3564
3643
|
#############
|
|
3565
3644
|
# EXPORT #
|
|
@@ -4808,7 +4887,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
4808
4887
|
1.05395590e-03, 1.07113920e-03, 1.08845123e-03, 1.10583974e-03,
|
|
4809
4888
|
1.12333211e-03, 1.14094222e-03]}
|
|
4810
4889
|
|
|
4811
|
-
def
|
|
4890
|
+
def calc_tth_max(self, scale=1.0):
|
|
4812
4891
|
"""
|
|
4813
4892
|
Calculate the maximum 2theta angle for the given geometry
|
|
4814
4893
|
- used to generate 2theta values to draw conics
|
|
@@ -4820,8 +4899,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
4820
4899
|
float, maximum 2-theta in radians
|
|
4821
4900
|
"""
|
|
4822
4901
|
# make screen grid
|
|
4823
|
-
size_h = np.array([-self.xdim, self.xdim])*
|
|
4824
|
-
size_v = np.array([-self.ydim, self.ydim])*
|
|
4902
|
+
size_h = np.array([-self.xdim, self.xdim])*scale
|
|
4903
|
+
size_v = np.array([-self.ydim, self.ydim])*scale
|
|
4825
4904
|
_gx, _gy = np.meshgrid(size_h, size_v, sparse=True)
|
|
4826
4905
|
# build vector -> 3 x n x m
|
|
4827
4906
|
_vec = np.full((3, 2, 2), self.geo.dist)
|
|
@@ -4839,7 +4918,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
4839
4918
|
D_a = _res[2]
|
|
4840
4919
|
# 2theta - Angle between pixel, sample, and POBI
|
|
4841
4920
|
tth = np.arctan2(R_a, D_a)
|
|
4842
|
-
# 2theta max
|
|
4921
|
+
# 2theta max
|
|
4843
4922
|
return tth.max()
|
|
4844
4923
|
|
|
4845
4924
|
def rot_100(self, a, cc=1):
|
|
@@ -5167,8 +5246,13 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
5167
5246
|
The full width at half maximum (FWHM) for the detector.
|
|
5168
5247
|
"""
|
|
5169
5248
|
# scale overlay to detector dimensions
|
|
5170
|
-
|
|
5171
|
-
|
|
5249
|
+
if res is not None and res > 0:
|
|
5250
|
+
_res_scale = self.ydim/self.xdim
|
|
5251
|
+
_res_v = int(round(_res_scale * res, 0))
|
|
5252
|
+
else:
|
|
5253
|
+
res = (self.det.hmp * self.det.hmn + self.det.hgp * (self.det.hmn-1) + self.det.cbh)
|
|
5254
|
+
_res_v = (self.det.vmp * self.det.vmn + self.det.vgp * (self.det.vmn-1) + self.det.cbh)
|
|
5255
|
+
|
|
5172
5256
|
# neutral overlay grid
|
|
5173
5257
|
grd = np.ones((_res_v, res))
|
|
5174
5258
|
|
|
@@ -5373,6 +5457,26 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
|
5373
5457
|
"""
|
|
5374
5458
|
return 1/(np.sqrt(2*np.pi)*s)*np.exp(-np.square((x - m)/s)/2)
|
|
5375
5459
|
|
|
5460
|
+
def calc_TCH_pV(self, tth, U, V, W, X, Y):
|
|
5461
|
+
"""
|
|
5462
|
+
Thompson-Cox-Hastings pseudo-Voigt
|
|
5463
|
+
Gaussian FWHM: U*tan(theta)^2 + V*tan(theta) + W
|
|
5464
|
+
Lorentzian FWHM: X*tan(theta) + Y/cos(theta)
|
|
5465
|
+
return pseudo Voigt FWHM
|
|
5466
|
+
"""
|
|
5467
|
+
theta = tth*np.pi/360
|
|
5468
|
+
tt = np.tan(theta)
|
|
5469
|
+
ct = np.cos(theta)
|
|
5470
|
+
G = np.sqrt(U*tt**2 + V*tt + W)
|
|
5471
|
+
L = X*tt + Y/ct
|
|
5472
|
+
|
|
5473
|
+
H = (G**5 + 2.69269*G**4*L + 2.42843*G**3*L**2 \
|
|
5474
|
+
+ 4.47163*G**2*L**3 + 0.07842*G*L**4 + L**5)**(1/5)
|
|
5475
|
+
# notice that the eta is defined differently in the FullProf manual
|
|
5476
|
+
# such that eta = 1-eta_FullProf
|
|
5477
|
+
eta = 1-(1.36603*L/H - 0.47719*(L/H)**2 + 0.11116*(L/H)**3)
|
|
5478
|
+
return H, eta
|
|
5479
|
+
|
|
5376
5480
|
#############
|
|
5377
5481
|
# SETTINGS #
|
|
5378
5482
|
#############
|
|
@@ -6384,4 +6488,71 @@ class Ref(object):
|
|
|
6384
6488
|
Returns:
|
|
6385
6489
|
bool: True if all required attributes are present, False otherwise.
|
|
6386
6490
|
"""
|
|
6387
|
-
return np.all([self.has_cif, self.has_dsp, self.has_hkl])
|
|
6491
|
+
return np.all([self.has_cif, self.has_dsp, self.has_hkl])
|
|
6492
|
+
|
|
6493
|
+
class HoverableCurveItem(pg.PlotCurveItem):
|
|
6494
|
+
def __init__(self, parent=None, hoverable=True, *args, **kwargs):
|
|
6495
|
+
super().__init__(*args, **kwargs)
|
|
6496
|
+
self.parent = parent
|
|
6497
|
+
self.basePen = self.opts['pen']
|
|
6498
|
+
self.hoverable = hoverable
|
|
6499
|
+
self.setAcceptHoverEvents(True)
|
|
6500
|
+
|
|
6501
|
+
def setData(self, *args, **kargs):
|
|
6502
|
+
# added the pen re-assignment to the function.
|
|
6503
|
+
if 'pen' in kargs:
|
|
6504
|
+
self.basePen = kargs['pen']
|
|
6505
|
+
return super().setData(*args, **kargs)
|
|
6506
|
+
|
|
6507
|
+
def highlight(self, pos=None):
|
|
6508
|
+
self.parent.patches['ref_hl_curve'].setData(self.xData, self.yData)
|
|
6509
|
+
self.parent.patches['ref_hl_curve'].setVisible(True)
|
|
6510
|
+
self.parent.patches['ref_hl_label'].setText(str(self.name))
|
|
6511
|
+
self.parent.patches['ref_hl_label'].setVisible(True)
|
|
6512
|
+
if pos is None:
|
|
6513
|
+
self.parent.patches['ref_hl_label'].setPos(self.xData[0], self.yData[0])
|
|
6514
|
+
else:
|
|
6515
|
+
self.parent.patches['ref_hl_label'].setPos(pos)
|
|
6516
|
+
|
|
6517
|
+
if self.parent.pxrd_win is not None and self.parent.pxrd_win.isVisible():
|
|
6518
|
+
self.parent.win_pxrd_highlight(self.index)
|
|
6519
|
+
|
|
6520
|
+
def lowlight(self):
|
|
6521
|
+
self.setPen(self.basePen)
|
|
6522
|
+
self.parent.patches['ref_hl_label'].setVisible(False)
|
|
6523
|
+
self.parent.patches['ref_hl_curve'].setVisible(False)
|
|
6524
|
+
self.parent.win_pxrd_lowlight()
|
|
6525
|
+
|
|
6526
|
+
def hoverEvent(self, ev):
|
|
6527
|
+
if self.hoverable and not ev.isExit() and ev.buttons() == QtCore.Qt.MouseButton.LeftButton:
|
|
6528
|
+
if self.mouseShape().contains(ev.pos()):
|
|
6529
|
+
self.highlight(ev.pos())
|
|
6530
|
+
|
|
6531
|
+
def mouseClickEvent(self, ev):
|
|
6532
|
+
if ev.buttons() == QtCore.Qt.MouseButton.LeftButton:
|
|
6533
|
+
if self.mouseShape().contains(ev.pos()):
|
|
6534
|
+
self.highlight(ev.pos())
|
|
6535
|
+
else:
|
|
6536
|
+
self.lowlight()
|
|
6537
|
+
return super().mouseClickEvent(ev)
|
|
6538
|
+
|
|
6539
|
+
class ClickableScatterPlotItem(pg.ScatterPlotItem):
|
|
6540
|
+
"""
|
|
6541
|
+
Reimplementation of the ScatterPlotItem class with added click event handling.
|
|
6542
|
+
The original class is limited to left mouse button click events.
|
|
6543
|
+
"""
|
|
6544
|
+
def __init__(self, *args, **kwargs):
|
|
6545
|
+
super().__init__(*args, **kwargs)
|
|
6546
|
+
|
|
6547
|
+
def mouseClickEvent(self, ev):
|
|
6548
|
+
if not ev.button() == QtCore.Qt.MouseButton.NoButton:
|
|
6549
|
+
pts = self.pointsAt(ev.pos())
|
|
6550
|
+
if len(pts) > 0:
|
|
6551
|
+
self.ptsClicked = pts
|
|
6552
|
+
ev.accept()
|
|
6553
|
+
self.sigClicked.emit(self, self.ptsClicked, ev)
|
|
6554
|
+
else:
|
|
6555
|
+
#print "no spots"
|
|
6556
|
+
ev.ignore()
|
|
6557
|
+
else:
|
|
6558
|
+
ev.ignore()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: xrdPlanner
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: A tool to project X-ray diffraction cones on a detector screen at different geometries (tilt, rotation, offset) and X-ray energies
|
|
5
5
|
Author-email: Lennard Krause <lkrause@chem.au.dk>
|
|
6
6
|
Project-URL: Homepage, https://github.com/LennardKrause/xrdPlanner
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|