xrdPlanner 2.2.1__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: xrdPlanner
3
- Version: 2.2.1
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.2.1'
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__ = '05.12.2024'
6
- __year__ = '2024'
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
  """
@@ -351,6 +349,8 @@ class MainWindow(QtWidgets.QMainWindow):
351
349
  'bs_label':None,
352
350
  'conic':[],
353
351
  'reference':[],
352
+ 'ref_hl_label':None,
353
+ 'ref_hl_curve':None,
354
354
  'labels':[],
355
355
  'polar_grid':[]}
356
356
 
@@ -368,12 +368,27 @@ class MainWindow(QtWidgets.QMainWindow):
368
368
 
369
369
  # add empty plot per reference contour line
370
370
  for i in range(self.plo.conic_ref_num):
371
- ref = pg.PlotCurveItem(useCache=True)
371
+ ref = HoverableCurveItem(self, useCache=True)
372
372
  self.ax.addItem(ref)
373
373
  self.patches['reference'].append(ref)
374
- self.patches['reference'][i].setClickable(True, width=self.plo.conic_ref_linewidth*2)
375
- self.patches['reference'][i].sigClicked.connect(self.tooltip_hkl_show)
376
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)
377
392
 
378
393
  # add empty plot per contour line
379
394
  for i in range(self.plo.conic_tth_num):
@@ -394,17 +409,17 @@ class MainWindow(QtWidgets.QMainWindow):
394
409
  self.ax.addItem(bs_label)
395
410
 
396
411
  # add poni scatter plot
397
- self.patches['poni'] = pg.ScatterPlotItem(symbol = self.plo.poni_marker,
398
- size = self.plo.poni_size,
399
- brush = pg.mkBrush(self.cont_cmap.map(0, mode='qcolor')),
400
- pen = pg.mkPen(None))
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))
401
416
  self.ax.addItem(self.patches['poni'])
402
417
 
403
418
  # add beam center scatter plot
404
- self.patches['beamcenter'] = pg.ScatterPlotItem(symbol = self.plo.beamcenter_marker,
405
- size = self.plo.beamcenter_size,
406
- brush = pg.mkBrush(self.cont_cmap.map(0, mode='qcolor')),
407
- pen = pg.mkPen(None))
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))
408
423
  self.ax.addItem(self.patches['beamcenter'])
409
424
 
410
425
  self.patches['overlay'] = pg.ImageItem()
@@ -652,6 +667,7 @@ class MainWindow(QtWidgets.QMainWindow):
652
667
  # reference contour
653
668
  self.conic_label_fill = QtGui.QColor(self.thm.dark_conic_label_fill)
654
669
  self.conic_ref_color = QtGui.QColor(self.thm.dark_conic_ref_color)
670
+ self.conic_highlight = QtGui.QColor(self.thm.dark_conic_highlight)
655
671
  self.det_module_color = QtGui.QColor(self.thm.dark_det_module_color)
656
672
  self.det_module_fill = QtGui.QColor(self.thm.dark_det_module_fill)
657
673
  # general
@@ -689,6 +705,7 @@ class MainWindow(QtWidgets.QMainWindow):
689
705
  # reference contour
690
706
  self.conic_label_fill = QtGui.QColor(self.thm.light_conic_label_fill)
691
707
  self.conic_ref_color = QtGui.QColor(self.thm.light_conic_ref_color)
708
+ self.conic_highlight = QtGui.QColor(self.thm.light_conic_highlight)
692
709
  self.det_module_color = QtGui.QColor(self.thm.light_det_module_color)
693
710
  self.det_module_fill = QtGui.QColor(self.thm.light_det_module_fill)
694
711
  # general
@@ -1413,7 +1430,7 @@ class MainWindow(QtWidgets.QMainWindow):
1413
1430
 
1414
1431
  # calculate the maximum resolution for the given geometry
1415
1432
  if self.plo.conic_tth_auto:
1416
- theta_max = np.rad2deg(self.calc_max_resolution(m=0.90))
1433
+ theta_max = np.rad2deg(self.calc_tth_max(scale=0.90))
1417
1434
  # make new array of 2theta values
1418
1435
  self.cont_geom_num = np.linspace(theta_max/self.plo.conic_tth_num, theta_max, self.plo.conic_tth_num)
1419
1436
 
@@ -1507,6 +1524,9 @@ class MainWindow(QtWidgets.QMainWindow):
1507
1524
  - self.conic_ref_color: Color for the reference contour lines.
1508
1525
  - self.patches['reference']: Dictionary to store the reference contour line patches.
1509
1526
  """
1527
+ # remove highlighted curve and hkl label
1528
+ self.patches['ref_hl_curve'].setVisible(False)
1529
+ self.patches['ref_hl_label'].setVisible(False)
1510
1530
  # plot reference contour lines
1511
1531
  # standard contour lines are to be drawn
1512
1532
  for _n in range(self.plo.conic_ref_num):
@@ -1554,10 +1574,13 @@ class MainWindow(QtWidgets.QMainWindow):
1554
1574
  #
1555
1575
  # self.setStyleSheet('''QToolTip {... ) didn't work
1556
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
1557
1578
  else:
1558
1579
  self.patches['reference'][_n].name = f'({h: 2.0f} {k: 2.0f} {l: 2.0f})'
1580
+ self.patches['reference'][_n].index = _n
1559
1581
  else:
1560
1582
  self.patches['reference'][_n].name = None
1583
+ self.patches['reference'][_n].index = None
1561
1584
 
1562
1585
  # current fraction for colormap
1563
1586
  if self.plo.colored_reference:
@@ -1831,13 +1854,13 @@ class MainWindow(QtWidgets.QMainWindow):
1831
1854
  plo.show_polarisation = True # [bool] Show polarisation overlay
1832
1855
  plo.show_solidangle = False # [bool] Show solid angle overlay
1833
1856
  plo.show_unit_hover = True # [bool] Show unit value on hover
1834
- plo.show_grid = False # [bool] Show azimuthal grid
1857
+ plo.show_grid = False # [bool] Show azimuthal grid
1835
1858
  plo.azimuth_num = 13 # [int] Number of azimuthal grid lines
1836
1859
  plo.overlay_resolution = 300 # [int] Overlay resolution
1837
1860
  plo.overlay_toggle_warn = True # [bool] Overlay warn color threshold
1838
1861
  # - pxrd plot -
1839
1862
  plo.pxrd_marker_symbol = 'arrow_up' # [marker] Symbol to mark peaks
1840
- plo.pxrd_marker_offset = 0.02 # [float] offset of marker from x-axis
1863
+ plo.pxrd_marker_offset = 0.05 # [float] offset of marker from x-axis
1841
1864
  # - extra functions -
1842
1865
  plo.show_fwhm = False # [bool] Show delta_d/d function
1843
1866
  plo.sensor_thickness = 1000e-6 # [float] Detector sensor thickness [m]
@@ -1905,6 +1928,7 @@ class MainWindow(QtWidgets.QMainWindow):
1905
1928
  # light mode
1906
1929
  thm.light_conic_label_fill = '#FFFFFF' # [color] Contour label fill color
1907
1930
  thm.light_conic_ref_color = '#DCDCDC' # [color] Reference contour color
1931
+ thm.light_conic_highlight = '#FF0000' # [color] Reference contour highlight color
1908
1932
  thm.light_beamstop_color = '#80FF0000' # [color] Beamstop color
1909
1933
  thm.light_beamstop_edge_color = '#FF0000' # [color] Beamstop edge color
1910
1934
  thm.light_det_module_color = '#404040' # [color] Detector module border color
@@ -1921,6 +1945,7 @@ class MainWindow(QtWidgets.QMainWindow):
1921
1945
  # dark mode
1922
1946
  thm.dark_conic_label_fill = '#000000' # [color] Contour label fill color
1923
1947
  thm.dark_conic_ref_color = '#303030' # [color] Reference contour color
1948
+ thm.dark_conic_highlight = '#FF0000' # [color] Reference contour highlight color
1924
1949
  thm.dark_beamstop_color = '#AAFF0000' # [color] Beamstop color
1925
1950
  thm.dark_beamstop_edge_color = '#FF0000' # [color] Beamstop edge color
1926
1951
  thm.dark_det_module_color = '#EEEEEE' # [color] Detector module border color
@@ -2600,66 +2625,6 @@ class MainWindow(QtWidgets.QMainWindow):
2600
2625
  rect_item.setOpacity(self.plo.det_module_alpha)
2601
2626
  self.ax.addItem(rect_item)
2602
2627
 
2603
- ##################
2604
- # HKL TOOLTIPS #
2605
- ##################
2606
- def tooltip_hkl_show(self, widget, event):
2607
- """
2608
- Displays a tooltip with the hkl value at the position of the given widget and highlights the widget.
2609
-
2610
- Parameters:
2611
- widget (QWidget): The widget for which the tooltip is to be shown. It should have a 'name' attribute and 'opts' dictionary.
2612
- event (QEvent): The event that triggered the tooltip display. It should have a 'screenPos' method.
2613
-
2614
- Behavior:
2615
- - If the widget does not have a name or the contour reference hkl is not set, the event is ignored.
2616
- - Otherwise, a tooltip is shown at the event's screen position with the widget's name.
2617
- - The widget is highlighted by changing its pen width.
2618
- - A timer is started to remove the highlight after a specified timeout.
2619
- - The timer is added to a list to keep the reference alive until it times out.
2620
-
2621
- Note:
2622
- - The tooltip is displayed using QTextEdit to allow HTML formatting.
2623
- - The highlight is removed by the 'tooltip_hkl_highlight' method.
2624
- """
2625
- if not widget.name or not self.cont_ref_hkl:
2626
- event.ignore()
2627
- else:
2628
- # show hkl tooltip at position
2629
- text = QtWidgets.QTextEdit(str(widget.name))
2630
- text.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
2631
- pos = QtCore.QPoint(*map(int, event.screenPos()))# - QtCore.QPoint(10,20)
2632
- QtWidgets.QToolTip.showText(pos, text.toHtml())
2633
- # highlight the contour
2634
- # store current pen as 'name' in widget.opts
2635
- pen = widget.opts['pen']
2636
- width = widget.opts['pen'].width()
2637
-
2638
- timer = QtCore.QTimer()
2639
- timer.setSingleShot(True)
2640
- timer.timeout.connect(lambda: self.tooltip_hkl_highlight(widget, pen, timer))
2641
- widget.setPen(pg.mkPen(self.beamstop_color, width=width))
2642
- #widget.setPen(pg.mkPen(self.cont_cmap.map(0.0, mode='qcolor'), width=width))
2643
- # add timer to self.list to keep pointers alive
2644
- # remove -> tooltip_hkl_highlight
2645
- self.highlight_timers.append(timer)
2646
- timer.start(self.plo.conic_ref_timeout)
2647
-
2648
- def tooltip_hkl_highlight(self, widget, pen, timer):
2649
- """
2650
- Resets the contour color of the given widget and removes the specified timer from the highlight timers list.
2651
-
2652
- Args:
2653
- widget (QGraphicsItem): The widget whose contour color needs to be reset.
2654
- width (int): The width of the pen to be used for resetting the contour color.
2655
- timer (QTimer): The timer to be removed from the highlight timers list.
2656
- """
2657
- # reset contour color
2658
- widget.setPen(pen)
2659
- # remove timer
2660
- if timer in self.highlight_timers:
2661
- self.highlight_timers.remove(timer)
2662
-
2663
2628
  #######
2664
2629
  # CIF #
2665
2630
  #######
@@ -3199,9 +3164,35 @@ class MainWindow(QtWidgets.QMainWindow):
3199
3164
  self.fwhm_plot.setLabel(axis='left', text='FWHM [\u00B0]', color=self.palette().text().color())
3200
3165
  self.fwhm_plot.scale(sx=2, sy=3)
3201
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)
3202
3171
  preview_box_layout.addWidget(self.fwhm_plot)
3203
3172
  layout.addWidget(preview_box)
3204
-
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
+
3205
3196
  # add description
3206
3197
  description = QtWidgets.QLabel('This feature is currently in <b>test phase</b>, feedback is very welcome!<br>\
3207
3198
  The estimated FWHM (H, in degrees) is shown in the bottom right corner.')
@@ -3296,7 +3287,7 @@ class MainWindow(QtWidgets.QMainWindow):
3296
3287
  dist = self.geo.dist * 1e-3
3297
3288
  tth = np.linspace(0, np.pi/2, 180)
3298
3289
  # or use:
3299
- # _current_tth_max = self.calc_max_resolution()
3290
+ # _current_tth_max = self.calc_tth_max()
3300
3291
  # to get max tth for the current setup
3301
3292
 
3302
3293
  # H2, FWHM
@@ -3372,6 +3363,28 @@ class MainWindow(QtWidgets.QMainWindow):
3372
3363
  # save compressed array to target
3373
3364
  np.savez_compressed(target, fwhm=np.flipud(_fwhm_grid))
3374
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
+
3375
3388
  ##########
3376
3389
  # PXRD #
3377
3390
  ##########
@@ -3395,6 +3408,7 @@ class MainWindow(QtWidgets.QMainWindow):
3395
3408
  self.pxrd_win.close()
3396
3409
  return
3397
3410
  self.win_pxrd_update()
3411
+ self.pxrd_win.adjustSize()
3398
3412
  self.pxrd_win.show()
3399
3413
  return
3400
3414
 
@@ -3411,10 +3425,19 @@ class MainWindow(QtWidgets.QMainWindow):
3411
3425
 
3412
3426
  self.pxrd_curve = pg.PlotCurveItem()
3413
3427
  self.pxrd_curve.setPen(pg.mkPen(color=self.palette().text().color()))
3414
- self.pxrd_scatt = pg.ScatterPlotItem(size=15)
3428
+ self.pxrd_scatt = ClickableScatterPlotItem(size=20, tip=None)
3415
3429
  self.pxrd_scatt.setPen(pg.mkPen(color=self.palette().highlight().color()))
3430
+ self.pxrd_scatt.setBrush(pg.mkBrush(color=self.palette().highlight().color()))
3416
3431
  self.pxrd_scatt.setSymbol(self.plo.pxrd_marker_symbol)
3417
- self.pxrd_scatt.sigClicked.connect(self.win_pxrd_show_hkl)
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)
3418
3441
  self.pxrd_regio = pg.LinearRegionItem(pen=pg.mkPen(None))
3419
3442
  self.pxrd_regio.setMovable(False)
3420
3443
 
@@ -3423,9 +3446,19 @@ class MainWindow(QtWidgets.QMainWindow):
3423
3446
  self.pxrd_plot.setMenuEnabled(False)
3424
3447
  self.pxrd_plot.setLabel(axis='left', text='Intensity [arb. units]', color=self.palette().text().color())
3425
3448
  self.pxrd_plot.showGrid(x=True, y=True, alpha=0.5)
3449
+ self.pxrd_plot.getPlotItem().getViewBox().setDefaultPadding(0.0)
3426
3450
  self.pxrd_plot.addItem(self.pxrd_curve)
3427
- self.pxrd_plot.addItem(self.pxrd_scatt)
3428
- self.pxrd_plot.addItem(self.pxrd_regio)
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)
3429
3462
 
3430
3463
  powder_box_layout.addWidget(self.pxrd_plot)
3431
3464
  layout.addWidget(powder_box)
@@ -3468,6 +3501,9 @@ class MainWindow(QtWidgets.QMainWindow):
3468
3501
  if self.pxrd_win is None:
3469
3502
  return
3470
3503
 
3504
+ # remove highlighting of hkl on update
3505
+ self.win_pxrd_lowlight()
3506
+
3471
3507
  # change color if theme changed
3472
3508
  self.pxrd_curve.setPen(self.palette().text().color())
3473
3509
  self.pxrd_plot.setBackground(self.palette().base().color())
@@ -3524,7 +3560,7 @@ class MainWindow(QtWidgets.QMainWindow):
3524
3560
  max_res_r = np.nanmax(self._tth)
3525
3561
  min_res_r = max(np.nanmin(self._tth), fwhm.mean()/10)
3526
3562
  else:
3527
- max_res_r = self.calc_max_resolution()
3563
+ max_res_r = self.calc_tth_max()
3528
3564
  min_res_r = fwhm.mean()/10
3529
3565
 
3530
3566
  # add beamstop shadow
@@ -3547,37 +3583,62 @@ class MainWindow(QtWidgets.QMainWindow):
3547
3583
  self.pxrd_curve.setData(x=xval, y=gauss)
3548
3584
 
3549
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)
3550
3590
  # remove peaks that are ouside of the detector screen
3551
- peak_idx = np.nonzero(peak_xval <= xval.max())[0]
3552
- offset = (gauss.max() - gauss.min())*self.plo.pxrd_marker_offset
3553
- self.pxrd_scatt.setData(x=peak_xval[peak_idx], y=np.zeros(len(peak_xval[peak_idx]))-offset, data=[hkl[i] for i in peak_idx])
3591
+ cond = (peak_xval <= xval.max()) & (peak_xval >= xval.min())
3592
+ self.pxrd_scatt.setPointsVisible(cond)
3593
+ self.pxrd_line.setPos(-offset*2)
3554
3594
 
3595
+ # add axis label
3555
3596
  self.pxrd_plot.setLabel(axis='bottom', text=self.unit_names[self.geo.unit], color=self.palette().text().color())
3597
+
3556
3598
  # flip axis for d-spacing
3557
3599
  if self.geo.unit == 1:
3558
3600
  self.pxrd_plot.getPlotItem().getViewBox().invertX(True)
3559
3601
  else:
3560
3602
  self.pxrd_plot.getPlotItem().getViewBox().invertX(False)
3561
3603
 
3562
- def win_pxrd_show_hkl(self, widget, points, event):
3563
- """
3564
- Displays the HKL (Miller indices) information in a tooltip when a point is clicked.
3565
-
3566
- Parameters:
3567
- widget (QtWidgets.QWidget): The widget that triggered the event.
3568
- points (list): A list of points where the event occurred.
3569
- event (QtGui.QMouseEvent): The mouse event that triggered the display.
3570
-
3571
- Returns:
3572
- None
3573
- """
3604
+ def win_pxrd_hkl_clicked(self, widget, points, event):
3574
3605
  if not widget.name:
3575
3606
  return
3576
3607
  elif len(points) > 0:
3577
- text = QtWidgets.QTextEdit(str(points[0].data()))
3578
- text.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
3579
- pos = QtCore.QPoint(*map(int, event.screenPos()))
3580
- QtWidgets.QToolTip.showText(pos, text.toHtml())
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)
3581
3642
 
3582
3643
  #############
3583
3644
  # EXPORT #
@@ -4826,7 +4887,7 @@ class MainWindow(QtWidgets.QMainWindow):
4826
4887
  1.05395590e-03, 1.07113920e-03, 1.08845123e-03, 1.10583974e-03,
4827
4888
  1.12333211e-03, 1.14094222e-03]}
4828
4889
 
4829
- def calc_max_resolution(self, m=1.0):
4890
+ def calc_tth_max(self, scale=1.0):
4830
4891
  """
4831
4892
  Calculate the maximum 2theta angle for the given geometry
4832
4893
  - used to generate 2theta values to draw conics
@@ -4838,8 +4899,8 @@ class MainWindow(QtWidgets.QMainWindow):
4838
4899
  float, maximum 2-theta in radians
4839
4900
  """
4840
4901
  # make screen grid
4841
- size_h = np.array([-self.xdim, self.xdim])*m
4842
- size_v = np.array([-self.ydim, self.ydim])*m
4902
+ size_h = np.array([-self.xdim, self.xdim])*scale
4903
+ size_v = np.array([-self.ydim, self.ydim])*scale
4843
4904
  _gx, _gy = np.meshgrid(size_h, size_v, sparse=True)
4844
4905
  # build vector -> 3 x n x m
4845
4906
  _vec = np.full((3, 2, 2), self.geo.dist)
@@ -4857,7 +4918,7 @@ class MainWindow(QtWidgets.QMainWindow):
4857
4918
  D_a = _res[2]
4858
4919
  # 2theta - Angle between pixel, sample, and POBI
4859
4920
  tth = np.arctan2(R_a, D_a)
4860
- # 2theta max
4921
+ # 2theta max
4861
4922
  return tth.max()
4862
4923
 
4863
4924
  def rot_100(self, a, cc=1):
@@ -5396,6 +5457,26 @@ class MainWindow(QtWidgets.QMainWindow):
5396
5457
  """
5397
5458
  return 1/(np.sqrt(2*np.pi)*s)*np.exp(-np.square((x - m)/s)/2)
5398
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
+
5399
5480
  #############
5400
5481
  # SETTINGS #
5401
5482
  #############
@@ -6407,4 +6488,71 @@ class Ref(object):
6407
6488
  Returns:
6408
6489
  bool: True if all required attributes are present, False otherwise.
6409
6490
  """
6410
- 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
1
+ Metadata-Version: 2.2
2
2
  Name: xrdPlanner
3
- Version: 2.2.1
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