diff --git a/Framework/MDAlgorithms/src/ConvertCWSDExpToMomentum.cpp b/Framework/MDAlgorithms/src/ConvertCWSDExpToMomentum.cpp index 5bb6cc662e53abe742bfb355143983a89573ce5d..22507befa430a047f4f1cb757bc943df762b53df 100644 --- a/Framework/MDAlgorithms/src/ConvertCWSDExpToMomentum.cpp +++ b/Framework/MDAlgorithms/src/ConvertCWSDExpToMomentum.cpp @@ -415,6 +415,13 @@ void ConvertCWSDExpToMomentum::convertSpiceMatrixToMomentumMDEvents( } expinfo->mutableRun().setGoniometer(dataws->run().getGoniometer(), false); expinfo->mutableRun().addProperty("run_number", runnumber); + // Add all the other propertys from original data workspace + const std::vector<Kernel::Property *> vec_property = + dataws->run().getProperties(); + for (size_t i = 0; i < vec_property.size(); ++i) { + expinfo->mutableRun().addProperty(vec_property[i]->clone()); + } + m_outputWS->addExperimentInfo(expinfo); return; diff --git a/Framework/Properties/Mantid.properties.template b/Framework/Properties/Mantid.properties.template index 27e669d1d7ff7c2297397b120797a006c97aa4b6..9b160ba84bb9860cdd4b2237dc08e7576198f65b 100644 --- a/Framework/Properties/Mantid.properties.template +++ b/Framework/Properties/Mantid.properties.template @@ -16,7 +16,7 @@ default.facility = ISIS default.instrument = # Set of PyQt interfaces to add to the Interfaces menu, separated by a space. Interfaces are seperated from their respective categories by a "/". -mantidqt.python_interfaces = Direct/DGS_Reduction.py Direct/DGSPlanner.py SANS/ORNL_SANS.py Reflectometry/REFL_Reduction.py Reflectometry/REFL_SF_Calculator.py Reflectometry/REFM_Reduction.py Utility/TofConverter.py Reflectometry/ISIS_Reflectometry.py Diffraction/Powder_Diffraction_Reduction.py Utility/FilterEvents.py Diffraction/HFIR_Powder_Diffraction_Reduction.py +mantidqt.python_interfaces = Direct/DGS_Reduction.py Direct/DGSPlanner.py SANS/ORNL_SANS.py Reflectometry/REFL_Reduction.py Reflectometry/REFL_SF_Calculator.py Reflectometry/REFM_Reduction.py Utility/TofConverter.py Reflectometry/ISIS_Reflectometry.py Diffraction/Powder_Diffraction_Reduction.py Utility/FilterEvents.py Diffraction/HFIR_Powder_Diffraction_Reduction.py Diffraction/HFIR_4Circle_Reduction.py mantidqt.python_interfaces_directory = @MANTID_ROOT@/scripts diff --git a/docs/source/algorithms/ConvertCWSDExpToMomentum-v1.rst b/docs/source/algorithms/ConvertCWSDExpToMomentum-v1.rst index 904b8dd2a5ccd21368da10509b6c936c4aa5fe42..1d88cdd45e69e58036d500ee26a3af2f5b079de6 100644 --- a/docs/source/algorithms/ConvertCWSDExpToMomentum-v1.rst +++ b/docs/source/algorithms/ConvertCWSDExpToMomentum-v1.rst @@ -19,18 +19,22 @@ In this algorithm's name, ConvertCWSDToMomentum, *CW* stands for constant wave This algorithm takes ??? as inputs. Futhermore, the unit of the output matrix workspace can be converted to -momentum transfer (:math:`Q`). +momentum transfer (Q). Outline of algorithm #################### -1. Create output workspace. +1. Create output workspace + * Build a virtual instrument, requiring + - position of source - position of sample - detector ID, position, detector size of pixels + 2. Read in data via table workspace + * From each row, (1) file name and (2) starting detector ID are read in. * Detector position in (virtual) instrument of MDEventWorkspace is compared with the position in MatrixWorkspace * Momentum is calcualted by goniometry values @@ -75,8 +79,6 @@ Each MDEvent in output MDEventWorkspace contain * Detector ID * Run Number - - Combine Experiment Into One MDEventWorkspace -------------------------------------------- @@ -90,7 +92,7 @@ In order to integrate them into an organized data structure, i.e., *MDEventWorks a virtual instrument is built in the algorithm. Virtual instrument -================== +################## A virtual instrument is built in the algorithm. In this virtual instrument, the number of detectors and their position are determined @@ -98,13 +100,13 @@ by the number of individual detector's positions in the *experiment*. MDEventWorkspace -================ +################ There is only one *virtual* instrument and *N* ExperimentInfo. *N* is the total number of experiment points in the *experiment*. Inconsistency between using virtual instrument and copying instrument -===================================================================== +##################################################################### It is found that the results, i.e., the peak's position in sample-momentum space, by FindPeaksMD, are different betweent the MDEventWorkspaces @@ -145,7 +147,7 @@ with 2D angular detector. Usage ----- -**Example - convert an HB3A's experiment to MDWorkspace in sample momentum workspae and creating virtual instrument.:** +**Example - convert an HB3A's experiment to MDWorkspace in sample momentum workspae and creating virtual instrument** .. testcode:: ExConvertHB3AToMDVirtualInstrument diff --git a/docs/source/interfaces/HFIR_4Circle_Reduction.rst b/docs/source/interfaces/HFIR_4Circle_Reduction.rst new file mode 100644 index 0000000000000000000000000000000000000000..01c42636b87472ebfb13e637f5a3ceb0597b8c99 --- /dev/null +++ b/docs/source/interfaces/HFIR_4Circle_Reduction.rst @@ -0,0 +1,116 @@ +HFIR Single Crystal Reduction Interface +======================================= + +.. contents:: Table of Contents + :local: + +Overview +-------- + +HFIR single crystalreduction interface is a GUI to download, view and reduce data from +HFIR's four-circle single crystal diffractometer in SPICE format. + + +Introduction of Tabs +-------------------- + + 1. **Setup and Data Access**: Configure the instrument name, data server URL and directories. + + - Configure the instrument name; + - Set up and test HB3A data server's URL; + - Configure the directory to save raw data; + - Configure the directory to save working result; + - Download data from server; + + 2. **View Raw Data**: View 2D image of counts on detector of one measurement. + + - Plot the counts of the 256 by 256 2D detector; + + 3. **Calculate UB**: Calculate UB matrix. + + - Find peak in one measurement; + - Option to load Miller index directly from SPICE file; + - Calculate UB matrix; + - Re-index the peaks; + + 4. **Merge Scan**: Merge all the measurements in a scan. + + - Merge all measuring points in a scan to an MDEventWorkspace in HKL-frame or Q-sample-frame; + - Allow various ways to set up UB matrix + + 5. **Refine UB**: Refine UB matrix + + - Disabled becaues it is in development still; + + 6. **Peak Integration**: Integrate peaks + + - Disabled because it is still in development. + +Use Cases +--------- + +Here are some use cases that can be used as examples. + + +Workflow to calculate UB matrix ++++++++++++++++++++++++++++++++ + +Here is a typical use case to calculate UB matrix + + 1. User specifies *Experiment* and pushes button *Set* + + 2. User enters tab *View Raw Data* + + 3. User inputs scan number and list all the measuring points + + 4. User views all the measurements + + 5. User finds out the measurement with the strongest reflection and push button use + + 6. GUI shifts to tab *Calculate UB* automatically + + 7. User pushes button *Find Peak* with checking *Load HKL from file* + + 8. GUI finds the peak center and load HKL + + 9. User pushes button *Add peak* to add the peak to table + + 10. User repeats step 2 to 9 to add other peaks + + 11. User select the peaks that are linearly independent and pushes *Calcualte UB* + + 12. GUI calculates UB matrix and show the result + + 13. User may push *Index peak* to use the calculated UB matrix to index peaks in the table to check UB matrix. + + +Workflow to merge measurements in scan +++++++++++++++++++++++++++++++++++++++ + +Here is a typical use case to merge all the measuring points (Pt.) in a scan. + + 1. User specifies *Experiment* and pushes button *Set* + + 2. User enters tab *Merge Scan* + + 3. User specifies the UB matrix either by *From tab Calculate UB* or by entering the values to text editor + + 4. User pushes button *Set*. + + 5. User specifies the frame in which the merged data will be in. If the target frame is Q-Sample-Sapce, then there is + no need to specify UB matrix; + + 6. User specifies the scan numbers and push button *Add*; + + 7. User specifies the base name for the output MDEventWorkspaces; + + 8. User pushes button *Process*; + + 9. User goes to MantidPlot to view the merged scan by SliceView or Vates. + + + +Limitation +---------- + +- HFIR single crystal reduction GUI supports for instrument HB3A only from release 3.5.0 and nightly. diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 7c00c508283118881ab92f0d7c8e6b079bc1f0f3..7cd45300f74869139a60cf613343c6613fe322d6 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(Interface/ui) add_subdirectory(lib1to2/gui) add_subdirectory(PyChop) add_subdirectory(TofConverter) +add_subdirectory(HFIR_4Circle_Reduction) # Chain all required interface custom targets into CompilePyUI add_custom_target(CompilePyUI DEPENDS @@ -15,6 +16,7 @@ add_custom_target(CompilePyUI DEPENDS CompileUITofConverter CompileUIUI CompileUILib1To2 + CompileUIHFIR_4Circle_Reduction ) set ( TEST_PY_FILES diff --git a/scripts/HFIR_4Circle_Reduction.py b/scripts/HFIR_4Circle_Reduction.py new file mode 100644 index 0000000000000000000000000000000000000000..89742c6bfa9f75bb50d994cf3148f7d9e1c84476 --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction.py @@ -0,0 +1,17 @@ +#pylint: disable=invalid-name +from HFIR_4Circle_Reduction import reduce4circleGUI +from PyQt4 import QtGui +import sys + +def qapp(): + if QtGui.QApplication.instance(): + _app = QtGui.QApplication.instance() + else: + _app = QtGui.QApplication(sys.argv) + return _app + +app = qapp() + +reducer = reduce4circleGUI.MainWindow() #the main ui class in this file is called MainWindow +reducer.show() +app.exec_() diff --git a/scripts/HFIR_4Circle_Reduction/CMakeLists.txt b/scripts/HFIR_4Circle_Reduction/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..67fe46df205d951ff83380c933f71c88d5325604 --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/CMakeLists.txt @@ -0,0 +1,8 @@ +include(UiToPy) + +# List of UIs to Auto convert +set( UI_FILES + MainWindow.ui +) + +UiToPy( UI_FILES CompileUIHFIR_4Circle_Reduction) diff --git a/scripts/HFIR_4Circle_Reduction/MainWindow.ui b/scripts/HFIR_4Circle_Reduction/MainWindow.ui new file mode 100644 index 0000000000000000000000000000000000000000..4f7fa462e7008c7cf9e167e822ad5b0b47996aca --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/MainWindow.ui @@ -0,0 +1,2312 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1292</width> + <height>783</height> + </rect> + </property> + <property name="windowTitle"> + <string>4-Circle Reduction</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QHBoxLayout" name="horizontalLayout_11"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_General"> + <item> + <widget class="QLabel" name="label_exp"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Experiment</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit_exp"> + <property name="minimumSize"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>60</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_setExp"> + <property name="text"> + <string>Set</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_11"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_15"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Unit Cell</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_16"> + <property name="text"> + <string>a</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit_a"> + <property name="maximumSize"> + <size> + <width>120</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_23"> + <property name="text"> + <string>b</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit_b"> + <property name="maximumSize"> + <size> + <width>120</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_24"> + <property name="text"> + <string>c</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit_c"> + <property name="maximumSize"> + <size> + <width>120</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_25"> + <property name="text"> + <string>alpha</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit_alpha"> + <property name="maximumSize"> + <size> + <width>120</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_26"> + <property name="text"> + <string>beta</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit_beta"> + <property name="maximumSize"> + <size> + <width>120</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_27"> + <property name="text"> + <string>gamma</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit_gamma"> + <property name="maximumSize"> + <size> + <width>120</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_Tab"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Setup && Data Access</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="1" column="0"> + <widget class="QTextEdit" name="textEdit"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="html"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">1. Configure the data reduction</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-style:italic;">(a) Do not modify ServerURL unless necessary;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-style:italic;">(b) Click 'Apply' to check internet connection and directories;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-style:italic;">(c) If 'Time out' is popped out, try to click 'Apply' again. Server may not be able to respond promptly.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-style:italic;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">2. Start to reduce data</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-style:italic;">(a) Set</span><span style=" font-size:10pt;"> Experiment</span></p></body></html></string> + </property> + </widget> + </item> + <item row="6" column="0"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0"> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Configuration</string> + </property> + <layout class="QGridLayout" name="gridLayout_11"> + <item row="1" column="2"> + <widget class="QPushButton" name="pushButton_testURLs"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>200</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string>Test</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QPushButton" name="pushButton_browseLocalDataDir"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="2" column="3"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="lineEdit_localSpiceDir"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Directory for local data storage</p></body></html></string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_instrument"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Instrument</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_dir"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Directory for local data storage</p></body></html></string> + </property> + <property name="text"> + <string>Data Directory</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="comboBox_instrument"> + <item> + <property name="text"> + <string>HB3A</string> + </property> + </item> + </widget> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="pushButton_useDefaultDir"> + <property name="font"> + <font> + <pointsize>10</pointsize> + </font> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Use default set up</p><p><br/></p></body></html></string> + </property> + <property name="text"> + <string>Load Default</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="lineEdit_workDir"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Directory to save outcome of the data reduction</p></body></html></string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="lineEdit_url"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_url"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="toolTip"> + <string><html><head/><body><p>URL of the http server to download HB3A data</p></body></html></string> + </property> + <property name="text"> + <string>Server URL</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QPushButton" name="pushButton_browseWorkDir"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>140</width> + <height>1</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>140</width> + <height>16777215</height> + </size> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Directory to save outcome of the data reduction</p></body></html></string> + </property> + <property name="text"> + <string>Working Direcotry</string> + </property> + </widget> + </item> + <item row="4" column="2"> + <widget class="QPushButton" name="pushButton_applySetup"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Apply</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="4" column="0"> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Data Download</string> + </property> + <layout class="QGridLayout" name="gridLayout_12"> + <item row="0" column="2"> + <widget class="QPushButton" name="pushButton_browseLocalCache"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>100</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Scans List</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="comboBox_mode"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Mode &quot;download&quot;: download data to local disk;</p><p>Mode &quot;http server only&quot;: download data to cache, process and delete cached data upon returning</p></body></html></string> + </property> + <item> + <property name="text"> + <string>Download Complete Experiment</string> + </property> + </item> + <item> + <property name="text"> + <string>Download Selected Scans</string> + </property> + </item> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit_localSrcDir"> + <property name="toolTip"> + <string><html><head/><body><p>Cache on local disk. The dowloaded data will be saved to here. </p></body></html></string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_datamode"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Download Mode</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>140</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>140</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string>Destination</string> + </property> + </widget> + </item> + <item row="0" column="6"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="pushButton_downloadExpData"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="toolTip"> + <string><html><head/><body><p><span style=" font-weight:400;">Download scans specified by 'Scans List'; </span></p><p><span style=" font-weight:400;">If 'Scans List' is empty, then the complete experiment data will be downloaded</span></p></body></html></string> + </property> + <property name="text"> + <string>Download</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="lineEdit_downloadScans"/> + </item> + <item row="2" column="2"> + <widget class="QPushButton" name="pushButton_ListScans"> + <property name="text"> + <string>List Scans</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="3" column="0"> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_4"> + <attribute name="title"> + <string>View Raw Data</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_9"> + <item row="0" column="0"> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="3"> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QGridLayout" name="gridLayout"/> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Scan</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLineEdit" name="lineEdit_run"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>10</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>10000</width> + <height>16777215</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_setScanInfo"> + <property name="text"> + <string>Set</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_showPtList"> + <property name="text"> + <string>List Pt.</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string> Pt</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLineEdit" name="lineEdit_rawDataPtNo"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>60</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>1000</width> + <height>16777215</height> + </size> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_plotRawPt"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Plot</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_usePt4UB"> + <property name="toolTip"> + <string><html><head/><body><p>Use this peak to calcualate UB matrix</p></body></html></string> + </property> + <property name="text"> + <string>Use</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_5"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <item> + <widget class="QPushButton" name="pushButton_prevPtNumber"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>30</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>40</width> + <height>16777215</height> + </size> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Previous Pt. No</p></body></html></string> + </property> + <property name="text"> + <string><---|</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_7"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>5</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_nextPtNumber"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>30</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>40</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string>|--></string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_6"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="0" column="0"> + <widget class="MplGraphicsView" name="graphicsView"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>Calculate UB</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_6"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QGroupBox" name="groupBox_7"> + <property name="title"> + <string>Add Peak</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QLabel" name="label_scanNo"> + <property name="text"> + <string>Scan Number</string> + </property> + </widget> + </item> + <item row="0" column="8"> + <widget class="QLineEdit" name="lineEdit_H"> + <property name="maximumSize"> + <size> + <width>60</width> + <height>40</height> + </size> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="label_PtNo"> + <property name="text"> + <string>Pt Number</string> + </property> + </widget> + </item> + <item row="3" column="12"> + <widget class="QLineEdit" name="lineEdit_sampleQy"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maximumSize"> + <size> + <width>120</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item row="0" column="11"> + <widget class="QLabel" name="label_11"> + <property name="text"> + <string>K</string> + </property> + </widget> + </item> + <item row="0" column="13"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>L</string> + </property> + </widget> + </item> + <item row="3" column="8"> + <widget class="QLineEdit" name="lineEdit_sampleQx"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maximumSize"> + <size> + <width>120</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item row="3" column="6"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string> Q-Sample </string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QLineEdit" name="lineEdit_ptNumber"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>60</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item row="3" column="11"> + <widget class="QLabel" name="label_9"> + <property name="text"> + <string>Y</string> + </property> + </widget> + </item> + <item row="3" column="14"> + <widget class="QLineEdit" name="lineEdit_sampleQz"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maximumSize"> + <size> + <width>120</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item row="3" column="7"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>X</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit_scanNumber"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>60</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item row="0" column="12"> + <widget class="QLineEdit" name="lineEdit_K"> + <property name="maximumSize"> + <size> + <width>60</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item row="0" column="14"> + <widget class="QLineEdit" name="lineEdit_L"> + <property name="maximumSize"> + <size> + <width>60</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item row="0" column="7"> + <widget class="QLabel" name="label_12"> + <property name="text"> + <string>H</string> + </property> + </widget> + </item> + <item row="3" column="13"> + <widget class="QLabel" name="label_10"> + <property name="text"> + <string>Z</string> + </property> + </widget> + </item> + <item row="0" column="5"> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="6"> + <widget class="QLabel" name="label_31"> + <property name="text"> + <string>Miller Index</string> + </property> + </widget> + </item> + <item row="0" column="4"> + <widget class="QPushButton" name="pushButton_findPeak"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Find Peak</string> + </property> + </widget> + </item> + <item row="3" column="4"> + <widget class="QCheckBox" name="checkBox_loadHKLfromFile"> + <property name="font"> + <font> + <pointsize>9</pointsize> + </font> + </property> + <property name="text"> + <string>Load HKL from file</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <item> + <widget class="UBMatrixPeakTable" name="tableWidget_peaksCalUB"/> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_8"> + <item> + <widget class="QPushButton" name="pushButton_addPeakToCalUB"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Add Peak</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="checkBox_roundHKLInt"> + <property name="font"> + <font> + <pointsize>9</pointsize> + </font> + </property> + <property name="text"> + <string>RoundHKL to int</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_11"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_deleteUBPeak"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>200</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string>Delete</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_clearUBPeakTable"> + <property name="text"> + <string>Clear</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_7"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_13"> + <item> + <widget class="QLabel" name="label_32"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>14</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>UB Matrix</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_6"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="UBMatrixTable" name="tableWidget_ubMatrix"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>360</width> + <height>0</height> + </size> + </property> + <row> + <property name="text"> + <string/> + </property> + </row> + <row> + <property name="text"> + <string/> + </property> + </row> + <row> + <property name="text"> + <string/> + </property> + </row> + <column> + <property name="text"> + <string/> + </property> + </column> + <column> + <property name="text"> + <string/> + </property> + </column> + <column> + <property name="text"> + <string/> + </property> + </column> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_12"> + <item> + <widget class="QPushButton" name="pushButton_calUB"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>100</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string>Calculate UB</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_8"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Ignored</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_acceptUB"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>100</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string>Accept</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_9"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <spacer name="horizontalSpacer_8"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Ignored</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_9"> + <item> + <widget class="QPushButton" name="pushButton_indexUBPeaks"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>100</width> + <height>16777215</height> + </size> + </property> + <property name="text"> + <string>Index Peak</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_12"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Ignored</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_resetPeakHKLs"> + <property name="text"> + <string>Reset HKL</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_advsetup"> + <attribute name="title"> + <string>Merge Scan</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox_5"> + <property name="title"> + <string>UB Matrix</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_13"> + <item> + <widget class="UBMatrixTable" name="tableWidget_ubSiceView"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>150</height> + </size> + </property> + <row> + <property name="text"> + <string/> + </property> + </row> + <row> + <property name="text"> + <string/> + </property> + </row> + <row> + <property name="text"> + <string/> + </property> + </row> + <column> + <property name="text"> + <string/> + </property> + </column> + <column> + <property name="text"> + <string/> + </property> + </column> + <column> + <property name="text"> + <string/> + </property> + </column> + <item row="0" column="0"> + <property name="text"> + <string>1</string> + </property> + </item> + <item row="1" column="1"> + <property name="text"> + <string>1</string> + </property> + </item> + <item row="2" column="2"> + <property name="text"> + <string>1</string> + </property> + </item> + </widget> + </item> + <item> + <spacer name="verticalSpacer_13"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Ignored</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="2"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>From tab 'Caclulate UB'</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QRadioButton" name="radioButton_ubFromTab1"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="0" column="1"> + <spacer name="horizontalSpacer_9"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="0"> + <widget class="QRadioButton" name="radioButton_ubFromTab3"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QLabel" name="label_28"> + <property name="text"> + <string>From tab 'Refine UB'</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QPlainTextEdit" name="plainTextEdit_ubInput"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="3" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_15"> + <item> + <widget class="QRadioButton" name="radioButton_ubFromList"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_15"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_14"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Ignored</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_setUBSliceView"> + <property name="text"> + <string>Set</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_10"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_6"> + <property name="title"> + <string>Process Data</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_14"> + <item> + <layout class="QGridLayout" name="gridLayout_10"> + <item row="1" column="0"> + <widget class="QLabel" name="label_29"> + <property name="toolTip"> + <string><html><head/><body><p>For MDEventsWorkspace with merged runs</p><p><br/></p><p>For example:</p></body></html></string> + </property> + <property name="text"> + <string>Base Workspace Name</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_30"> + <property name="text"> + <string>Scans List</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit_listScansSliceView"/> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="pushButton_addScanSliceView"> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLineEdit" name="lineEdit_baseMergeMDName"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboBox_mergeScanFrame"> + <property name="minimumSize"> + <size> + <width>140</width> + <height>0</height> + </size> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Type of frame that the final merged scan will be in.</p></body></html></string> + </property> + <item> + <property name="text"> + <string>HKL-Space</string> + </property> + </item> + <item> + <property name="text"> + <string>Q-Sample-Space</string> + </property> + </item> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_10"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="pushButton_process4SliceView"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Process</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="ProcessTableWidget" name="tableWidget_sliceViewProgress"> + <property name="toolTip"> + <string><html><head/><body><p>? columns: </p><p><br/></p><p>1. Scan number</p><p>2. Number of Pts.</p><p>3. Status: </p><p>(a) done</p><p>(b) error with error message</p><p>(c) on-going</p><p>(d) empty as not yet</p></body></html></string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_3"> + <attribute name="title"> + <string>Refine UB</string> + </attribute> + <layout class="QGridLayout" name="gridLayout_8"> + <item row="0" column="0"> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_14"> + <item> + <widget class="QLabel" name="label_14"> + <property name="text"> + <string>Scan</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit_scanRefineUB"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_13"> + <property name="text"> + <string>Pt.</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit_ptRefineUB"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_addToRefine"> + <property name="toolTip"> + <string><html><head/><body><p>Add a data point or data points (defined by scan number and pt number) to the list to refine UB matrix; </p><p><br/></p><p>Scan number can be a list of integers;</p><p><br/></p><p>Pt number can be empty such that all Pt. of that scan will be searched for peak and then used to refine UB matrix</p></body></html></string> + </property> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_addAllRefineUB"> + <property name="text"> + <string>Add All</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_15"> + <item> + <widget class="UBMatrixTable" name="tableWidget_refinedUB"> + <row> + <property name="text"> + <string/> + </property> + </row> + <row> + <property name="text"> + <string/> + </property> + </row> + <row> + <property name="text"> + <string/> + </property> + </row> + <column> + <property name="text"> + <string/> + </property> + </column> + <column> + <property name="text"> + <string/> + </property> + </column> + <column> + <property name="text"> + <string/> + </property> + </column> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_7"/> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_10"> + <item> + <widget class="QPushButton" name="pushButton_acceptRefinedUB"> + <property name="text"> + <string>Accept</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_resetRefinedUB"> + <property name="text"> + <string>Reset</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QGridLayout" name="gridLayout_7"> + <item row="0" column="0"> + <widget class="QLabel" name="label_17"> + <property name="text"> + <string>a</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="label_19"> + <property name="text"> + <string>c</string> + </property> + </widget> + </item> + <item row="0" column="4"> + <widget class="QLabel" name="label_21"> + <property name="text"> + <string>beta</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="label_18"> + <property name="text"> + <string>b</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QLabel" name="label_20"> + <property name="text"> + <string>alpha</string> + </property> + </widget> + </item> + <item row="0" column="5"> + <widget class="QLabel" name="label_22"> + <property name="text"> + <string>gamma</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="lineEdit_bUnitCell"/> + </item> + <item row="1" column="2"> + <widget class="QLineEdit" name="lineEdit_cUnitCell"/> + </item> + <item row="1" column="3"> + <widget class="QLineEdit" name="lineEdit_alphaUnitCell"/> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="lineEdit_bError"/> + </item> + <item row="1" column="4"> + <widget class="QLineEdit" name="lineEdit_betaUnitCell"/> + </item> + <item row="1" column="0"> + <widget class="QLineEdit" name="lineEdit_aUnitCell"/> + </item> + <item row="2" column="2"> + <widget class="QLineEdit" name="lineEdit_cError"/> + </item> + <item row="2" column="0"> + <widget class="QLineEdit" name="lineEdit_aError"/> + </item> + <item row="2" column="3"> + <widget class="QLineEdit" name="lineEdit_alphaError"/> + </item> + <item row="2" column="4"> + <widget class="QLineEdit" name="lineEdit_betaError"/> + </item> + <item row="1" column="5"> + <widget class="QLineEdit" name="lineEdit_gammaUnitCell"/> + </item> + <item row="2" column="5"> + <widget class="QLineEdit" name="lineEdit_gammaError"/> + </item> + </layout> + </item> + </layout> + </item> + <item row="1" column="0"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_indexPeak"> + <attribute name="title"> + <string>Peak Integration</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_11"> + <item> + <layout class="QGridLayout" name="gridLayout_13"> + <item row="1" column="6"> + <widget class="QCheckBox" name="checkBox_adaptQBkgd"> + <property name="text"> + <string>Adaptive Q Background</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit_scanIntegratePeak"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="checkBox_cylinder"> + <property name="text"> + <string>Cylinder</string> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QLineEdit" name="lineEdit_bkgdInnerR"/> + </item> + <item row="1" column="5"> + <widget class="QLineEdit" name="lineEdit_bkgdOuterR"/> + </item> + <item row="2" column="4"> + <widget class="QLabel" name="label_36"> + <property name="text"> + <string>Percent Background</string> + </property> + </widget> + </item> + <item row="1" column="7"> + <widget class="QCheckBox" name="checkBox_integrateOnEdge"> + <property name="text"> + <string>Integrate on Edge</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Scan Number</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_34"> + <property name="text"> + <string>Peak Radius</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QLineEdit" name="lineEdit_ptNumListIntPeak"/> + </item> + <item row="2" column="7"> + <widget class="QComboBox" name="comboBox_cylinderIntOption"/> + </item> + <item row="0" column="7"> + <widget class="QPushButton" name="pushButton_integratePeak"> + <property name="text"> + <string>Integrate Peaks</string> + </property> + </widget> + </item> + <item row="2" column="5"> + <widget class="QLineEdit" name="lineEdit_cylinderBkgdPercent"/> + </item> + <item row="1" column="4"> + <widget class="QLabel" name="label_35"> + <property name="text"> + <string>Background Outer Radius</string> + </property> + </widget> + </item> + <item row="0" column="5"> + <widget class="QComboBox" name="comboBox_3"> + <property name="toolTip"> + <string><html><head/><body><p>Choose the name of the tab, in which the UB matrix is used to convert signals to HKL frame.</p></body></html></string> + </property> + <item> + <property name="text"> + <string>Calculate UB</string> + </property> + </item> + <item> + <property name="text"> + <string>Merge Scan</string> + </property> + </item> + <item> + <property name="text"> + <string>Refine UB</string> + </property> + </item> + </widget> + </item> + <item row="2" column="6"> + <widget class="QComboBox" name="comboBox_cylinderProfile"> + <item> + <property name="text"> + <string>Gaussian</string> + </property> + </item> + </widget> + </item> + <item row="2" column="2"> + <widget class="QLabel" name="label_39"> + <property name="text"> + <string>Cylinder Length</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QLabel" name="label_37"> + <property name="text"> + <string>Background Inner Radius</string> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QLineEdit" name="lineEdit_cylinderLength"/> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="label_33"> + <property name="text"> + <string>Pt Numbers</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="lineEdit_peakRadius"/> + </item> + </layout> + </item> + <item> + <widget class="IntegratePeaksTableWidget" name="tableWidget_peakIndex"/> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1292</width> + <height>25</height> + </rect> + </property> + <widget class="QMenu" name="menuFile"> + <property name="title"> + <string>File</string> + </property> + <addaction name="separator"/> + <addaction name="actionExit"/> + <addaction name="separator"/> + </widget> + <widget class="QMenu" name="menuTools"> + <property name="title"> + <string>Tools</string> + </property> + <addaction name="actionSave_Session"/> + <addaction name="actionLoad_Session"/> + <addaction name="separator"/> + </widget> + <addaction name="menuFile"/> + <addaction name="menuTools"/> + </widget> + <widget class="QStatusBar" name="statusbar"/> + <action name="actionNew"> + <property name="text"> + <string>New</string> + </property> + <property name="shortcut"> + <string>Ctrl+N</string> + </property> + </action> + <action name="actionOpen"> + <property name="text"> + <string>Open</string> + </property> + <property name="shortcut"> + <string>Ctrl+O</string> + </property> + </action> + <action name="actionSave"> + <property name="text"> + <string>Save</string> + </property> + </action> + <action name="actionLog"> + <property name="text"> + <string>Log</string> + </property> + <property name="shortcut"> + <string>Ctrl+L</string> + </property> + </action> + <action name="actionSave_Session"> + <property name="text"> + <string>Save Session</string> + </property> + <property name="shortcut"> + <string>Ctrl+Shift+S</string> + </property> + </action> + <action name="actionExit"> + <property name="text"> + <string>Exit</string> + </property> + <property name="shortcut"> + <string>Ctrl+Q</string> + </property> + </action> + <action name="actionLoad_Session"> + <property name="text"> + <string>Load Session</string> + </property> + <property name="shortcut"> + <string>Ctrl+Shift+L</string> + </property> + </action> + </widget> + <customwidgets> + <customwidget> + <class>MplGraphicsView</class> + <extends>QGraphicsView</extends> + <header>mplgraphicsview.h</header> + </customwidget> + <customwidget> + <class>UBMatrixPeakTable</class> + <extends>QTableWidget</extends> + <header>hfctables.h</header> + </customwidget> + <customwidget> + <class>UBMatrixTable</class> + <extends>QTableWidget</extends> + <header>hfctables.h</header> + </customwidget> + <customwidget> + <class>ProcessTableWidget</class> + <extends>QTableWidget</extends> + <header>hfctables.h</header> + </customwidget> + <customwidget> + <class>IntegratePeaksTableWidget</class> + <extends>QTableWidget</extends> + <header>hfctables.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/scripts/HFIR_4Circle_Reduction/NTableWidget.py b/scripts/HFIR_4Circle_Reduction/NTableWidget.py new file mode 100644 index 0000000000000000000000000000000000000000..198725f41ef1492b822dfddb572ecb7c75b0a3aa --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/NTableWidget.py @@ -0,0 +1,268 @@ +#pylint: disable=C0103,R0904 +# N(DAV)TableWidget +# + +from PyQt4 import QtGui, QtCore + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + _fromUtf8 = lambda s: s + + +class NTableWidget(QtGui.QTableWidget): + """ + NdavTableWidget inherits from QTableWidget by extending the features + for easy application. + """ + def __init__(self, parent): + """ + + :param parent: + :return: + """ + QtGui.QTableWidget.__init__(self, parent) + + self._myParent = parent + + self._myHeaderList = None + self._myColumnTypeList = None + + return + + def append_row(self, row_value_list, type_list=None): + """ + + :param row_value_list: + :return: + """ + # Check input + assert isinstance(row_value_list, list) + if type_list is not None: + assert isinstance(type_list, list) + assert len(row_value_list) == len(type_list) + else: + type_list = self._myColumnTypeList + if len(row_value_list) != self.columnCount(): + ret_msg = 'Input number of values (%d) is different from ' \ + 'column number (%d).' % (len(row_value_list), self.columnCount()) + return False, ret_msg + else: + ret_msg = '' + + # Insert new row + row_number = self.rowCount() + self.insertRow(row_number) + + # Set values + for i_col in xrange(min(len(row_value_list), self.columnCount())): + item = QtGui.QTableWidgetItem() + item.setText(_fromUtf8(str(row_value_list[i_col]))) + item.setFlags(item.flags() & ~QtCore.Qt.ItemIsEditable) + if type_list[i_col] == 'checkbox': + self.set_check_box(row_number, i_col, False) + else: + self.setItem(row_number, i_col, item) + # END-FOR(i_col) + + return True, ret_msg + + def delete_rows(self, row_number_list): + """ Delete rows + :param row_number_list: + :return: + """ + # Check and re-order row numbers + assert isinstance(row_number_list, list) + row_number_list.sort(reverse=True) + + for row_number in row_number_list: + self.removeRow(row_number) + + return + + def get_selected_rows(self): + """ + + :return: list of row numbers that are selected + """ + rows_list = list() + index_status = self._myColumnTypeList.index('checkbox') + for i_row in xrange(self.rowCount()): + is_checked = self.get_row_value(i_row)[index_status] + if is_checked: + rows_list.append(i_row) + + return rows_list + + def get_cell_value(self, row_index, col_index): + """ + + :param row_index: + :param col_index: + :return: + """ + c_type = self._myColumnTypeList[col_index] + + return_value = None + if c_type == 'checkbox': + # Check box + cell_i_j = self.cellWidget(row_index, col_index) + assert isinstance(cell_i_j, QtGui.QCheckBox) + return_value = cell_i_j.isChecked() + else: + # Regular cell + item_i_j = self.item(row_index, col_index) + assert isinstance(item_i_j, QtGui.QTableWidgetItem) + value = str(item_i_j.text()) + if c_type == 'int': + return_value = int(value) + elif c_type == 'float': + return_value = float(value) + + return return_value + + def get_row_value(self, row_index): + """ + :param row_index: + :return: list of objects + """ + if row_index < 0 or row_index >= self.rowCount(): + raise IndexError('Index of row (%d) is out of range.' % row_index) + + ret_list = list() + for i_col in xrange(len(self._myColumnTypeList)): + c_type = self._myColumnTypeList[i_col] + + if c_type == 'checkbox': + # Check box + cell_i_j = self.cellWidget(row_index, i_col) + assert isinstance(cell_i_j, QtGui.QCheckBox) + is_checked = cell_i_j.isChecked() + ret_list.append(is_checked) + else: + # Regular cell + item_i_j = self.item(row_index, i_col) + assert isinstance(item_i_j, QtGui.QTableWidgetItem) + value = str(item_i_j.text()).strip() + if len(value) > 0: + if c_type == 'int': + value = int(value) + elif c_type == 'float': + value = float(value) + else: + value = None + + ret_list.append(value) + # END-IF-ELSE + # END-FOR + + return ret_list + + def init_setup(self, column_tup_list): + """ Initial setup + :param column_tup_list: list of 2-tuple as string (column name) and string (data type) + :return: + """ + # Define column headings + num_cols = len(column_tup_list) + + # Class variables + self._myHeaderList = list() + self._myColumnTypeList = list() + + for c_tup in column_tup_list: + c_name = c_tup[0] + c_type = c_tup[1] + self._myHeaderList.append(c_name) + self._myColumnTypeList.append(c_type) + + self.setColumnCount(num_cols) + self.setHorizontalHeaderLabels(self._myHeaderList) + + return + + def init_size(self, num_rows, num_cols): + """ + + :return: + """ + self.setColumnCount(num_cols) + self.setRowCount(num_rows) + + return + + def set_check_box(self, row, col, state): + """ function to add a new select checkbox to a cell in a table row + won't add a new checkbox if one already exists + """ + # Check input + assert isinstance(state, bool) + + # Check if cellWidget exists + if self.cellWidget(row,col): + # existing: just set the value + self.cellWidget(row, col).setChecked(state) + else: + # case to add checkbox + checkbox = QtGui.QCheckBox() + checkbox.setText('') + checkbox.setChecked(state) + + # Adding a widget which will be inserted into the table cell + # then centering the checkbox within this widget which in turn, + # centers it within the table column :-) + self.setCellWidget(row, col, checkbox) + # END-IF-ELSE + + return + + def set_value_cell(self, row, col, value=''): + """ + Set value to a cell with integer, float or string + :param row: + :param col: + :param value: + :return: + """ + # Check + if row < 0 or row >= self.rowCount() or col < 0 or col >= self.columnCount(): + raise IndexError('Input row number or column number is out of range.') + + # Init cell + cell_item = QtGui.QTableWidgetItem() + cell_item.setText(_fromUtf8(str(value))) + cell_item.setFlags(cell_item.flags() & ~QtCore.Qt.ItemIsEditable) + + self.setItem(row, col, cell_item) + + return + + def update_cell_value(self, row, col, value): + """ + + :param row: + :param col: + :param value: + :return: + """ + cell_item = self.item(row, col) + cell_widget = self.cellWidget(row, col) + + if cell_item is not None and cell_widget is None: + # TableWidgetItem + assert isinstance(cell_item, QtGui.QTableWidgetItem) + if isinstance(value, float): + cell_item.setText(_fromUtf8('%.7f' % value)) + else: + cell_item.setText(_fromUtf8(str(value))) + elif cell_item is None and cell_widget is not None: + # TableCellWidget + if isinstance(cell_item, QtGui.QCheckBox) is True: + cell_item.setChecked(value) + else: + raise TypeError('Cell of type %s is not supported.' % str(type(cell_item))) + else: + raise TypeError('Table cell (%d, %d) is in an unsupported situation!' % (row, col)) + + return diff --git a/scripts/HFIR_4Circle_Reduction/__init__.py b/scripts/HFIR_4Circle_Reduction/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/scripts/HFIR_4Circle_Reduction/fourcircle_utility.py b/scripts/HFIR_4Circle_Reduction/fourcircle_utility.py new file mode 100644 index 0000000000000000000000000000000000000000..35dc1fe387e6bd1597eed0a9eb6c52f8c08811f8 --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/fourcircle_utility.py @@ -0,0 +1,188 @@ +#pylint: disable=W0633,too-many-branches +__author__ = 'wzz' + +import os +import urllib2 +import socket + + +def check_url(url, read_lines=False): + """ Check whether a URL is valid + :param url: + :return: boolean, error message + """ + lines = None + try: + # Access URL + url_stream = urllib2.urlopen(url, timeout=2) + + # Read lines + if read_lines is True: + lines = url_stream.readlines() + except urllib2.URLError as url_error: + url_stream = url_error + except socket.timeout: + return False, 'Time out. Try again!' + + # Return result + if url_stream.code in (200, 401): + url_good = True + else: + url_good = False + + # Close connect + url_stream.close() + + # Return + if read_lines is True: + return url_good, lines + if url_good is False: + error_message = 'Unable to access %s. Check internet access. Code %d' % (url, url_stream.code) + else: + error_message = '' + + return url_good, error_message + + +def get_scans_list(server_url, exp_no, return_list=False): + """ Get list of scans under one experiment + :param server_url: + :param exp_no: + :return: message + """ + if server_url.endswith('/') is False: + server_url = '%s/' % server_url + data_dir_url = '%sexp%d/Datafiles' % (server_url, exp_no) + + does_exist, raw_lines = check_url(data_dir_url, read_lines=True) + if does_exist is False: + return "Experiment %d's URL %s cannot be found." % (exp_no, data_dir_url) + + # Scan through the index page + scan_list = [] + header = 'HB3A_exp%04d_scan' % exp_no + for line in raw_lines: + if line.count(header) > 0: + # try to find file HB3A_exp0123_scan6789.dat + term = line.split(header)[1].split('.dat')[0] + scan = int(term) + # check + if '%04d' % scan == term: + scan_list.append(scan) + # END_FOR + scan_list = sorted(scan_list) + if return_list is True: + return scan_list + + message = 'Experiment %d: Scan from %d to %d' % (exp_no, scan_list[0], scan_list[-1]) + + return message + + +def get_scans_list_local_disk(local_dir, exp_no): + """ Get scans from a specified directory on local disk + :param local_dir: + :param exp_no: + :return: + """ + scan_list = [] + + file_names = os.listdir(local_dir) + header = 'HB3A_exp%04d_scan' % exp_no + for name in file_names: + if name.count(header) > 0: + scan = int(name.split(header)[1].split('.dat')[0]) + scan_list.append(scan) + + scan_list = sorted(scan_list) + + if len(scan_list) == 0: + message = 'Experiment %d: No scan can be found.' % exp_no + else: + message = 'Experiment %d: Scan from %d to %d ' % (exp_no, scan_list[0], scan_list[-1]) + num_skip_scans = scan_list[-1] - scan_list[0] + 1 - len(scan_list) + if num_skip_scans > 0: + message += 'with %d ' % num_skip_scans + else: + message += 'without ' + message += 'missing scans.' + + return message + + +def parse_int_array(int_array_str): + """ Validate whether the string can be divided into integer strings. + Allowed: a, b, c-d, e, f + :param int_array_str: + :return: + """ + int_array_str = str(int_array_str) + if int_array_str == "": + return True, [] + + # Split by "," + term_level_0 = int_array_str.split(",") + integer_list = [] + + # For each term + err_msg = "" + ret_status = True + + for level0_term in term_level_0: + level0_term = level0_term.strip() + + # split upon dash - + num_dashes = level0_term.count("-") + if num_dashes == 0: + # one integer + value_str = level0_term + try: + int_value = int(value_str) + if str(int_value) != value_str: + ret_status = False + err_msg = "Contains non-integer string %s." % value_str + except ValueError: + ret_status = False + err_msg = "String %s is not an integer." % (value_str) + else: + integer_list.append(int_value) + + elif num_dashes == 1: + # Integer range + two_terms = level0_term.split("-") + temp_list = [] + for i in xrange(2): + value_str = two_terms[i] + try: + int_value = int(value_str) + if str(int_value) != value_str: + ret_status = False + err_msg = "Contains non-integer string %s." % (value_str) + except ValueError: + ret_status = False + err_msg = "String %s is not an integer." % (value_str) + else: + temp_list.append(int_value) + + # break loop + if ret_status is False: + break + # END_FOR(i) + integer_list.extend(range(temp_list[0], temp_list[1]+1)) + + else: + # Undefined situation + ret_status = False + err_msg = "Term %s contains more than 1 dash." % level0_term + # END-IF-ELSE + + # break loop if something is wrong + if ret_status is False: + break + # END-FOR(level0_term) + + # Return with false + if ret_status is False: + return False, err_msg + + return True, integer_list diff --git a/scripts/HFIR_4Circle_Reduction/guiutility.py b/scripts/HFIR_4Circle_Reduction/guiutility.py new file mode 100644 index 0000000000000000000000000000000000000000..79237a5729470b372017478b24a0564159d67fb1 --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/guiutility.py @@ -0,0 +1,164 @@ +# +# GUI Utility Methods +# +from PyQt4 import QtGui + + +def parse_float_array(array_str): + """ Parse a string to an array of float + :param array_str: + :return: boolean, list of floats/error message + """ + print array_str + assert isinstance(array_str, str) + array_str = array_str.replace(',', ' ') + array_str = array_str.replace('\n', ' ') + array_str = array_str.replace('\t ', ' ') + array_str = array_str.strip() + print '[DB] After processing: ', array_str + + float_str_list = array_str.split() + float_list = list() + for float_str in float_str_list: + try: + value = float(float_str) + except ValueError as value_error: + return False, 'Unable to parse %s due to %s.' % (float_str, str(value_error)) + else: + float_list.append(value) + # END-FOR + + return True, float_list + + +def parse_integer_list(array_str): + """ Parse a string to an array of integer separated by ',' + also, the format as 'a-b' is supported too + :param array_str: + :return: boolean, list of floats/error message + """ + assert isinstance(array_str, str) + array_str = array_str.replace(' ', '') + array_str = array_str.replace('\n', '') + array_str = array_str.replace('\t ', '') + + int_str_list = array_str.split(',') + integer_list = list() + for int_str in int_str_list: + + try: + int_value = int(int_str) + integer_list.append(int_value) + except ValueError: + num_dash = int_str.count('-') + if num_dash == 1: + terms = int_str.split('-') + try: + start_value = int(terms[0]) + end_value = int(terms[1]) + except ValueError: + raise RuntimeError('Unable to parse %s due to value error' % int_str) + elif num_dash == 2 and int_str.startswith('-'): + terms = int_str[1:].split('-') + try: + start_value = int(terms[0])*-1 + end_value = int(terms[1]) + except ValueError: + raise RuntimeError('Unable to parse %s due to value error' % int_str) + elif num_dash == 3: + terms = int_str.split('-') + try: + start_value = -1*int(terms[1]) + end_value = -1*int(terms[3]) + except ValueError: + raise RuntimeError('Unable to parse %s due to value error' % int_str) + except IndexError: + raise RuntimeError('Unable to parse %s due to value error' % int_str) + else: + raise RuntimeError('Unable to parse %s due to value error' % int_str) + + integer_list.extend(xrange(start_value, end_value+1)) + # END-FOR + + return integer_list + + +def parse_float_editors(line_edits): + """ + :param line_edits: + :return: (True, list of floats); (False, error message) + """ + # Set flag + return_single_value = False + + if isinstance(line_edits, QtGui.QLineEdit) is True: + line_edit_list = [line_edits] + return_single_value = True + elif isinstance(line_edits, list) is True: + line_edit_list = line_edits + else: + raise RuntimeError('Input is not LineEdit or list of LineEdit.') + + error_message = '' + float_list = [] + + for line_edit in line_edit_list: + assert isinstance(line_edit, QtGui.QLineEdit) + try: + str_value = str(line_edit.text()).strip() + float_value = float(str_value) + except ValueError as value_err: + error_message += 'Unable to parse to integer. %s\n' % (str(value_err)) + else: + float_list.append(float_value) + # END-TRY + # END-FOR + + if len(error_message) > 0: + return False, error_message + elif return_single_value is True: + return True, float_list[0] + + return True, float_list + + +def parse_integers_editors(line_edits): + """ + :param line_edits: + :return: (True, list of integers); (False, error message) + """ + # Set flag + return_single_value = False + + if isinstance(line_edits, QtGui.QLineEdit) is True: + line_edit_list = [line_edits] + return_single_value = True + elif isinstance(line_edits, list) is True: + line_edit_list = line_edits + else: + raise RuntimeError('Input is not LineEdit or list of LineEdit.') + + error_message = '' + integer_list = [] + + for line_edit in line_edit_list: + assert isinstance(line_edit, QtGui.QLineEdit) + try: + str_value = str(line_edit.text()).strip() + int_value = int(str_value) + except ValueError as value_err: + error_message += 'Unable to parse to integer. %s\n' % (str(value_err)) + else: + if str_value != '%d' % int_value: + error_message += 'Value %s is not a proper integer.\n' % str_value + else: + integer_list.append(int_value) + # END-TRY + # END-FOR + + if len(error_message) > 0: + return False, error_message + elif return_single_value is True: + return True, integer_list[0] + + return True, integer_list diff --git a/scripts/HFIR_4Circle_Reduction/hfctables.py b/scripts/HFIR_4Circle_Reduction/hfctables.py new file mode 100644 index 0000000000000000000000000000000000000000..dba8cb64a3aefb4adff82b540655251d9fe41311 --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/hfctables.py @@ -0,0 +1,385 @@ +#pylint: disable=W0403,C0103,R0901,R0904 +import numpy +import NTableWidget as tableBase + +# UB peak information table +Peak_Integration_Table_Setup = [('Scan', 'int'), + ('Pt', 'int'), + ('H', 'float'), + ('K', 'float'), + ('L', 'float'), + ('Q_x', 'float'), + ('Q_y', 'float'), + ('Q_z', 'float'), + ('Intensity', 'float')] + + +class IntegratePeaksTableWidget(tableBase.NTableWidget): + """ + Extended table widget for peak integration + """ + def __init__(self, parent): + """ + :param parent: + """ + tableBase.NTableWidget.__init__(self, parent) + + return + + def setup(self): + """ + Init setup + :return: + """ + self.init_setup(Peak_Integration_Table_Setup) + + return + + +class UBMatrixTable(tableBase.NTableWidget): + """ + Extended table for UB matrix + """ + def __init__(self, parent): + """ + + :param parent: + :return: + """ + tableBase.NTableWidget.__init__(self, parent) + + # Matrix + self._matrix = numpy.ndarray((3, 3), float) + for i in xrange(3): + for j in xrange(3): + self._matrix[i][j] = 0. + + return + + def _set_to_table(self): + """ + Set values in holder '_matrix' to TableWidget + :return: + """ + for i_row in xrange(3): + for j_col in xrange(3): + self.update_cell_value(i_row, j_col, self._matrix[i_row][j_col]) + + return + + def get_matrix(self): + """ + Get the copy of the matrix + :return: + """ + # print '[DB] MatrixTable: _Matrix = ', self._matrix + return self._matrix.copy() + + def set_from_list(self, element_array): + """ + Set table value including holder and QTable from a 1D numpy array + :param element_array: + :return: + """ + # Check + assert isinstance(element_array, list) + assert len(element_array) == 9 + + # Set value + i_array = 0 + for i in xrange(3): + for j in xrange(3): + self._matrix[i][j] = element_array[i_array] + i_array += 1 + + # Set to table + self._set_to_table() + + return + + def set_from_matrix(self, matrix): + """ + Set value to both holder and QTable from a numpy 3 x 3 matrix + :param matrix: + :return: + """ + # Check + assert isinstance(matrix, numpy.ndarray) + assert matrix.shape == (3, 3) + + for i in xrange(3): + for j in xrange(3): + self._matrix[i][j] = matrix[i][j] + + self._set_to_table() + + return + + def setup(self): + """ + Init setup + :return: + """ + # self.init_size(3, 3) + + for i in xrange(3): + for j in xrange(3): + self.set_value_cell(i, j) + + self._set_to_table() + + return + + +# UB peak information table +UB_Peak_Table_Setup = [('Scan', 'int'), + ('Pt', 'int'), + ('H', 'float'), + ('K', 'float'), + ('L', 'float'), + ('Q_x', 'float'), + ('Q_y', 'float'), + ('Q_z', 'float'), + ('Selected', 'checkbox'), + ('m1', 'float'), + ('Error', 'float')] + + +class UBMatrixPeakTable(tableBase.NTableWidget): + """ + Extended table for peaks used to calculate UB matrix + """ + def __init__(self, parent): + """ + + :param parent: + :return: + """ + tableBase.NTableWidget.__init__(self, parent) + + return + + def get_exp_info(self, row_index): + """ + Get experiment information from a row + :return: scan number, pt number + """ + assert isinstance(row_index, int) + + scan_number = self.get_cell_value(row_index, 0) + assert isinstance(scan_number, int) + pt_number = self.get_cell_value(row_index, 1) + assert isinstance(pt_number, int) + + return scan_number, pt_number + + def get_hkl(self, row_index): + """ + Get reflection's miller index + :param row_index: + :return: + """ + assert isinstance(row_index, int) + + m_h = self.get_cell_value(row_index, 2) + m_k = self.get_cell_value(row_index, 3) + m_l = self.get_cell_value(row_index, 4) + + assert isinstance(m_h, float) + assert isinstance(m_k, float) + assert isinstance(m_l, float) + + return m_h, m_k, m_l + + def get_scan_pt(self, row_number): + """ + Get Scan and Pt from a row + :param row_number: + :return: + """ + scan_number = self.get_cell_value(row_number, 0) + pt_number = self.get_cell_value(row_number, 1) + + return scan_number, pt_number + + def is_selected(self, row_index): + """ + + :return: + """ + if row_index < 0 or row_index >= self.rowCount(): + raise IndexError('Input row number %d is out of range [0, %d)' % (row_index, self.rowCount())) + + col_index = UB_Peak_Table_Setup.index(('Selected', 'checkbox')) + + return self.get_cell_value(row_index, col_index) + + def setup(self): + """ + Init setup + :return: + """ + self.init_setup(UB_Peak_Table_Setup) + + return + + def set_hkl(self, i_row, hkl, error=None): + """ + Set HKL to table + :param irow: + :param hkl: + """ + # Check + assert isinstance(i_row, int) + assert isinstance(hkl, list) + + i_col_h = UB_Peak_Table_Setup.index(('H', 'float')) + i_col_k = UB_Peak_Table_Setup.index(('K', 'float')) + i_col_l = UB_Peak_Table_Setup.index(('L', 'float')) + + self.update_cell_value(i_row, i_col_h, hkl[0]) + self.update_cell_value(i_row, i_col_k, hkl[1]) + self.update_cell_value(i_row, i_col_l, hkl[2]) + + if error is not None: + i_col_error = UB_Peak_Table_Setup.index(('Error', 'float')) + self.update_cell_value(i_row, i_col_error, error) + + return + + def update_hkl(self, i_row, h, k, l): + """ Update HKL value + """ + self.update_cell_value(i_row, 2, h) + self.update_cell_value(i_row, 3, k) + self.update_cell_value(i_row, 4, l) + + return + + +# Processing status table +Process_Table_Setup = [('Scan', 'int'), + ('Number Pt', 'int'), + ('Status', 'str'), + ('Merged Workspace', 'str'), + ('Group Name', 'str'), + ('Select', 'checkbox')] + + +class ProcessTableWidget(tableBase.NTableWidget): + """ + Extended table for peaks used to calculate UB matrix + """ + def __init__(self, parent): + """ + + :param parent: + :return: + """ + tableBase.NTableWidget.__init__(self, parent) + + return + + def append_scans(self, scans): + """ Append rows + :param scans: + :return: + """ + # Check + assert isinstance(scans, list) + + # Append rows + for scan in scans: + row_value_list = [scan, 0, 'In Queue', '', '', False] + status, err = self.append_row(row_value_list) + if status is False: + raise RuntimeError(err) + + return + + def setup(self): + """ + Init setup + :return: + """ + self.init_setup(Process_Table_Setup) + + return + + def set_scan_pt(self, scan_no, pt_list): + """ + :param scan_no: + :param pt_list: + :return: + """ + # Check + assert isinstance(scan_no, int) + + num_rows = self.rowCount() + set_done = False + for i_row in xrange(num_rows): + tmp_scan_no = self.get_cell_value(i_row, 0) + if scan_no == tmp_scan_no: + self.update_cell_value(i_row, 1, len(pt_list)) + set_done = True + break + # END-FOR + + if set_done is False: + return 'Unable to find scan %d in table.' % scan_no + + return '' + + def set_status(self, scan_no, status): + """ + Set the status for merging scan to QTable + :param status: + :return: + """ + # Check + assert isinstance(scan_no, int) + + num_rows = self.rowCount() + set_done = False + for i_row in xrange(num_rows): + tmp_scan_no = self.get_cell_value(i_row, 0) + if scan_no == tmp_scan_no: + self.update_cell_value(i_row, 2, status) + set_done = True + break + # END-FOR + + if set_done is False: + return 'Unable to find scan %d in table.' % scan_no + + return '' + + def set_ws_names(self, scan_num, merged_md_name, ws_group_name): + """ + Set the output workspace and workspace group's names to QTable + :param merged_md_name: + :param ws_group_name: + :return: + """ + # Check + assert isinstance(scan_num, int) + assert isinstance(merged_md_name, str) or merged_md_name is None + assert isinstance(ws_group_name, str) or ws_group_name is None + + num_rows = self.rowCount() + set_done = False + for i_row in xrange(num_rows): + tmp_scan_no = self.get_cell_value(i_row, 0) + if scan_num == tmp_scan_no: + if merged_md_name is not None: + self.update_cell_value(i_row, 3, merged_md_name) + if ws_group_name is not None: + self.update_cell_value(i_row, 4, ws_group_name) + set_done = True + break + # END-FOR + + if set_done is False: + return 'Unable to find scan %d in table.' % scan_num + + return diff --git a/scripts/HFIR_4Circle_Reduction/mplgraphicsview.py b/scripts/HFIR_4Circle_Reduction/mplgraphicsview.py new file mode 100644 index 0000000000000000000000000000000000000000..152ffc29c4775c6f98b8b67befe0554a36d66032 --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/mplgraphicsview.py @@ -0,0 +1,1255 @@ +#pylint: disable=invalid-name,too-many-public-methods,too-many-arguments,non-parent-init-called,R0902,too-many-branches,C0302 +import os +import numpy as np + +from PyQt4 import QtGui + +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar2 +from matplotlib.figure import Figure +import matplotlib.image + +MplLineStyles = ['-' , '--' , '-.' , ':' , 'None' , ' ' , ''] +MplLineMarkers = [ + ". (point )", + "* (star )", + "x (x )", + "o (circle )", + "s (square )", + "D (diamond )", + ", (pixel )", + "v (triangle_down )", + "^ (triangle_up )", + "< (triangle_left )", + "> (triangle_right)", + "1 (tri_down )", + "2 (tri_up )", + "3 (tri_left )", + "4 (tri_right )", + "8 (octagon )", + "p (pentagon )", + "h (hexagon1 )", + "H (hexagon2 )", + "+ (plus )", + "d (thin_diamond )", + "| (vline )", + "_ (hline )", + "None (nothing )"] + +# Note: in colors, "white" is removed +MplBasicColors = [ + "black", + "red", + "blue", + "green", + "cyan", + "magenta", + "yellow"] + + +class IndicatorManager(object): + """ Manager for all indicator lines + """ + def __init__(self): + """ + + :return: + """ + # Auto color index + self._colorIndex = 0 + # Auto line ID + self._autoLineID = 1 + + self._lineManager = dict() + self._canvasLineKeyDict = dict() + self._indicatorTypeDict = dict() # value: 0 (horizontal), 1 (vertical), 2 (2-way) + + return + + def add_2way_indicator(self, x, x_min, x_max, y, y_min, y_max, color): + """ + + :param x: + :param x_min: + :param x_max: + :param y: + :param y_min: + :param y_max: + :param color: + :return: + """ + # Set up indicator ID + this_id = str(self._autoLineID) + self._autoLineID += 1 + + # Set up vectors + vec_x_horizontal = np.array([x_min, x_max]) + vec_y_horizontal = np.array([y, y]) + + vec_x_vertical = np.array([x, x]) + vec_y_vertical = np.array([y_min, y_max]) + + # + self._lineManager[this_id] = [vec_x_horizontal, vec_y_horizontal, vec_x_vertical, vec_y_vertical, color] + self._indicatorTypeDict[this_id] = 2 + + return this_id + + def add_horizontal_indicator(self, y, x_min, x_max, color): + """ + Add a horizontal indicator + :param y: + :param x_min: + :param x_max: + :param color: + :return: + """ + # Get ID + this_id = str(self._autoLineID) + self._autoLineID += 1 + + # + vec_x = np.array([x_min, x_max]) + vec_y = np.array([y, y]) + + # + self._lineManager[this_id] = [vec_x, vec_y, color] + self._indicatorTypeDict[this_id] = 0 + + return this_id + + def add_vertical_indicator(self, x, y_min, y_max, color): + """ + Add a vertical indicator to data structure + :return: indicator ID + """ + # Get ID + this_id = str(self._autoLineID) + self._autoLineID += 1 + + # + vec_x = np.array([x, x]) + vec_y = np.array([y_min, y_max]) + + # + self._lineManager[this_id] = [vec_x, vec_y, color] + self._indicatorTypeDict[this_id] = 1 + + return this_id + + def get_canvas_line_index(self, my_id): + """ + + :param my_id: + :return: + """ + assert isinstance(my_id, str) + + if my_id not in self._canvasLineKeyDict: + raise RuntimeError('Indicator ID %s cannot be found. Current keys are %s.' % ( + my_id, str(sorted(self._canvasLineKeyDict.keys())) + )) + return self._canvasLineKeyDict[my_id] + + def get_line_type(self, my_id): + """ + + :param my_id: + :return: + """ + return self._indicatorTypeDict[my_id] + + def get_2way_data(self, line_id): + """ + + :param line_id: + :return: + """ + assert self._indicatorTypeDict.has_key(line_id) + assert self._indicatorTypeDict[line_id] == 2 + + vec_set = [self._lineManager[line_id][0:2], self._lineManager[line_id][2:4]] + + return vec_set + + def get_data(self, line_id): + """ + Get line's vector x and vector y + :param line_id: + :return: + """ + return self._lineManager[line_id][0], self._lineManager[line_id][1] + + def get_line_style(self, line_id=None): + """ + + :param line_id: + :return: + """ + assert isinstance(line_id, None) + return '--' + + def get_live_indicator_ids(self): + """ + + :return: + """ + return sorted(self._lineManager.keys()) + + def get_marker(self): + """ + Get the marker a line + :param line_id: + :return: + """ + return 'o' + + def get_next_color(self): + """ + Get next color by auto color index + :return: string as color + """ + next_color = MplBasicColors[self._colorIndex] + + # Advance and possibly reset color scheme + self._colorIndex += 1 + if self._colorIndex == len(MplBasicColors): + self._colorIndex = 0 + + return next_color + + def set_canvas_line_index(self, my_id, canvas_line_index): + """ + + :param my_id: + :param canvas_line_index: + :return: + """ + self._canvasLineKeyDict[my_id] = canvas_line_index + + def shift(self, my_id, dx, dy): + """ + + :param my_id: + :param dx: + :param dy: + :return: + """ + print self._lineManager[my_id][0] + + if self._indicatorTypeDict[my_id] == 0: + # horizontal + self._lineManager[my_id][1] += dy + + elif self._indicatorTypeDict[my_id] == 1: + # vertical + self._lineManager[my_id][0] += dx + + elif self._indicatorTypeDict[my_id] == 2: + # 2-way + self._lineManager[my_id][2] += dx + self._lineManager[my_id][1] += dy + + else: + raise RuntimeError('Unsupported indicator of type %d' % self._indicatorTypeDict[my_id]) + + return + + def update_indicators_range(self, x_range, y_range): + """ + Update indicator's range + :param x_range: + :param y_range: + :return: + """ + for i_id in self._lineManager.keys(): + # NEXT - Need a new flag for direction of the indicating line, vertical or horizontal + if True: + self._lineManager[i_id][1][0] = y_range[0] + self._lineManager[i_id][1][-1] = y_range[1] + else: + self._lineManager[i_id][0][0] = x_range[0] + self._lineManager[i_id][0][-1] = x_range[1] + + return + + +class MplGraphicsView(QtGui.QWidget): + """ A combined graphics view including matplotlib canvas and + a navigation tool bar + + Note: Merged with HFIR_Powder_Reduction.MplFigureCAnvas + """ + def __init__(self, parent): + """ Initialization + """ + # Initialize parent + QtGui.QWidget.__init__(self, parent) + + # set up canvas + self._myCanvas = Qt4MplCanvas(self) + self._myToolBar = MyNavigationToolbar(self, self._myCanvas) + + # set up layout + self._vBox = QtGui.QVBoxLayout(self) + self._vBox.addWidget(self._myCanvas) + self._vBox.addWidget(self._myToolBar) + + # auto line's maker+color list + self._myLineMarkerColorList = [] + self._myLineMarkerColorIndex = 0 + self.setAutoLineMarkerColorCombo() + + # Declaration of class variables + self._indicatorKey = None + + # Indicator manager + self._myIndicatorsManager = IndicatorManager() + + return + + def add_line_set(self, vec_set, color, marker, line_style, line_width): + """ Add a set of line and manage together + :param vec_set: + :param color: + :param marker: + :param line_style: + :param line_width: + :return: + """ + key_list = list() + for vec_x, vec_y in vec_set: + temp_key = self._myCanvas.add_plot_1d(vec_x, vec_y, color=color, marker=marker, + line_style=line_style, line_width=line_width) + assert isinstance(temp_key, int) + assert temp_key >= 0 + key_list.append(temp_key) + + return key_list + + def add_plot_1d(self, vec_x, vec_y, y_err=None, color=None, label="", x_label=None, y_label=None, + marker=None, line_style=None, line_width=1): + """ Add a new plot + """ + line_key = self._myCanvas.add_plot_1d(vec_x, vec_y, y_err, color, label, x_label, y_label, marker, line_style, + line_width) + + return line_key + + def add_plot_1d_right(self, vec_x, vec_y, color=None, label='', marker=None, line_style=None, line_width=1): + """ + Add 1 line (1-d plot) to right axis + :param vec_x: + :param vec_y: + :param color: + :param label: + :param marker: + :param line_style: + :param line_width: + :return: + """ + line_key = self._myCanvas.add_1d_plot_right(vec_x, vec_y, label=label, + color=color, marker=marker, + linestyle=line_style, linewidth=line_width) + + return line_key + + def add_2way_indicator(self, x=None, y=None, color=None, master_line=None): + """ Add a 2-way indicator following an existing line? + :param x: + :param y: + :param color: + :return: + """ + if master_line is not None: + raise RuntimeError('Implement how to use master_line ASAP.') + + x_min, x_max = self._myCanvas.getXLimit() + if x is None: + x = (x_min + x_max) * 0.5 + else: + assert isinstance(x, float) + + y_min, y_max = self._myCanvas.getYLimit() + if y is None: + y = (y_min + y_max) * 0.5 + else: + assert isinstance(y, float) + + if color is None: + color = self._myIndicatorsManager.get_next_color() + else: + assert isinstance(color, str) + + my_id = self._myIndicatorsManager.add_2way_indicator(x, x_min, x_max, + y, y_min, y_max, + color) + vec_set = self._myIndicatorsManager.get_2way_data(my_id) + + canvas_line_index = self.add_line_set(vec_set, color=color, + marker=self._myIndicatorsManager.get_marker(), + line_style=self._myIndicatorsManager.get_line_style(), + line_width=1) + self._myIndicatorsManager.set_canvas_line_index(my_id, canvas_line_index) + + return my_id + + def add_horizontal_indicator(self, y=None, color=None): + """ Add an indicator line + """ + # Default + if y is None: + y_min, y_max = self._myCanvas.getYLimit() + y = (y_min + y_max) * 0.5 + else: + assert isinstance(y, float) + + x_min, x_max = self._myCanvas.getXLimit() + + # For color + if color is None: + color = self._myIndicatorsManager.get_next_color() + else: + assert isinstance(color, str) + + # Form + my_id = self._myIndicatorsManager.add_horizontal_indicator(y, x_min, x_max, color) + vec_x, vec_y = self._myIndicatorsManager.get_data(my_id) + + canvas_line_index = self._myCanvas.add_plot_1d(vec_x=vec_x, vec_y=vec_y, + color=color, marker=self._myIndicatorsManager.get_marker(), + line_style=self._myIndicatorsManager.get_line_style(), + line_width=1) + + self._myIndicatorsManager.set_canvas_line_index(my_id, canvas_line_index) + + return my_id + + def add_vertical_indicator(self, x=None, color=None): + """ + Add a vertical indicator line + :param x: None as the automatic mode using default from middle of canvas + :param color: None as the automatic mode using default + :return: indicator ID + """ + # For indicator line's position + if x is None: + x_min, x_max = self._myCanvas.getXLimit() + x = (x_min + x_max) * 0.5 + else: + assert isinstance(x, float) + + y_min, y_max = self._myCanvas.getYLimit() + + # For color + if color is None: + color = self._myIndicatorsManager.get_next_color() + else: + assert isinstance(color, str) + + # Form + my_id = self._myIndicatorsManager.add_vertical_indicator(x, y_min, y_max, color) + vec_x, vec_y = self._myIndicatorsManager.get_data(my_id) + + canvas_line_index = self._myCanvas.add_plot_1d(vec_x=vec_x, vec_y=vec_y, + color=color, marker=self._myIndicatorsManager.get_marker(), + line_style=self._myIndicatorsManager.get_line_style(), + line_width=1) + + self._myIndicatorsManager.set_canvas_line_index(my_id, canvas_line_index) + + return my_id + + def add_plot_2d(self, array2d, x_min, x_max, y_min, y_max, hold_prev_image=True, y_tick_label=None): + """ + Add a 2D image to canvas + :param array2d: numpy 2D array + :param x_min: + :param x_max: + :param y_min: + :param y_max: + :param hold_prev_image: + :param y_tick_label: + :return: + """ + self._myCanvas.addPlot2D(array2d, x_min, x_max, y_min, y_max, hold_prev_image, y_tick_label) + + return + + + def addImage(self, imagefilename): + """ Add an image by file + """ + # check + if os.path.exists(imagefilename) is False: + raise NotImplementedError("Image file %s does not exist." % (imagefilename)) + + self._myCanvas.addImage(imagefilename) + + return + + def clear_all_lines(self): + """ + """ + self._myCanvas.clear_all_1d_plots() + + def clear_canvas(self): + """ Clear canvas + """ + return self._myCanvas.clear_canvas() + + def draw(self): + """ Draw to commit the change + """ + return self._myCanvas.draw() + + def evt_view_updated(self): + """ Event handling as canvas size updated + :return: + """ + # update the indicator + new_x_range = self.getXLimit() + new_y_range = self.getYLimit() + + self._myIndicatorsManager.update_indicators_range(new_x_range, new_y_range) + for indicator_key in self._myIndicatorsManager.get_live_indicator_ids(): + canvas_line_id = self._myIndicatorsManager.get_canvas_line_index(indicator_key) + data_x, data_y = self._myIndicatorsManager.get_data(indicator_key) + self.updateLine(canvas_line_id, data_x, data_y) + # END-FOR + + return + + def getPlot(self): + """ + """ + return self._myCanvas.getPlot() + + def getLastPlotIndexKey(self): + """ Get ... + """ + return self._myCanvas.getLastPlotIndexKey() + + def getXLimit(self): + """ Get limit of Y-axis + """ + return self._myCanvas.getXLimit() + + def getYLimit(self): + """ Get limit of Y-axis + """ + return self._myCanvas.getYLimit() + + def move_indicator(self, line_id, dx, dy): + """ + Move the indicator line in horizontal + :param line_id: + :param dx: + :return: + """ + # Shift value + self._myIndicatorsManager.shift(line_id, dx=dx, dy=dy) + + # apply to plot on canvas + if self._myIndicatorsManager.get_line_type(line_id) < 2: + # horizontal or vertical + canvas_line_index = self._myIndicatorsManager.get_canvas_line_index(line_id) + vec_x, vec_y = self._myIndicatorsManager.get_data(line_id) + self._myCanvas.updateLine(ikey=canvas_line_index, vecx=vec_x, vecy=vec_y) + else: + # 2-way + canvas_line_index_h, canvas_line_index_v = self._myIndicatorsManager.get_canvas_line_index(line_id) + h_vec_set, v_vec_set = self._myIndicatorsManager.get_2way_data(line_id) + + self._myCanvas.updateLine(ikey=canvas_line_index_h, vecx=h_vec_set[0], vecy=h_vec_set[1]) + self._myCanvas.updateLine(ikey=canvas_line_index_v, vecx=v_vec_set[0], vecy=v_vec_set[1]) + + return + + def remove_indicator(self, indicator_key): + """ Remove indicator line + :param indicator_key: + :return: + """ + # + plot_id = self._myIndicatorsManager.get_canvas_line_index(indicator_key) + self._myCanvas.remove_plot_1d(plot_id) + + return + + def removePlot(self, ikey): + """ + """ + return self._myCanvas.remove_plot_1d(ikey) + + def updateLine(self, ikey, vecx, vecy, linestyle=None, linecolor=None, marker=None, markercolor=None): + """ + """ + return self._myCanvas.updateLine(ikey, vecx, vecy, linestyle, linecolor, marker, markercolor) + + def update_indicator(self, i_key, color): + """ + Update indicator with new color + :param i_key: + :param vec_x: + :param vec_y: + :param color: + :return: + """ + if self._myIndicatorsManager.get_line_type(i_key) < 2: + # horizontal or vertical + canvas_line_index = self._myIndicatorsManager.get_canvas_line_index(i_key) + self._myCanvas.updateLine(ikey=canvas_line_index, vecx=None, vecy=None, linecolor=color) + else: + # 2-way + canvas_line_index_h, canvas_line_index_v = self._myIndicatorsManager.get_canvas_line_index(i_key) + # h_vec_set, v_vec_set = self._myIndicatorsManager.get_2way_data(i_key) + + self._myCanvas.updateLine(ikey=canvas_line_index_h, vecx=None, vecy=None, linecolor=color) + self._myCanvas.updateLine(ikey=canvas_line_index_v, vecx=None, vecy=None, linecolor=color) + + return + + def get_indicator_position(self, indicator_key): + """ Get position (x or y) of the indicator + :return: + """ + # NEXT - Consider a better and more consistent return + vec_x, vec_y = self._myIndicatorsManager.get_data(indicator_key) + if vec_x[0] == vec_x[1]: + return vec_x[0] + + return vec_y[0] + + def getLineStyleList(self): + """ + """ + return MplLineStyles + + def getLineMarkerList(self): + """ + """ + return MplLineMarkers + + def getLineBasicColorList(self): + """ + """ + return MplBasicColors + + def getDefaultColorMarkerComboList(self): + """ Get a list of line/marker color and marker style combination + as default to add more and more line to plot + """ + return self._myCanvas.getDefaultColorMarkerComboList() + + def getNextLineMarkerColorCombo(self): + """ As auto line's marker and color combo list is used, + get the NEXT marker/color combo + """ + # get from list + marker, color = self._myLineMarkerColorList[self._myLineMarkerColorIndex] + # process marker if it has information + if marker.count(' (') > 0: + marker = marker.split(' (')[0] + print "[DB] Print line %d: marker = %s, color = %s" % (self._myLineMarkerColorIndex, marker, color) + + # update the index + self._myLineMarkerColorIndex += 1 + if self._myLineMarkerColorIndex == len(self._myLineMarkerColorList): + self._myLineMarkerColorIndex = 0 + + return marker, color + + def resetLineColorStyle(self): + """ Reset the auto index for line's color and style + """ + self._myLineMarkerColorIndex = 0 + return + + # NEXT-Urgent! - Find out difference between setXYLimit() and setXYLimits() + def setXYLimit(self, xmin, xmax, ymin, ymax): + """ Set X-Y limit automatically + """ + self._myCanvas.axes.set_xlim([xmin, xmax]) + self._myCanvas.axes.set_ylim([ymin, ymax]) + + self._myCanvas.draw() + + return + + def setXYLimits(self, xmin=None, xmax=None, ymin=None, ymax=None): + """ + """ + return self._myCanvas.setXYLimit(xmin, xmax, ymin, ymax) + + def setAutoLineMarkerColorCombo(self): + """ + """ + self._myLineMarkerColorList = [] + for marker in MplLineMarkers: + for color in MplBasicColors: + self._myLineMarkerColorList.append( (marker, color) ) + + return + + def setLineMarkerColorIndex(self, newindex): + """ + """ + self._myLineMarkerColorIndex = newindex + + return + + +class Qt4MplCanvas(FigureCanvas): + """ A customized Qt widget for matplotlib figure. + It can be used to replace GraphicsView of QtGui + """ + def __init__(self, parent): + """ Initialization + """ + # from mpl_toolkits.axes_grid1 import host_subplot + # import mpl_toolkits.axisartist as AA + # import matplotlib.pyplot as plt + + # Instantiating matplotlib Figure + self.fig = Figure() + self.fig.patch.set_facecolor('white') + + if True: + self.axes = self.fig.add_subplot(111) # return: matplotlib.axes.AxesSubplot + self.axes2 = None + else: + self.axes = self.fig.add_host_subplot(111) + + # Initialize parent class and set parent + FigureCanvas.__init__(self, self.fig) + self.setParent(parent) + + # Set size policy to be able to expanding and resizable with frame + FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding) + FigureCanvas.updateGeometry(self) + + # Variables to manage all lines/subplot + self._lineDict = {} + self._lineIndex = 0 + + # legend and color bar + self._colorBar = None + + return + + def add_plot_1d(self, vec_x, vec_y, y_err=None, color=None, label="", x_label=None, y_label=None, + marker=None, line_style=None, line_width=1): + """ + + :param vec_x: numpy array X + :param vec_y: numpy array Y + :param y_err: + :param color: + :param label: + :param x_label: + :param y_label: + :param marker: + :param line_style: + :param line_width: + :return: new key + """ + # Check input + if isinstance(vec_x, np.ndarray) is False or isinstance(vec_y, np.ndarray) is False: + raise NotImplementedError('Input vec_x or vec_y for addPlot() must be numpy.array.') + plot_error = y_err is not None + if plot_error is True: + if isinstance(y_err, np.ndarray) is False: + raise NotImplementedError('Input y_err must be either None or numpy.array.') + + if len(vec_x) != len(vec_y): + raise NotImplementedError('Input vec_x and vec_y must have same size.') + if plot_error is True and len(y_err) != len(vec_x): + raise NotImplementedError('Input vec_x, vec_y and y_error must have same size.') + + # Hold previous data + self.axes.hold(True) + + # process inputs and defaults + if color is None: + color = (0,1,0,1) + if marker is None: + marker = 'o' + if line_style is None: + line_style = '-' + + # color must be RGBA (4-tuple) + if plot_error is False: + print "[DB] line_style = ", line_style, "line_width = ", line_width, "marker = ", marker, "color = ", color + r = self.axes.plot(vec_x, vec_y, color=color, marker=marker, linestyle=line_style, + label=label, linewidth=line_width) + # return: list of matplotlib.lines.Line2D object + else: + r = self.axes.errorbar(vec_x, vec_y, yerr=y_err, color=color, marker=marker, linestyle=line_style, + label=label, linewidth=line_width) + + self.axes.set_aspect('auto') + + # set x-axis and y-axis label + if x_label is not None: + self.axes.set_xlabel(x_label, fontsize=20) + if y_label is not None: + self.axes.set_ylabel(y_label, fontsize=20) + + # set/update legend + self._setupLegend() + + # Register + line_key = self._lineIndex + if len(r) == 1: + self._lineDict[line_key] = r[0] + self._lineIndex += 1 + else: + print "Impoooooooooooooooosible! Return from plot is a %d-tuple. " % (len(r)) + + # Flush/commit + self.draw() + + return line_key + + def add_1d_plot_right(self, x, y, color=None, label="", x_label=None, ylabel=None, marker=None, linestyle=None, + linewidth=1): + """ Add a line (1-d plot) at right axis + """ + if self.axes2 is None: + self.axes2 = self.axes.twinx() + # print self.par1, type(self.par1) + + # Hold previous data + self.axes2.hold(True) + + # Default + if color is None: + color = (0, 1, 0, 1) + if marker is None: + marker = 'o' + if linestyle is None: + linestyle = '-' + + # Special default + if len(label) == 0: + label = 'right' + color = 'red' + + # color must be RGBA (4-tuple) + r = self.axes2.plot(x, y, color=color, marker=marker, linestyle=linestyle, + label=label, linewidth=linewidth) + # return: list of matplotlib.lines.Line2D object + + self.axes2.set_aspect('auto') + + # set x-axis and y-axis label + if x_label is not None: + self.axes2.set_xlabel(x_label, fontsize=20) + if ylabel is not None: + self.axes2.set_ylabel(ylabel, fontsize=20) + + # set/update legend + self._setupLegend() + + # Register + line_key = -1 + if len(r) == 1: + line_key = self._lineIndex + self._lineDict[line_key] = r[0] + self._lineIndex += 1 + else: + print "Impoooooooooooooooosible!" + + # Flush/commit + self.draw() + + return line_key + + + def addPlot2D(self, array2d, xmin, xmax, ymin, ymax, holdprev, yticklabels=None): + """ Add a 2D plot + + Arguments: + - yticklabels :: list of string for y ticks + """ + # Release the current image + self.axes.hold(holdprev) + + # Do plot + # y ticks will be shown on line 1, 4, 23, 24 and 30 + # yticks = [1, 4, 23, 24, 30] + # self.axes.set_yticks(yticks) + + # show image + imgplot = self.axes.imshow(array2d, extent=[xmin,xmax,ymin,ymax], interpolation='none') + # set y ticks as an option: + if yticklabels is not None: + # it will always label the first N ticks even image is zoomed in + print "--------> [FixMe]: Set up the Y-axis ticks is erroreous" + #self.axes.set_yticklabels(yticklabels) + + # explicitly set aspect ratio of the image + self.axes.set_aspect('auto') + + # Set color bar. plt.colorbar() does not work! + if self._colorBar is None: + # set color map type + imgplot.set_cmap('spectral') + self._colorBar = self.fig.colorbar(imgplot) + else: + self._colorBar.update_bruteforce(imgplot) + + # Flush... + self._flush() + + return + + def addImage(self, imagefilename): + """ Add an image by file + """ + #import matplotlib.image as mpimg + + # set aspect to auto mode + self.axes.set_aspect('auto') + + img = matplotlib.image.imread(str(imagefilename)) + # lum_img = img[:,:,0] + # FUTURE : refactor for image size, interpolation and origin + imgplot = self.axes.imshow(img, extent=[0, 1000, 800, 0], interpolation='none', origin='lower') + + # Set color bar. plt.colorbar() does not work! + if self._colorBar is None: + # set color map type + imgplot.set_cmap('spectral') + self._colorBar = self.fig.colorbar(imgplot) + else: + self._colorBar.update_bruteforce(imgplot) + + self._flush() + + return + + def clear_all_1d_plots(self): + """ Remove all lines from the canvas + """ + for ikey in self._lineDict.keys(): + plot = self._lineDict[ikey] + if plot is None: + continue + if isinstance(plot, tuple) is False: + try: + self.axes.lines.remove(plot) + except ValueError as e: + print "[Error] Plot %s is not in axes.lines which has %d lines. Error mesage: %s" % ( + str(plot), len(self.axes.lines), str(e)) + self._lineDict[ikey] = None + else: + # error bar + plot[0].remove() + for line in plot[1]: + line.remove() + for line in plot[2]: + line.remove() + self._lineDict[ikey] = None + # ENDIF(plot) + # ENDFOR + + self._setupLegend() + + self.draw() + + return + + def clear_canvas(self): + """ Clear data including lines and image from canvas + """ + # clear the image for next operation + self.axes.hold(False) + + # Clear all lines + self.clear_all_1d_plots() + + # clear image + self.axes.cla() + # Try to clear the color bar + if len(self.fig.axes) > 1: + self.fig.delaxes(self.fig.axes[1]) + self._colorBar = None + # This clears the space claimed by color bar but destroys sub_plot too. + self.fig.clear() + # Re-create subplot + self.axes = self.fig.add_subplot(111) + + # flush/commit + self._flush() + + return + + + def getLastPlotIndexKey(self): + """ Get the index/key of the last added line + """ + return self._lineIndex-1 + + + def getPlot(self): + """ reture figure's axes to expose the matplotlib figure to PyQt client + """ + return self.axes + + def getXLimit(self): + """ Get limit of Y-axis + """ + return self.axes.get_xlim() + + def getYLimit(self): + """ Get limit of Y-axis + """ + return self.axes.get_ylim() + + def setXYLimit(self, xmin, xmax, ymin, ymax): + """ + """ + # for X + xlims = self.axes.get_xlim() + xlims = list(xlims) + if xmin is not None: + xlims[0] = xmin + if xmax is not None: + xlims[1] = xmax + self.axes.set_xlim(xlims) + + # for Y + ylims = self.axes.get_ylim() + ylims = list(ylims) + if ymin is not None: + ylims[0] = ymin + if ymax is not None: + ylims[1] = ymax + self.axes.set_ylim(ylims) + + # try draw + self.draw() + + return + + def remove_plot_1d(self, plot_key): + """ Remove the line with its index as key + :param plot_key: + :return: + """ + # self._lineDict[ikey].remove() + print 'Remove line... ', + + # Get all lines in list + lines = self.axes.lines + assert isinstance(lines, list) + + print 'Number of lines = %d, List: %s' % (len(lines), str(lines)) + print 'Line to remove: key = %s, Line Dict has key = %s' % (str(plot_key), str(self._lineDict.has_key(plot_key))) + + if plot_key in self._lineDict: + self.axes.lines.remove(self._lineDict[plot_key]) + self._lineDict[plot_key] = None + else: + raise RuntimeError('Line with ID %s is not recorded.' % plot_key) + + # Draw + self.draw() + + return + + def updateLine(self, ikey, vecx, vecy, linestyle=None, linecolor=None, marker=None, markercolor=None): + """ + """ + line = self._lineDict[ikey] + + if vecx is not None and vecy is not None: + line.set_xdata(vecx) + line.set_ydata(vecy) + + if linecolor is not None: + line.set_color(linecolor) + + if linestyle is not None: + line.set_linestyle(linestyle) + + if marker is not None: + line.set_marker(marker) + + if markercolor is not None: + line.set_markerfacecolor(markercolor) + + oldlabel = line.get_label() + line.set_label(oldlabel) + + self.axes.legend() + + # commit + self.draw() + + return + + def getLineStyleList(self): + """ + """ + return MplLineStyles + + + def getLineMarkerList(self): + """ + """ + return MplLineMarkers + + def getLineBasicColorList(self): + """ + """ + return MplBasicColors + + def getDefaultColorMarkerComboList(self): + """ Get a list of line/marker color and marker style combination + as default to add more and more line to plot + """ + combolist = [] + nummarkers = len(MplLineMarkers) + numcolors = len(MplBasicColors) + + for i in xrange(nummarkers): + marker = MplLineMarkers[i] + for j in xrange(numcolors): + color = MplBasicColors[j] + combolist.append( (marker, color) ) + # ENDFOR (j) + # ENDFOR(i) + + return combolist + + def _flush(self): + """ A dirty hack to flush the image + """ + w, h = self.get_width_height() + self.resize(w+1,h) + self.resize(w,h) + + return + + def _setupLegend(self, location='best'): + """ Set up legend + self.axes.legend() + Handler is a Line2D object. Lable maps to the line object + """ + loclist = [ + "best", + "upper right", + "upper left", + "lower left", + "lower right", + "right", + "center left", + "center right", + "lower center", + "upper center", + "center"] + + # Check legend location valid or not + if location not in loclist: + location = 'best' + + handles, labels = self.axes.get_legend_handles_labels() + self.axes.legend(handles, labels, loc=location) + # print handles + # print labels + #self.axes.legend(self._myLegendHandlers, self._myLegentLabels) + + return + +# END-OF-CLASS (MplGraphicsView) + + +class MyNavigationToolbar(NavigationToolbar2): + """ A customized navigation tool bar attached to canvas + Note: + * home, left, right: will not disable zoom/pan mode + * zoom and pan: will turn on/off both's mode + + Other methods + * drag_pan(self, event): event handling method for dragging canvas in pan-mode + """ + NAVIGATION_MODE_NONE = 0 + NAVIGATION_MODE_PAN = 1 + NAVIGATION_MODE_ZOOM = 2 + + def __init__(self, parent, canvas): + """ Initialization + """ + NavigationToolbar2.__init__(self, canvas, canvas) + + self._myParent = parent + self._navigationMode = MyNavigationToolbar.NAVIGATION_MODE_NONE + + return + + def get_mode(self): + """ + :return: integer as none/pan/zoom mode + """ + return self._navigationMode + + # Overriding base's methods + def draw(self): + """ + Canvas is drawn called by pan(), zoom() + :return: + """ + NavigationToolbar2.draw(self) + + self._myParent.evt_view_updated() + + return + + def pan(self, *args): + """ + + :param args: + :return: + """ + NavigationToolbar2.pan(self, args) + + if self._navigationMode == MyNavigationToolbar.NAVIGATION_MODE_PAN: + # out of pan mode + self._navigationMode = MyNavigationToolbar.NAVIGATION_MODE_NONE + else: + # into pan mode + self._navigationMode = MyNavigationToolbar.NAVIGATION_MODE_PAN + + return + + def zoom(self, *args): + """ + Turn on/off zoom (zoom button) + :param args: + :return: + """ + NavigationToolbar2.zoom(self, args) + + if self._navigationMode == MyNavigationToolbar.NAVIGATION_MODE_ZOOM: + # out of zoom mode + self._navigationMode = MyNavigationToolbar.NAVIGATION_MODE_NONE + else: + # into zoom mode + self._navigationMode = MyNavigationToolbar.NAVIGATION_MODE_ZOOM + + return + + def _update_view(self): + """ + view update called by home(), back() and forward() + :return: + """ + NavigationToolbar2._update_view(self) + + self._myParent.evt_view_updated() + + return + diff --git a/scripts/HFIR_4Circle_Reduction/reduce4circleControl.py b/scripts/HFIR_4Circle_Reduction/reduce4circleControl.py new file mode 100644 index 0000000000000000000000000000000000000000..bb11355d3c1b09e8e5b011cd8b698d64614939aa --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/reduce4circleControl.py @@ -0,0 +1,1263 @@ +#pylint: disable=C0302,C0103,R0902,R0904,R0913,W0212,W0621,R0912 +################################################################################ +# +# Controlling class +# +# == Data download and storage == +# - Local data storage (local-mode) +# - Download from internet to cache (download-mode) +# +################################################################################ +import os +import urllib2 + +import numpy + +import mantid +import mantid.simpleapi as api +from mantid.api import AnalysisDataService + +DebugMode = True + +DET_X_SIZE = 256 +DET_Y_SIZE = 256 + + +class PeakInfo(object): + """ Class containing a peak's information for GUI + In order to manage some operations for a peak + It does not contain peak workspace but will hold + """ + def __init__(self, parent): + """ Init + """ + assert isinstance(parent, CWSCDReductionControl) + + # Define class variable + self._myParent = parent + self._userHKL = [0, 0, 0] + + self._myExpNumber = None + self._myScanNumber = None + self._myPtNumber = None + + self._myPeakWSKey = (None, None, None) + self._myPeakIndex = None + # IPeak instance + self._myPeak = None + + self._myLastPeakUB = None + + return + + def get_peak_workspace(self): + """ + Get peak workspace related + :return: + """ + assert isinstance(self._myPeakWSKey, tuple) + exp_number, scan_number, pt_number = self._myPeakWSKey + + return self._myParent.get_ub_peak_ws(exp_number, scan_number, pt_number)[1] + + def get_peak_ws_hkl(self): + """ Get HKL from PeakWorkspace + :return: + """ + hkl = self._myPeak.getHKL() + + return hkl.getX(), hkl.getY(), hkl.getZ() + + def get_user_hkl(self): + """ + Get HKL set to this object by client + :return: 3-tuple of float as (H, K, L) + """ + hkl = self._userHKL + + return hkl[0], hkl[1], hkl[2] + + def set_from_run_info(self, exp_number, scan_number, pt_number): + """ Set from run information with parent + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + status, peak_ws = self._myParent.get_ub_peak_ws(exp_number, scan_number, pt_number) + if status is True: + self._myPeakWSKey = (exp_number, scan_number, pt_number) + self._myPeakIndex = 0 + self._myPeak = peak_ws.getPeak(0) + else: + error_message = peak_ws + return False, error_message + + self._myExpNumber = exp_number + self._myScanNumber = scan_number + self._myPtNumber = pt_number + + return True, '' + + def set_from_peak_ws(self, peak_ws, peak_index): + """ + Set from peak workspace + :param peak_ws: + :return: + """ + # Check + assert isinstance(peak_ws, mantid.dataobjects.PeaksWorkspace) + + # Get peak + try: + peak = peak_ws.getPeak(peak_index) + except RuntimeError as run_err: + raise RuntimeError(run_err) + + self._myPeak = peak + + return + + def set_peak_ws_hkl_from_user(self): + """ + + :return: + """ + # Check + if isinstance(self._myPeak, mantid.api.IPeak) is False: + raise RuntimeError('self._myPeakWS should be an instance of mantid.api.IPeak. ' + 'But it is of instance of %s now.' % str(type(self._myPeak))) + + # Get hkl + h, k, l = self._userHKL + print '[DB] PeakInfo Get User HKL = (%f, %f, %f) to IPeak ' % (h, k, l) + + self._myPeak.setHKL(h, k, l) + + return + + def set_user_hkl(self, h, k, l): + """ + Set HKL to this peak Info + :return: + """ + assert isinstance(h, float) + assert isinstance(k, float) + assert isinstance(l, float) + + self._userHKL[0] = h + self._userHKL[1] = k + self._userHKL[2] = l + + print '[DB] PeakInfo Set User HKL to (%f, %f, %f) ' % (self._userHKL[0], self._userHKL[1], self._userHKL[2]) + + return + + def getExpInfo(self): + """ + + :return: 3-tuple of integer as experiment number, scan number and Pt number + """ + return self._myExpNumber, self._myScanNumber, self._myPtNumber + + def getQSample(self): + """ + + :return: 3-tuple of floats as Qx, Qy, Qz + """ + q_sample = self._myPeak.getQSampleFrame() + return q_sample.getX(), q_sample.getY(), q_sample.getZ() + + +class CWSCDReductionControl(object): + """ Controlling class for reactor-based single crystal diffraction reduction + """ + def __init__(self, instrument_name=None): + """ init + """ + if isinstance(instrument_name, str): + self._instrumentName = instrument_name + elif instrument_name is None: + self._instrumentName = '' + else: + raise RuntimeError('Instrument name %s of type %s is not allowed.' % (str(instrument_name), + str(type(instrument_name)))) + + # Experiment number, data storage + # No Use/Confusing: self._expNumber = 0 + + self._dataDir = None + self._workDir = '/tmp' + + self._myServerURL = '' + + # Some set up + self._expNumber = None + + # Container for MDEventWorkspace for each Pt. + self._myPtMDDict = dict() + # Container for loaded workspaces + self._mySpiceTableDict = {} + # Container for loaded raw pt workspace + self._myRawDataWSDict = dict() + # Container for PeakWorkspaces for calculating UB matrix + self._myUBPeakWSDict = dict() + # Container for UB matrix + self._myUBMatrixDict = dict() + + # Peak Info + self._myPeakInfoDict = dict() + # Last UB matrix calculated + self._myLastPeakUB = None + # Flag for data storage + self._cacheDataOnly = False + + # A dictionary to manage all loaded and processed MDEventWorkspaces + # self._expDataDict = {} + + return + + def add_peak_info(self, exp_number, scan_number, pt_number): + """ Add a peak info for calculating UB matrix + :param exp_number: + :param scan_number: + :param pt_number: + :return: (boolean, PeakInfo/string) + """ + has_peak_ws, peak_ws = self.get_ub_peak_ws(exp_number, scan_number, pt_number) + if has_peak_ws is False: + err_msg = 'No peak workspace found for Exp %s Scan %s Pt %s' % ( + exp_number, scan_number, pt_number) + print '\n[DB] Fail to add peak info due to %s\n' % err_msg + return False, err_msg + + if peak_ws.rowCount() > 1: + err_msg = 'There are more than 1 peak in PeaksWorkspace.' + print '\n[DB] Fail to add peak info due to %s\n' % err_msg + return False, err_msg + + peak_info = PeakInfo(self) + peak_info.set_from_run_info(exp_number, scan_number, pt_number) + + # Add to data management + self._myPeakInfoDict[(exp_number, scan_number, pt_number)] = peak_info + + return True, peak_info + + def calculate_ub_matrix(self, peak_info_list, a, b, c, alpha, beta, gamma): + """ + Calculate UB matrix + + Set Miller index from raw data in Workspace2D. + :param peakws: + :param a: + :param b: + :param c: + :param alpha: + :param beta: + :param gamma: + :return: + """ + # Check + assert isinstance(peak_info_list, list) + for peak_info in peak_info_list: + if isinstance(peak_info, PeakInfo) is False: + raise NotImplementedError('Input PeakList is of type %s.' % str(type(peak_info_list[0]))) + assert isinstance(peak_info, PeakInfo) + + if len(peak_info_list) < 2: + return False, 'Too few peaks are input to calculate UB matrix. Must be >= 2.' + + # Construct a new peak workspace by combining all single peak + ub_peak_ws_name = 'Temp_UB_Peak' + ub_peak_ws = api.CloneWorkspace(InputWorkspace=peak_info_list[0].get_peak_workspace(), + OutputWorkspace=ub_peak_ws_name) + + for i_peak_info in xrange(1, len(peak_info_list)): + # Set HKL as optional + peak_ws = peak_info_list[i_peak_info].get_peak_workspace() + + # Combine peak workspace + ub_peak_ws = api.CombinePeaksWorkspaces(LHSWorkspace=ub_peak_ws, + RHSWorkspace=peak_ws, + CombineMatchingPeaks=False, + OutputWorkspace=ub_peak_ws_name) + # END-FOR(i_peak_info) + + # Calculate UB matrix + try: + api.CalculateUMatrix(PeaksWorkspace=ub_peak_ws_name, + a=a, b=b, c=c, alpha=alpha, beta=beta, gamma=gamma) + except ValueError as val_err: + return False, str(val_err) + + ub_matrix = ub_peak_ws.sample().getOrientedLattice().getUB() + + self._myLastPeakUB = ub_peak_ws + + return True, ub_matrix + + def does_raw_loaded(self, exp_no, scan_no, pt_no): + """ + Check whether the raw Workspace2D for a Pt. exists + :param exp_no: + :param scan_no: + :param pt_no: + :return: + """ + return (exp_no, scan_no, pt_no) in self._myRawDataWSDict + + def does_spice_loaded(self, exp_no, scan_no): + """ Check whether a SPICE file has been loaded + :param exp_no: + :param scan_no: + :return: + """ + return (exp_no, scan_no) in self._mySpiceTableDict + + def download_spice_file(self, exp_number, scan_number, over_write): + """ + Download a scan/pt data from internet + :param exp_number: experiment number + :param scan_number: + :return: + """ + # Check + if exp_number is None: + exp_number = self._expNumber + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + + # Generate the URL for SPICE data file + file_url = '%sexp%d/Datafiles/HB3A_exp%04d_scan%04d.dat' % (self._myServerURL, exp_number, + exp_number, scan_number) + file_name = '%s_exp%04d_scan%04d.dat' % (self._instrumentName, exp_number, scan_number) + file_name = os.path.join(self._dataDir, file_name) + if os.path.exists(file_name) is True and over_write is False: + return True, file_name + + # Download + try: + api.DownloadFile(Address=file_url, Filename=file_name) + except RuntimeError as run_err: + return False, str(run_err) + + # Check file exist? + if os.path.exists(file_name) is False: + return False, "Unable to locate downloaded file %s." % file_name + + return True, file_name + + def download_spice_xml_file(self, scan_no, pt_no, exp_no=None, overwrite=False): + """ Download a SPICE XML file for one measurement in a scan + :param scan_no: + :param pt_no: + :param exp_no: + :param overwrite: + :return: tuple (boolean, local file name/error message) + """ + # Experiment number + if exp_no is None: + exp_no = self._expNumber + + # Form the target file name and path + xml_file_name = '%s_exp%d_scan%04d_%04d.xml' % (self._instrumentName, exp_no, scan_no, pt_no) + local_xml_file_name = os.path.join(self._dataDir, xml_file_name) + if os.path.exists(local_xml_file_name) is True and overwrite is False: + return True, local_xml_file_name + + # Generate the URL for XML file + xml_file_url = '%sexp%d/Datafiles/%s' % (self._myServerURL, exp_no, xml_file_name) + + # Download + try: + api.DownloadFile(Address=xml_file_url, + Filename=local_xml_file_name) + except RuntimeError as run_err: + return False, 'Unable to download Detector XML file %s dur to %s.' % (xml_file_name, str(run_err)) + + # Check file exist? + if os.path.exists(local_xml_file_name) is False: + return False, "Unable to locate downloaded file %s."%(local_xml_file_name) + + return True, local_xml_file_name + + def download_data_set(self, scan_list, overwrite=False): + """ + Download data set including (1) spice file for a scan and (2) XML files for measurements + :param scan_list: + :return: + """ + # Check + if self._expNumber is None: + raise RuntimeError('Experiment number is not set up for controller.') + + error_message = '' + + for scan_no in scan_list: + # Download single spice file for a run + status, ret_obj = self.download_spice_file(exp_number=self._expNumber, + scan_number=scan_no, + over_write=overwrite) + + # Reject if SPICE file cannot download + if status is False: + error_message += '%s\n' % ret_obj + continue + + # Load SPICE file to Mantid + spice_file_name = ret_obj + status, ret_obj = self.load_spice_scan_file(self._expNumber, scan_no, spice_file_name) + if status is False: + error_message = ret_obj + return False, error_message + else: + spice_table = self._mySpiceTableDict[(self._expNumber, scan_no)] + assert spice_table + pt_no_list = self._get_pt_list_from_spice_table(spice_table) + + # Download all single-measurement file + for pt_no in pt_no_list: + status, ret_obj = self.download_spice_xml_file(scan_no, pt_no, overwrite=overwrite) + if status is False: + error_message += '%s\n' % ret_obj + # END-FOR + # END-FOR (scan_no) + + return True, error_message + + def existDataFile(self, scanno, ptno): + """ + Check whether data file for a scan or pt number exists + :param scanno: + :param ptno: + :return: + """ + # Check spice file + spice_file_name = '%s_exp%04d_scan%04d.dat'%(self._instrumentName, + self._expNumber, scanno) + spice_file_name = os.path.join(self._dataDir, spice_file_name) + if os.path.exists(spice_file_name) is False: + return False, 'Spice data file %s cannot be found.'% spice_file_name + + # Check xml file + xmlfilename = '%s_exp%d_scan%04d_%04d.xml'%(self._instrumentName, self._expNumber, + scanno, ptno) + xmlfilename = os.path.join(self._dataDir, xmlfilename) + if os.path.exists(xmlfilename) is False: + return (False, "Pt. XML file %s cannot be found."%(xmlfilename)) + + return True, "" + + def find_peak(self, exp_number, scan_number, pt_number): + """ Find 1 peak in sample Q space for UB matrix + :param scan_number: + :param pt_number: + :return:tuple as (boolean, object) such as (false, error message) and (true, PeakInfo object) + + This part will be redo as 11847_Load_HB3A_Experiment + """ + # Check + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + # Download or make sure data are there + status_sp, err_msg_sp = self.download_spice_file(exp_number, scan_number, over_write=False) + status_det, err_msg_det = self.download_spice_xml_file(scan_number, pt_number, exp_number, + overwrite=False) + if status_sp is False or status_det is False: + return False, 'Unable to access data (1) %s (2) %s' % (err_msg_sp, err_msg_det) + + # Collect reduction information: example + exp_info_ws_name = get_pt_info_ws_name(exp_number, scan_number) + virtual_instrument_info_table_name = get_virtual_instrument_table_name(exp_number, scan_number, pt_number) + api.CollectHB3AExperimentInfo( + ExperimentNumber=exp_number, + GenerateVirtualInstrument=False, + ScanList=[scan_number], + PtLists=[-1, pt_number], + DataDirectory=self._dataDir, + GetFileFromServer=False, + Detector2ThetaTolerance=0.01, + OutputWorkspace=exp_info_ws_name, + DetectorTableWorkspace=virtual_instrument_info_table_name) + + # Load XML file to MD + pt_md_ws_name = get_single_pt_md_name(exp_number, scan_number, pt_number) + api.ConvertCWSDExpToMomentum(InputWorkspace=exp_info_ws_name, + CreateVirtualInstrument=False, + OutputWorkspace=pt_md_ws_name, + Directory=self._dataDir) + + # Find peak in Q-space + pt_peak_ws_name = get_single_pt_peak_ws_name(exp_number, scan_number, pt_number) + api.FindPeaksMD(InputWorkspace=pt_md_ws_name, MaxPeaks=10, + DensityThresholdFactor=0.01, OutputWorkspace=pt_peak_ws_name) + peak_ws = AnalysisDataService.retrieve(pt_peak_ws_name) + pt_md_ws = AnalysisDataService.retrieve(pt_md_ws_name) + self._myPtMDDict[(exp_number, scan_number, pt_number)] = pt_md_ws + + num_peaks = peak_ws.getNumberPeaks() + if num_peaks != 1: + err_msg = 'Find %d peak from scan %d pt %d. ' \ + 'For UB matrix calculation, 1 and only 1 peak is allowed' % (num_peaks, scan_number, pt_number) + return False, err_msg + else: + self._add_ub_peak_ws(exp_number, scan_number, pt_number, peak_ws) + status, ret_obj = self.add_peak_info(exp_number, scan_number, pt_number) + if status is True: + pass + # peak_info = ret_obj + # peak_info.set_md_ws(pt_md_ws) + else: + err_msg = ret_obj + return False, err_msg + + return True, '' + + def get_experiment(self): + """ + Get experiment number + :return: + """ + return self._expNumber + + def get_pt_numbers(self, exp_no, scan_no, load_spice_scan=False): + """ Get Pt numbers (as a list) for a scan in an experiment + :param exp_no: + :param scan_no: + :param load_spice_scan: + :return: (Boolean, Object) as (status, pt number list/error message) + """ + # Check + if exp_no is None: + exp_no = self._expNumber + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + + # Get workspace + table_ws = self._get_spice_workspace(exp_no, scan_no) + if table_ws is None: + if load_spice_scan is False: + return False, 'Spice file for Exp %d Scan %d is not loaded.' % (exp_no, scan_no) + else: + status, error_message = self.load_spice_scan_file(exp_no, scan_no) + if status is True: + table_ws = self._get_spice_workspace(exp_no, scan_no) + if table_ws is None: + raise NotImplementedError('Logic error! Cannot happen!') + else: + return False, 'Unable to load Spice file for Exp %d Scan %d due to %s.' % ( + exp_no, scan_no, error_message) + + col_name_list = table_ws.getColumnNames() + i_pt = col_name_list.index('Pt.') + if i_pt < 0 or i_pt >= len(col_name_list): + return False, 'No column with name Pt. can be found in SPICE table.' + + pt_number_list = [] + num_rows = table_ws.rowCount() + for i in xrange(num_rows): + pt_number = table_ws.cell(i, i_pt) + pt_number_list.append(pt_number) + + return True, pt_number_list + + def get_raw_detector_counts(self, exp_no, scan_no, pt_no): + """ + Get counts on raw detector + :param scan_no: + :param pt_no: + :return: boolean, 2D numpy data + """ + # Get workspace (in memory or loading) + raw_ws = self.get_raw_data_workspace(exp_no, scan_no, pt_no) + if raw_ws is None: + return False, 'Raw data for Exp %d Scan %d Pt %d is not loaded.' % (exp_no, scan_no, pt_no) + + # Convert to numpy array + array2d = numpy.ndarray(shape=(DET_X_SIZE, DET_Y_SIZE), dtype='float') + for i in xrange(DET_X_SIZE): + for j in xrange(DET_Y_SIZE): + array2d[i][j] = raw_ws.readY(i * DET_X_SIZE + j)[0] + + return array2d + + def get_sample_log_value(self, exp_number, scan_number, pt_number, log_name): + """ + Get sample log's value + :param exp_number: + :param scan_number:167 + :param pt_number: + :param log_name: + :return: float + """ + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + assert isinstance(log_name, str) + try: + md_ws = self._myPtMDDict[(exp_number, scan_number, pt_number)] + except KeyError as ke: + return 'Unable to find log value %s due to %s.' % (log_name, str(ke)) + + return md_ws.getExperimentInfo(0).run().getProperty(log_name).value + + def get_peak_info(self, exp_number, scan_number, pt_number): + """ + get peak information instance + :param exp_number: experiment number. if it is None, then use the current exp number + :param scan_number: + :param pt_number: + :return: + """ + # Check for type + if exp_number is None: + exp_number = self._expNumber + + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + # Check for existence + if (exp_number, scan_number, pt_number) not in self._myUBPeakWSDict: # self._myPeakInfoDict: + err_msg = 'Unable to find PeakInfo for Exp %d Scan %d Pt %d. ' \ + 'Existing keys are %s' % (exp_number, scan_number, pt_number, + str(self._myUBPeakWSDict.keys())) + return False, err_msg + + print '[DB] PeakInfoDictionary Keys = %s' % str(self._myPeakInfoDict.keys()) + + return True, self._myPeakInfoDict[(exp_number, scan_number, pt_number)] + + def get_ub_peak_ws(self, exp_number, scan_number, pt_number): + """ + Get peak workspace for the peak picked to calculate UB matrix + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + if (exp_number, scan_number, pt_number) not in self._myUBPeakWSDict: + return False, 'Exp %d Scan %d Pt %d has no peak workspace.' % (exp_number, + scan_number, + pt_number) + + return True, self._myUBPeakWSDict[(exp_number, scan_number, pt_number)] + + def index_peak(self, ub_matrix, scan_number, pt_number): + """ Index peaks in a Pt. + :param ub_matrix: numpy.ndarray (3, 3) + :param scan_number: + :param pt_number: + :return: boolean, object (list of HKL or error message) + """ + # Check + assert isinstance(ub_matrix, numpy.ndarray) + assert ub_matrix.shape == (3, 3) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + # Find out the peak workspace + exp_num = self._expNumber + if (exp_num, scan_number, pt_number) in self._myUBPeakWSDict is False: + err_msg = 'No PeakWorkspace is found for exp %d scan %d pt %d' % ( + exp_num, scan_number, pt_number) + return False, err_msg + + peak_ws = self._myUBPeakWSDict[(exp_num, scan_number, pt_number)] + ub_1d = ub_matrix.reshape(9,) + print '[DB] UB matrix = ', ub_1d + + # Set UB + api.SetUB(Workspace=peak_ws, UB=ub_1d) + + # Note: IndexPeaks and CalcualtePeaksHKL do the same job + # while IndexPeaks has more control on the output + num_peak_index, error = api.IndexPeaks(PeaksWorkspace=peak_ws, + Tolerance=0.4, + RoundHKLs=False) + + if num_peak_index == 0: + return False, 'No peak can be indexed.' + elif num_peak_index > 1: + raise RuntimeError('Case for PeaksWorkspace containing more than 1 peak is not ' + 'considered. Contact developer for this issue.') + else: + hkl_v3d = peak_ws.getPeak(0).getHKL() + hkl = [hkl_v3d.X(), hkl_v3d.Y(), hkl_v3d.Z()] + + return True, (hkl, error) + + def load_spice_scan_file(self, exp_no, scan_no, spice_file_name=None): + """ + Load a SPICE scan file to table workspace and run information matrix workspace. + :param scan_no: + :param spice_file_name: + :return: status (boolean), error message (string) + """ + # Default for exp_no + if exp_no is None: + exp_no = self._expNumber + + # Check whether the workspace has been loaded + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + out_ws_name = get_spice_table_name(exp_no, scan_no) + if (exp_no, scan_no) in self._mySpiceTableDict: + return True, out_ws_name + + # Form standard name for a SPICE file if name is not given + if spice_file_name is None: + spice_file_name = os.path.join(self._dataDir, get_spice_file_name(exp_no, scan_no)) + + # Download SPICE file if necessary + if os.path.exists(spice_file_name) is False: + self.download_spice_file(exp_no, scan_no, over_write=True) + + try: + spice_table_ws, info_matrix_ws = api.LoadSpiceAscii(Filename=spice_file_name, + OutputWorkspace=out_ws_name, + RunInfoWorkspace='TempInfo') + api.DeleteWorkspace(Workspace=info_matrix_ws) + except RuntimeError as run_err: + return False, 'Unable to load SPICE data %s due to %s' % (spice_file_name, str(run_err)) + + # Store + self._add_spice_workspace(exp_no, scan_no, spice_table_ws) + + return True, out_ws_name + + def load_spice_xml_file(self, exp_no, scan_no, pt_no, xml_file_name=None): + """ + Load SPICE's XML file to + :param scan_no: + :param pt_no: + :return: + """ + # Form XMIL file as ~/../HB3A_exp355_scan%04d_%04d.xml'%(scan_no, pt) + if xml_file_name is None: + xml_file_name = os.path.join(self._dataDir, + 'HB3A_exp%d_scan%04d_%04d.xml' % (exp_no, scan_no, pt_no)) + + # Get spice table + spice_table_ws = self._get_spice_workspace(exp_no, scan_no) + assert isinstance(spice_table_ws, mantid.dataobjects.TableWorkspace) + spice_table_name = spice_table_ws.name() + + # Load SPICE Pt. file + # spice_table_name = 'Table_Exp%d_Scan%04d' % (exp_no, scan_no) + pt_ws_name = get_raw_data_workspace_name(exp_no, scan_no, pt_no) + try: + ret_obj = api.LoadSpiceXML2DDet(Filename=xml_file_name, + OutputWorkspace=pt_ws_name, + DetectorGeometry='256,256', + SpiceTableWorkspace=spice_table_name, + PtNumber=pt_no) + except RuntimeError as run_err: + return False, str(run_err) + + pt_ws = ret_obj + + # Add data storage + self._add_raw_workspace(exp_no, scan_no, pt_no, pt_ws) + + return True, pt_ws_name + + def group_workspaces(self, exp_number, group_name): + """ + + :return: + """ + # Find out the input workspace name + ws_names_str = '' + for key in self._myRawDataWSDict.keys(): + if key[0] == exp_number: + ws_names_str += '%s,' % self._myRawDataWSDict[key].name() + + for key in self._mySpiceTableDict.keys(): + if key[0] == exp_number: + ws_names_str += '%s,' % self._mySpiceTableDict[key].name() + + # Check + if len(ws_names_str) == 0: + return False, 'No workspace is found for experiment %d.' % exp_number + + # Remove last ',' + ws_names_str = ws_names_str[:-1] + + # Group + api.GroupWorkspaces(InputWorkspaces=ws_names_str, + OutputWorkspace=group_name) + + return + + def merge_pts_in_scan(self, exp_no, scan_no, target_ws_name, target_frame): + """ + Merge Pts in Scan + All the workspaces generated as internal results will be grouped + :param exp_no: + :param scan_no: + :param target_ws_name: + :param target_frame: + :return: (merged workspace name, workspace group name) + """ + # Check + if exp_no is None: + exp_no = self._expNumber + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + assert isinstance(target_frame, str) + assert isinstance(target_ws_name, str) + + ub_matrix_1d = None + + # Target frame + if target_frame.lower().startswith('hkl'): + target_frame = 'hkl' + ub_matrix_1d = self._myUBMatrixDict[self._expNumber].reshape(9,) + elif target_frame.lower().startswith('q-sample'): + target_frame = 'qsample' + + else: + raise RuntimeError('Target frame %s is not supported.' % target_frame) + + # Process data and save + status, pt_num_list = self.get_pt_numbers(exp_no, scan_no, True) + if status is False: + err_msg = pt_num_list + return False, err_msg + else: + print '[DB] Number of Pts for Scan %d is %d' % (scan_no, len(pt_num_list)) + print '[DB] Data directory: %s' % self._dataDir + max_pts = 0 + ws_names_str = '' + ws_names_to_group = '' + + for pt in pt_num_list: + try: + self.download_spice_xml_file(scan_no, pt, overwrite=False) + api.CollectHB3AExperimentInfo(ExperimentNumber=exp_no, ScanList='%d' % scan_no, PtLists='-1,%d' % pt, + DataDirectory=self._dataDir, + GenerateVirtualInstrument=False, + OutputWorkspace='ScanPtInfo_Exp%d_Scan%d' % (exp_no, scan_no), + DetectorTableWorkspace='MockDetTable') + + out_q_name = 'HB3A_Exp%d_Scan%d_Pt%d_MD' % (exp_no, scan_no, pt) + api.ConvertCWSDExpToMomentum(InputWorkspace='ScanPtInfo_Exp406_Scan%d' % scan_no, + CreateVirtualInstrument=False, + OutputWorkspace=out_q_name, + Directory=self._dataDir) + + ws_names_to_group += out_q_name + ',' + if target_frame == 'hkl': + out_hkl_name = 'HKL_Scan%d_Pt%d' % (scan_no, pt) + api.ConvertCWSDMDtoHKL(InputWorkspace=out_q_name, + UBMatrix=ub_matrix_1d, + OutputWorkspace=out_hkl_name) + ws_names_str += out_hkl_name + ',' + ws_names_to_group += out_hkl_name + ',' + else: + ws_names_str += out_q_name + ',' + + except RuntimeError as e: + print '[Error] Reducing scan %d pt %d due to %s' % (scan_no, pt, str(e)) + continue + + else: + max_pts = pt + # END-FOR + + # Merge + if target_frame == 'qsample': + out_ws_name = target_ws_name + '_QSample' + elif target_frame == 'hkl': + out_ws_name = target_ws_name + '_HKL' + else: + raise RuntimeError('Impossible to have target frame %s' % target_frame) + + ws_names_str = ws_names_str[:-1] + api.MergeMD(InputWorkspaces=ws_names_str, OutputWorkspace=out_ws_name, SplitInto=max_pts) + + # Group workspaces + group_name = 'Group_Exp406_Scan%d' % scan_no + api.GroupWorkspaces(InputWorkspaces=ws_names_to_group, OutputWorkspace=group_name) + spice_table_name = get_spice_table_name(exp_no, scan_no) + api.GroupWorkspaces(InputWorkspaces='%s,%s' % (group_name, spice_table_name), OutputWorkspace=group_name) + + ret_tup = out_ws_name, group_name + + return ret_tup + + def set_server_url(self, server_url): + """ + Set URL for server to download the data + :param server_url: + :return: + """ + # Server URL must end with '/' + self._myServerURL = str(server_url) + if self._myServerURL.endswith('/') is False: + self._myServerURL += '/' + + # Test URL valid or not + is_url_good = False + error_message = None + try: + result = urllib2.urlopen(self._myServerURL) + except urllib2.HTTPError, err: + error_message = str(err.code) + except urllib2.URLError, err: + error_message = str(err.args) + else: + is_url_good = True + result.close() + + if error_message is None: + error_message = '' + else: + error_message = 'Unable to open data server URL: %s due to %s.' % (server_url, error_message) + + return is_url_good, error_message + + def setWebAccessMode(self, mode): + """ + Set data access mode form server + :param mode: + :return: + """ + if isinstance(mode, str) is False: + raise RuntimeError('Input mode is not string') + + if mode == 'cache': + self._cacheDataOnly = True + elif mode == 'download': + self._cacheDataOnly = False + + return + + def set_local_data_dir(self, local_dir): + """ + Set local data storage + :param local_dir: + :return: + """ + # Get absolute path + if os.path.isabs(local_dir) is False: + # Input is relative path to current working directory + cwd = os.getcwd() + local_dir = os.path.join(cwd, local_dir) + + # Create cache directory if necessary + if os.path.exists(local_dir) is False: + try: + os.mkdir(local_dir) + except OSError as os_err: + return False, str(os_err) + + # Check whether the target is writable + if os.access(local_dir, os.W_OK) is False: + return False, 'Specified local data directory %s is not writable.' % local_dir + + # Successful + self._dataDir = local_dir + + return True, '' + + def set_ub_matrix(self, exp_number, ub_matrix): + """ + Set up UB matrix to _UBMatrix dictionary + :param exp_number: + :param ub_matrix: + :return: + """ + # Check + if exp_number is None: + exp_number = self._expNumber + + assert isinstance(exp_number, int) + assert isinstance(ub_matrix, numpy.ndarray) + assert ub_matrix.shape == (3, 3) + + # Set up + self._myUBMatrixDict[exp_number] = ub_matrix + + def set_working_directory(self, work_dir): + """ + Set up the directory for working result + :return: (boolean, string) + """ + if os.path.exists(work_dir) is False: + try: + os.mkdir(work_dir) + except OSError as os_err: + return False, 'Unable to create working directory %s due to %s.' % (work_dir, str(os_err)) + elif os.access(work_dir, os.W_OK) is False: + return False, 'User specified working directory %s is not writable.' % work_dir + + self._workDir = work_dir + + return True, '' + + def set_instrument_name(self, instrument_name): + """ + Set instrument name + :param instrument_name: + :return: + """ + # Check + if isinstance(instrument_name, str) is False: + return False, 'Input instrument name is not a string but of type %s.' % str(type(instrument_name)) + if len(instrument_name) == 0: + return False, 'Input instrument name is an empty string.' + + self._instrumentName = instrument_name + + return True, '' + + def set_exp_number(self, exp_number): + """ Add experiment number + :param exp_number: + :return: + """ + assert isinstance(exp_number, int) + self._expNumber = exp_number + + return True + + def set_hkl_to_peak(self, exp_number, scan_number, pt_number): + """ + Get HKL as _h, _k, _l from MDEventWorkspace. It is for HB3A only + :return: + """ + status, peak_info = self.get_peak_info(exp_number, scan_number, pt_number) + if status is False: + err_msg = peak_info + return False, err_msg + + md_ws = self._myPtMDDict[(exp_number, scan_number, pt_number)] + assert md_ws.getNumExperimentInfo() == 1 + exp_info = md_ws.getExperimentInfo(0) + + try: + m_h = float(exp_info.run().getProperty('_h').value) + m_k = float(exp_info.run().getProperty('_k').value) + m_l = float(exp_info.run().getProperty('_l').value) + except RuntimeError as error: + return False, 'Unable to retrieve HKL due to %s.' % (str(error)) + + peak_ws = peak_info.get_peak_workspace() + peak = peak_ws.getPeak(0) + peak.setHKL(m_h, m_k, m_l) + + return True, (m_h, m_k, m_l) + + def _add_raw_workspace(self, exp_no, scan_no, pt_no, raw_ws): + """ Add raw Pt.'s workspace + :param exp_no: + :param scan_no: + :param pt_no: + :param raw_ws: workspace or name of the workspace + :return: None + """ + # Check + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + assert isinstance(pt_no, int) + + if isinstance(raw_ws, str): + # Given by name + matrix_ws = AnalysisDataService.retrieve(raw_ws) + else: + matrix_ws = raw_ws + assert isinstance(matrix_ws, mantid.dataobjects.Workspace2D) + + self._myRawDataWSDict[(exp_no, scan_no, pt_no)] = matrix_ws + + return + + def _add_md_workspace(self, exp_no, scan_no, pt_no, md_ws): + """ + Add MD workspace to storage + :param exp_no: + :param scan_no: + :param pt_no: + :param md_ws: + :return: + """ + # Check input + print '[DB] Type of md_ws is %s.' % str(type(md_ws)) + assert isinstance(md_ws, mantid.dataobjects.MDEventWorkspace) + + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + assert isinstance(pt_no) + + self._myPtMDDict[(exp_no, scan_no, pt_no)] = md_ws + + return + + def _add_ub_peak_ws(self, exp_number, scan_number, pt_number, peak_ws): + """ + Add peak workspace for UB matrix + :param exp_number: + :param scan_number: + :param pt_number: + :param peak_ws: + :return: + """ + # Check + assert isinstance(peak_ws, mantid.dataobjects.PeaksWorkspace) + + assert isinstance(exp_number, int) + assert isinstance(scan_number, int) + assert isinstance(pt_number, int) + + # Add + self._myUBPeakWSDict[(exp_number, scan_number, pt_number)] = peak_ws + + return + + def _add_spice_workspace(self, exp_no, scan_no, spice_table_ws): + """ + """ + assert isinstance(exp_no, int) + assert isinstance(scan_no, int) + assert isinstance(spice_table_ws, mantid.dataobjects.TableWorkspace) + self._mySpiceTableDict[(exp_no, scan_no)] = spice_table_ws + + return + + def _get_spice_workspace(self, exp_no, scan_no): + """ Get SPICE's scan table workspace + :param exp_no: + :param scan_no: + :return: Table workspace or None + """ + try: + ws = self._mySpiceTableDict[(exp_no, scan_no)] + except KeyError: + print '[DB] Keys to SPICE TABLE: %s' % str(self._mySpiceTableDict.keys()) + return None + + return ws + + def get_raw_data_workspace(self, exp_no, scan_no, pt_no): + """ Get raw workspace + """ + try: + ws = self._myRawDataWSDict[(exp_no, scan_no, pt_no)] + assert isinstance(ws, mantid.dataobjects.Workspace2D) + except KeyError: + return None + + return ws + + def _get_pt_list_from_spice_table(self, spice_table_ws): + """ + Get list of Pt. from a SPICE table workspace + :param spice_table_ws: SPICE table workspace + :return: list of Pt. + """ + numrows = spice_table_ws.rowCount() + ptlist = [] + for irow in xrange(numrows): + ptno = int(spice_table_ws.cell(irow, 0)) + ptlist.append(ptno) + + return ptlist + + +def get_spice_file_name(exp_number, scan_number): + """ + Get standard HB3A SPICE file name from experiment number and scan number + :param exp_number: + :param scan_num: + :return: + """ + file_name = 'HB3A_exp%04d_scan%04d.dat' % (exp_number, scan_number) + + return file_name + + +def get_spice_table_name(exp_number, scan_number): + """ Form the name of the table workspace for SPICE + :param exp_number: + :param scan_number: + :return: + """ + table_name = 'HB3A_%03d_%04d_SpiceTable' % (exp_number, scan_number) + + return table_name + + +def get_raw_data_workspace_name(exp_number, scan_number, pt_number): + """ Form the name of the matrix workspace to which raw pt. XML file is loaded + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'HB3A_exp%d_scan%04d_%04d' % (exp_number, scan_number, pt_number) + + return ws_name + + +def get_pt_info_ws_name(exp_number, scan_number): + """ + Information table workspace'name from CollectHB3AInfo + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'ScanPtInfo_Exp%d_Scan%d' % (exp_number, scan_number) + + return ws_name + + +def get_single_pt_peak_ws_name(exp_number, scan_number, pt_number): + """ + Form the name of the peak workspace + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'Peak_Exp%d_Scan%d_Pt%d' % (exp_number, scan_number, pt_number) + + return ws_name + + +def get_single_pt_md_name(exp_number, scan_number, pt_number): + """ Form the name of the MDEvnetWorkspace for a single Pt. measurement + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'HB3A_Exp%d_Scan%d_Pt%d_MD' % (exp_number, scan_number, pt_number) + + return ws_name + + +def get_virtual_instrument_table_name(exp_number, scan_number, pt_number): + """ + Generate the name of the table workspace containing the virtual instrument information + :param exp_number: + :param scan_number: + :param pt_number: + :return: + """ + ws_name = 'VirtualInstrument_Exp%d_Scan%d_Pt%d_Table' % (exp_number, scan_number, pt_number) + + return ws_name diff --git a/scripts/HFIR_4Circle_Reduction/reduce4circleGUI.py b/scripts/HFIR_4Circle_Reduction/reduce4circleGUI.py new file mode 100644 index 0000000000000000000000000000000000000000..7009c1e8b8ec763ec515120f212242332cf5e2b9 --- /dev/null +++ b/scripts/HFIR_4Circle_Reduction/reduce4circleGUI.py @@ -0,0 +1,1092 @@ +#pylint: disable=invalid-name,relative-import,W0611,R0921,R0902,R0904,R0921,C0302 +################################################################################ +# +# MainWindow application for reducing HFIR 4-circle +# +################################################################################ +import os +import math +import csv +import time + +from PyQt4 import QtCore, QtGui + +import reduce4circleControl as r4c +import guiutility as gutil +import fourcircle_utility as fcutil + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + def _fromUtf8(s): + return s + +# import line for the UI python class +from ui_MainWindow import Ui_MainWindow + + +class MainWindow(QtGui.QMainWindow): + """ Class of Main Window (top) + """ + def __init__(self, parent=None): + """ Initialization and set up + """ + # Base class + QtGui.QMainWindow.__init__(self,parent) + + # UI Window (from Qt Designer) + self.ui = Ui_MainWindow() + self.ui.setupUi(self) + + # Mantid configuration + self._instrument = str(self.ui.comboBox_instrument.currentText()) + # config = ConfigService.Instance() + # self._instrument = config["default.instrument"] + + # Event handling definitions + # Top + self.connect(self.ui.pushButton_setExp, QtCore.SIGNAL('clicked()'), + self.do_set_experiment) + + # Tab 'Data Access' + self.connect(self.ui.pushButton_applySetup, QtCore.SIGNAL('clicked()'), + self.do_apply_setup) + self.connect(self.ui.pushButton_browseLocalDataDir, QtCore.SIGNAL('clicked()'), + self.do_browse_local_spice_data) + self.connect(self.ui.pushButton_testURLs, QtCore.SIGNAL('clicked()'), + self.do_test_url) + self.connect(self.ui.pushButton_ListScans, QtCore.SIGNAL('clicked()'), + self.do_list_scans) + self.connect(self.ui.pushButton_downloadExpData, QtCore.SIGNAL('clicked()'), + self.do_download_spice_data) + self.connect(self.ui.comboBox_mode, QtCore.SIGNAL('currentIndexChanged(int)'), + self.change_data_access_mode) + + # Tab 'View Raw Data' + self.connect(self.ui.pushButton_setScanInfo, QtCore.SIGNAL('clicked()'), + self.do_load_scan_info) + self.connect(self.ui.pushButton_plotRawPt, QtCore.SIGNAL('clicked()'), + self.do_plot_pt_raw) + self.connect(self.ui.pushButton_prevPtNumber, QtCore.SIGNAL('clicked()'), + self.do_plot_prev_pt_raw) + self.connect(self.ui.pushButton_nextPtNumber, QtCore.SIGNAL('clicked()'), + self.do_plot_next_pt_raw) + self.connect(self.ui.pushButton_showPtList, QtCore.SIGNAL('clicked()'), + self.show_scan_pt_list) + self.connect(self.ui.pushButton_usePt4UB, QtCore.SIGNAL('clicked()'), + self.do_add_peak_to_find) + + # Tab 'calculate ub matrix' + self.connect(self.ui.pushButton_findPeak, QtCore.SIGNAL('clicked()'), + self.do_find_peak) + self.connect(self.ui.pushButton_addPeakToCalUB, QtCore.SIGNAL('clicked()'), + self.do_add_ub_peak) + self.connect(self.ui.pushButton_calUB, QtCore.SIGNAL('clicked()'), + self.do_cal_ub_matrix) + self.connect(self.ui.pushButton_acceptUB, QtCore.SIGNAL('clicked()'), + self.doAcceptCalUB) + self.connect(self.ui.pushButton_indexUBPeaks, QtCore.SIGNAL('clicked()'), + self.do_index_ub_peaks) + self.connect(self.ui.pushButton_deleteUBPeak, QtCore.SIGNAL('clicked()'), + self.do_del_ub_peaks) + self.connect(self.ui.pushButton_clearUBPeakTable, QtCore.SIGNAL('clicked()'), + self.do_clear_ub_peaks) + self.connect(self.ui.pushButton_resetPeakHKLs, QtCore.SIGNAL('clicked()'), + self.do_reset_ub_peaks_hkl) + + # Tab 'Slice View' + self.connect(self.ui.pushButton_setUBSliceView, QtCore.SIGNAL('clicked()'), + self.do_set_ub_sv) + self.connect(self.ui.pushButton_process4SliceView, QtCore.SIGNAL('clicked()'), + self.do_merge_scans) + + # Tab 'Advanced' + self.connect(self.ui.pushButton_useDefaultDir, QtCore.SIGNAL('clicked()'), + self.do_setup_dir_default) + self.connect(self.ui.pushButton_browseLocalCache, QtCore.SIGNAL('clicked()'), + self.do_browse_local_cache_dir) + self.connect(self.ui.pushButton_browseWorkDir, QtCore.SIGNAL('clicked()'), + self.do_browse_working_dir) + self.connect(self.ui.comboBox_instrument, QtCore.SIGNAL('currentIndexChanged(int)'), + self.change_instrument_name) + + # Refine UB matrix + self.connect(self.ui.pushButton_addToRefine, QtCore.SIGNAL('clicked()'), + self.do_refine_ub) + self.connect(self.ui.pushButton_addAllRefineUB, QtCore.SIGNAL('clicked()'), + self.do_refine_ub) + self.connect(self.ui.pushButton_acceptRefinedUB, QtCore.SIGNAL('clicked()'), + self.do_refine_ub) + self.connect(self.ui.pushButton_resetRefinedUB, QtCore.SIGNAL('clicked()'), + self.do_refine_ub) + + # Tab 'Integrate Peaks' + self.connect(self.ui.pushButton_integratePeak, QtCore.SIGNAL('clicked()'), + self.do_integrate_peaks) + + # Menu + self.connect(self.ui.actionExit, QtCore.SIGNAL('triggered()'), + self.menu_quit) + + self.connect(self.ui.actionSave_Session, QtCore.SIGNAL('triggered()'), + self.save_current_session) + self.connect(self.ui.actionLoad_Session, QtCore.SIGNAL('triggered()'), + self.load_session) + + # Event handling for tab 'refine ub matrix' + self.connect(self.ui.pushButton_addToRefine, QtCore.SIGNAL('clicked()'), + self.doAddScanPtToRefineUB) + + # Validator ... (NEXT) + + # Declaration of class variable + # some configuration + self._homeSrcDir = os.getcwd() + self._homeDir = os.getcwd() + + # Control + self._myControl = r4c.CWSCDReductionControl(self._instrument) + self._allowDownload = True + self._dataAccessMode = 'Download' + + # Initial setup + self.ui.tabWidget.setCurrentIndex(0) + self.ui.tabWidget.setTabEnabled(4, False) + self.ui.tabWidget.setTabEnabled(5, False) + self._init_ub_table() + self.ui.radioButton_ubFromTab1.setChecked(True) + + # Tab 'Access' + self.ui.lineEdit_url.setText('http://neutron.ornl.gov/user_data/hb3a/') + self.ui.comboBox_mode.setCurrentIndex(0) + self.ui.lineEdit_localSpiceDir.setEnabled(True) + self.ui.pushButton_browseLocalDataDir.setEnabled(True) + + return + + def do_integrate_peaks(self): + """ + + :return: + """ + raise RuntimeError('ASAP') + + def do_refine_ub(self): + """ + + :return: + """ + raise RuntimeError('Next Release') + + def _init_ub_table(self): + """ DOC + :return: + """ + # UB-peak table + # NOTE: have to call this because pyqt set column and row to 0 after __init__ + # thus a 2-step initialization has to been adopted + self.ui.tableWidget_peaksCalUB.setup() + + self.ui.tableWidget_ubMatrix.setup() + self.ui.tableWidget_ubSiceView.setup() + self.ui.tableWidget_refinedUB.setup() + + self.ui.tableWidget_sliceViewProgress.setup() + + return + + def change_data_access_mode(self): + """ Change data access mode between downloading from server and local + Event handling methods + :return: + """ + new_mode = str(self.ui.comboBox_mode.currentText()) + self._dataAccessMode = new_mode + + if new_mode.startswith('Local') is True: + self.ui.lineEdit_localSpiceDir.setEnabled(True) + self.ui.pushButton_browseLocalDataDir.setEnabled(True) + self.ui.lineEdit_url.setEnabled(False) + self.ui.lineEdit_localSrcDir.setEnabled(False) + self.ui.pushButton_browseLocalCache.setEnabled(False) + self._allowDownload = False + else: + self.ui.lineEdit_localSpiceDir.setEnabled(False) + self.ui.pushButton_browseLocalDataDir.setEnabled(False) + self.ui.lineEdit_url.setEnabled(True) + self.ui.lineEdit_localSrcDir.setEnabled(True) + self.ui.pushButton_browseLocalCache.setEnabled(True) + self._allowDownload = True + + return + + def change_instrument_name(self): + """ Handing the event as the instrument name is changed + :return: + """ + new_instrument = str(self.ui.comboBox_instrument.currentText()) + self.pop_one_button_dialog('Change of instrument during data processing is dangerous.') + status, error_message = self._myControl.set_instrument_name(new_instrument) + if status is False: + self.pop_one_button_dialog(error_message) + + return + + def do_add_ub_peak(self): + """ Add current to ub peaks + :return: + """ + # Add peak + status, int_list = gutil.parse_integers_editors([self.ui.lineEdit_exp, + self.ui.lineEdit_scanNumber, + self.ui.lineEdit_ptNumber]) + if status is False: + self.pop_one_button_dialog(int_list) + exp_no, scan_no, pt_no = int_list + + # Get HKL from GUI + status, float_list = gutil.parse_float_editors([self.ui.lineEdit_H, + self.ui.lineEdit_K, + self.ui.lineEdit_L]) + if status is False: + err_msg = float_list + self.pop_one_button_dialog(err_msg) + return + h, k, l = float_list + + status, peak_info_obj = self._myControl.get_peak_info(exp_no, scan_no, pt_no) + if status is False: + error_message = peak_info_obj + self.pop_one_button_dialog(error_message) + return + assert isinstance(peak_info_obj, r4c.PeakInfo) + + if self.ui.checkBox_roundHKLInt.isChecked(): + h = math.copysign(1, h)*int(abs(h)+0.5) + k = math.copysign(1, k)*int(abs(k)+0.5) + l = math.copysign(1, l)*int(abs(l)+0.5) + peak_info_obj.set_user_hkl(h, k, l) + self.set_ub_peak_table(peak_info_obj) + + # Clear + self.ui.lineEdit_scanNumber.setText('') + self.ui.lineEdit_ptNumber.setText('') + + self.ui.lineEdit_sampleQx.setText('') + self.ui.lineEdit_sampleQy.setText('') + self.ui.lineEdit_sampleQz.setText('') + + self.ui.lineEdit_H.setText('') + self.ui.lineEdit_K.setText('') + self.ui.lineEdit_L.setText('') + + return + + def doAcceptCalUB(self): + """ Accept the calculated UB matrix + """ + raise RuntimeError('ASAP') + return + + def doAddScanPtToRefineUB(self): + """ Add scan/pt numbers to the list of data points for refining ub matrix + + And the added scan number and pt numbers will be reflected in the (left sidebar) + + """ + raise RuntimeError("ASAP") + + def do_add_peak_to_find(self): + """ + Add the scan/pt to the next + :return: + """ + scan_no = self.ui.lineEdit_run.text() + pt_no = self.ui.lineEdit_rawDataPtNo.text() + + self.ui.lineEdit_scanNumber.setText(scan_no) + self.ui.lineEdit_ptNumber.setText(pt_no) + + self.ui.tabWidget.setCurrentIndex(2) + + def do_browse_local_cache_dir(self): + """ Browse local cache directory + :return: + """ + local_cache_dir = str(QtGui.QFileDialog.getExistingDirectory(self, + 'Get Local Cache Directory', + self._homeSrcDir)) + + # Set local directory to control + status, error_message = self._myControl.set_local_data_dir(local_cache_dir) + if status is False: + self.pop_one_button_dialog(error_message) + return + + # Synchronize to local data/spice directory and local cache directory + if str(self.ui.lineEdit_localSpiceDir.text()) != '': + prev_dir = str(self.ui.lineEdit_localSrcDir.text()) + self.pop_one_button_dialog('Local data directory was set up as %s' % + prev_dir) + self.ui.lineEdit_localSrcDir.setText(local_cache_dir) + self.ui.lineEdit_localSpiceDir.setText(local_cache_dir) + + return + + def do_browse_local_spice_data(self): + """ Browse local source SPICE data directory + """ + src_spice_dir = str(QtGui.QFileDialog.getExistingDirectory(self, 'Get Directory', + self._homeSrcDir)) + # Set local data directory to controller + status, error_message = self._myControl.set_local_data_dir(src_spice_dir) + if status is False: + self.pop_one_button_dialog(error_message) + return + + self._homeSrcDir = src_spice_dir + self.ui.lineEdit_localSpiceDir.setText(src_spice_dir) + + return + + def do_browse_working_dir(self): + """ + Browse and set up working directory + :return: + """ + work_dir = str(QtGui.QFileDialog.getExistingDirectory(self, 'Get Working Directory', self._homeDir)) + status, error_message = self._myControl.set_working_directory(work_dir) + if status is False: + self.pop_one_button_dialog(error_message) + else: + self.ui.lineEdit_workDir.setText(work_dir) + + return + + def do_cal_ub_matrix(self): + """ Calculate UB matrix by 2 or 3 reflections + """ + # Get reflections + num_rows = self.ui.tableWidget_peaksCalUB.rowCount() + peak_info_list = list() + status, exp_number = gutil.parse_integers_editors(self.ui.lineEdit_exp) + for i_row in xrange(num_rows): + if self.ui.tableWidget_peaksCalUB.is_selected(i_row) is True: + scan_num, pt_num = self.ui.tableWidget_peaksCalUB.get_exp_info(i_row) + status, peak_info = self._myControl.get_peak_info(exp_number, scan_num, pt_num) + peak_info.set_peak_ws_hkl_from_user() + if status is False: + self.pop_one_button_dialog(peak_info) + return + assert isinstance(peak_info, r4c.PeakInfo) + peak_info_list.append(peak_info) + # END-FOR + + # Get lattice + status, ret_obj = self._get_lattice_parameters() + if status is True: + a, b, c, alpha, beta, gamma = ret_obj + else: + err_msg = ret_obj + self.pop_one_button_dialog(err_msg) + return + + # Calculate UB matrix + status, ub_matrix = self._myControl.calculate_ub_matrix(peak_info_list, a, b, c, + alpha, beta, gamma) + + # Deal with result + if status is True: + self._show_ub_matrix(ub_matrix) + else: + err_msg = ub_matrix + self.pop_one_button_dialog(err_msg) + + return + + def do_clear_ub_peaks(self): + """ + Clear all peaks in UB-Peak table + :return: + """ + self.ui.tableWidget_peaksCalUB.clear() + + return + + def do_del_ub_peaks(self): + """ + Delete a peak in UB-Peak table + :return: + """ + # Find out the lines to get deleted + row_num_list = self.ui.tableWidget_peaksCalUB.get_selected_rows() + print '[DB] Row %s are selected' % str(row_num_list) + + # Delete + self.ui.tableWidget_peaksCalUB.delete_rows(row_num_list) + + return + + def do_download_spice_data(self): + """ Download SPICE data + :return: + """ + # Check scans to download + scan_list_str = str(self.ui.lineEdit_downloadScans.text()) + if len(scan_list_str) > 0: + # user specifies scans to download + valid, scan_list = fcutil.parse_int_array(scan_list_str) + if valid is False: + error_message = scan_list + self.pop_one_button_dialog(error_message) + else: + # Get all scans + status, ret_obj = gutil.parse_integers_editors([self.ui.lineEdit_exp]) + if status is False: + self.pop_one_button_dialog(ret_obj) + return + exp_no = ret_obj + assert isinstance(exp_no, int) + server_url = str(self.ui.lineEdit_url.text()) + scan_list = fcutil.get_scans_list(server_url, exp_no, return_list=True) + self.pop_one_button_dialog('Going to download scans %s.' % str(scan_list)) + + # Check location + destination_dir = str(self.ui.lineEdit_localSrcDir.text()) + status, error_message = self._myControl.set_local_data_dir(destination_dir) + if status is False: + self.pop_one_button_dialog(error_message) + else: + self.pop_one_button_dialog('Spice files will be downloaded to %s.' % destination_dir) + + # Set up myControl for downloading data + exp_no = int(self.ui.lineEdit_exp.text()) + self._myControl.set_exp_number(exp_no) + + server_url = str(self.ui.lineEdit_url.text()) + status, error_message = self._myControl.set_server_url(server_url) + if status is False: + self.pop_one_button_dialog(error_message) + return + + # Download + self._myControl.download_data_set(scan_list) + + return + + def do_find_peak(self): + """ Find peak in a given scan/pt and record it + """ + # Get experiment, scan and pt + status, ret_obj = gutil.parse_integers_editors([self.ui.lineEdit_exp, + self.ui.lineEdit_scanNumber, + self.ui.lineEdit_ptNumber]) + if status is True: + exp_no, scan_no, pt_no = ret_obj + else: + self.pop_one_button_dialog(ret_obj) + return + + # Find peak + status, err_msg = self._myControl.find_peak(exp_no, scan_no, pt_no) + if status is False: + self.pop_one_button_dialog(ret_obj) + return + if self.ui.checkBox_loadHKLfromFile.isChecked() is True: + # This is the first time that in the workflow to get HKL from MD workspace + status, err_msg = self._myControl.set_hkl_to_peak(exp_no, scan_no, pt_no) + if status is False: + self.pop_one_button_dialog('Unable to locate peak info due to %s.' % err_msg) + + # Set up correct values to table tableWidget_peaksCalUB + self._myControl.add_peak_info(exp_no, scan_no, pt_no) + status, peak_info = self._myControl.get_peak_info(exp_no, scan_no, pt_no) + if status is False: + err_msg = peak_info + raise KeyError(err_msg) + assert isinstance(peak_info, r4c.PeakInfo) + + # Set the HKL value from PeakInfo directly + # BAD PROGRAMMING! THERE ARE TOO MANY WAYS TO ACCESS STORED HKL + h, k, l = peak_info.get_peak_ws_hkl() + self.ui.lineEdit_H.setText('%.2f' % h) + self.ui.lineEdit_K.setText('%.2f' % k) + self.ui.lineEdit_L.setText('%.2f' % l) + + q_sample = peak_info.getQSample() + self.ui.lineEdit_sampleQx.setText('%.5E' % q_sample[0]) + self.ui.lineEdit_sampleQy.setText('%.5E' % q_sample[1]) + self.ui.lineEdit_sampleQz.setText('%.5E' % q_sample[2]) + + # self.set_ub_peak_table(peak_info) + + return + + def do_index_ub_peaks(self): + """ Index the peaks in the UB matrix peak table + :return: + """ + # Get UB matrix + ub_matrix = self.ui.tableWidget_ubMatrix.get_matrix() + print '[DB] Get UB matrix ', ub_matrix + + # Do it for each peak + num_peaks = self.ui.tableWidget_peaksCalUB.rowCount() + err_msg = '' + for i_peak in xrange(num_peaks): + scan_no, pt_no = self.ui.tableWidget_peaksCalUB.get_exp_info(i_peak) + status, ret_obj = self._myControl.index_peak(ub_matrix, scan_number=scan_no, + pt_number=pt_no) + if status is True: + new_hkl = ret_obj[0] + error = ret_obj[1] + self.ui.tableWidget_peaksCalUB.set_hkl(i_peak, new_hkl, error) + else: + err_msg += ret_obj + '\n' + # END-FOR + + if len(err_msg) > 0: + self.pop_one_button_dialog(err_msg) + + return + + def do_list_scans(self): + """ List all scans available + :return: + """ + # Experiment number + exp_no = int(self.ui.lineEdit_exp.text()) + + access_mode = str(self.ui.comboBox_mode.currentText()) + if access_mode == 'Local': + spice_dir = str(self.ui.lineEdit_localSpiceDir.text()) + message = fcutil.get_scans_list_local_disk(spice_dir, exp_no) + else: + url = str(self.ui.lineEdit_url.text()) + message = fcutil.get_scans_list(url, exp_no) + + self.pop_one_button_dialog(message) + + return + + def do_load_scan_info(self): + """ Load SIICE's scan file + :return: + """ + # Get scan number + status, ret_obj = gutil.parse_integers_editors([self.ui.lineEdit_run]) + if status is True: + scan_no = ret_obj[0] + else: + err_msg = ret_obj + self.pop_one_button_dialog('Unable to get scan number in raw data tab due to %s.' % err_msg) + return + + status, err_msg = self._myControl.load_spice_scan_file(exp_no=None, scan_no=scan_no) + if status is False: + self.pop_one_button_dialog(err_msg) + + return + + def do_plot_pt_raw(self): + """ Plot the Pt. + """ + # Get measurement pt and the file number + status, ret_obj = gutil.parse_integers_editors([self.ui.lineEdit_exp, + self.ui.lineEdit_run, + self.ui.lineEdit_rawDataPtNo]) + if status is True: + exp_no = ret_obj[0] + scan_no = ret_obj[1] + pt_no = ret_obj[2] + else: + self.pop_one_button_dialog(ret_obj) + return + + # Call to plot 2D + self._plot_raw_xml_2d(exp_no, scan_no, pt_no) + + return + + def do_plot_prev_pt_raw(self): + """ Plot the Pt. + """ + # Get measurement pt and the file number + status, ret_obj = gutil.parse_integers_editors([self.ui.lineEdit_exp, + self.ui.lineEdit_run, + self.ui.lineEdit_rawDataPtNo]) + if status is True: + exp_no = ret_obj[0] + scan_no = ret_obj[1] + pt_no = ret_obj[2] + else: + self.pop_one_button_dialog(ret_obj) + return + + # Previous one + pt_no -= 1 + if pt_no <= 0: + self.pop_one_button_dialog('Pt. = 1 is the first one.') + return + else: + self.ui.lineEdit_rawDataPtNo.setText('%d' % pt_no) + + # Plot + self._plot_raw_xml_2d(exp_no, scan_no, pt_no) + + return + + def do_plot_next_pt_raw(self): + """ Plot the Pt. + """ + # Get measurement pt and the file number + status, ret_obj = gutil.parse_integers_editors([self.ui.lineEdit_exp, + self.ui.lineEdit_run, + self.ui.lineEdit_rawDataPtNo]) + if status is True: + exp_no = ret_obj[0] + scan_no = ret_obj[1] + pt_no = ret_obj[2] + else: + self.pop_one_button_dialog(ret_obj) + return + + # Previous one + pt_no += 1 + # get last Pt. number + status, last_pt_no = self._myControl.get_pt_numbers(exp_no, scan_no) + if status is False: + error_message = last_pt_no + self.pop_one_button_dialog('Unable to access Spice table for scan %d. Reason" %s.' % ( + scan_no, error_message)) + if pt_no > last_pt_no: + self.pop_one_button_dialog('Pt. = %d is the last one of scan %d.' % (pt_no, scan_no)) + return + else: + self.ui.lineEdit_rawDataPtNo.setText('%d' % pt_no) + + # Plot + self._plot_raw_xml_2d(exp_no, scan_no, pt_no) + + return + + def do_merge_scans(self): + """ Process data for slicing view + :return: + """ + # Get UB matrix + ub_matrix = self.ui.tableWidget_ubSiceView.get_matrix() + self._myControl.set_ub_matrix(exp_number=None, ub_matrix=ub_matrix) + + # Get list of scans + scan_list = gutil.parse_integer_list(str(self.ui.lineEdit_listScansSliceView.text())) + if len(scan_list) == 0: + self.pop_one_button_dialog('Scan list is empty.') + + # Set table + self.ui.tableWidget_sliceViewProgress.append_scans(scans=scan_list) + + # Warning + self.pop_one_button_dialog('Data processing is long. Be patient!') + + # Process + base_name = str(self.ui.lineEdit_baseMergeMDName.text()) + scan_list.sort() + frame = str(self.ui.comboBox_mergeScanFrame.currentText()) + for scan_no in scan_list: + # Download/check SPICE file + self._myControl.download_spice_file(None, scan_no, over_write=False) + + # Get some information + status, pt_list = self._myControl.get_pt_numbers(None, scan_no, load_spice_scan=True) + if status is False: + err_msg = pt_list + self.pop_one_button_dialog('Failed to get Pt. number: %s' % err_msg) + return + else: + # Set information to table + err_msg = self.ui.tableWidget_sliceViewProgress.set_scan_pt(scan_no, pt_list) + if len(err_msg) > 0: + self.pop_one_button_dialog(err_msg) + + out_ws_name = base_name + '%04d' % scan_no + self.ui.tableWidget_sliceViewProgress.set_scan_pt(scan_no, 'In Processing') + try: + ret_tup = self._myControl.merge_pts_in_scan(exp_no=None, scan_no=scan_no, + target_ws_name=out_ws_name, + target_frame=frame) + merge_status = 'Done' + merged_name = ret_tup[0] + group_name = ret_tup[1] + except RuntimeError as e: + merge_status = 'Failed. Reason: %s' % str(e) + merged_name = '' + group_name = '' + finally: + self.ui.tableWidget_sliceViewProgress.set_status(scan_no, merge_status) + self.ui.tableWidget_sliceViewProgress.set_ws_names(scan_no, merged_name, group_name) + + # Sleep for a while + time.sleep(0.1) + # END-FOR + + return + + def do_reset_ub_peaks_hkl(self): + """ + Reset user specified HKL value to peak table + :return: + """ + num_rows = self.ui.tableWidget_peaksCalUB.rowCount() + for i_row in xrange(num_rows): + print '[DB] Update row %d' % (i_row) + scan, pt = self.ui.tableWidget_peaksCalUB.get_scan_pt(i_row) + status, peak_info = self._myControl.get_peak_info(None, scan, pt) + if status is False: + error_message = peak_info + raise RuntimeError(error_message) + h, k, l = peak_info.get_user_hkl() + self.ui.tableWidget_peaksCalUB.update_hkl(i_row, h, k, l) + # END-FOR + + return + + def do_set_experiment(self): + """ Set experiment + :return: + """ + status, ret_obj = gutil.parse_integers_editors([self.ui.lineEdit_exp]) + if status is True: + exp_number = ret_obj[0] + curr_exp_number = self._myControl.get_experiment() + if curr_exp_number is not None and exp_number != curr_exp_number: + self.pop_one_button_dialog('Changing experiment to %d. Clean previous experiment %d\'s result' + ' in Mantid manually.' % (exp_number, curr_exp_number)) + self._myControl.set_exp_number(exp_number) + self.ui.lineEdit_exp.setStyleSheet('color: black') + else: + err_msg = ret_obj + self.pop_one_button_dialog('Unable to set experiment as %s' % err_msg) + self.ui.lineEdit_exp.setStyleSheet('color: red') + + self.ui.tabWidget.setCurrentIndex(0) + + return + + def do_set_ub_sv(self): + """ Set UB matrix in Slice view + :return: + """ + if self.ui.radioButton_ubFromTab1.isChecked(): + self.ui.tableWidget_ubSiceView.set_from_matrix(self.ui.tableWidget_ubMatrix.get_matrix()) + elif self.ui.radioButton_ubFromTab3.isChecked(): + self.ui.tableWidget_ubSiceView.set_from_matrix(self.ui.tableWidget_refinedUB.get_matrix()) + elif self.ui.radioButton_ubFromList.isChecked(): + status, ret_obj = gutil.parse_float_array(str(self.ui.plainTextEdit_ubInput.toPlainText())) + if status is False: + self.pop_one_button_dialog(ret_obj) + elif len(ret_obj) != 9: + self.pop_one_button_dialog('Requiring 9 floats for UB matrix. Only %d are given.' % len(ret_obj)) + else: + self.ui.tableWidget_ubSiceView.set_from_list(ret_obj) + else: + self.pop_one_button_dialog('None is selected to set UB matrix.') + + return + + def do_setup_dir_default(self): + """ + Set up default directory for storing data and working + :return: + """ + home_dir = os.path.expanduser('~') + + # Data cache directory + data_cache_dir = os.path.join(home_dir, 'Temp/HB3ATest') + self.ui.lineEdit_localSpiceDir.setText(data_cache_dir) + self.ui.lineEdit_localSrcDir.setText(data_cache_dir) + + work_dir = os.path.join(data_cache_dir, 'Workspace') + self.ui.lineEdit_workDir.setText(work_dir) + + return + + def do_apply_setup(self): + """ + Apply set up ... + :return: + """ + # Local data directory + local_data_dir = str(self.ui.lineEdit_localSpiceDir.text()) + if os.path.exists(local_data_dir) is False: + try: + os.mkdir(local_data_dir) + except OSError as os_error: + self.pop_one_button_dialog('Unable to create local data directory %s due to %s.' % ( + local_data_dir, str(os_error))) + self.ui.lineEdit_localSpiceDir.setStyleSheet("color: red;") + return + else: + self.ui.lineEdit_localSpiceDir.setStyleSheet("color: black;") + # END-IF + + # Working directory + working_dir = str(self.ui.lineEdit_workDir.text()) + if os.path.exists(working_dir) is False: + try: + os.mkdir(working_dir) + except OSError as os_error: + self.pop_one_button_dialog('Unable to create working directory %s due to %s.' % ( + working_dir, str(os_error))) + self.ui.lineEdit_workDir.setStyleSheet("color: red;") + return + else: + self.ui.lineEdit_workDir.setStyleSheet("color: black;") + # END-IF + + # Server URL + data_server = str(self.ui.lineEdit_url.text()) + url_is_good = self.do_test_url() + if url_is_good is False: + self.ui.lineEdit_url.setStyleSheet("color: red;") + return + else: + self.ui.lineEdit_url.setStyleSheet("color: black;") + + # Set to control + self._myControl.set_local_data_dir(local_data_dir) + self._myControl.set_working_directory(working_dir) + self._myControl.set_server_url(data_server) + + return + + def do_test_url(self): + """ Test whether the root URL provided specified is good + """ + url = str(self.ui.lineEdit_url.text()) + + url_is_good, err_msg = fcutil.check_url(url) + if url_is_good is True: + self.pop_one_button_dialog("URL %s is valid." % url) + else: + self.pop_one_button_dialog(err_msg) + + return url_is_good + + def pop_one_button_dialog(self, message): + """ Pop up a one-button dialog + :param message: + :return: + """ + assert isinstance(message, str) + QtGui.QMessageBox.information(self, '4-circle Data Reduction', message) + + return + + def save_current_session(self, filename=None): + """ Save current session/value setup to + :return: + """ + # Set up dictionary + save_dict = dict() + + # Setup + save_dict['lineEdit_localSpiceDir'] = str(self.ui.lineEdit_localSpiceDir.text()) + save_dict['lineEdit_url'] = str(self.ui.lineEdit_url.text()) + save_dict['lineEdit_workDir']= str(self.ui.lineEdit_workDir.text()) + + # Experiment + save_dict['lineEdit_exp'] = str(self.ui.lineEdit_exp.text()) + save_dict['lineEdit_scanNumber'] = self.ui.lineEdit_scanNumber.text() + save_dict['lineEdit_ptNumber'] = str(self.ui.lineEdit_ptNumber.text()) + + # Lattice + save_dict['lineEdit_a'] = str(self.ui.lineEdit_a.text()) + save_dict['lineEdit_b'] = str(self.ui.lineEdit_b.text()) + save_dict['lineEdit_c'] = str(self.ui.lineEdit_c.text()) + save_dict['lineEdit_alpha'] = str(self.ui.lineEdit_alpha.text()) + save_dict['lineEdit_beta'] = str(self.ui.lineEdit_beta.text()) + save_dict['lineEdit_gamma'] = str(self.ui.lineEdit_gamma.text()) + + # Merge scan + save_dict['plainTextEdit_ubInput'] = str(self.ui.plainTextEdit_ubInput.toPlainText()) + save_dict['lineEdit_listScansSliceView'] = str(self.ui.lineEdit_listScansSliceView.text()) + save_dict['lineEdit_baseMergeMDName'] = str(self.ui.lineEdit_baseMergeMDName.text()) + + # Save to csv file + if filename is None: + filename = 'session_backup.csv' + ofile = open(filename, 'w') + writer = csv.writer(ofile) + for key, value in save_dict.items(): + writer.writerow([key, value]) + ofile.close() + + return + + def load_session(self, filename=None): + """ + To load a session, i.e., read it back: + :param filename: + :return: + """ + if filename is None: + filename = 'session_backup.csv' + + in_file = open(filename, 'r') + reader = csv.reader(in_file) + my_dict = dict(x for x in reader) + + # ... + for key, value in my_dict.items(): + if key.startswith('lineEdit') is True: + self.ui.__getattribute__(key).setText(value) + elif key.startswith('plainText') is True: + self.ui.__getattribute__(key).setPlainText(value) + elif key.startswith('comboBox') is True: + self.ui.__getattribute__(key).setCurrentIndex(int(value)) + else: + self.pop_one_button_dialog('Error! Widget name %s is not supported' % key) + # END-FOR + + # ... + self._myControl.set_local_data_dir(str(self.ui.lineEdit_localSpiceDir.text())) + + return + + def menu_quit(self): + """ + + :return: + """ + self.close() + + def show_scan_pt_list(self): + """ Show the range of Pt. in a scan + :return: + """ + # Get parameters + status, inp_list = gutil.parse_integers_editors([self.ui.lineEdit_exp, self.ui.lineEdit_run]) + if status is False: + self.pop_one_button_dialog(inp_list) + return + else: + exp_no = inp_list[0] + scan_no = inp_list[1] + + status, ret_obj = self._myControl.get_pt_numbers(exp_no, scan_no) + + # Form message + if status is False: + # Failed to get Pt. list + error_message = ret_obj + self.pop_one_button_dialog(error_message) + else: + # Form message + pt_list = sorted(ret_obj) + num_pts = len(pt_list) + info = 'Exp %d Scan %d has %d Pt. ranging from %d to %d.\n' % (exp_no, scan_no, num_pts, + pt_list[0], pt_list[-1]) + num_miss_pt = pt_list[-1] - pt_list[0] + 1 - num_pts + if num_miss_pt > 0: + info += 'There are %d Pt. skipped.\n' % num_miss_pt + + self.pop_one_button_dialog(info) + + return + + def set_ub_peak_table(self, peakinfo): + """ + DOC + :param peak_info: + :return: + """ + assert isinstance(peakinfo, r4c.PeakInfo) + + # Get data + exp_number, scan_number, pt_number = peakinfo.getExpInfo() + h, k, l = peakinfo.get_user_hkl() + q_sample = peakinfo.getQSample() + m1 = self._myControl.get_sample_log_value(exp_number, scan_number, pt_number, '_m1') + + # Set to table + status, err_msg = self.ui.tableWidget_peaksCalUB.append_row( + [scan_number, pt_number, h, k, l, q_sample[0], q_sample[1], q_sample[2], False, m1, '']) + if status is False: + self.pop_one_button_dialog(err_msg) + + return + + def _get_lattice_parameters(self): + """ + Get lattice parameters from GUI + :return: (Boolean, Object). True, 6-tuple as a, b, c, alpha, beta, gamm + False: error message + """ + status, ret_list = gutil.parse_float_editors([self.ui.lineEdit_a, + self.ui.lineEdit_b, + self.ui.lineEdit_c, + self.ui.lineEdit_alpha, + self.ui.lineEdit_beta, + self.ui.lineEdit_gamma]) + if status is False: + err_msg = ret_list + err_msg = 'Unable to parse unit cell due to %s' % err_msg + return False, err_msg + + a, b, c, alpha, beta, gamma = ret_list + + return True, (a, b, c, alpha, beta, gamma) + + def _plot_raw_xml_2d(self, exp_no, scan_no, pt_no): + """ Plot raw workspace from XML file for a measurement/pt. + """ + # Check and load SPICE table file + does_exist = self._myControl.does_spice_loaded(exp_no, scan_no) + if does_exist is False: + # Download data + status, error_message = self._myControl.download_spice_file(exp_no, scan_no, over_write=False) + if status is True: + status, error_message = self._myControl.load_spice_scan_file(exp_no, scan_no) + if status is False and self._allowDownload is False: + self.pop_one_button_dialog(error_message) + return + else: + self.pop_one_button_dialog(error_message) + return + # END-IF(does_exist) + + # Load Data for Pt's xml file + does_exist = self._myControl.does_raw_loaded(exp_no, scan_no, pt_no) + + if does_exist is False: + # Check whether needs to download + status, error_message = self._myControl.download_spice_xml_file(scan_no, pt_no, exp_no=exp_no) + if status is False: + self.pop_one_button_dialog(error_message) + return + # Load SPICE xml file + status, error_message = self._myControl.load_spice_xml_file(exp_no, scan_no, pt_no) + if status is False: + self.pop_one_button_dialog(error_message) + return + + # Convert a list of vector to 2D numpy array for imshow() + # Get data and plot + raw_det_data = self._myControl.get_raw_detector_counts(exp_no, scan_no, pt_no) + self.ui.graphicsView.clear_canvas() + self.ui.graphicsView.add_plot_2d(raw_det_data, x_min=0, x_max=256, y_min=0, y_max=256, + hold_prev_image=False) + + return + + def _show_ub_matrix(self, ubmatrix): + """ Show UB matrix + :param ubmatrix: + :return: + """ + assert ubmatrix.shape == (3, 3) + + self.ui.tableWidget_ubMatrix.set_from_matrix(ubmatrix) + + return diff --git a/scripts/SANS/SANSBatchMode.py b/scripts/SANS/SANSBatchMode.py index 3419a9c6cd16d07aede08f6ca2a48e6918fb1d9b..38f01016a7b8b7744b9bdd942883cb8c133268b4 100644 --- a/scripts/SANS/SANSBatchMode.py +++ b/scripts/SANS/SANSBatchMode.py @@ -178,7 +178,8 @@ def BatchReduce(filename, format, plotresults=False, saveAlgs={'SaveRKH':'txt'}, original_settings = settings, original_prop_man_settings = prop_man_settings) except (RunTimeError, ValueError) as e: - sanslog.warning("Error in Batchmode user files: Could not reset the specified user file %s. More info: %s" %(str(run['user_file']),str(e))) + sanslog.warning("Error in Batchmode user files: Could not reset the specified user file %s. More info: %s" %( + str(run['user_file']),str(e))) local_settings = copy.deepcopy(ReductionSingleton().reference()) local_prop_man_settings = ReductionSingleton().settings.clone("TEMP_SETTINGS")