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 &amp;&amp; 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>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;1. Configure the data reduction&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-style:italic;&quot;&gt;(a) Do not modify ServerURL unless necessary;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-style:italic;&quot;&gt;(b) Click 'Apply' to check internet connection and directories;&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-style:italic;&quot;&gt;(c) If 'Time out' is popped out, try to click 'Apply' again. Server may not be able to respond promptly.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-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;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600;&quot;&gt;2. Start to reduce data&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-style:italic;&quot;&gt;(a) Set&lt;/span&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; Experiment&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Directory for local data storage&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Directory for local data storage&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use default set up&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Directory to save outcome of the data reduction&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;URL of the http server to download HB3A data&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Directory to save outcome of the data reduction&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Mode &amp;quot;download&amp;quot;: download data to local disk;&lt;/p&gt;&lt;p&gt;Mode &amp;quot;http server only&amp;quot;: download data to cache, process and delete cached data upon returning&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Cache on local disk.  The dowloaded data will be saved to here. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:400;&quot;&gt;Download scans specified by 'Scans List'; &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:400;&quot;&gt;If 'Scans List' is empty, then the complete experiment data will be downloaded&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use this peak to calcualate UB matrix&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Previous Pt. No&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+                       </property>
+                       <property name="text">
+                        <string>&lt;---|</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>|--&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;For MDEventsWorkspace with merged runs&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;For example:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Type of frame that the final merged scan will be in.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;? columns: &lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;1. Scan number&lt;/p&gt;&lt;p&gt;2. Number of Pts.&lt;/p&gt;&lt;p&gt;3. Status: &lt;/p&gt;&lt;p&gt;(a) done&lt;/p&gt;&lt;p&gt;(b) error with error message&lt;/p&gt;&lt;p&gt;(c) on-going&lt;/p&gt;&lt;p&gt;(d) empty as not yet&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add a data point or data points (defined by scan number and pt number) to the list to refine UB matrix; &lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Scan number can be a list of integers;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Pt number can be empty such that all Pt. of that scan will be searched for peak and then used to refine UB matrix&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Choose the name of the tab, in which the UB matrix is used to convert signals to HKL frame.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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")