Skip to content

Commit a495731

Browse files
committed
feat: update reference handling in CIF tree and add d-spacing conversion functions
1 parent f48e1c6 commit a495731

4 files changed

Lines changed: 86 additions & 22 deletions

File tree

plaid/misc.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,43 @@
1010
"""
1111
import numpy as np
1212

13+
HC = 12.3984193 # Planck constant times speed of light in keV·Å
1314

1415
def q_to_tth(q, E):
1516
"""Convert q to 2theta."""
1617
# Convert 2theta to radians
17-
wavelength = 12.398 / E
18+
wavelength = HC / E
1819
tth = 2 * np.degrees(np.arcsin(q * wavelength / (4 * np.pi)))
1920
return tth
2021

2122
def tth_to_q(tth, E):
2223
"""Convert 2theta to q."""
2324
# Convert 2theta to radians
24-
wavelength = 12.398 / E
25+
wavelength = HC / E
2526
q = (4 * np.pi / wavelength) * np.sin(np.radians(tth) / 2)
2627
return q
2728

29+
def d_to_q(d):
30+
"""Convert d-spacing to q."""
31+
return 2 * np.pi / d
32+
33+
def q_to_d(q):
34+
"""Convert q to d-spacing."""
35+
return 2 * np.pi / q
36+
37+
def d_to_tth(d, E):
38+
"""Convert d-spacing to 2theta."""
39+
wavelength = HC / E
40+
tth = 2 * np.degrees(np.arcsin(wavelength / (2 * d)))
41+
return tth
42+
43+
def tth_to_d(tth, E):
44+
"""Convert 2theta to d-spacing."""
45+
wavelength = HC / E
46+
d = wavelength / (2 * np.sin(np.radians(tth) / 2))
47+
return d
48+
49+
2850
def get_divisors(x):
2951
"""Get all divisors of an integer x."""
3052
divisors = []

plaid/plaid.py

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from plaid.dialogs import H5Dialog, ExportSettingsDialog, ColorCycleDialog
3838
from plaid.reference import Reference
3939
from plaid.plot_widgets import HeatmapWidget, PatternWidget, AuxiliaryPlotWidget, CorrelationMapWidget, DiffractionMapWidget
40-
from plaid.misc import q_to_tth, tth_to_q, get_divisors
40+
from plaid.misc import q_to_tth, tth_to_q, d_to_q, d_to_tth, get_divisors
4141
from plaid.data_containers import AzintData, AuxData
4242
from plaid import __version__ as CURRENT_VERSION
4343
import plaid.resources
@@ -168,7 +168,7 @@ def save_recent_refs_settings(recent_refs):
168168
recent_refs = [r for r in recent_refs if r] # Remove empty entries
169169
# Limit to the last 10 references
170170
if len(recent_refs) > 10:
171-
recent_refs = recent_refs[-10:]
171+
recent_refs = recent_refs[:10]
172172
# Save the recent references
173173
settings.setValue("recent-references", recent_refs)
174174
settings.endGroup()
@@ -231,7 +231,7 @@ def __init__(self):
231231
self.E = None # Energy in keV
232232
self.is_Q = False # flag to indicate if the data is in Q space (True) or 2theta space (False)
233233

234-
self.azint_data = AzintData()
234+
self.azint_data = AzintData(self)
235235
self.aux_data = {}
236236

237237
self.locked_patterns = [] # list of (is_Q, E) tuples for locked patterns
@@ -398,10 +398,11 @@ def _init_connections(self):
398398
self.file_tree.sigI0DataRequested.connect(self.load_I0_data)
399399
self.file_tree.sigAuxiliaryDataRequested.connect(self.load_auxiliary_data)
400400
# Connect the CIF tree signals to the appropriate slots
401-
self.cif_tree.sigItemAdded.connect(self.add_reference)
402-
self.cif_tree.sigItemChecked.connect(self.toggle_reference)
403-
self.cif_tree.sigItemDoubleClicked.connect(self.rescale_reference)
404-
self.cif_tree.sigItemRemoved.connect(self.remove_reference)
401+
self.cif_tree.sigItemAdded.connect(self.add_reference) # --> str
402+
self.cif_tree.sigItemChecked.connect(self.toggle_reference) # --> int, bool
403+
self.cif_tree.sigItemDoubleClicked.connect(self.rescale_reference) # --> int, str
404+
self.cif_tree.sigItemRemoved.connect(self.remove_reference) # --> int
405+
self.cif_tree.sigItemReloadRequested.connect(self.reload_reference) # --> int
405406
# Connect the heatmap signals to the appropriate slots
406407
self.heatmap.sigHLineMoved.connect(self.hline_moved)
407408
self.heatmap.sigXRangeChanged.connect(self.pattern.set_xrange)
@@ -1008,24 +1009,49 @@ def add_reference(self, cif_file, Qmax=None):
10081009
self.plot_reference(color=color)
10091010
tooltip = f"{self.ref.get_spacegroup_info()}\n{self.ref.get_cell_parameter_info()}"
10101011
self.cif_tree.set_latest_item_tooltip(tooltip)
1011-
1012-
def plot_reference(self, Qmax=None, dmin=None, color=None):
1013-
"""Plot the reference pattern in the pattern plot."""
1012+
1013+
def get_reference_reflections(self, Qmax=None, dmin=None):
1014+
"""
1015+
Get the reference reflections from the current reference pattern,
1016+
converted to the current x-axis units.
1017+
"""
10141018
if Qmax is None:
10151019
Qmax = self.getQmax()
10161020
hkl, d, I = self.ref.get_reflections(Qmax=Qmax, dmin=dmin)
10171021
if len(hkl) == 0:
10181022
QMessageBox.warning(self, "No Reflections", "No reflections found in the reference pattern.")
10191023
return
1020-
10211024
if self.is_Q:
10221025
# Convert d to Q
1023-
x = 4*np.pi/d
1026+
x = d_to_q(d)
10241027
else:
10251028
# Convert d to 2theta
1026-
x = np.degrees(2 * np.arcsin((12.398 / self.E) / (2 * d)))
1029+
x = d_to_tth(d, self.E)
1030+
return hkl, x, I
1031+
1032+
def plot_reference(self, Qmax=None, dmin=None, color=None):
1033+
"""Plot the reference pattern in the pattern plot."""
1034+
hkl, x, I = self.get_reference_reflections(Qmax=Qmax, dmin=dmin)
10271035
self.pattern.add_reference(hkl, x, I,color=color)
10281036

1037+
def reload_reference(self,index, Qmax=None):
1038+
"""Reload the reference pattern at the given index from the cif tree."""
1039+
cif_file = self.cif_tree.files[index]
1040+
# check that the cif file still exists
1041+
if not os.path.exists(cif_file):
1042+
QMessageBox.critical(self, "Error", f"CIF file not found: {cif_file}")
1043+
return
1044+
item = self.cif_tree.file_tree.topLevelItem(index)
1045+
if Qmax is None:
1046+
Qmax = self.getQmax()
1047+
self.ref = Reference(cif_file,E=self.E, Qmax=Qmax)
1048+
hkl, x, I = self.get_reference_reflections(Qmax=Qmax)
1049+
self.pattern.update_reference(index, hkl, x, I)
1050+
# update the tooltip
1051+
tooltip = f"{self.ref.get_spacegroup_info()}\n{self.ref.get_cell_parameter_info()}"
1052+
item.setToolTip(0, tooltip)
1053+
self.statusBar().showMessage(f"Reloaded reference from {cif_file}")
1054+
10291055
def toggle_reference(self, index, is_checked):
10301056
"""
10311057
Toggle the visibility of the reference pattern.
@@ -1039,6 +1065,7 @@ def rescale_reference(self,index,name):
10391065
This method is called when a reference item is double-clicked in the CIF tree.
10401066
"""
10411067
self.pattern.rescale_reference(index)
1068+
self.statusBar().showMessage(f"Rescaled {name}")
10421069

10431070
def load_I0_data(self, aname=None, fname=None):
10441071
"""Load auxillary data as I0. Called when the user requests I0 data from the file tree."""

plaid/plot_widgets.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,6 @@ def remove_locked_pattern(self):
678678
pattern = self.locked_pattern_items.pop(-1)
679679
self.plot_widget.getPlotItem().removeItem(pattern)
680680
self.legend.removeItem(pattern)
681-
682681

683682
def add_reference(self, hkl, x, I,color=None):
684683
"""Add a reference pattern to the plot."""
@@ -692,15 +691,24 @@ def add_reference(self, hkl, x, I,color=None):
692691
self.plot_widget.getPlotItem().addItem(reference_item)
693692
self.reference_items.append(reference_item)
694693
self.reference_hkl[reference_item] = (x,hkl) # Store the hkl indices for the reference item
695-
# tth = np.degrees(np.arcsin(lambd/(2*d)))*2
694+
self._set_reference_data(reference_item, x, I)
695+
696+
def update_reference(self, index, hkl, x, I):
697+
"""Update a reference pattern in the plot."""
698+
reference_item = self.reference_items[index]
699+
self.reference_hkl[reference_item] = (x,hkl) # Update the hkl indices for the reference item
700+
self._set_reference_data(reference_item, x, I)
701+
702+
def _set_reference_data(self, item, x, I):
703+
"""Set the data for a reference pattern item."""
696704
x = np.repeat(x,2)
697705
I = np.repeat(I,2)
698706
I[::2] = 0 # Set the intensity to 0 for the first point of each pair
699707
if self.y is None:
700708
scale = 100
701709
else:
702710
scale = self.y.max() if self.y.max()>0 else 1.
703-
reference_item.setData(x, I*scale) # Initialize with test data
711+
item.setData(x, I*scale) # Update with new data
704712

705713
def remove_reference(self, index=-1):
706714
"""Remove a reference pattern from the plot."""

plaid/trees.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ class CIFTreeWidget(QWidget):
331331
sigItemChecked = QtCore.pyqtSignal(int, bool)
332332
sigItemDoubleClicked = QtCore.pyqtSignal(int, str)
333333
sigItemRemoved = QtCore.pyqtSignal(int)
334+
sigItemReloadRequested = QtCore.pyqtSignal(int)
334335
def __init__(self, parent=None):
335336
super().__init__(parent)
336337
self.parent = parent
@@ -433,13 +434,13 @@ def customMenuEvent(self, pos):
433434
# create a context menu for the item
434435
menu = QMenu(self)
435436
# # add an action to reload the CIF file
436-
# reload_action = menu.addAction("Reload CIF")
437-
# reload_action.setToolTip("Reload the selected CIF file")
438-
# reload_action.triggered.connect(lambda: print("reload action triggered")) # Placeholder for reload action
437+
reload_action = menu.addAction("Reload CIF")
438+
reload_action.setToolTip("Reload the selected CIF file")
439+
reload_action.triggered.connect(lambda: self._request_reload(item))
439440
# add an action to remove the item
440441
remove_action = menu.addAction("Remove")
441442
remove_action.setToolTip("Remove the selected CIF file from the tree")
442-
remove_action.triggered.connect(lambda: self.remove_item(item)) # Placeholder for remove action
443+
remove_action.triggered.connect(lambda: self.remove_item(item))
443444
menu.exec(self.file_tree.viewport().mapToGlobal(pos))
444445
menu.deleteLater() # Clean up the menu after use
445446

@@ -469,6 +470,12 @@ def _update_item_colors(self):
469470
color = QColor(self.color_cycle[i % len(self.color_cycle)])
470471
item.setForeground(0, color)
471472

473+
def _request_reload(self, item):
474+
"""Request reload of the specified item. Emits the sigItemReloadRequested signal with the index of the item."""
475+
index = self.file_tree.indexOfTopLevelItem(item)
476+
if index == -1:
477+
return
478+
self.sigItemReloadRequested.emit(index)
472479

473480
if __name__ == "__main__":
474481
pass

0 commit comments

Comments
 (0)