"""
FitPanel class contains fields allowing to display results when
fitting a model and one data
"""
from __future__ import print_function
import sys
import wx
import wx.lib.newevent
import numpy as np
import copy
import math
import time
import traceback
import logging
from sasmodels.weights import MODELS as POLYDISPERSITY_MODELS
from sas.sascalc.fit.qsmearing import smear_selection
from sas.sascalc.dataloader.data_info import Data1D, Data2D
from sas.sasgui.guiframe.events import StatusEvent, NewPlotEvent, \
PlotQrangeEvent
from sas.sasgui.guiframe.dataFitting import check_data_validity
from sas.sasgui.guiframe.utils import format_number, check_float
from sas.sasgui.guiframe.documentation_window import DocumentationWindow
from sas.sasgui.perspectives.fitting.basepage import BasicPage as BasicPage
from sas.sasgui.perspectives.fitting.basepage import PageInfoEvent as \
PageInfoEvent
from .basepage import ModelTextCtrl
logger = logging.getLogger(__name__)
(Chi2UpdateEvent, EVT_CHI2_UPDATE) = wx.lib.newevent.NewEvent()
_BOX_WIDTH = 76
_DATA_BOX_WIDTH = 300
SMEAR_SIZE_H = 0.00
CUSTOM_MODEL = 'Plugin Models'
[docs]class FitPage(BasicPage):
"""
FitPanel class contains fields allowing to display results when
fitting a model and one data
:note: For Fit to be performed the user should check at least one parameter
on fit Panel window.
"""
def __init__(self, parent, color=None):
"""
Initialization of the Panel
"""
BasicPage.__init__(self, parent, color=color)
# draw sizer
self._fill_data_sizer()
self.is_2D = None
self.fit_started = False
self.weightbt_string = None
self.m_name = None
# get smear info from data
self._get_smear_info()
self._fill_model_sizer(self.sizer1)
self._get_defult_custom_smear()
self._fill_range_sizer()
self._set_smear(self.data)
self.Bind(EVT_CHI2_UPDATE, self.on_complete_chisqr)
# bind key event
self.Bind(wx.EVT_RIGHT_DOWN, self.on_right_down)
self._set_bookmark_flag(False)
self._set_save_flag(False)
self._set_preview_flag(False)
self._set_copy_flag(False)
self._set_paste_flag(False)
self.btFit.SetFocus()
self.enable_fit_button()
self.fill_data_combobox(data_list=self.data_list)
# create a default data for an empty panel
self.create_default_data()
self._manager.frame.Bind(wx.EVT_SET_FOCUS, self.on_set_focus)
[docs] def enable_fit_button(self):
"""
Enable fit button if data is valid and model is valid
"""
flag = check_data_validity(self.data) & (self.model is not None)
self.btFit.Enable(flag)
[docs] def on_set_focus(self, event):
"""
Override the basepage focus method to ensure the save flag is set
properly when focusing on the fit page.
"""
flag = check_data_validity(self.data) & (self.model is not None)
self._set_save_flag(flag)
self.parent.on_set_focus(event)
self.on_tap_focus()
def _fill_data_sizer(self):
"""
fill sizer 0 with data info
"""
self.data_box_description = wx.StaticBox(self, wx.ID_ANY,
'I(q) Data Source')
if check_data_validity(self.data):
dname_color = wx.BLUE
else:
dname_color = wx.RED
self.data_box_description.SetForegroundColour(dname_color)
boxsizer1 = wx.StaticBoxSizer(self.data_box_description, wx.VERTICAL)
# ----------------------------------------------------------
sizer_data = wx.BoxSizer(wx.HORIZONTAL)
self.dataSource = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
wx.EVT_COMBOBOX(self.dataSource, wx.ID_ANY, self.on_select_data)
self.dataSource.SetMinSize((_DATA_BOX_WIDTH, -1))
sizer_data.Add(wx.StaticText(self, wx.ID_ANY, 'Name : '))
sizer_data.Add(self.dataSource)
sizer_data.Add((0, 5))
boxsizer1.Add(sizer_data, 0, wx.ALL, 10)
self.sizer0.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
self.sizer0.Layout()
[docs] def enable_datasource(self):
"""
Enable or disable data source control depending on existing data
"""
if not self.data_list:
self.dataSource.Disable()
else:
self.dataSource.Enable()
[docs] def fill_data_combobox(self, data_list):
"""
Get a list of data and fill the corresponding combobox
"""
self.dataSource.Clear()
self.data_list = data_list
self.enable_datasource()
if len(data_list) > 0:
# find the maximum range covering all data
qmin, qmax, npts = self.compute_data_set_range(data_list)
self.qmin_data_set = qmin
self.qmax_data_set = qmax
self.npts_data_set = npts
self.qmin.SetValue(str(self.qmin_data_set))
self.qmax.SetValue(str(self.qmax_data_set))
self.qmin.SetBackgroundColour("white")
self.qmax.SetBackgroundColour("white")
self.qmin_x = self.qmin_data_set
self.qmax_x = self.qmax_data_set
self.state.qmin = self.qmin_x
self.state.qmax = self.qmax_x
is_data = False
for data in self.data_list:
if data is not None:
self.dataSource.Append(str(data.name), clientData=data)
if not is_data:
is_data = check_data_validity(data)
if is_data:
self.dataSource.SetSelection(0)
self.on_select_data(event=None)
if len(data_list) == 1:
self.dataSource.Disable()
[docs] def on_select_data(self, event=None):
"""
On_select_data
"""
if self.dataSource.GetCount() > 0:
pos = self.dataSource.GetSelection() if event is not None else 0
data = self.dataSource.GetClientData(pos)
self.set_data(data)
def _on_fit_complete(self):
"""
When fit is complete ,reset the fit button label.
"""
self.fit_started = False
self.set_fitbutton()
def _is_2D(self):
"""
Check if data_name is Data2D
:return: True or False
"""
if isinstance(self.data, Data2D) or self.enable2D:
return True
return False
def _fill_range_sizer(self):
"""
Fill the Fitting sizer on the fit panel which contains: the smearing
information (dq), the weighting information (dI or other), the plotting
range, access to the 2D mask editor, the compute, fit, and help
buttons, xi^2, number of points etc.
"""
is_2d_data = False
# Check if data is 2D
if isinstance(self.data, Data2D) or self.enable2D:
is_2d_data = True
title = "Fitting"
# smear messages & titles
smear_message_none = "No smearing is selected..."
smear_message_dqdata = "The dQ data is being used for smearing..."
smear_message_2d = \
"Higher accuracy is very time-expensive. Use it with care..."
smear_message_new_ssmear = \
"Please enter only the value of interest to customize smearing..."
smear_message_new_psmear = \
"Please enter a fixed percentage to be applied to all Q values..."
smear_message_2d_x_title = "<dQ/Q>_r[%]:"
smear_message_2d_y_title = "<dQ/Q>_phi[%]:"
smear_message_pinhole_percent_min_title = "[dQ/Q]min(%):"
smear_message_pinhole_percent_max_title = "[dQ/Q]max(%):"
smear_message_pinhole_percent_title = "dQ/Q(%):"
smear_message_slit_height_title = "Slit height[1/A]:"
smear_message_slit_width_title = "Slit width[1/A]:"
self._get_smear_info()
# Sizers
box_description_range = wx.StaticBox(self, wx.ID_ANY, str(title))
box_description_range.SetForegroundColour(wx.BLUE)
boxsizer_range = wx.StaticBoxSizer(box_description_range, wx.VERTICAL)
self.sizer_set_smearer = wx.BoxSizer(wx.VERTICAL)
sizer_smearer = wx.BoxSizer(wx.HORIZONTAL)
self.sizer_new_smear = wx.BoxSizer(wx.HORIZONTAL)
self.sizer_set_masking = wx.BoxSizer(wx.HORIZONTAL)
sizer_chi2 = wx.BoxSizer(wx.VERTICAL)
smear_set_box = wx.StaticBox(self, wx.ID_ANY,
'Set Instrumental Smearing')
sizer_smearer_box = wx.StaticBoxSizer(smear_set_box, wx.HORIZONTAL)
sizer_smearer_box.SetMinSize((_DATA_BOX_WIDTH, 60))
weighting_set_box = wx.StaticBox(self, wx.ID_ANY,
'Set Weighting by Selecting dI Source')
weighting_box = wx.StaticBoxSizer(weighting_set_box, wx.HORIZONTAL)
sizer_weighting = wx.BoxSizer(wx.HORIZONTAL)
weighting_box.SetMinSize((_DATA_BOX_WIDTH, 40))
# Filling the sizer containing weighting info.
self.dI_noweight = wx.RadioButton(self, wx.ID_ANY,
'No Weighting', style=wx.RB_GROUP)
self.dI_didata = wx.RadioButton(self, wx.ID_ANY, 'Use dI Data')
self.dI_sqrdata = wx.RadioButton(self, wx.ID_ANY, 'Use |sqrt(I Data)|')
self.dI_idata = wx.RadioButton(self, wx.ID_ANY, 'Use |I Data|')
self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
id=self.dI_noweight.GetId())
self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
id=self.dI_didata.GetId())
self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
id=self.dI_sqrdata.GetId())
self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
id=self.dI_idata.GetId())
self.dI_noweight.SetValue(True)
# add 4 types of weighting to the sizer
sizer_weighting.Add(self.dI_noweight, 0, wx.LEFT, 10)
sizer_weighting.Add((14, 10))
sizer_weighting.Add(self.dI_didata)
sizer_weighting.Add((14, 10))
sizer_weighting.Add(self.dI_sqrdata)
sizer_weighting.Add((14, 10))
sizer_weighting.Add(self.dI_idata)
sizer_weighting.Add((10, 10))
self.dI_noweight.Enable(True)
self.dI_didata.Enable(False)
self.dI_sqrdata.Enable(False)
self.dI_idata.Enable(False)
weighting_box.Add(sizer_weighting)
# combobox for smear2d accuracy selection
self.smear_accuracy = wx.ComboBox(self, wx.ID_ANY,
size=(50, -1), style=wx.CB_READONLY)
self._set_accuracy_list()
self.smear_accuracy.SetValue(self.smear2d_accuracy)
self.smear_accuracy.SetSelection(0)
self.smear_accuracy.SetToolTipString(
"'Higher' uses more Gaussian points for smearing computation.")
wx.EVT_COMBOBOX(self.smear_accuracy, wx.ID_ANY,
self._on_select_accuracy)
# Fit button
self.btFit = wx.Button(self, self._ids.next(), 'Fit')
self.default_bt_colour = self.btFit.GetDefaultAttributes()
self.btFit.Bind(wx.EVT_BUTTON, self._onFit, id=self.btFit.GetId())
self.btFit.SetToolTipString("Start fitting.")
# General Help button
self.btFitHelp = wx.Button(self, wx.ID_ANY, 'Help')
self.btFitHelp.SetToolTipString("General fitting help.")
self.btFitHelp.Bind(wx.EVT_BUTTON, self._onFitHelp)
# Resolution Smearing Help button (for now use same technique as
# used for dI help to get tiniest possible button that works
# both on MAC and PC. Should completely rewrite the fitting sizer
# in future. This is minimum to get out release 3.1
# comment June 14, 2015 --- PDB
if sys.platform.count("win32") > 0:
size_q = (20, 15) # on PC
else:
size_q = (30, 20) # on MAC
self.btSmearHelp = wx.Button(self, wx.ID_ANY, '?',
style=wx.BU_EXACTFIT, size=size_q)
self.btSmearHelp.SetToolTipString("Resolution smearing help.")
self.btSmearHelp.Bind(wx.EVT_BUTTON, self._onSmearHelp)
# textcntrl for custom resolution
self.smear_pinhole_percent = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH - 25, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=
self.onPinholeSmear)
self.smear_slit_height = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH - 25, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self.onSlitSmear)
self.smear_slit_width = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH - 25, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self.onSlitSmear)
# smear
self.smear_data_left = BGTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH - 25, 20), style=0)
self.smear_data_left.SetValue(str(self.dq_l))
self.smear_data_right = BGTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH - 25, 20), style=0)
self.smear_data_right.SetValue(str(self.dq_r))
# set default values for smear
self.smear_pinhole_percent.SetValue(str(self.dx_percent))
self.smear_slit_height.SetValue(str(self.dxl))
self.smear_slit_width.SetValue(str(self.dxw))
# Filling the sizer containing instruments smearing info.
self.disable_smearer = wx.RadioButton(self, wx.ID_ANY,
'None', style=wx.RB_GROUP)
self.enable_smearer = wx.RadioButton(self, wx.ID_ANY, 'Use dQ Data')
# self.enable_smearer.SetToolTipString(
# "Click to use the loaded dQ data for smearing.")
self.pinhole_smearer = wx.RadioButton(self, wx.ID_ANY,
'Custom Pinhole Smear')
# self.pinhole_smearer.SetToolTipString
# ("Click to input custom resolution for pinhole smearing.")
self.slit_smearer = wx.RadioButton(self, wx.ID_ANY, 'Custom Slit Smear')
# self.slit_smearer.SetToolTipString
# ("Click to input custom resolution for slit smearing.")
self.Bind(wx.EVT_RADIOBUTTON, self.onSmear,
id=self.disable_smearer.GetId())
self.Bind(wx.EVT_RADIOBUTTON, self.onSmear,
id=self.enable_smearer.GetId())
self.Bind(wx.EVT_RADIOBUTTON, self.onPinholeSmear,
id=self.pinhole_smearer.GetId())
self.Bind(wx.EVT_RADIOBUTTON, self.onSlitSmear,
id=self.slit_smearer.GetId())
self.disable_smearer.SetValue(True)
sizer_smearer.Add(self.disable_smearer, 0, wx.LEFT, 10)
sizer_smearer.Add(self.enable_smearer)
sizer_smearer.Add(self.pinhole_smearer)
sizer_smearer.Add(self.slit_smearer)
sizer_smearer.Add(self.btSmearHelp)
sizer_smearer.Add((10, 10))
# StaticText for chi2, N(for fitting), Npts + Log/linear spacing
self.tcChi = BGTextCtrl(self, wx.ID_ANY, "-", size=(75, 20), style=0)
self.tcChi.SetToolTipString("Chi2/DOF (DOF=Npts-Npar fitted)")
self.Npts_fit = BGTextCtrl(self, wx.ID_ANY, "-", size=(75, 20), style=0)
self.Npts_fit.SetToolTipString(
" Npts : number of points selected for fitting")
self.Npts_total = ModelTextCtrl(self, wx.ID_ANY, size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self._onQrangeEnter)
self.Npts_total.SetValue(format_number(self.npts_x))
self.Npts_total.SetToolTipString(
" Total Npts : total number of data points")
# Update and Draw button
self.draw_button = wx.Button(self, self._ids.next(), 'Compute')
self.draw_button.Bind(wx.EVT_BUTTON,
self._onDraw, id=self.draw_button.GetId())
self.draw_button.SetToolTipString("Compute and Draw.")
self.points_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.pointsbox = wx.CheckBox(self, wx.ID_ANY, 'Log?', (10, 10))
self.pointsbox.SetValue(False)
self.pointsbox.SetToolTipString("Check mark to use log spaced points")
wx.EVT_CHECKBOX(self, self.pointsbox.GetId(), self.select_log)
self.points_sizer.Add(wx.StaticText(self, wx.ID_ANY, 'Npts '))
self.points_sizer.Add(self.pointsbox)
box_description_1 = wx.StaticText(self, wx.ID_ANY, 'Reduced Chi2')
box_description_2 = wx.StaticText(self, wx.ID_ANY, 'Npts(Fit)')
# StaticText for smear
self.smear_description_none = wx.StaticText(self, wx.ID_ANY,
smear_message_none, style=wx.ALIGN_LEFT)
self.smear_description_dqdata = wx.StaticText(self, wx.ID_ANY,
smear_message_dqdata, style=wx.ALIGN_LEFT)
self.smear_description_type = wx.StaticText(self, wx.ID_ANY,
"Type:", style=wx.ALIGN_LEFT)
self.smear_description_accuracy_type = wx.StaticText(self, wx.ID_ANY,
"Accuracy:", style=wx.ALIGN_LEFT)
self.smear_description_smear_type = BGTextCtrl(self, wx.ID_ANY,
size=(57, 20), style=0)
self.smear_description_smear_type.SetValue(str(self.dq_l))
self.SetBackgroundColour(self.GetParent().GetBackgroundColour())
self.smear_description_2d = wx.StaticText(self, wx.ID_ANY,
smear_message_2d, style=wx.ALIGN_LEFT)
self.smear_message_new_s = wx.StaticText(self, wx.ID_ANY,
smear_message_new_ssmear, style=wx.ALIGN_LEFT)
self.smear_message_new_p = wx.StaticText(self, wx.ID_ANY,
smear_message_new_psmear, style=wx.ALIGN_LEFT)
self.smear_description_2d_x = wx.StaticText(self, wx.ID_ANY,
smear_message_2d_x_title, style=wx.ALIGN_LEFT)
self.smear_description_2d_x.SetToolTipString(
" dQ_r q_r in polar coordinates.")
self.smear_description_2d_y = wx.StaticText(self, wx.ID_ANY,
smear_message_2d_y_title, style=wx.ALIGN_LEFT)
self.smear_description_2d_y.SetToolTipString(
" dQ_phi q_phi in polar coordinates.")
self.smear_description_pin_percent_min = wx.StaticText(self, wx.ID_ANY,
smear_message_pinhole_percent_min_title,
style=wx.ALIGN_LEFT)
self.smear_description_pin_percent_max = wx.StaticText(self, wx.ID_ANY,
smear_message_pinhole_percent_max_title,
style=wx.ALIGN_LEFT)
self.smear_description_pin_percent = wx.StaticText(self, wx.ID_ANY,
smear_message_pinhole_percent_title,
style=wx.ALIGN_LEFT)
self.smear_description_slit_height = wx.StaticText(self, wx.ID_ANY,
smear_message_slit_height_title, style=wx.ALIGN_LEFT)
self.smear_description_slit_width = wx.StaticText(self, wx.ID_ANY,
smear_message_slit_width_title, style=wx.ALIGN_LEFT)
# arrange sizers
self.sizer_set_smearer.Add(sizer_smearer)
self.sizer_set_smearer.Add((10, 10))
self.sizer_set_smearer.Add(self.smear_description_none,
0, wx.CENTER, 10)
self.sizer_set_smearer.Add(self.smear_description_dqdata,
0, wx.CENTER, 10)
self.sizer_set_smearer.Add(self.smear_description_2d,
0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_description_type,
0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_description_accuracy_type,
0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_accuracy)
self.sizer_new_smear.Add(self.smear_description_smear_type,
0, wx.CENTER, 10)
self.sizer_new_smear.Add((15, -1))
self.sizer_new_smear.Add(self.smear_description_2d_x, 0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_description_pin_percent_min,
0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_description_pin_percent,
0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_description_slit_height,
0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_pinhole_percent, 0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_slit_height, 0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_data_left, 0, wx.CENTER, 10)
self.sizer_new_smear.Add((20, -1))
self.sizer_new_smear.Add(self.smear_description_2d_y,
0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_description_pin_percent_max,
0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_description_slit_width,
0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_slit_width, 0, wx.CENTER, 10)
self.sizer_new_smear.Add(self.smear_data_right, 0, wx.CENTER, 10)
self.sizer_set_smearer.Add(self.smear_message_new_s, 0, wx.CENTER, 10)
self.sizer_set_smearer.Add(self.smear_message_new_p, 0, wx.CENTER, 10)
self.sizer_set_smearer.Add((5, 2))
self.sizer_set_smearer.Add(self.sizer_new_smear, 0, wx.CENTER, 10)
# add all to chi2 sizer
sizer_smearer_box.Add(self.sizer_set_smearer)
sizer_chi2.Add(sizer_smearer_box)
sizer_chi2.Add((-1, 5))
sizer_chi2.Add(weighting_box)
sizer_chi2.Add((-1, 5))
# hide all smear messages and textctrl
self._hide_all_smear_info()
# get smear_selection
self.current_smearer = smear_selection(self.data, self.model)
# Show only the relevant smear messages, etc
if self.current_smearer is None:
if not is_2d_data:
self.smear_description_none.Show(True)
self.enable_smearer.Disable()
else:
self.smear_description_none.Show(True)
self.slit_smearer.Disable()
if self.data is None:
self.slit_smearer.Disable()
self.pinhole_smearer.Disable()
self.enable_smearer.Disable()
else:
self._show_smear_sizer()
boxsizer_range.Add(self.sizer_set_masking)
# 2D data? default
is_2d_data = False
# check if it is 2D data
if isinstance(self.data, Data2D) or self.enable2D:
is_2d_data = True
self.sizer5.Clear(True)
self.qmin = ModelTextCtrl(self, wx.ID_ANY, size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER,
set_focus_callback=self.qrang_set_focus,
text_enter_callback=self._onQrangeEnter,
name='qmin')
self.qmin.SetValue(str(self.qmin_x))
q_tip = "Click outside of the axes\n to remove the lines."
qmin_tip = "Minimun value of Q.\n"
qmin_tip += q_tip
self.qmin.SetToolTipString(qmin_tip)
self.qmax = ModelTextCtrl(self, wx.ID_ANY, size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER,
set_focus_callback=self.qrang_set_focus,
text_enter_callback=self._onQrangeEnter,
name='qmax')
self.qmax.SetValue(str(self.qmax_x))
qmax_tip = "Maximum value of Q.\n"
qmax_tip += q_tip
self.qmax.SetToolTipString(qmax_tip)
self.qmin.Bind(wx.EVT_MOUSE_EVENTS, self.qrange_click)
self.qmax.Bind(wx.EVT_MOUSE_EVENTS, self.qrange_click)
self.qmin.Bind(wx.EVT_KEY_DOWN, self.on_key)
self.qmax.Bind(wx.EVT_KEY_DOWN, self.on_key)
self.qmin.Bind(wx.EVT_TEXT, self.on_qrange_text)
self.qmax.Bind(wx.EVT_TEXT, self.on_qrange_text)
wx_id = self._ids.next()
self.reset_qrange = wx.Button(self, wx_id, 'Reset')
self.reset_qrange.Bind(wx.EVT_BUTTON, self.on_reset_clicked, id=wx_id)
self.reset_qrange.SetToolTipString("Reset Q range to the default")
sizer = wx.GridSizer(5, 5, 2, 6)
self.btEditMask = wx.Button(self, self._ids.next(), 'Editor')
self.btEditMask.Bind(wx.EVT_BUTTON, self._onMask,
id=self.btEditMask.GetId())
self.btEditMask.SetToolTipString("Edit Mask.")
self.EditMask_title = wx.StaticText(self, wx.ID_ANY, ' Masking(2D)')
sizer.Add(wx.StaticText(self, wx.ID_ANY, ' Q range'))
sizer.Add(wx.StaticText(self, wx.ID_ANY, ' Min[1/A]'))
sizer.Add(wx.StaticText(self, wx.ID_ANY, ' Max[1/A]'))
sizer.Add(self.EditMask_title)
sizer.Add((-1, 5))
sizer.Add(self.reset_qrange)
sizer.Add(self.qmin)
sizer.Add(self.qmax)
sizer.Add(self.btEditMask)
sizer.Add((-1, 5))
sizer.AddMany(5*[(-1, 5)])
sizer.Add(box_description_1, 0, 0)
sizer.Add(box_description_2, 0, 0)
sizer.Add(self.points_sizer, 0, 0)
sizer.Add(self.draw_button, 0, 0)
sizer.Add((-1, 5))
sizer.Add(self.tcChi, 0, 0)
sizer.Add(self.Npts_fit, 0, 0)
sizer.Add(self.Npts_total, 0, 0)
sizer.Add(self.btFit, 0, 0)
sizer.Add(self.btFitHelp, 0, 0)
boxsizer_range.Add(sizer_chi2)
boxsizer_range.Add(sizer)
if is_2d_data:
self.btEditMask.Enable()
self.EditMask_title.Enable()
else:
self.btEditMask.Disable()
self.EditMask_title.Disable()
# save state
self.save_current_state()
self.sizer5.Add(boxsizer_range, 0, wx.EXPAND | wx.ALL, 10)
self.sizer5.Layout()
def _set_sizer_dispersion(self):
"""
draw sizer with gaussian dispersity parameters
"""
self.fittable_param = []
self.fixed_param = []
self.orientation_params_disp = []
self.sizer4_4.Clear(True)
if self.model is None:
# no model is selected
return
if not self.enable_disp.GetValue():
# the user didn't select dispersity display
return
self._reset_dispersity()
# fill a sizer with the combobox to select dispersion type
model_disp = wx.StaticText(self, wx.ID_ANY, 'Function')
CHECK_STATE = False
ix = 0
iy = 0
disp = wx.StaticText(self, wx.ID_ANY, ' ')
self.sizer4_4.Add(disp, (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
ix += 1
values = wx.StaticText(self, wx.ID_ANY, 'PD[ratio]')
polytext = "Polydispersity (= STD/mean); "
polytext += "the standard deviation over the mean value."
values.SetToolTipString(polytext)
self.sizer4_4.Add(values, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix += 2
if self.is_mac:
err_text = 'Error'
else:
err_text = ''
self.text_disp_1 = wx.StaticText(self, wx.ID_ANY, err_text)
self.sizer4_4.Add(self.text_disp_1, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix += 1
self.text_disp_min = wx.StaticText(self, wx.ID_ANY, 'Min')
self.sizer4_4.Add(self.text_disp_min, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix += 1
self.text_disp_max = wx.StaticText(self, wx.ID_ANY, 'Max')
self.sizer4_4.Add(self.text_disp_max, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix += 1
npts = wx.StaticText(self, wx.ID_ANY, 'Npts')
npts.SetToolTipString("Number of sampling points for the numerical\n\
integration over the distribution function.")
self.sizer4_4.Add(npts, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix += 1
nsigmas = wx.StaticText(self, wx.ID_ANY, 'Nsigs')
nsigmas.SetToolTipString("Number of sigmas between which the range\n\
of the distribution function will be used for weighting. \n\
The value '3' covers 99.5% for Gaussian distribution \n\
function. Note: Not recommended to change this value.")
self.sizer4_4.Add(nsigmas, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix += 1
self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
self.text_disp_max.Show(True)
self.text_disp_min.Show(True)
for item in self.model.dispersion.keys():
if not self.magnetic_on:
if item in self.model.magnetic_params:
continue
if item not in self.model.orientation_params:
if item not in self.disp_cb_dict:
self.disp_cb_dict[item] = None
name0 = "Distribution of " + item
name1 = item + ".width"
name2 = item + ".npts"
name3 = item + ".nsigmas"
if name1 not in self.model.details:
self.model.details[name1] = ["", None, None]
iy += 1
for p in self.model.dispersion[item].keys():
if p == "width":
ix = 0
cb = wx.CheckBox(self, wx.ID_ANY, name0, (10, 10))
cb.SetValue(CHECK_STATE)
cb.SetToolTipString("Check mark to fit")
wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
self.sizer4_4.Add(cb, (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
ix = 1
value = self.model.getParam(name1)
ctl1 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 1.3, 20),
style=wx.TE_PROCESS_ENTER)
ctl1.SetLabel('PD[ratio]')
poly_text = "Polydispersity (STD/mean) of %s\n" % item
poly_text += "STD: the standard deviation"
poly_text += " from the mean value."
ctl1.SetToolTipString(poly_text)
ctl1.SetValue(str(format_number(value, True)))
self.sizer4_4.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
# text to show error sign
ix = 2
text2 = wx.StaticText(self, wx.ID_ANY, '+/-')
self.sizer4_4.Add(text2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
if not self.is_mac:
text2.Hide()
ix = 3
ctl2 = BGTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 1.3, 20))
self.sizer4_4.Add(ctl2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
if not self.is_mac:
ctl2.Hide()
ix = 4
ctl3 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 2, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self._onparamRangeEnter)
self.sizer4_4.Add(ctl3, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix = 5
ctl4 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 2, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self._onparamRangeEnter)
self.sizer4_4.Add(ctl4, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ctl3.Show(True)
ctl4.Show(True)
elif p == "npts":
ix = 6
value = self.model.getParam(name2)
Tctl = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 2.2, 20),
style=wx.TE_PROCESS_ENTER)
Tctl.SetValue(str(format_number(value)))
self.sizer4_4.Add(Tctl, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
self.fixed_param.append([None, name2, Tctl, None, None,
None, None, None])
elif p == "nsigmas":
ix = 7
value = self.model.getParam(name3)
Tct2 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 2.2, 20),
style=wx.TE_PROCESS_ENTER)
Tct2.SetValue(str(format_number(value)))
self.sizer4_4.Add(Tct2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
self.fixed_param.append([None, name3, Tct2,
None, None, None,
None, None])
ix = 8
disp_box = wx.ComboBox(self, wx.ID_ANY, size=(65, -1),
style=wx.CB_READONLY, name='%s' % name1)
for key, value in POLYDISPERSITY_MODELS.iteritems():
name_disp = str(key)
disp_box.Append(name_disp, value)
disp_box.SetStringSelection("gaussian")
wx.EVT_COMBOBOX(disp_box, wx.ID_ANY, self._on_disp_func)
self.sizer4_4.Add(disp_box, (iy, ix), (1, 1), wx.EXPAND)
self.fittable_param.append([cb, name1, ctl1, text2,
ctl2, ctl3, ctl4, disp_box])
ix = 0
iy += 1
self.sizer4_4.Add((20, 20), (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
first_orient = True
for item in self.model.dispersion.keys():
if not self.magnetic_on:
if item in self.model.magnetic_params:
continue
if item in self.model.orientation_params:
if item not in self.disp_cb_dict:
self.disp_cb_dict[item] = None
name0 = "Distribution of " + item
name1 = item + ".width"
name2 = item + ".npts"
name3 = item + ".nsigmas"
if name1 not in self.model.details:
self.model.details[name1] = ["", None, None]
iy += 1
for p in self.model.dispersion[item].keys():
if p == "width":
ix = 0
cb = wx.CheckBox(self, wx.ID_ANY, name0, (10, 10))
cb.SetValue(CHECK_STATE)
cb.SetToolTipString("Check mark to fit")
wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
self.sizer4_4.Add(cb, (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
if isinstance(self.data, Data2D) or self.enable2D:
cb.Show(True)
elif cb.IsShown():
cb.Hide()
ix = 1
value = self.model.getParam(name1)
ctl1 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 1.3, 20),
style=wx.TE_PROCESS_ENTER)
poly_tip = "Absolute Sigma for %s." % item
ctl1.SetToolTipString(poly_tip)
ctl1.SetValue(str(format_number(value, True)))
if isinstance(self.data, Data2D) or self.enable2D:
if first_orient:
values.SetLabel('PD[ratio], Sig[deg]')
poly_text = "PD(polydispersity for lengths):\n"
poly_text += "It should be a value between"
poly_text += "0 and 1\n"
poly_text += "Sigma for angles: \n"
poly_text += "It is the STD (ratio*mean)"
poly_text += " of the distribution.\n "
values.SetToolTipString(poly_text)
first_orient = False
ctl1.Show(True)
elif ctl1.IsShown():
ctl1.Hide()
self.sizer4_4.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
# text to show error sign
ix = 2
text2 = wx.StaticText(self, wx.ID_ANY, '+/-')
self.sizer4_4.Add(text2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
text2.Hide()
ix = 3
ctl2 = wx.TextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 1.3, 20),
style=0)
self.sizer4_4.Add(ctl2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ctl2.Hide()
if isinstance(self.data, Data2D) or self.enable2D:
if self.is_mac:
text2.Show(True)
ctl2.Show(True)
ix = 4
ctl3 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 2, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self._onparamRangeEnter)
self.sizer4_4.Add(ctl3, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ctl3.Hide()
ix = 5
ctl4 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 2, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self._onparamRangeEnter)
self.sizer4_4.Add(ctl4, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ctl4.Hide()
if isinstance(self.data, Data2D) or self.enable2D:
ctl3.Show(True)
ctl4.Show(True)
elif p == "npts":
ix = 6
value = self.model.getParam(name2)
Tctl = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 2.2, 20),
style=wx.TE_PROCESS_ENTER)
Tctl.SetValue(str(format_number(value)))
if isinstance(self.data, Data2D) or self.enable2D:
Tctl.Show(True)
else:
Tctl.Hide()
self.sizer4_4.Add(Tctl, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
self.fixed_param.append([None, name2, Tctl, None, None,
None, None, None])
self.orientation_params_disp.append([None, name2,
Tctl, None, None,
None, None, None])
elif p == "nsigmas":
ix = 7
value = self.model.getParam(name3)
Tct2 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 2.2, 20),
style=wx.TE_PROCESS_ENTER)
Tct2.SetValue(str(format_number(value)))
if isinstance(self.data, Data2D) or self.enable2D:
Tct2.Show(True)
else:
Tct2.Hide()
self.sizer4_4.Add(Tct2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
self.fixed_param.append([None, name3, Tct2,
None, None, None, None, None])
self.orientation_params_disp.append([None, name3,
Tct2, None, None, None, None, None])
ix = 8
disp_box = wx.ComboBox(self, wx.ID_ANY, size=(65, -1),
style=wx.CB_READONLY, name='%s' % name1)
for key, value in POLYDISPERSITY_MODELS.iteritems():
name_disp = str(key)
disp_box.Append(name_disp, value)
disp_box.SetStringSelection("gaussian")
wx.EVT_COMBOBOX(disp_box, wx.ID_ANY, self._on_disp_func)
self.sizer4_4.Add(disp_box, (iy, ix), (1, 1), wx.EXPAND)
self.fittable_param.append([cb, name1, ctl1, text2,
ctl2, ctl3, ctl4, disp_box])
self.orientation_params_disp.append([cb, name1, ctl1,
text2, ctl2, ctl3, ctl4, disp_box])
if isinstance(self.data, Data2D) or self.enable2D:
disp_box.Show(True)
else:
disp_box.Hide()
self.state.disp_cb_dict = copy.deepcopy(self.disp_cb_dict)
self.state.model = self.model.clone()
# save state into
self._copy_parameters_state(self.parameters, self.state.parameters)
self._copy_parameters_state(self.orientation_params_disp,
self.state.orientation_params_disp)
self._copy_parameters_state(self.fittable_param,
self.state.fittable_param)
self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
wx.PostEvent(self.parent,
StatusEvent(status=" Selected Distribution: Gaussian"))
# Fill the list of fittable parameters
self.get_all_checked_params()
self.Layout()
def _onDraw(self, event):
"""
Update and Draw the model
"""
if self.model is None:
msg = "Please select a Model first..."
wx.MessageBox(msg, 'Info')
return
"""
if not self.data.is_data:
self.npts_x = self.Npts_total.GetValue()
self.Npts_fit.SetValue(self.npts_x)
self.create_default_data()
"""
flag, is_modified = self._update_paramv_on_fit()
wx.CallAfter(self._onparamEnter_helper, is_modified)
if not flag:
msg = "The parameters are invalid"
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
return
def _onFit(self, event):
"""
Allow to fit
"""
if event is not None:
event.Skip()
if self.fit_started:
self._StopFit()
self.fit_started = False
wx.CallAfter(self.set_fitbutton)
return
if self.data is None:
msg = "Please get Data first..."
wx.MessageBox(msg, 'Info')
wx.PostEvent(self._manager.parent,
StatusEvent(status="Fit: %s" % msg))
return
if self.model is None:
msg = "Please select a Model first..."
wx.MessageBox(msg, 'Info')
wx.PostEvent(self._manager.parent,
StatusEvent(status="Fit: %s" % msg, type="stop"))
return
if len(self.param_toFit) <= 0:
msg = "Select at least one parameter to fit"
wx.MessageBox(msg, 'Info')
wx.PostEvent(self._manager.parent,
StatusEvent(status=msg, type="stop"))
return
flag = self._update_paramv_on_fit()
if self.batch_on and not self._is_2D():
if not self._validate_Npts_1D():
return
if not flag:
msg = "Fitting range or parameters are invalid"
wx.PostEvent(self._manager.parent,
StatusEvent(status=msg, type="stop"))
return
self.select_param()
# Remove or do not allow fitting on the Q=0 point, especially
# when y(q=0)=None at x[0].
self.qmin_x = float(self.qmin.GetValue())
self.qmax_x = float(self.qmax.GetValue())
self._manager._reset_schedule_problem(value=0, uid=self.uid)
self._manager.schedule_for_fit(uid=self.uid, value=1)
self._manager.set_fit_range(uid=self.uid, qmin=self.qmin_x,
qmax=self.qmax_x)
# single fit
# self._manager.onFit(uid=self.uid)
self.fit_started = self._manager.onFit(uid=self.uid)
wx.CallAfter(self.set_fitbutton)
def _onFitHelp(self, event):
"""
Bring up the Full Fitting Documentation whenever the HELP button is
clicked.
Calls DocumentationWindow with the path of the location within the
documentation tree (after /doc/ ....". Note that when using old
versions of Wx (before 2.9) and thus not the release version of
installers, the help comes up at the top level of the file as
web browser does not pass anything past the # to the browser when it is
running "file:///...."
:param evt: Triggers on clicking the help button
"""
_TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html"
_doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
"General Fitting Help")
def _onSmearHelp(self, event):
"""
Bring up the instrumental resolution smearing Documentation whenever
the ? button in the smearing box is clicked.
Calls DocumentationWindow with the path of the location within the
documentation tree (after /doc/ ....". Note that when using old
versions of Wx (before 2.9) and thus not the release version of
installers, the help comes up at the top level of the file as
web browser does not pass anything past the # to the browser when it is
running "file:///...."
:param evt: Triggers on clicking the help button
"""
_TreeLocation = "user/sasgui/perspectives/fitting/resolution.html"
_doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
"Instrumental Resolution Smearing \
Help")
[docs] def set_fitbutton(self):
"""
Set fit button label depending on the fit_started[bool]
"""
# Skip this feature if we are not on Windows
# NOTE: the is_mac data member actually means "is no Windows".
if self.is_mac:
return
if self.fit_started:
label = "Stop"
color = "red"
else:
label = "Fit"
color = "black"
# self.btFit.Enable(False)
self.btFit.SetLabel(label)
self.btFit.SetForegroundColour(color)
self.btFit.Enable(True)
[docs] def get_weight_flag(self):
"""
Get flag corresponding to a given weighting dI data.
"""
button_list = [self.dI_noweight,
self.dI_didata,
self.dI_sqrdata,
self.dI_idata]
flag = 1
for item in button_list:
if item.GetValue():
if button_list.index(item) == 0:
flag = 0 # dy = np.ones_like(dy_data)
elif button_list.index(item) == 1:
flag = 1 # dy = dy_data
elif button_list.index(item) == 2:
flag = 2 # dy = np.sqrt(np.abs(data))
elif button_list.index(item) == 3:
flag = 3 # dy = np.abs(data)
break
return flag
def _StopFit(self, event=None):
"""
Stop fit
"""
if event is not None:
event.Skip()
self._manager.stop_fit(self.uid)
self._manager._reset_schedule_problem(value=0)
self._on_fit_complete()
[docs] def rename_model(self):
"""
find a short name for model
"""
if self.model is not None:
self.model.name = "M" + str(self.index_model)
def _on_select_model(self, event=None, keep_pars=False):
"""
call back for model selection
"""
self.Show(False)
if event is not None:
control = event.GetEventObject()
if ((control == self.formfactorbox
and self.structurebox.GetLabel() != 'None')
or control == self.structurebox
or control == self.multifactorbox):
keep_pars = True
if keep_pars:
saved_pars = self.get_copy_params()
is_poly_enabled = self.enable_disp.GetValue()
else:
saved_pars = None
is_poly_enabled = None
try:
self._on_select_model_helper()
except Exception as e:
evt = StatusEvent(status=e.message, info="error")
wx.PostEvent(self._manager.parent, evt)
# Set S(Q) to None
self.structurebox.SetSelection(0)
self._on_select_model()
return
self.set_model_param_sizer(self.model)
if self.model is None:
self._set_bookmark_flag(False)
self._keep.Enable(False)
self._set_save_flag(False)
self.enable_disp.SetValue(False)
self.disable_disp.SetValue(True)
# TODO: should not have an untrapped exception when displaying disperser
# TODO: do we need to create the disperser panel on every model change?
# Note: if we fix this, then remove ID_DISPERSER_HELP from basepage
try:
self.set_dispers_sizer()
except Exception:
pass
self.state.enable_disp = self.enable_disp.GetValue()
self.state.disable_disp = self.disable_disp.GetValue()
self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
self.state.slit_smearer = self.slit_smearer.GetValue()
self.state.structurecombobox = self.structurebox.GetValue()
self.state.formfactorcombobox = self.formfactorbox.GetValue()
self.state.categorycombobox = self.categorybox.GetValue()
self.enable_fit_button()
if self.model is not None:
self.m_name = self.model.name
self.state.m_name = self.m_name
self.rename_model()
self._set_copy_flag(True)
self._set_paste_flag(True)
if self.data is not None:
is_data = check_data_validity(self.data)
if is_data:
self._set_bookmark_flag(not self.batch_on)
self._keep.Enable(not self.batch_on)
self._set_save_flag(True)
#Setting smearing for cases with and without data.
self._set_smear(self.data)
# more disables for 2D
self._set_smear_buttons()
try:
# update smearer sizer
#This call for smearing set up caused double evaluation of
#I(q) and double compilation as results
#self.onSmear(None)
temp_smear = None
if not self.disable_smearer.GetValue():
# Set the smearer environments
temp_smear = self.current_smearer
except:
raise
# error occured on chisqr computation
# pass
# event to post model to fit to fitting plugins
(ModelEventbox, EVT_MODEL_BOX) = wx.lib.newevent.NewEvent()
# set smearing value whether or not data contain the smearing info
evt = ModelEventbox(model=self.model,
smearer=temp_smear,
enable_smearer=not self.disable_smearer.GetValue(),
qmin=float(self.qmin_x),
uid=self.uid,
caption=self.window_caption,
qmax=float(self.qmax_x))
self._manager._on_model_panel(evt=evt)
self.mbox_description.SetLabel("Model [ %s ]" %
str(self.model.name))
self.mbox_description.SetForegroundColour(wx.BLUE)
self.state.model = self.model.clone()
self.state.model.name = self.model.name
# when select a model only from guictr/button
if is_poly_enabled is not None:
self.enable_disp.SetValue(is_poly_enabled)
self.disable_disp.SetValue(not is_poly_enabled)
self._set_dipers_Param(event=None)
self.state.enable_disp = self.enable_disp.GetValue()
self.state.disable_disp = self.disable_disp.GetValue()
# Keep the previous param values
if saved_pars:
self.get_paste_params(saved_pars)
# Make sure the model parameters correspond to the fit parameters
self._update_paramv_on_fit()
if event is not None:
# update list of plugins if new plugin is available
# mod_cat = self.categorybox.GetStringSelection()
# if mod_cat == CUSTOM_MODEL:
# temp = self.parent.update_model_list()
# for v in self.parent.model_dictionary.values():
# if v.id == self.model.id:
# self.model = v()
# break
# if temp:
# self.model_list_box = temp
# current_val = self.formfactorbox.GetLabel()
# pos = self.formfactorbox.GetSelection()
# self._show_combox_helper()
# self.formfactorbox.SetStringSelection(current_val)
# self.formfactorbox.SetValue(current_val)
# post state to fit panel
new_event = PageInfoEvent(page=self)
wx.PostEvent(self.parent, new_event)
wx.CallAfter(self._onDraw, None)
else:
self._draw_model()
if self.batch_on:
self.slit_smearer.Enable(False)
self.pinhole_smearer.Enable(False)
self.btEditMask.Disable()
self.EditMask_title.Disable()
self.Show(True)
self.SetupScrolling()
def _onparamEnter(self, event):
"""
when enter value on panel redraw model according to changed
"""
if self.model is None:
msg = "Please select a Model first..."
wx.MessageBox(msg, 'Info')
return
# default flag
flag = False
self.fitrange = True
# get event object
tcrtl = event.GetEventObject()
# Clear msg if previously shown.
msg = ""
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
if check_float(tcrtl):
flag = self._onparamEnter_helper()
self.show_npts2fit()
if self.fitrange:
temp_smearer = None
if not self.disable_smearer.GetValue():
temp_smearer = self.current_smearer
# set smearing value whether or not data contain the
# smearing info
if self.slit_smearer.GetValue():
flag1 = self.update_slit_smear()
flag = flag or flag1
elif self.pinhole_smearer.GetValue():
flag1 = self.update_pinhole_smear()
flag = flag or flag1
elif not isinstance(self.data, Data2D) and not self.enable2D:
enable_smearer = not self.disable_smearer.GetValue()
self._manager.set_smearer(smearer=temp_smearer,
fid=self.data.id,
uid=self.uid,
qmin=float(self.qmin_x),
qmax=float(self.qmax_x),
enable_smearer=enable_smearer)
if flag:
# self.compute_chisqr(smearer= temp_smearer)
# new state posted
if self.state_change:
# self._undo.Enable(True)
event = PageInfoEvent(page=self)
wx.PostEvent(self.parent, event)
self.state_change = False
else:
# invalid fit range: do nothing here:
# msg already displayed in validate
return
else:
self.save_current_state()
msg = "Cannot Plot: Must enter a number!!! "
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
self.save_current_state()
return
def _onparamRangeEnter(self, event):
"""
Check validity of value enter in the parameters range field
"""
tcrtl = event.GetEventObject()
# Clear msg if previously shown.
msg = ""
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
# Flag to register when a parameter has changed.
is_modified = False
if tcrtl.GetValue().lstrip().rstrip() != "":
try:
tcrtl.SetBackgroundColour(wx.WHITE)
self._check_value_enter(self.fittable_param)
self._check_value_enter(self.parameters)
except:
tcrtl.SetBackgroundColour("pink")
msg = "Model Error:wrong value entered : %s" % sys.exc_value
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
return
else:
tcrtl.SetBackgroundColour(wx.WHITE)
# self._undo.Enable(True)
self.save_current_state()
event = PageInfoEvent(page=self)
wx.PostEvent(self.parent, event)
self.state_change = False
[docs] def qrang_set_focus(self, event=None):
"""
ON Qrange focus
"""
if event is not None:
event.Skip()
# tcrtl = event.GetEventObject()
self._validate_qrange(self.qmin, self.qmax)
[docs] def qrange_click(self, event):
"""
On Qrange textctrl click, make the qrange lines in the plot
"""
if event is not None:
event.Skip()
if isinstance(self.data, Data2D):
return
is_click = event.LeftDown()
if is_click:
d_id = self.data.id
d_group_id = self.data.group_id
act_ctrl = event.GetEventObject()
wx.PostEvent(self._manager.parent,
PlotQrangeEvent(ctrl=[self.qmin, self.qmax], id=d_id,
group_id=d_group_id, leftdown=is_click,
active=act_ctrl))
[docs] def on_qrange_text(self, event):
"""
#On q range value updated. DO not combine with qrange_click().
"""
if event is not None:
event.Skip()
if isinstance(self.data, Data2D):
return
act_ctrl = event.GetEventObject()
d_id = self.data.id
d_group_id = self.data.group_id
wx.PostEvent(self._manager.parent,
PlotQrangeEvent(ctrl=[self.qmin, self.qmax], id=d_id,
group_id=d_group_id, leftdown=False,
active=act_ctrl))
self._validate_qrange(self.qmin, self.qmax)
[docs] def on_key(self, event):
"""
On Key down
"""
event.Skip()
if isinstance(self.data, Data2D):
return
ctrl = event.GetEventObject()
try:
x_data = float(ctrl.GetValue())
except:
return
key = event.GetKeyCode()
length = len(self.data.x)
indx = (np.abs(self.data.x - x_data)).argmin()
# return array.flat[idx]
if key == wx.WXK_PAGEUP or key == wx.WXK_NUMPAD_PAGEUP:
indx += 1
if indx >= length:
indx = length - 1
elif key == wx.WXK_PAGEDOWN or key == wx.WXK_NUMPAD_PAGEDOWN:
indx -= 1
if indx < 0:
indx = 0
else:
return
ctrl.SetValue(str(self.data.x[indx]))
self._validate_qrange(self.qmin, self.qmax)
def _onQrangeEnter(self, event):
"""
Check validity of value enter in the Q range field
"""
tcrtl = event.GetEventObject()
# Clear msg if previously shown.
msg = ""
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
# For theory mode
if not self.data.is_data:
self.npts_x = self.Npts_total.GetValue()
self.Npts_fit.SetValue(self.npts_x)
self.create_default_data()
# Flag to register when a parameter has changed.
if tcrtl.GetValue().lstrip().rstrip() != "":
try:
tcrtl.SetBackgroundColour(wx.WHITE)
# If qmin and qmax have been modified, update qmin and qmax
if self._validate_qrange(self.qmin, self.qmax):
tempmin = float(self.qmin.GetValue())
if tempmin != self.qmin_x:
self.qmin_x = tempmin
tempmax = float(self.qmax.GetValue())
if tempmax != self.qmax_x:
self.qmax_x = tempmax
else:
tcrtl.SetBackgroundColour("pink")
msg = "Model Error:wrong value entered : %s" % sys.exc_value
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
return
except:
tcrtl.SetBackgroundColour("pink")
msg = "Model Error:wrong value entered : %s" % sys.exc_value
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
return
# Check if # of points for theory model are valid(>0).
# check for 2d
if isinstance(self.data, Data2D) or self.enable2D:
# set mask
radius = np.sqrt(self.data.qx_data * self.data.qx_data +
self.data.qy_data * self.data.qy_data)
index_data = ((self.qmin_x <= radius) & (radius <= self.qmax_x))
index_data = (index_data) & (self.data.mask)
index_data = (index_data) & (np.isfinite(self.data.data))
if len(index_data[index_data]) < 10:
msg = "Cannot Plot :No or too little npts in"
msg += " that data range!!! "
wx.PostEvent(self._manager.parent,
StatusEvent(status=msg))
return
else:
# self.data.mask = index_data
# self.Npts_fit.SetValue(str(len(self.data.mask)))
self.show_npts2fit()
else:
index_data = ((self.qmin_x <= self.data.x) &
(self.data.x <= self.qmax_x))
self.Npts_fit.SetValue(str(len(self.data.x[index_data])))
self.npts_x = self.Npts_total.GetValue()
self.create_default_data()
self._save_plotting_range()
else:
tcrtl.SetBackgroundColour("pink")
msg = "Model Error:wrong value entered!!!"
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
self._draw_model()
self.save_current_state()
event = PageInfoEvent(page=self)
wx.PostEvent(self.parent, event)
self.state_change = False
return
def _clear_Err_on_Fit(self):
"""
hide the error text control shown
after fitting
"""
if self.is_mac:
return
if hasattr(self, "text2_3"):
self.text2_3.Hide()
if len(self.parameters) > 0:
for item in self.parameters:
if item[0].IsShown():
# Skip the angle parameters if 1D data
if not isinstance(self.data, Data2D) and not self.enable2D:
if item in self.orientation_params:
continue
if item in self.param_toFit:
continue
# hide statictext +/-
if len(item) < 4:
continue
if item[3] is not None and item[3].IsShown():
item[3].Hide()
# hide textcrtl for error after fit
if item[4] is not None and item[4].IsShown():
item[4].Hide()
if len(self.fittable_param) > 0:
for item in self.fittable_param:
if item[0].IsShown():
# Skip the angle parameters if 1D data
if not isinstance(self.data, Data2D) and not self.enable2D:
if item in self.orientation_params:
continue
if item in self.param_toFit:
continue
if len(item) < 4:
continue
# hide statictext +/-
if item[3] is not None and item[3].IsShown():
item[3].Hide()
# hide textcrtl for error after fit
if item[4] is not None and item[4].IsShown():
item[4].Hide()
return
def _get_defult_custom_smear(self):
"""
Get the defult values for custum smearing.
"""
# get the default values
if self.dxl is None:
self.dxl = 0.0
if self.dxw is None:
self.dxw = ""
if self.dx_percent is None:
self.dx_percent = SMEAR_SIZE_H
def _get_smear_info(self):
"""
Get the smear info from data.
:return: self.smear_type, self.dq_l and self.dq_r,
respectively the type of the smear, The average <dq/q> radial(p)
and <dq/q> theta (s)s for 2D pinhole resolution in % (slit is not
currently supported in 2D), (dq/q)_min and (dq/q)_max for 1D pinhole
smeared data, again in %, and dxl and/or dxw for slit smeared data
given in 1/A and assumed constant.
"""
# set up defaults
self.smear_type = None
self.dq_l = None
self.dq_r = None
data = self.data
#sanity check - this should only be called if data exits
if self.data is None:
return
#First check if data is 2D
#If so check that data set has smearing info and that none are zero.
#Otherwise no smearing can be applied using smear from data (a Gaussian
#width of zero will cause a divide by zero error)
if isinstance(self.data, Data2D):
if data.dqx_data is not None and data.dqy_data is not None \
and data.dqx_data.all()and data.dqy_data.all():
self.smear_type = "Pinhole2d"
#report as average dQ/Q % as for 1D pinhole
self.dq_l = format_number(np.average(data.dqx_data
/ abs(data.qx_data)) * 100)
self.dq_r = format_number(np.average(data.dqy_data
/ abs(data.qy_data)) * 100)
#if not then log that data did not contain resolutin info / abs(data.qy_data)) * 100)
else:
self.msg = "2D Data did not contain recognizable " \
"resolution info."
logger.info(self.msg)
#If not check that data is 1D
#If so check for pinhole vs slit by veryfing whehter dx or dxl or dxw
#have data (currently sasview only supports either dx or dxl/dxw but
#not both simultaneously) and, as for 2D, are non zero .
#Otherwise no smearing can be applied using smear from data (a Gaussian
#width of zero will cause a divide by zero error)
elif isinstance(self.data, Data1D):
#is it valid 1D pinhole resolution data?
if data.dx is not None and np.all(data.dx):
self.smear_type = "Pinhole"
#report in % for display makes more sense than absolute value
#for pinhole smearing .. but keep old names of dq_l
self.dq_l = format_number(data.dx[0] / data.x[0] * 100,1)
self.dq_r = format_number(data.dx[-1] / data.x[-1] * 100,1)
#If not, is it valid 1D slit resolution data?
elif (data.dxl is not None or data.dxw is not None) \
and (np.all(data.dxl, 0) or np.all(data.dxw, 0)):
self.smear_type = "Slit"
#for slit units of 1/A make most sense
if data.dxl is not None and np.all(data.dxl, 0):
self.dq_l = format_number(data.dxl[0],1)
if data.dxw is not None and np.all(data.dxw, 0):
self.dq_r = format_number(data.dxw[0],1)
#otherwise log that the data did not conatain resolution info
else:
self.msg = "1D Data did not contain recognizable " \
"resolution info."
logger.info(self.msg)
#If drops to here it is neither data1D or data2D so log that
else:
self.msg = "Data was not recognized as either 1D or 2D data."
logger.info(self.msg)
# return self.smear_type,self.dq_l,self.dq_r
def _show_smear_sizer(self):
"""
Show only the sizers depending on smear selection
"""
# smear disabled = No Smearing used
if self.disable_smearer.GetValue():
self.smear_description_none.Show(True)
# 2Dsmearing - for use with 2D data only
elif self._is_2D():
self.smear_description_accuracy_type.Show(True)
self.smear_accuracy.Show(True)
self.smear_description_2d.Show(True)
#2D custom pinhole smearing
if self.pinhole_smearer.GetValue():
self.smear_description_pin_percent.Show(True)
self.smear_pinhole_percent.Show(True)
#get 2D smearing from data
elif self.enable_smearer.GetValue():
self.smear_description_2d_x.Show(True)
self.smear_description_2d_y.Show(True)
self.smear_data_left.Show(True)
self.smear_data_right.Show(True)
#Currently 2D custom slit smearing is not currently supported
else:
logger.error("2D custom smearing cannot use slit smearing")
# 1D smearing from data
elif self.enable_smearer.GetValue():
self.smear_description_dqdata.Show(True)
if self.smear_type is not None:
self.smear_description_smear_type.Show(True)
#1D data has slit smearing
if self.smear_type == 'Slit':
self.smear_description_slit_height.Show(True)
self.smear_description_slit_width.Show(True)
#1D data has pinhole smearing
elif self.smear_type == 'Pinhole':
self.smear_description_pin_percent_min.Show(True)
self.smear_description_pin_percent_max.Show(True)
self.smear_description_smear_type.Show(True)
self.smear_description_type.Show(True)
self.smear_data_left.Show(True)
self.smear_data_right.Show(True)
# 1D custom pinhole smearing
elif self.pinhole_smearer.GetValue():
self.smear_message_new_p.Show(True)
self.smear_description_pin_percent.Show(True)
self.smear_pinhole_percent.Show(True)
# 1D custom slit smear
elif self.slit_smearer.GetValue():
self.smear_message_new_s.Show(True)
self.smear_description_slit_height.Show(True)
self.smear_slit_height.Show(True)
self.smear_description_slit_width.Show(True)
self.smear_slit_width.Show(True)
else:
logger.error("smearing type is not defined")
def _hide_all_smear_info(self):
"""
Hide all smearing messages in the set_smearer sizer
"""
self.smear_description_none.Hide()
self.smear_description_dqdata.Hide()
self.smear_description_type.Hide()
self.smear_description_smear_type.Hide()
self.smear_description_accuracy_type.Hide()
self.smear_description_2d_x.Hide()
self.smear_description_2d_y.Hide()
self.smear_description_2d.Hide()
self.smear_accuracy.Hide()
self.smear_data_left.Hide()
self.smear_data_right.Hide()
self.smear_description_pin_percent_min.Hide()
self.smear_description_pin_percent_max.Hide()
self.smear_description_pin_percent.Hide()
self.smear_pinhole_percent.Hide()
self.smear_description_slit_height.Hide()
self.smear_slit_height.Hide()
self.smear_description_slit_width.Hide()
self.smear_slit_width.Hide()
self.smear_message_new_p.Hide()
self.smear_message_new_s.Hide()
def _set_accuracy_list(self):
"""
Set the list of an accuracy in 2D custum smear:
Xhigh, High, Med, or Low
"""
# list of accuracy choices
list = ['Low', 'Med', 'High', 'Xhigh']
for idx in range(len(list)):
self.smear_accuracy.Append(list[idx], idx)
def _set_fun_box_list(self, fun_box):
"""
Set the list of func for multifunctional models
"""
# Check if it is multi_functional model
if self.model.__class__ not in self.model_list_box["Multi-Functions"] \
and not self.temp_multi_functional:
return None
for index, choice in enumerate(self.model.fun_list):
fun_box.Append(choice, index)
def _on_select_accuracy(self, event):
"""
Select an accuracy in 2D custom smear: Xhigh, High, Med, or Low
"""
# event.Skip()
# Check if the accuracy is same as before
# self.smear2d_accuracy = event.GetEventObject().GetValue()
self.smear2d_accuracy = self.smear_accuracy.GetValue()
if self.pinhole_smearer.GetValue():
self.onPinholeSmear(event=None)
else:
self.onSmear(event=None)
if self.current_smearer is not None:
self.current_smearer.set_accuracy(accuracy=\
self.smear2d_accuracy)
event.Skip()
def _on_fun_box(self, event):
"""
Select an func: Erf,Rparabola,LParabola
"""
fun_val = None
fun_box = event.GetEventObject()
name = fun_box.Name
value = fun_box.GetValue()
if value in self.model.fun_list:
fun_val = self.model.fun_list.index(value)
self.model.setParam(name, fun_val)
# save state
self._copy_parameters_state(self.str_parameters,
self.state.str_parameters)
# update params
self._update_paramv_on_fit()
# draw
self._draw_model()
self.Refresh()
# get ready for new event
event.Skip()
def _onMask(self, event):
"""
Build a panel to allow to edit Mask
"""
from sas.sasgui.guiframe.local_perspectives.plotting.masking import \
MaskPanel as MaskDialog
self.panel = MaskDialog(base=self, data=self.data, id=wx.NewId())
self.panel.ShowModal()
def _draw_masked_model(self, event):
"""
Draw model image w/mask
"""
is_valid_qrange = self._update_paramv_on_fit()
if is_valid_qrange and self.model is not None:
self.panel.MakeModal(False)
event.Skip()
# try re draw the model plot if it exists
self._draw_model()
self.show_npts2fit()
elif self.model is None:
self.panel.MakeModal(False)
event.Skip()
self.show_npts2fit()
msg = "No model is found on updating MASK in the model plot... "
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
else:
event.Skip()
msg = ' Please consider your Q range, too.'
self.panel.ShowMessage(msg)
def _set_smear(self, data):
"""
Set_smear
"""
if data is None:
return
self.current_smearer = smear_selection(data, self.model)
flag = self.disable_smearer.GetValue()
if self.current_smearer is None:
self.enable_smearer.Disable()
else:
self.enable_smearer.Enable()
if not flag:
self.onSmear(None)
[docs] def get_view_mode(self):
"""
return True if the panel allow 2D or False if 1D
"""
return self.enable2D
[docs] def compute_data_set_range(self, data_list):
"""
find the range that include all data in the set
return the minimum and the maximum values
"""
if data_list is not None and data_list != []:
for data in data_list:
qmin, qmax, npts = self.compute_data_range(data)
self.qmin_data_set = min(self.qmin_data_set, qmin)
self.qmax_data_set = max(self.qmax_data_set, qmax)
self.npts_data_set += npts
return self.qmin_data_set, self.qmax_data_set, self.npts_data_set
[docs] def compute_data_range(self, data):
"""
compute the minimum and the maximum range of the data
return the npts contains in data
:param data:
"""
qmin, qmax, npts = None, None, None
if data is not None:
if not hasattr(data, "data"):
try:
qmin = min(data.x)
# Maximum value of data
qmax = max(data.x)
npts = len(data.x)
except:
msg = "Unable to find min/max/length of \n data named %s" %\
data.filename
wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
info="error"))
raise ValueError, msg
else:
qmin = 0
try:
x = max(math.fabs(data.xmin), math.fabs(data.xmax))
y = max(math.fabs(data.ymin), math.fabs(data.ymax))
except:
msg = "Unable to find min/max of \n data named %s" % \
data.filename
wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
info="error"))
raise ValueError, msg
# Maximum value of data
qmax = math.sqrt(x * x + y * y)
npts = len(data.data)
return qmin, qmax, npts
[docs] def set_data(self, data):
"""
reset the current data
"""
id = None
flag = False
is_data = False
npts = 0
try:
old_id = self.data.id
old_group_id = self.data.group_id
except:
old_id = id
old_group_id = id
if self.data is not None:
is_data = check_data_validity(self.data)
if not is_data and data is not None:
flag = True
if data is not None:
if is_data:
self.graph_id = self.data.group_id
flag = (data.id != self.data.id)
self.data = data
if check_data_validity(data):
self.graph_id = data.group_id
self.data.group_id = self.graph_id
if self.data is None:
data_name = ""
self._set_bookmark_flag(False)
self._keep.Enable(False)
self._set_save_flag(False)
else:
if self.model is not None:
self._set_bookmark_flag(not self.batch_on)
self._keep.Enable(not self.batch_on)
if self.data.is_data:
self._set_save_flag(True)
self._set_preview_flag(True)
# more disables for 2D
di_flag = False
dq_flag = False
if isinstance(self.data, Data2D) or self.enable2D:
self.slit_smearer.Disable()
self.pinhole_smearer.Enable(True)
self.default_mask = copy.deepcopy(self.data.mask)
if self.data.err_data is not None \
and np.any(self.data.err_data):
di_flag = True
if self.data.dqx_data is not None \
and np.any(self.data.dqx_data):
dq_flag = True
else:
self.slit_smearer.Enable(True)
self.pinhole_smearer.Enable(True)
if self.data.dy is not None and np.any(self.data.dy):
di_flag = True
if self.data.dx is not None and np.any(self.data.dx):
dq_flag = True
elif self.data.dxl is not None and np.any(self.data.dxl):
dq_flag = True
if dq_flag:
self.enable_smearer.Enable(True)
self.enable_smearer.SetValue(True)
self.disable_smearer.SetValue(False)
else:
self.enable_smearer.Disable()
self.disable_smearer.Enable(True)
self.disable_smearer.SetValue(True)
if di_flag:
self.dI_didata.Enable(True)
self.dI_didata.SetValue(True)
self.weightbt_string = self.dI_didata.GetLabelText()
else:
self.dI_didata.Enable(False)
self.dI_noweight.SetValue(True)
self.weightbt_string = self.dI_noweight.GetLabelText()
# Enable weighting radio buttons
self.dI_noweight.Enable(True)
self.dI_sqrdata.Enable(True)
self.dI_idata.Enable(True)
self.formfactorbox.Enable()
self.structurebox.Enable()
data_name = self.data.name
_, _, npts = self.compute_data_range(self.data)
# set maximum range for x in linear scale
if not hasattr(self.data, "data"): # Display only for 1D data fit
self.btEditMask.Disable()
self.EditMask_title.Disable()
else:
self.btEditMask.Enable()
self.EditMask_title.Enable()
self.Npts_total.SetValue(str(npts))
# default:number of data points selected to fit
self.Npts_fit.SetValue(str(npts))
self.Npts_total.SetEditable(False)
self.Npts_total.SetBackgroundColour(
self.GetParent().GetBackgroundColour())
self.Npts_total.Bind(wx.EVT_MOUSE_EVENTS, self._npts_click)
self.pointsbox.Disable()
self.dataSource.SetValue(data_name)
self.state.data = data
self.enable_fit_button()
# send graph_id to page_finder
self._manager.set_graph_id(uid=self.uid, graph_id=self.graph_id)
# focus the page
if check_data_validity(data):
self.data_box_description.SetForegroundColour(wx.BLUE)
if self.batch_on:
self.slit_smearer.Enable(False)
self.pinhole_smearer.Enable(False)
self.btEditMask.Disable()
self.EditMask_title.Disable()
self.on_smear_helper()
self.on_set_focus(None)
self.Layout()
self.Refresh()
# update model plot with new data information
if flag:
if isinstance(self.data, Data2D):
self.enable2D = True
self.model_view.SetLabel("2D Mode")
else:
self.enable2D = False
self.model_view.SetLabel("1D Mode")
self.model_view.Disable()
# replace data plot on combo box selection
# by removing the previous selected data
try:
wx.PostEvent(self._manager.parent,
NewPlotEvent(action="delete",
group_id=old_group_id, id=old_id))
except:
pass
# plot the current selected data
wx.PostEvent(self._manager.parent,
NewPlotEvent(action="check", plot=self.data,
title=str(self.data.title)))
self._draw_model()
def _npts_click(self, event):
"""
Prevent further handling of the mouse event on Npts_total
by not calling Skip().
"""
pass
[docs] def reset_page(self, state, first=False):
"""
reset the state
"""
try:
self.reset_page_helper(state)
self.select_param()
# Save state_fit
self.save_current_state_fit()
self.onSmear(None)
self._onDraw(None)
except:
self._show_combox_helper()
msg = "Error: This model state has missing or outdated "
msg += "information.\n"
msg += traceback.format_exc()
wx.PostEvent(self._manager.parent,
StatusEvent(status=msg, info="error"))
self._lay_out()
self.Refresh()
[docs] def get_range(self):
"""
return the fitting range
"""
return float(self.qmin_x), float(self.qmax_x)
[docs] def get_npts2fit(self):
"""
return numbers of data points within qrange
:Note: This is to normalize chisq by Npts of fit
"""
if self.data is None:
return
npts2fit = 0
qmin, qmax = self.get_range()
if isinstance(self.data, Data2D) or self.enable2D:
radius = np.sqrt(self.data.qx_data * self.data.qx_data +
self.data.qy_data * self.data.qy_data)
index_data = (self.qmin_x <= radius) & (radius <= self.qmax_x)
index_data = (index_data) & (self.data.mask)
index_data = (index_data) & (np.isfinite(self.data.data))
npts2fit = len(self.data.data[index_data])
else:
for qx in self.data.x:
if qmax >= qx >= qmin:
npts2fit += 1
return npts2fit
[docs] def show_npts2fit(self):
"""
setValue Npts for fitting
"""
self.Npts_fit.SetValue(str(self.get_npts2fit()))
[docs] def get_chi2(self):
"""
return the current chi2
"""
return self.tcChi.GetValue()
[docs] def onsetValues(self, chisqr, p_name, out, cov):
"""
Build the panel from the fit result
:param chisqr: Value of the goodness of fit metric
:param p_name: the name of parameters
:param out: list of parameter with the best value found during fitting
:param cov: Covariance matrix
"""
# make sure stop button to fit button all the time
self._on_fit_complete()
if out is None or not np.isfinite(chisqr):
raise ValueError, "Fit error occured..."
is_modified = False
has_error = False
dispersity = ''
# Hide textctrl boxes of errors.
self._clear_Err_on_Fit()
# Check if chi2 is finite
if chisqr is not None and np.isfinite(chisqr):
# format chi2
chi2 = format_number(chisqr, True)
self.tcChi.SetValue(chi2)
self.tcChi.Refresh()
else:
self.tcChi.SetValue("-")
# Hide error title
if self.text2_3.IsShown() and not self.is_mac:
self.text2_3.Hide()
try:
if self.enable_disp.GetValue():
if hasattr(self, "text_disp_1"):
if self.text_disp_1 is not None and not self.is_mac:
self.text_disp_1.Hide()
except:
dispersity = None
pass
i = 0
# Set the panel when fit result are list
for item in self.param_toFit:
if len(item) > 5 and item is not None:
if item[0].IsShown():
# reset error value to initial state
if not self.is_mac:
item[3].Hide()
item[4].Hide()
for ind in range(len(out)):
if item[1] == p_name[ind]:
break
if len(out) > 0 and out[ind] is not None:
val_out = format_number(out[ind], True)
item[2].SetValue(val_out)
if(cov is not None and len(cov) == len(out)):
try:
if dispersity is not None:
if self.enable_disp.GetValue():
if hasattr(self, "text_disp_1"):
if self.text_disp_1 is not None:
if not self.text_disp_1.IsShown()\
and not self.is_mac:
self.text_disp_1.Show(True)
except:
pass
if cov[ind] is not None:
if np.isfinite(float(cov[ind])):
val_err = format_number(cov[ind], True)
item[4].SetForegroundColour(wx.BLACK)
else:
val_err = 'NaN'
item[4].SetForegroundColour(wx.RED)
if not self.is_mac:
item[3].Show(True)
item[4].Show(True)
item[4].SetValue(val_err)
has_error = True
i += 1
else:
raise ValueError, "onsetValues: Invalid parameters..."
# Show error title when any errors displayed
if has_error:
if not self.text2_3.IsShown():
self.text2_3.Show(True)
# save current state
self.save_current_state()
if not self.is_mac:
self.Layout()
self.Refresh()
# plot model ( when drawing, do not update chisqr value again)
self._draw_model(update_chisqr=False, source='fit')
[docs] def onWeighting(self, event):
"""
On Weighting radio button event, sets the weightbt_string
"""
self.weightbt_string = event.GetEventObject().GetLabelText()
self._set_weight()
def _set_weight(self, is_2D=None):
"""
Set weight in fit problem
"""
# compute weight for the current data
flag_weight = self.get_weight_flag()
if is_2D is None:
is_2D = self._is_2D()
self._manager.set_fit_weight(uid=self.uid,
flag=flag_weight,
is2d=is_2D,
fid=None)
[docs] def onPinholeSmear(self, event):
"""
Create a custom pinhole smear object that will change the way residuals
are compute when fitting
:Note: accuracy is given by strings'High','Med', 'Low' FOR 2d,
None for 1D
"""
# Need update param values
self._update_paramv_on_fit()
if event is not None:
tcrtl = event.GetEventObject()
# event case of radio button
if tcrtl.GetValue():
self.dx_percent = 0.0
is_new_pinhole = True
else:
is_new_pinhole = self._is_changed_pinhole()
else:
is_new_pinhole = True
# if any value is changed
if is_new_pinhole:
self._set_pinhole_smear()
# hide all silt sizer
self._hide_all_smear_info()
# show relevant slit sizers
self._show_smear_sizer()
self.sizer_set_smearer.Layout()
# we need FitInside here not just self.Layout to ensure all the sizers
# end up with the necessasary space to in the scroll panel. In
# particular the compute and fit buttons end up on top of each other
# PDB Nov 28 2015.
self.FitInside()
if event is not None:
event.Skip()
# self._undo.Enable(True)
self.save_current_state()
event = PageInfoEvent(page=self)
wx.PostEvent(self.parent, event)
def _is_changed_pinhole(self):
"""
check if any of pinhole smear is changed
:return: True or False
"""
# get the values
pin_percent = self.smear_pinhole_percent.GetValue()
# Check changes in slit heigth
try:
dx_percent = float(pin_percent)
except:
return True
if self.dx_percent != dx_percent:
return True
return False
def _set_pinhole_smear(self):
"""
Set custom pinhole smear
:return: msg
"""
# copy data
data = copy.deepcopy(self.data)
if self._is_2D():
self.smear_type = 'Pinhole2d'
len_data = len(data.data)
data.dqx_data = np.zeros(len_data)
data.dqy_data = np.zeros(len_data)
else:
self.smear_type = 'Pinhole'
len_data = len(data.x)
data.dx = np.zeros(len_data)
data.dxl = None
data.dxw = None
msg = None
get_pin_percent = self.smear_pinhole_percent
if not check_float(get_pin_percent):
get_pin_percent.SetBackgroundColour("pink")
msg = "Model Error:wrong value entered!!!"
else:
if len_data < 2:
len_data = 2
self.dx_percent = float(get_pin_percent.GetValue())
if self.dx_percent < 0:
get_pin_percent.SetBackgroundColour("pink")
msg = "Model Error:This value can not be negative!!!"
elif self.dx_percent is not None:
percent = self.dx_percent/100
if self._is_2D():
q = np.sqrt(data.qx_data**2 + data.qy_data**2)
data.dqx_data = data.dqy_data = percent*q
else:
data.dx = percent * data.x
self.current_smearer = smear_selection(data, self.model)
# 2D need to set accuracy
if self._is_2D():
self.current_smearer.set_accuracy(
accuracy=self.smear2d_accuracy)
if msg is not None:
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
else:
get_pin_percent.SetBackgroundColour("white")
# set smearing value whether or not the data contain the smearing info
enable_smearer = not self.disable_smearer.GetValue()
self._manager.set_smearer(smearer=self.current_smearer,
fid=self.data.id,
qmin=float(self.qmin_x),
qmax=float(self.qmax_x),
enable_smearer=enable_smearer,
uid=self.uid)
return msg
[docs] def update_pinhole_smear(self):
"""
called by kill_focus on pinhole TextCntrl
to update the changes
:return: False when wrong value was entered
"""
# msg default
msg = None
# check if any value is changed
if self._is_changed_pinhole():
msg = self._set_pinhole_smear()
wx.CallAfter(self.save_current_state)
if msg is not None:
return False
else:
return True
[docs] def onSlitSmear(self, event):
"""
Create a custom slit smear object that will change the way residuals
are compute when fitting
"""
# Need update param values
self._update_paramv_on_fit()
# msg default
msg = None
# for event given
if event is not None:
tcrtl = event.GetEventObject()
# event case of radio button
if tcrtl.GetValue():
self.dxl = 0.0
self.dxw = 0.0
is_new_slit = True
else:
is_new_slit = self._is_changed_slit()
else:
is_new_slit = True
# if any value is changed
if is_new_slit:
msg = self._set_slit_smear()
# hide all silt sizer
self._hide_all_smear_info()
# show relevant slit sizers
self._show_smear_sizer()
self.sizer_set_smearer.Layout()
# we need FitInside here not just self.Layout to ensure all the sizers
# end up with the necessasary space to in the scroll panel. In
# particular the compute and fit buttons end up on top of each other
# PDB Nov 28 2015.
self.FitInside()
if event is not None:
event.Skip()
self.save_current_state()
event = PageInfoEvent(page=self)
wx.PostEvent(self.parent, event)
if msg is not None:
wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
def _is_changed_slit(self):
"""
check if any of slit lengths is changed
:return: True or False
"""
# get the values
width = self.smear_slit_width.GetValue()
height = self.smear_slit_height.GetValue()
# check and change the box bg color if it was pink
# but it should be white now
# because this is the case that _set_slit_smear() will not handle
if height.lstrip().rstrip() == "":
self.smear_slit_height.SetBackgroundColour(wx.WHITE)
if width.lstrip().rstrip() == "":
self.smear_slit_width.SetBackgroundColour(wx.WHITE)
# Check changes in slit width
if width == "":
dxw = 0.0
else:
try:
dxw = float(width)
except:
return True
if self.dxw != dxw:
return True
# Check changes in slit heigth
if height == "":
dxl = 0.0
else:
try:
dxl = float(height)
except:
return True
if self.dxl != dxl:
return True
return False
def _set_slit_smear(self):
"""
Set custom slit smear
:return: message to inform the user about the validity
of the values entered for slit smear
"""
if isinstance(self.data, Data2D) or self.enable2D:
return
# make sure once more if it is smearer
data = copy.deepcopy(self.data)
data_len = len(data.x)
data.dx = None
data.dxl = None
data.dxw = None
msg = None
try:
self.dxl = float(self.smear_slit_height.GetValue())
data.dxl = self.dxl * np.ones(data_len)
self.smear_slit_height.SetBackgroundColour(wx.WHITE)
except:
self.dxl = None
data.dxl = np.zeros(data_len)
if self.smear_slit_height.GetValue().lstrip().rstrip() != "":
self.smear_slit_height.SetBackgroundColour("pink")
msg = "Wrong value entered... "
else:
self.smear_slit_height.SetBackgroundColour(wx.WHITE)
try:
self.dxw = float(self.smear_slit_width.GetValue())
self.smear_slit_width.SetBackgroundColour(wx.WHITE)
data.dxw = self.dxw * np.ones(data_len)
except:
self.dxw = None
data.dxw = np.zeros(data_len)
if self.smear_slit_width.GetValue().lstrip().rstrip() != "":
self.smear_slit_width.SetBackgroundColour("pink")
msg = "Wrong Fit value entered... "
else:
self.smear_slit_width.SetBackgroundColour(wx.WHITE)
self.current_smearer = smear_selection(data, self.model)
# set smearing value whether or not the data contain the smearing info
enable_smearer = not self.disable_smearer.GetValue()
self._manager.set_smearer(smearer=self.current_smearer,
fid=self.data.id,
qmin=float(self.qmin_x),
qmax=float(self.qmax_x),
enable_smearer=enable_smearer,
uid=self.uid)
return msg
[docs] def update_slit_smear(self):
"""
called by kill_focus on pinhole TextCntrl
to update the changes
:return: False when wrong value was entered
"""
# msg default
msg = None
# check if any value is changed
if self._is_changed_slit():
msg = self._set_slit_smear()
# self._undo.Enable(True)
self.save_current_state()
if msg is not None:
return False
else:
return True
[docs] def onSmear(self, event):
"""
Create a smear object that will change the way residuals
are computed when fitting
"""
if event is not None:
event.Skip()
if self.data is None:
return
# Need update param values
self._update_paramv_on_fit()
if self.model is not None:
if self.data.is_data:
self._manager.page_finder[self.uid].add_data(data=self.data)
temp_smearer = self.on_smear_helper()
self.sizer_set_smearer.Layout()
# we need FitInside here not just self.Layout to ensure all the sizers
# end up with the necessasary space to in the scroll panel. In
# particular the compute and fit buttons end up on top of each other
# PDB Nov 28 2015.
self.FitInside()
self._set_weight()
# set smearing value whether or not the data contain the smearing info
enable_smearer = not self.disable_smearer.GetValue()
wx.CallAfter(self._manager.set_smearer, uid=self.uid,
smearer=temp_smearer,
fid=self.data.id,
qmin=float(self.qmin_x),
qmax=float(self.qmax_x),
enable_smearer=enable_smearer)
self.state.enable_smearer = self.enable_smearer.GetValue()
self.state.disable_smearer = self.disable_smearer.GetValue()
self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
self.state.slit_smearer = self.slit_smearer.GetValue()
[docs] def on_smear_helper(self, update=False):
"""
Help for onSmear
:param update: force or not to update
"""
self._get_smear_info()
# renew smear sizer
if self.smear_type is not None:
self.smear_description_smear_type.SetValue(str(self.smear_type))
self.smear_data_left.SetValue(str(self.dq_l))
self.smear_data_right.SetValue(str(self.dq_r))
self._hide_all_smear_info()
data = copy.deepcopy(self.data)
# make sure once more if it is smearer
temp_smearer = smear_selection(data, self.model)
if self.current_smearer != temp_smearer or update:
self.current_smearer = temp_smearer
if self.enable_smearer.GetValue():
if self.current_smearer is None:
wx.PostEvent(self._manager.parent,
StatusEvent(status="Data contains no smearing information"))
else:
wx.PostEvent(self._manager.parent,
StatusEvent(status="Data contains smearing information"))
self.smear_data_left.Show(True)
self.smear_data_right.Show(True)
temp_smearer = self.current_smearer
elif self.disable_smearer.GetValue():
self.smear_description_none.Show(True)
elif self.pinhole_smearer.GetValue():
self.onPinholeSmear(None)
elif self.slit_smearer.GetValue():
self.onSlitSmear(None)
self._show_smear_sizer()
return temp_smearer
[docs] def on_complete_chisqr(self, event):
"""
Display result chisqr on the panel
:event: activated by fitting/ complete after draw
"""
try:
if event is None:
output = "-"
elif not np.isfinite(event.output):
output = "-"
else:
output = event.output
self.tcChi.SetValue(str(format_number(output, True)))
self.state.tcChi = self.tcChi.GetValue()
except:
pass
[docs] def get_all_checked_params(self):
"""
Found all parameters current check and add them to list of parameters
to fit
"""
self.param_toFit = []
for item in self.parameters:
if item[0].GetValue() and item not in self.param_toFit:
if item[0].IsShown():
self.param_toFit.append(item)
for item in self.fittable_param:
if item[0].GetValue() and item not in self.param_toFit:
if item[0].IsShown():
self.param_toFit.append(item)
self.save_current_state_fit()
event = PageInfoEvent(page=self)
wx.PostEvent(self.parent, event)
param2fit = []
for item in self.param_toFit:
if item[0] and item[0].IsShown():
param2fit.append(item[1])
self._manager.set_param2fit(self.uid, param2fit)
[docs] def select_param(self, event=None):
"""
Select TextCtrl checked for fitting purpose and stores them
in self.param_toFit=[] list
"""
self.param_toFit = []
for item in self.parameters:
# Skip t ifhe angle parameters if 1D data
if not isinstance(self.data, Data2D) and not self.enable2D:
if item in self.orientation_params:
continue
# Select parameters to fit for list of primary parameters
if item[0].GetValue() and item[0].IsShown():
if not (item in self.param_toFit):
self.param_toFit.append(item)
else:
#remove parameters from the fitting list
if item in self.param_toFit:
self.param_toFit.remove(item)
# Select parameters to fit for list of fittable parameters
# with dispersion
for item in self.fittable_param:
# Skip t ifhe angle parameters if 1D data
if not isinstance(self.data, Data2D) and not self.enable2D:
if item in self.orientation_params:
continue
if item[0].GetValue() and item[0].IsShown():
if not (item in self.param_toFit):
self.param_toFit.append(item)
else:
# remove parameters from the fitting list
if item in self.param_toFit:
self.param_toFit.remove(item)
# Calculate num. of angle parameters
if isinstance(self.data, Data2D) or self.enable2D:
len_orient_para = 0
else:
len_orient_para = len(self.orientation_params) # assume even len
# Total num. of angle parameters
if len(self.fittable_param) > 0:
len_orient_para *= 2
self.save_current_state_fit()
if event is not None:
# post state to fit panel
event = PageInfoEvent(page=self)
wx.PostEvent(self.parent, event)
param2fit = []
for item in self.param_toFit:
if item[0] and item[0].IsShown():
param2fit.append(item[1])
self._manager.set_param2fit(self.uid, param2fit)
[docs] def set_model_param_sizer(self, model):
"""
Build the panel from the model content
:param model: the model selected in combo box for fitting purpose
"""
self.sizer3.Clear(True)
self.parameters = []
self.str_parameters = []
self.param_toFit = []
self.fittable_param = []
self.fixed_param = []
self.orientation_params = []
self.orientation_params_disp = []
if model is None:
self.sizer3.Layout()
self.SetupScrolling()
return
box_description = wx.StaticBox(self, wx.ID_ANY, str("Model Parameters"))
boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
sizer = wx.GridBagSizer(5, 5)
# save the current model
self.model = model
keys = self.model.getParamList()
# list of dispersion parameters
self.disp_list = self.model.getDispParamList()
def custom_compare(a, b):
"""
Custom compare to order, first by alphabets then second by number.
"""
# number at the last digit
a_last = a[len(a) - 1]
b_last = b[len(b) - 1]
# default
num_a = None
num_b = None
# split the names
a2 = a.lower().split('_')
b2 = b.lower().split('_')
# check length of a2, b2
len_a2 = len(a2)
len_b2 = len(b2)
# check if it contains a int number(<10)
try:
num_a = int(a_last)
except:
pass
try:
num_b = int(b_last)
except:
pass
# Put 'scale' near the top; happens
# when numbered param name exists
if a == 'scale':
return -1
# both have a number
if num_a is not None and num_b is not None:
if num_a > num_b:
return -1
# same number
elif num_a == num_b:
# different last names
if a2[len_a2 - 1] != b2[len_b2 - 1] and num_a != 0:
return -cmp(a2[len_a2 - 1], b2[len_b2 - 1])
else:
return cmp(a, b)
else:
return 1
# one of them has a number
elif num_a is not None:
return 1
elif num_b is not None:
return -1
# no numbers
else:
return cmp(a.lower(), b.lower())
# keys obtained now from ordered dict, so commenting alphabetical
# ordering keys.sort(custom_compare)
iy = 0
ix = 0
sizer.Add(wx.StaticText(self, wx.ID_ANY, 'Parameter'),
(iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix += 1
self.text2_2 = wx.StaticText(self, wx.ID_ANY, 'Value')
sizer.Add(self.text2_2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix += 2
self.text2_3 = wx.StaticText(self, wx.ID_ANY, 'Error')
sizer.Add(self.text2_3, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
if not self.is_mac:
self.text2_3.Hide()
ix += 1
self.text2_min = wx.StaticText(self, wx.ID_ANY, 'Min')
sizer.Add(self.text2_min, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
# self.text2_min.Hide()
ix += 1
self.text2_max = wx.StaticText(self, wx.ID_ANY, 'Max')
sizer.Add(self.text2_max, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
# self.text2_max.Hide()
ix += 1
self.text2_4 = wx.StaticText(self, wx.ID_ANY, '[Units]')
sizer.Add(self.text2_4, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
self.text2_4.Hide()
CHECK_STATE = False
for item in keys:
if item not in self.disp_list and not item in \
self.model.orientation_params:
# prepare a spot to store errors
if item not in self.model.details:
self.model.details[item] = ["", None, None]
iy += 1
ix = 0
if (self.model.__class__ in
self.model_list_box["Multi-Functions"] or
self.temp_multi_functional)\
and (item in self.model.non_fittable):
non_fittable_name = wx.StaticText(self, wx.ID_ANY, item)
sizer.Add(non_fittable_name, (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 21)
# add parameter value
ix += 1
value = self.model.getParam(item)
if len(self.model.fun_list) > 0:
# num = item.split('_')[1][5:7]
fun_box = wx.ComboBox(self, wx.ID_ANY, size=(100, -1),
style=wx.CB_READONLY, name='%s' % item)
self._set_fun_box_list(fun_box)
fun_box.SetSelection(0)
# self.fun_box.SetToolTipString("A function
# describing the interface")
wx.EVT_COMBOBOX(fun_box, wx.ID_ANY, self._on_fun_box)
else:
fun_box = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER, name='%s' % item)
fun_box.SetToolTipString(
"Hit 'Enter' after typing to update the plot.")
fun_box.SetValue(format_number(value, True))
sizer.Add(fun_box, (iy, ix), (1, 1), wx.EXPAND)
self.str_parameters.append([None, item, fun_box,
None, None, None,
None, None])
else:
# add parameters name with checkbox for selecting to fit
cb = wx.CheckBox(self, wx.ID_ANY, item)
cb.SetValue(CHECK_STATE)
cb.SetToolTipString(" Check mark to fit.")
# cb.SetValue(True)
wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
sizer.Add(cb, (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
# add parameter value
ix += 1
value = self.model.getParam(item)
ctl1 = ModelTextCtrl(self, wx.ID_ANY, size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER)
ctl1.SetToolTipString(
"Hit 'Enter' after typing to update the plot.")
ctl1.SetValue(format_number(value, True))
sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
# text to show error sign
ix += 1
text2 = wx.StaticText(self, wx.ID_ANY, '+/-')
sizer.Add(text2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
if not self.is_mac:
text2.Hide()
ix += 1
ctl2 = BGTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 1.2, 20))
sizer.Add(ctl2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
if not self.is_mac:
ctl2.Hide()
ix += 1
ctl3 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 1.9, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self._onparamRangeEnter)
min_bound = self.model.details[item][1]
if min_bound is not None:
ctl3.SetValue(format_number(min_bound, True))
sizer.Add(ctl3, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ix += 1
ctl4 = ModelTextCtrl(self, wx.ID_ANY,
size=(_BOX_WIDTH / 1.9, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self._onparamRangeEnter)
max_bound = self.model.details[item][2]
if max_bound is not None:
ctl4.SetValue(format_number(max_bound, True))
sizer.Add(ctl4, (iy, ix), (1, 1),
wx.EXPAND | wx.FIXED_MINSIZE, 0)
ix += 1
# Units
if item in self.model.details:
units = wx.StaticText(self, wx.ID_ANY,
self.model.details[item][0], style=wx.ALIGN_LEFT)
else:
units = wx.StaticText(self, wx.ID_ANY, "",
style=wx.ALIGN_LEFT)
sizer.Add(units, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
self.parameters.append([cb, item, ctl1,
text2, ctl2, ctl3, ctl4, units])
iy += 1
sizer.Add((10, 10), (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
# type can be either Guassian or Array
if len(self.model.dispersion.values()) > 0:
type = self.model.dispersion.values()[0]["type"]
else:
type = "Gaussian"
iy += 1
ix = 0
# Add tile for orientational angle
for item in keys:
if item in self.model.orientation_params:
orient_angle = wx.StaticText(self, wx.ID_ANY, '[For 2D only]:')
mag_on_button = wx.Button(self, wx.ID_ANY, "Magnetic ON")
mag_on_button.SetToolTipString("Turn Pol Beam/Mag scatt on/off")
mag_on_button.Bind(wx.EVT_BUTTON, self._on_mag_on)
mag_angle_help_button = wx.Button(self, wx.ID_ANY,
"Magnetic angles?")
mag_angle_help_button.SetToolTipString("see angle definitions")
mag_help_button = wx.Button(self, wx.ID_ANY, "Mag HELP")
mag_help_button.SetToolTipString("Help on pol beam/mag fitting")
mag_help_button.Bind(wx.EVT_BUTTON, self._on_mag_help)
mag_angle_help_button.Bind(wx.EVT_BUTTON,
self._on_mag_angle_help)
sizer.Add(orient_angle, (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
iy += 1
sizer.Add(mag_on_button, (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
ix += 1
sizer.Add(mag_angle_help_button, (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
sizer.Add(mag_help_button, (iy, ix + 1), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
# handle the magnetic buttons
# clean this up so that assume mag is off then turn
# all buttons on IF mag has mag and has 2D
if not self._has_magnetic:
mag_on_button.Show(False)
elif not isinstance(self.data, Data2D):
mag_on_button.Show(False)
else:
mag_on_button.Show(True)
mag_help_button.Show(False)
mag_angle_help_button.Show(False)
if mag_on_button.IsShown():
if self.magnetic_on:
mag_on_button.SetLabel("Magnetic OFF")
mag_help_button.Show(True)
mag_angle_help_button.Show(True)
else:
mag_on_button.SetLabel("Magnetic ON")
mag_help_button.Show(False)
mag_angle_help_button.Show(False)
if not isinstance(self.data, Data2D) and not self.enable2D:
orient_angle.Hide()
else:
orient_angle.Show(True)
break
# For Gaussian only
if type.lower() != "array":
for item in self.model.orientation_params:
if not self.magnetic_on:
if item in self.model.magnetic_params:
continue
if item not in self.disp_list:
# prepare a spot to store min max
if item not in self.model.details:
self.model.details[item] = ["", None, None]
iy += 1
ix = 0
# add parameters name with checkbox for selecting to fit
cb = wx.CheckBox(self, wx.ID_ANY, item)
cb.SetValue(CHECK_STATE)
cb.SetToolTipString("Check mark to fit")
wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
if isinstance(self.data, Data2D) or self.enable2D:
cb.Show(True)
else:
cb.Hide()
sizer.Add(cb, (iy, ix), (1, 1),
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
# add parameter value
ix += 1
value = self.model.getParam(item)
ctl1 = ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
style=wx.TE_PROCESS_ENTER)
ctl1.SetToolTipString(
"Hit 'Enter' after typing to update the plot.")
ctl1.SetValue(format_number(value, True))
if isinstance(self.data, Data2D) or self.enable2D:
ctl1.Show(True)
else:
ctl1.Hide()
sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
# text to show error sign
ix += 1
text2 = wx.StaticText(self, -1, '+/-')
sizer.Add(text2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
text2.Hide()
ix += 1
ctl2 = wx.TextCtrl(self, -1,
size=(_BOX_WIDTH / 1.2, 20), style=0)
sizer.Add(ctl2, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ctl2.Hide()
ix += 1
ctl3 = ModelTextCtrl(self, -1,
size=(_BOX_WIDTH / 1.8, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self._onparamRangeEnter)
sizer.Add(ctl3, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ctl3.Hide()
ix += 1
ctl4 = ModelTextCtrl(self, -1,
size=(_BOX_WIDTH / 1.8, 20),
style=wx.TE_PROCESS_ENTER,
text_enter_callback=self._onparamRangeEnter)
sizer.Add(ctl4, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
ctl4.Hide()
if isinstance(self.data, Data2D) or self.enable2D:
if self.is_mac:
text2.Show(True)
ctl2.Show(True)
ctl3.Show(True)
ctl4.Show(True)
ix += 1
# Units
if item in self.model.details:
units = wx.StaticText(self, -1,
self.model.details[item][0],
style=wx.ALIGN_LEFT)
else:
units = wx.StaticText(self, -1, "",
style=wx.ALIGN_LEFT)
if isinstance(self.data, Data2D) or self.enable2D:
units.Show(True)
else:
units.Hide()
sizer.Add(units, (iy, ix), (1, 1),
wx.EXPAND | wx.ADJUST_MINSIZE, 0)
self.parameters.append([cb, item, ctl1,
text2, ctl2, ctl3, ctl4, units])
self.orientation_params.append([cb, item, ctl1,
text2, ctl2, ctl3, ctl4, units])
iy += 1
box_description.SetForegroundColour(wx.BLUE)
# Display units text on panel
for item in keys:
if item in self.model.details:
self.text2_4.Show()
# Fill the list of fittable parameters
self.get_all_checked_params()
self.save_current_state_fit()
boxsizer1.Add(sizer)
self.sizer3.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
self.sizer3.Layout()
self.Layout()
[docs] def on_right_down(self, event):
"""
Get key stroke event
"""
if self.data is None:
return
# Figuring out key combo: Cmd for copy, Alt for paste
if event.AltDown() and event.ShiftDown():
flag = True
elif event.AltDown() or event.ShiftDown():
flag = False
else:
return
# make event free
event.Skip()
# messages depending on the flag
if not flag:
infor = 'warning'
# inform msg to wx
wx.PostEvent(self._manager.parent,
StatusEvent(status=msg, info=infor))
def _onModel2D(self, event):
"""
toggle view of model from 1D to 2D or 2D from 1D
"""
if self.model_view.GetLabelText() == "Show 2D":
self.model_view.SetLabel("Show 1D")
self.enable2D = True
else:
self.model_view.SetLabel("Show 2D")
self.enable2D = False
self.Show(False)
self.create_default_data()
self._manager.store_data(self.uid, data_list=[self.data])
self.set_model_param_sizer(self.model)
self._set_sizer_dispersion()
self._set_weight(is_2D=self.enable2D)
self._set_smear_buttons()
self.Show(True)
self.SetupScrolling()
self._draw_model()
self.state.enable2D = copy.deepcopy(self.enable2D)
def _set_smear_buttons(self):
"""
Set semarer radio buttons
"""
# more disables for 2D
if isinstance(self.data, Data2D) or self.enable2D:
self.slit_smearer.Disable()
self.default_mask = copy.deepcopy(self.data.mask)
if self.model is not None:
self.pinhole_smearer.Enable(True)
elif isinstance(self.data, Data1D):
if self.model is not None:
self.slit_smearer.Enable(True)
self.pinhole_smearer.Enable(True)
else:
msg="data is not recognized as either 1D or 2D"
logger.info(msg)
[docs]class BGTextCtrl(wx.TextCtrl):
"""
Text control used to display outputs.
No editing allowed. The background is
grayed out. User can't select text.
"""
def __init__(self, *args, **kwds):
wx.TextCtrl.__init__(self, *args, **kwds)
self.SetEditable(False)
self.SetBackgroundColour(self.GetParent().parent.GetBackgroundColour())
# Bind to mouse event to avoid text highlighting
# The event will be skipped once the call-back
# is called.
self.Bind(wx.EVT_MOUSE_EVENTS, self._click)
def _click(self, event):
"""
Prevent further handling of the mouse event
by not calling Skip().
"""
pass