Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Background refine #1574

Merged
merged 3 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 64 additions & 8 deletions hexrd/ui/calibration/wppf_options_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ def generate_params(self):
'method': self.method,
'materials': self.materials,
'peak_shape': self.peak_shape_index,
'bkgmethod': self.background_method_dict
}
return generate_params(**kwargs)

Expand All @@ -260,6 +261,10 @@ def reset_params(self):
self.update_table()

def update_params(self):
if not hasattr(self, 'params'):
# Params have not been created yet. Nothing to update.
return

params = self.generate_params()

# Remake the dict to use the ordering of `params`
Expand Down Expand Up @@ -358,14 +363,42 @@ def background_method_dict(self):
method = self.background_method
widgets = self.dynamic_background_widgets
if not widgets:
value = None
elif len(widgets) == 1:
value = widgets[0].value()
# Make sure these are updated
self.update_background_parameters()
widgets = self.dynamic_background_widgets

if not widgets:
# This background method doesn't have any widgets
value = [None]
else:
value = [x.value() for x in widgets]

if len(value) == 1:
value = value[0]

return {method: value}

@background_method_dict.setter
def background_method_dict(self, v):
method = list(v)[0]

self.background_method = method

# Make sure these get updated (it may have already been called, but
# calling it twice is not a problem)
self.update_background_parameters()
if v[method]:
widgets = self.dynamic_background_widgets
if len(widgets) == 1:
widgets[0].set_value(v[method])
else:
for w, value in zip(widgets, v[method]):
w.set_value(value)

if method == 'chebyshev':
# We probably need to update the parameters as well
self.update_params()

@property
def limit_tth(self):
return self.ui.limit_tth.isChecked()
Expand Down Expand Up @@ -435,14 +468,27 @@ def load_settings(self):
if not settings:
return

# Apply these settings first, in order. The other settings
# can be disorded.
apply_first_keys = [
# background_method should no longer be in the settings, as it
# was replaced by background_method_dict, but just in case it is...
'background_method',
'background_method_dict',
]

with block_signals(*self.all_widgets):
for k in apply_first_keys:
if k in settings:
setattr(self, k, settings[k])

for k, v in settings.items():
if k == 'params' and isinstance(v, dict):
# The older WPPF dialog used a dict. Skip this
# as it is no longer compatible.
continue

if not hasattr(self, k):
if not hasattr(self, k) or k in apply_first_keys:
# Skip it...
continue

Expand All @@ -458,7 +504,7 @@ def save_settings(self):
'method',
'refinement_steps',
'peak_shape',
'background_method',
'background_method_dict',
'use_experiment_file',
'experiment_file',
'display_wppf_plot',
Expand Down Expand Up @@ -553,22 +599,32 @@ def update_background_parameters(self):
descriptions = background_methods[self.background_method]
if not descriptions:
# Nothing more to do
self.update_params()
return

for d in descriptions:
layout = QHBoxLayout()
main_layout.addLayout(layout)

w = DynamicWidget(d)
w = DynamicWidget(d, self.ui)
if w.label is not None:
# Add the label
layout.addWidget(w.label)

if w.widget is not None:
layout.addWidget(w.widget)

if self.background_method == 'chebyshev':
# We need to update parameters when the chebyshev options
# are modified.
w.value_changed.connect(self.update_params)

self.dynamic_background_widgets.append(w)

# We may need to update the parameters as well, since some background
# methods have parameters.
self.update_params()

def clear_table(self):
self.value_spinboxes.clear()
self.minimum_spinboxes.clear()
Expand Down Expand Up @@ -839,15 +895,15 @@ def validate_import_params(self, import_params, filename):
raise Exception(msg)


def generate_params(method, materials, peak_shape):
def generate_params(method, materials, peak_shape, bkgmethod):
func_dict = {
'LeBail': _generate_default_parameters_LeBail,
'Rietveld': _generate_default_parameters_Rietveld,
}
if method not in func_dict:
raise Exception(f'Unknown method: {method}')

return func_dict[method](materials, peak_shape)
return func_dict[method](materials, peak_shape, bkgmethod)


def param_to_dict(param):
Expand Down
47 changes: 41 additions & 6 deletions hexrd/ui/dynamic_widget.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
from PySide2.QtCore import QObject, Signal
from PySide2.QtWidgets import QCheckBox, QDoubleSpinBox, QLabel, QSpinBox

from hexrd.ui.scientificspinbox import ScientificDoubleSpinBox


class DynamicWidget:
class DynamicWidget(QObject):
"""Provides an interface for a dynamically created widget from a dict.

Some of the keys currently accepted from the dict are 'label', 'type',
'min', 'max', 'value', and 'tooltip'.
"""
def __init__(self, description):

# Emitted when the value is changed
value_changed = Signal()

def __init__(self, description, parent=None):
super().__init__(parent)

self.description = description
self.label = label_from_description(description)
self.widget = widget_from_description(description)
self.widget = widget_from_description(description, parent)

if self.widget:
value_changed_signal(self.widget).connect(self._on_value_changed)

def value(self):
return widget_value(self.widget)

def set_value(self, v):
set_widget_value(self.widget, v)

def _on_value_changed(self, *args):
self.value_changed.emit()


def label_from_description(x):
"""Dynamically create a label from a description dict.
Expand All @@ -34,7 +50,7 @@ def label_from_description(x):
return QLabel(x['label'])


def widget_from_description(x):
def widget_from_description(x, parent=None):
"""Dynamically create a widget from a description dict.

Some of the keys currently accepted are 'type', 'min', 'max', and
Expand All @@ -46,7 +62,8 @@ def widget_from_description(x):
if x['type'] in (int, float):
# It's a spin box
widget_type = QSpinBox if x['type'] == int else ScientificDoubleSpinBox
widget = widget_type()
widget = widget_type(parent=parent)
widget.setKeyboardTracking(False)

if 'max' in x:
widget.setMaximum(x['max'])
Expand All @@ -56,7 +73,7 @@ def widget_from_description(x):
widget.setValue(x['value'])

elif x['type'] == bool:
widget = QCheckBox()
widget = QCheckBox(parent=parent)
if 'value' in x:
widget.setChecked(x)

Expand All @@ -75,3 +92,21 @@ def widget_value(w):
return w.isChecked()
else:
raise NotImplementedError(f'Type of widget not implemented: {type(w)}')


def set_widget_value(w, v):
if isinstance(w, (QSpinBox, QDoubleSpinBox)):
w.setValue(v)
elif isinstance(w, QCheckBox):
w.setChecked(v)
else:
raise NotImplementedError(f'Type of widget not implemented: {type(w)}')


def value_changed_signal(w):
if isinstance(w, (QSpinBox, QDoubleSpinBox)):
return w.valueChanged
elif isinstance(w, QCheckBox):
return w.toggled
else:
raise NotImplementedError(f'Type of widget not implemented: {type(w)}')