Index: src/sas/qtgui/Calculators/GenericScatteringCalculator.py
===================================================================
--- src/sas/qtgui/Calculators/GenericScatteringCalculator.py	(revision 30e0be0b77d88f4352e614a87d289868c9480fc1)
+++ src/sas/qtgui/Calculators/GenericScatteringCalculator.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -33,4 +33,5 @@
     trigger_plot_3d = QtCore.pyqtSignal()
     calculationFinishedSignal = QtCore.pyqtSignal()
+    loadingFinishedSignal = QtCore.pyqtSignal(list)
 
     def __init__(self, parent=None):
@@ -104,4 +105,7 @@
         # plots - 3D in real space
         self.calculationFinishedSignal.connect(self.plot_1_2d)
+
+        # notify main thread about file load complete
+        self.loadingFinishedSignal.connect(self.complete_loading)
 
         # TODO the option Ellipsoid has not been implemented
@@ -167,5 +171,5 @@
                         str(self.datafile))))
                 self.reader = GenReader(path=str(self.datafile), loader=loader,
-                                        completefn=self.complete_loading,
+                                        completefn=self.complete_loading_ex,
                                         updatefn=self.load_update)
                 self.reader.queue()
@@ -184,6 +188,15 @@
         logging.info(status_type)
 
+    def complete_loading_ex(self, data=None):
+        """
+        Send the finish message from calculate threads to main thread
+        """
+        self.loadingFinishedSignal.emit(data)
+
     def complete_loading(self, data=None):
         """ Function used in GenRead"""
+        assert isinstance(data, list)
+        assert len(data)==1
+        data = data[0]
         self.cbShape.setEnabled(False)
         try:
Index: src/sas/qtgui/MainWindow/DataExplorer.py
===================================================================
--- src/sas/qtgui/MainWindow/DataExplorer.py	(revision ebcdb028e10e042622af7e7cce371902d3f511be)
+++ src/sas/qtgui/MainWindow/DataExplorer.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -49,4 +49,10 @@
         self.parent = guimanager
         self.loader = Loader()
+
+        # Read in default locations
+        self.default_save_location = None
+        self.default_load_location = GuiUtils.DEFAULT_OPEN_FOLDER
+        self.default_project_location = None
+
         self.manager = manager if manager is not None else DataManager()
         self.txt_widget = QtWidgets.QTextEdit(None)
@@ -99,4 +105,6 @@
         self.communicator.extMaskEditorSignal.connect(self.extShowEditDataMask)
         self.communicator.changeDataExplorerTabSignal.connect(self.changeTabs)
+        self.communicator.forcePlotDisplaySignal.connect(self.displayData)
+        self.communicator.updateModelFromPerspectiveSignal.connect(self.updateModelFromPerspective)
 
         self.cbgraph.editTextChanged.connect(self.enableGraphCombo)
@@ -205,14 +213,19 @@
         Opens the Qt "Open Folder..." dialog
         """
-        folder = QtWidgets.QFileDialog.getExistingDirectory(self, "Choose a directory", "",
-              QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog)
+        kwargs = {
+            'parent'    : self,
+            'caption'   : 'Choose a directory',
+            'options'   : QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog,
+            'directory' : self.default_load_location
+        }
+        folder = QtWidgets.QFileDialog.getExistingDirectory(**kwargs)
+
         if folder is None:
             return
 
         folder = str(folder)
-
         if not os.path.isdir(folder):
             return
-
+        self.default_load_location = folder
         # get content of dir into a list
         path_str = [os.path.join(os.path.abspath(folder), filename)
@@ -251,4 +264,5 @@
         filename = QtWidgets.QFileDialog.getOpenFileName(**kwargs)[0]
         if filename:
+            self.default_project_location = os.path.dirname(filename)
             self.deleteAllItems()
             self.readProject(filename)
@@ -276,5 +290,6 @@
             'caption'   : 'Save Project',
             'filter'    : 'Project (*.json)',
-            'options'   : QtWidgets.QFileDialog.DontUseNativeDialog
+            'options'   : QtWidgets.QFileDialog.DontUseNativeDialog,
+            'directory' : self.default_project_location
         }
         name_tuple = QtWidgets.QFileDialog.getSaveFileName(**kwargs)
@@ -282,4 +297,5 @@
         if not filename:
             return
+        self.default_project_location = os.path.dirname(filename)
         _, extension = os.path.splitext(filename)
         if not extension:
@@ -616,7 +632,4 @@
         Send selected item data to the current perspective and set the relevant notifiers
         """
-        # Set the signal handlers
-        self.communicator.updateModelFromPerspectiveSignal.connect(self.updateModelFromPerspective)
-
         def isItemReady(index):
             item = self.model.item(index)
@@ -903,7 +916,10 @@
             # Try the current item
             main_data = GuiUtils.dataFromItem(plot_item)
+        # 1D dependent plots of 2D sets - special treatment
+        if isinstance(main_data, Data2D) and isinstance(plot_to_show, Data1D):
+            main_data = None
 
         # Make sure main data for 2D is always displayed
-        if main_data and not self.isPlotShown(main_data):
+        if main_data is not None and not self.isPlotShown(main_data):
             if isinstance(main_data, Data2D):
                 self.plotData([(plot_item, main_data)])
@@ -923,5 +939,5 @@
             # Plots with main data points on the same chart
             # Get the main data plot
-            if main_data and not self.isPlotShown(main_data):
+            if main_data is not None and not self.isPlotShown(main_data):
                 new_plots.append((plot_item, main_data))
             new_plots.append((plot_item, plot_to_show))
@@ -1083,9 +1099,14 @@
         # List of known extensions
         wlist = self.getWlist()
-
         # Location is automatically saved - no need to keep track of the last dir
         # But only with Qt built-in dialog (non-platform native)
-        paths = QtWidgets.QFileDialog.getOpenFileNames(self, "Choose a file", "",
-                wlist, None, QtWidgets.QFileDialog.DontUseNativeDialog)[0]
+        kwargs = {
+            'parent'    : self,
+            'caption'   : 'Choose files',
+            'filter'    : wlist,
+            'options'   : QtWidgets.QFileDialog.DontUseNativeDialog,
+            'directory' : self.default_load_location
+        }
+        paths = QtWidgets.QFileDialog.getOpenFileNames(**kwargs)[0]
         if not paths:
             return
@@ -1094,4 +1115,5 @@
             paths = [paths]
 
+        self.default_load_location = os.path.dirname(paths[0])
         return paths
 
@@ -1729,9 +1751,6 @@
             raise AttributeError(msg)
 
-        # TODO: Assert other properties
-
-        # Reset the view
-        ##self.model.reset()
-        # Pass acting as a debugger anchor
+        # send in the new item
+        self.model.appendRow(model_item)
         pass
 
Index: src/sas/qtgui/MainWindow/GuiManager.py
===================================================================
--- src/sas/qtgui/MainWindow/GuiManager.py	(revision ebcdb028e10e042622af7e7cce371902d3f511be)
+++ src/sas/qtgui/MainWindow/GuiManager.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -51,4 +51,5 @@
 
 from sas.qtgui.Utilities.AddMultEditor import AddMultEditor
+from sas.qtgui.Utilities.ImageViewer import ImageViewer
 
 logger = logging.getLogger(__name__)
@@ -352,4 +353,6 @@
         # Exit if yes
         if reply == QMessageBox.Yes:
+            # save the paths etc.
+            self.saveCustomConfig()
             reactor.callFromThread(reactor.stop)
             return True
@@ -457,5 +460,5 @@
         self._workspace.actionReset.setVisible(False)
         self._workspace.actionStartup_Settings.setVisible(False)
-        self._workspace.actionImage_Viewer.setVisible(False)
+        #self._workspace.actionImage_Viewer.setVisible(False)
         self._workspace.actionCombine_Batch_Fit.setVisible(False)
         # orientation viewer set to invisible SASVIEW-1132
@@ -825,6 +828,12 @@
         """
         """
-        print("actionImage_Viewer TRIGGERED")
-        pass
+        try:
+            self.image_viewer = ImageViewer(self)
+            if sys.platform == "darwin":
+                self.image_viewer.menubar.setNativeMenuBar(False)
+            self.image_viewer.show()
+        except Exception as ex:
+            logging.error(str(ex))
+            return
 
     #============ FITTING =================
@@ -1114,2 +1123,61 @@
         elif isinstance(perspective, Perspectives.PERSPECTIVES["Corfunc"]):
             self.checkAnalysisOption(self._workspace.actionCorfunc)
+
+    def saveCustomConfig(self):
+        """
+        Save the config file based on current session values
+        """
+        # Load the current file
+        config_content = GuiUtils.custom_config
+
+        changed = self.customSavePaths(config_content)
+        changed = changed or self.customSaveOpenCL(config_content)
+
+        if changed:
+            self.writeCustomConfig(config_content)
+
+    def customSavePaths(self, config_content):
+        """
+        Update the config module with current session paths
+        Returns True if update was done, False, otherwise
+        """
+        changed = False
+        # Find load path
+        open_path = GuiUtils.DEFAULT_OPEN_FOLDER
+        defined_path = self.filesWidget.default_load_location
+        if open_path != defined_path:
+            # Replace the load path
+            config_content.DEFAULT_OPEN_FOLDER = defined_path
+            changed = True
+        return changed
+
+    def customSaveOpenCL(self, config_content):
+        """
+        Update the config module with current session OpenCL choice
+        Returns True if update was done, False, otherwise
+        """
+        changed = False
+        # Find load path
+        file_value = GuiUtils.SAS_OPENCL
+        session_value = os.environ.get("SAS_OPENCL", "")
+        if file_value != session_value:
+            # Replace the load path
+            config_content.SAS_OPENCL = session_value
+            changed = True
+        return changed
+
+    def writeCustomConfig(self, config):
+        """
+        Write custom configuration
+        """
+        from sas import make_custom_config_path
+        path = make_custom_config_path()
+        # Just clobber the file - we already have its content read in
+        with open(path, 'w') as out_f:
+            out_f.write("#Application appearance custom configuration\n")
+            for key, item in config.__dict__.items():
+                if key[:2] != "__":
+                    if isinstance(item, str):
+                        item = '"' + item + '"'
+                    out_f.write("%s = %s\n" % (key, str(item)))
+        pass # debugger anchor
Index: src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision 17e2d502f3eee0b33fe32c43efbfb449831ddf14)
+++ src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -2448,9 +2448,6 @@
 
         # If multiple rows selected - toggle all of them, filtering uncheckable
-        # Switch off signaling from the model to avoid recursion
-        self._model_model.blockSignals(True)
         # Convert to proper indices and set requested enablement
         self.setParameterSelection(status)
-        self._model_model.blockSignals(False)
 
         # update the list of parameters to fit
Index: src/sas/qtgui/Perspectives/Fitting/GPUOptions.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/GPUOptions.py	(revision db05c441f1bca349861936b538a8d7e34c9af8a6)
+++ src/sas/qtgui/Perspectives/Fitting/GPUOptions.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -2,8 +2,12 @@
 import os
 import sys
-import sasmodels
 import json
 import platform
 import webbrowser
+import logging
+
+import sasmodels
+import sasmodels.model_test
+import sasmodels.kernelcl
 
 import sas.qtgui.Utilities.GuiUtils as GuiUtils
@@ -26,4 +30,5 @@
         return QtWidgets.QApplication.translate(context, text, disambig)
 
+logger = logging.getLogger(__name__)
 
 class GPUOptions(QtWidgets.QDialog, Ui_GPUOptions):
@@ -106,8 +111,5 @@
                 del os.environ["SAS_OPENCL"]
         # Sasmodels kernelcl doesn't exist when initiated with None
-        if 'sasmodels.kernelcl' in sys.modules:
-            sasmodels.kernelcl.ENV = None
-        from importlib import reload # assumed Python > 3.3
-        reload(sasmodels.core)
+        sasmodels.kernelcl.reset_environment()
         return no_opencl_msg
 
@@ -123,6 +125,5 @@
 
         try:
-            from sasmodels.kernelcl import environment
-            env = environment()
+            env = sasmodels.kernelcl.environment()
             clinfo = [(ctx.devices[0].platform.vendor,
                        ctx.devices[0].platform.version,
@@ -131,5 +132,5 @@
                        ctx.devices[0].version)
                       for ctx in env.context]
-        except ImportError:
+        except Exception:
             clinfo = None
 
@@ -222,11 +223,22 @@
     clinfo = []
     cl_platforms = []
+
     try:
         import pyopencl as cl
-        cl_platforms = cl.get_platforms()
     except ImportError:
-        print("pyopencl import failed. Using only CPU computations")
-    except cl.LogicError as e:
-        print(e.value)
+        cl = None
+
+    if cl is None:
+        logger.warn("Unable to import the pyopencl package.  It may not "
+                    "have been installed.  If you wish to use OpenCL, try "
+                    "running pip install --user pyopencl")
+    else:
+        try:
+            cl_platforms = cl.get_platforms()
+        except cl.LogicError as err:
+            logger.warn("Unable to fetch the OpenCL platforms.  This likely "
+                        "means that the opencl drivers for your system are "
+                        "not installed.")
+            logger.warn(err)
 
     p_index = 0
Index: src/sas/qtgui/Plotting/MaskEditor.py
===================================================================
--- src/sas/qtgui/Plotting/MaskEditor.py	(revision dce68f6de07b9f0e6e1229fad93048531fe97b25)
+++ src/sas/qtgui/Plotting/MaskEditor.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -42,7 +42,7 @@
         layout.setContentsMargins(0, 0, 0, 0)
         self.frame.setLayout(layout)
+
+        self.plotter.plot()
         layout.addWidget(self.plotter)
-
-        self.plotter.plot()
         self.subplot = self.plotter.ax
 
Index: src/sas/qtgui/Plotting/Plotter2D.py
===================================================================
--- src/sas/qtgui/Plotting/Plotter2D.py	(revision f5cec7c827fc0441a124d1b72e24fd24e8d3a5f1)
+++ src/sas/qtgui/Plotting/Plotter2D.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -281,4 +281,5 @@
         new_plot.id = "Circ avg " + self.data.name
         new_plot.is_data = True
+        item = self._item
         if self._item.parent() is not None:
             item = self._item.parent()
@@ -286,4 +287,8 @@
 
         self.manager.communicator.plotUpdateSignal.emit([new_plot])
+
+        self.manager.communicator.forcePlotDisplaySignal.emit([item, new_plot])
+
+        # Show the plot
 
     def setSlicer(self, slicer):
@@ -508,4 +513,14 @@
             self.figure.canvas.draw()
 
+    def imageShow(self, img, origin=None):
+        """
+        Show background image
+        :Param img: [imread(path) from matplotlib.pyplot]
+        """
+        if origin is not None:
+            im = self.ax.imshow(img, origin=origin)
+        else:
+            im = self.ax.imshow(img)
+
     def update(self):
         self.figure.canvas.draw()
Index: src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py	(revision 63467b61b55727b3a1e6bd20f8e86e6b45006844)
+++ src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -148,4 +148,5 @@
         GuiUtils.updateModelItemWithPlot(item, new_plot, new_plot.id)
         self.base.manager.communicator.plotUpdateSignal.emit([new_plot])
+        self.base.manager.communicator.forcePlotDisplaySignal.emit([item, new_plot])
 
         if self.update_model:
Index: src/sas/qtgui/Plotting/Slicers/BoxSlicer.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/BoxSlicer.py	(revision 63467b61b55727b3a1e6bd20f8e86e6b45006844)
+++ src/sas/qtgui/Plotting/Slicers/BoxSlicer.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -188,7 +188,9 @@
         new_plot.id = (self.averager.__name__) + self.base.data.name
         new_plot.is_data = True
+        item = self._item
         if self._item.parent() is not None:
             item = self._item.parent()
         GuiUtils.updateModelItemWithPlot(item, new_plot, new_plot.id)
+        self.base.manager.communicator.forcePlotDisplaySignal.emit([item, new_plot])
 
         if self.update_model:
Index: src/sas/qtgui/Plotting/Slicers/SectorSlicer.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/SectorSlicer.py	(revision 63467b61b55727b3a1e6bd20f8e86e6b45006844)
+++ src/sas/qtgui/Plotting/Slicers/SectorSlicer.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -167,4 +167,5 @@
         new_plot.id = "SectorQ" + self.base.data.name
         new_plot.is_data = True
+        item = self._item
         if self._item.parent() is not None:
             item = self._item.parent()
@@ -172,4 +173,5 @@
 
         self.base.manager.communicator.plotUpdateSignal.emit([new_plot])
+        self.base.manager.communicator.forcePlotDisplaySignal.emit([item, new_plot])
 
         if self.update_model:
Index: src/sas/qtgui/Plotting/UI/MaskEditorUI.ui
===================================================================
--- src/sas/qtgui/Plotting/UI/MaskEditorUI.ui	(revision e20870bce5dcfe5fc479f9b0858c50abf55ae0d0)
+++ src/sas/qtgui/Plotting/UI/MaskEditorUI.ui	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -7,6 +7,6 @@
     <x>0</x>
     <y>0</y>
-    <width>687</width>
-    <height>507</height>
+    <width>824</width>
+    <height>453</height>
    </rect>
   </property>
Index: src/sas/qtgui/Utilities/GenericReader.py
===================================================================
--- src/sas/qtgui/Utilities/GenericReader.py	(revision 749b715f280e81297ccee885592a53f964909d62)
+++ src/sas/qtgui/Utilities/GenericReader.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -41,5 +41,5 @@
         try:
             data = self.loader.read(self.path)
-            self.complete(data=data)
+            self.complete(data=[data])
         except:
             # Thread was interrupted, just proceed and re-raise.
Index: src/sas/qtgui/Utilities/GuiUtils.py
===================================================================
--- src/sas/qtgui/Utilities/GuiUtils.py	(revision 17e2d502f3eee0b33fe32c43efbfb449831ddf14)
+++ src/sas/qtgui/Utilities/GuiUtils.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -162,4 +162,5 @@
     DEFAULT_PERSPECTIVE = custom_config.DEFAULT_PERSPECTIVE
     CLEANUP_PLOT = custom_config.CLEANUP_PLOT
+    SAS_OPENCL = custom_config.SAS_OPENCL
     # custom open_path
     open_folder = custom_config.DEFAULT_OPEN_FOLDER
@@ -182,4 +183,5 @@
     CLEANUP_PLOT = False
     DEFAULT_OPEN_FOLDER = PATH_APP
+    SAS_OPENCL = config.SAS_OPENCL
 
 #DEFAULT_STYLE = config.DEFAULT_STYLE
@@ -294,4 +296,7 @@
     # Plot fitting results (FittingWidget->GuiManager)
     resultPlotUpdateSignal = QtCore.pyqtSignal(list)
+
+    # show the plot as a regular in-workspace object
+    forcePlotDisplaySignal = QtCore.pyqtSignal(list)
 
 def updateModelItemWithPlot(item, update_data, name="", checkbox_state=None):
Index: src/sas/qtgui/Utilities/ImageViewer.py
===================================================================
--- src/sas/qtgui/Utilities/ImageViewer.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
+++ src/sas/qtgui/Utilities/ImageViewer.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -0,0 +1,327 @@
+"""
+Image viewer widget.
+"""
+
+import os
+import logging
+import numpy as np
+import matplotlib
+import matplotlib.image as mpimg
+
+from PyQt5 import QtWidgets
+
+from sas.sascalc.dataloader.manipulations import reader2D_converter
+
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
+from sas.qtgui.Plotting.Plotter2D import Plotter2D
+from sas.qtgui.Plotting.PlotterData import Data2D
+from sas.sascalc.dataloader.data_info import Detector
+
+# Local UI
+from sas.qtgui.Utilities.UI.ImageViewerUI import Ui_ImageViewerUI
+from sas.qtgui.Utilities.UI.ImageViewerOptionsUI import Ui_ImageViewerOptionsUI
+matplotlib.interactive(False)
+
+class ImageViewer(QtWidgets.QMainWindow, Ui_ImageViewerUI):
+    """
+    Implemented as QMainWindow to enable easy menus
+    """
+    def __init__(self, parent=None):
+        super(ImageViewer, self).__init__(parent._parent)
+
+        self.parent = parent
+        self.setupUi(self)
+
+        # Other globals
+        self.plotter = None
+        self.hbox = None
+        self.filename = None
+        self.is_png = False
+        self.image = None
+
+        # disable menu items on empty canvas
+        self.disableMenus()
+
+        # set up menu item triggers
+        self.addTriggers()
+
+    def disableMenus(self):
+        """
+        All menu items but "Load File" and "Help" should be disabled
+        when no data is present
+        """
+        self.actionSave_Image.setEnabled(False)
+        self.actionPrint_Image.setEnabled(False)
+        self.actionCopy_Image.setEnabled(False)
+        self.actionConvert_to_Data.setEnabled(False)
+
+    def enableMenus(self):
+        """
+        Enable all menu items when data is present
+        """
+        self.actionSave_Image.setEnabled(True)
+        self.actionPrint_Image.setEnabled(True)
+        self.actionCopy_Image.setEnabled(True)
+        self.actionConvert_to_Data.setEnabled(True)
+
+    def addTriggers(self):
+        """
+        Trigger definitions for all menu/toolbar actions.
+        """
+        # File
+        self.actionLoad_Image.triggered.connect(self.actionLoadImage)
+        self.actionSave_Image.triggered.connect(self.actionSaveImage)
+        self.actionPrint_Image.triggered.connect(self.actionPrintImage)
+        # Edit
+        self.actionCopy_Image.triggered.connect(self.actionCopyImage)
+        # Image
+        self.actionConvert_to_Data.triggered.connect(self.actionConvertToData)
+        # Help
+        self.actionHow_To.triggered.connect(self.actionHowTo)
+
+    def actionLoadImage(self):
+        """
+        Image loader given files extensions
+        """
+        wildcards = "Images (*.bmp *.gif *jpeg *jpg *.png *tif *.tiff) ;;"\
+                    "Bitmap (*.bmp *.BMP);; "\
+                    "GIF (*.gif *.GIF);; "\
+                    "JPEG (*.jpg  *.jpeg *.JPG *.JPEG);; "\
+                    "PNG (*.png *.PNG);; "\
+                    "TIFF (*.tif *.tiff *.TIF *.TIFF);; "\
+                    "All files (*.*)"
+
+        filepath = QtWidgets.QFileDialog.getOpenFileName(
+            self, "Choose a file", "", wildcards)[0]
+
+        if filepath:
+            self.showImage(filepath)
+
+    def actionSaveImage(self):
+        """
+        Use the internal MPL method for saving to file
+        """
+        if self.plotter is not None:
+            self.plotter.onImageSave()
+
+    def actionPrintImage(self):
+        """
+        Display printer dialog and print the MPL widget area
+        """
+        if self.plotter is not None:
+            self.plotter.onImagePrint()
+
+    def actionCopyImage(self):
+        """
+        Copy MPL widget area to buffer
+        """
+        if self.plotter is not None:
+            self.plotter.onClipboardCopy()
+
+    def actionConvertToData(self):
+        """
+        Show the options dialog and if accepted, send data to conversion
+        """
+        options = ImageViewerOptions(self)
+        if options.exec_() != QtWidgets.QDialog.Accepted:
+            return
+
+        (xmin, xmax, ymin, ymax, zscale) = options.getState()
+        image = self.image
+        try:
+            self.convertImage(image, xmin, xmax, ymin, ymax, zscale)
+        except Exception as ex:
+            err_msg = "Error occurred while converting Image to Data: " + str(ex)
+            logging.error(err_msg)
+
+    def actionHowTo(self):
+        ''' Send the image viewer help URL to the help viewer '''
+        location = "/user/qtgui/Calculators/image_viewer_help.html"
+        self.parent.showHelp(location)
+
+    def addPlotter(self):
+        """
+        Add a new plotter to the frame
+        """
+        self.plotter = Plotter2D(self, quickplot=True)
+
+        # remove existing layout
+        if self.hbox is not None:
+            for i in range(self.hbox.count()):
+                layout_item = self.hbox.itemAt(i)
+                self.hbox.removeItem(layout_item)
+            self.hbox.addWidget(self.plotter)
+        else:
+            # add the plotter to the QLayout
+            self.hbox = QtWidgets.QHBoxLayout()
+            self.hbox.addWidget(self.plotter)
+            self.imgFrame.setLayout(self.hbox)
+
+    def showImage(self, filename):
+        """
+        Show the requested image in the main frame
+        """
+        self.filename = os.path.basename(filename)
+        _, extension = os.path.splitext(self.filename)
+        try:
+            # Note that matplotlib only reads png natively.
+            # Any other formats (tiff, jpeg, etc) are passed
+            # to PIL which seems to have a problem in version
+            # 1.1.7 that causes a close error which shows up in
+            # the log file.  This does not seem to have any adverse
+            # effects.  PDB   --- September 17, 2017.
+            self.image = mpimg.imread(filename)
+            self.is_png = extension.lower() == '.png'
+            self.addPlotter()
+            ax = self.plotter.ax
+            flipped_image = np.flipud(self.image)
+            origin = None
+            if self.is_png:
+                origin = 'lower'
+            self.plotter.imageShow(flipped_image, origin=origin)
+            if not self.is_png:
+                ax.set_ylim(ax.get_ylim()[::-1])
+            ax.set_xlabel('x [pixel]')
+            ax.set_ylabel('y [pixel]')
+            self.plotter.figure.subplots_adjust(left=0.15, bottom=0.1,
+                                                right=0.95, top=0.95)
+            title = 'Picture: ' + self.filename
+            self.setWindowTitle(title)
+            self.plotter.draw()
+        except IOError as ex:
+            err_msg = "Failed to load '%s'.\n" % self.filename
+            logging.error(err_msg+str(ex))
+            return
+        except Exception as ex:
+            err_msg = "Failed to show '%s'.\n" % self.filename
+            logging.error(err_msg+str(ex))
+            return
+
+        # Loading successful - enable menu items
+        self.enableMenus()
+
+    def convertImage(self, rgb, xmin, xmax, ymin, ymax, zscale):
+        """
+        Convert image to data2D
+        """
+        x_len = len(rgb[0])
+        y_len = len(rgb)
+        x_vals = np.linspace(xmin, xmax, num=x_len)
+        y_vals = np.linspace(ymin, ymax, num=y_len)
+        # Instantiate data object
+        output = Data2D()
+        output.filename = self.filename
+        output.id = output.filename
+        detector = Detector()
+        detector.pixel_size.x = None
+        detector.pixel_size.y = None
+        # Store the sample to detector distance
+        detector.distance = None
+        output.detector.append(detector)
+        # Initiazed the output data object
+        output.data = zscale * self.rgb2gray(rgb)
+        output.err_data = np.zeros([x_len, y_len])
+        output.mask = np.ones([x_len, y_len], dtype=bool)
+        output.xbins = x_len
+        output.ybins = y_len
+        output.x_bins = x_vals
+        output.y_bins = y_vals
+        output.qx_data = np.array(x_vals)
+        output.qy_data = np.array(y_vals)
+        output.xmin = xmin
+        output.xmax = xmax
+        output.ymin = ymin
+        output.ymax = ymax
+        output.xaxis('\\rm{Q_{x}}', r'\AA^{-1}')
+        output.yaxis('\\rm{Q_{y}}', r'\AA^{-1}')
+        # Store loading process information
+        output.meta_data['loader'] = self.filename.split('.')[-1] + "Reader"
+        output.is_data = True
+        try:
+            output = reader2D_converter(output)
+        except Exception as ex:
+            err_msg = "Image conversion failed: '%s'.\n" % str(ex)
+            logging.error(err_msg)
+
+        # Create item and add to the data explorer
+        try:
+            item = GuiUtils.createModelItemWithPlot(output, output.filename)
+            self.parent.communicate.updateModelFromPerspectiveSignal.emit(item)
+        except Exception as ex:
+            err_msg = "Failed to create new index '%s'.\n" % str(ex)
+            logging.error(err_msg)
+
+    def rgb2gray(self, rgb):
+        """
+        RGB to Grey
+        """
+        if self.is_png:
+            # png image limits: 0 to 1, others 0 to 255
+            #factor = 255.0
+            rgb = rgb[::-1]
+        if rgb.ndim == 2:
+            grey = np.rollaxis(rgb, axis=0)
+        else:
+            red, green, blue = np.rollaxis(rgb[..., :3], axis=-1)
+            grey = 0.299 * red + 0.587 * green + 0.114 * blue
+        max_i = rgb.max()
+        factor = 255.0/max_i
+        grey *= factor
+        return np.array(grey)
+
+class ImageViewerOptions(QtWidgets.QDialog, Ui_ImageViewerOptionsUI):
+    """
+    Logics for the image viewer options UI
+    """
+    def __init__(self, parent=None):
+        super(ImageViewerOptions, self).__init__(parent)
+
+        self.parent = parent
+        self.setupUi(self)
+
+        # fill in defaults
+        self.addDefaults()
+
+        # add validators
+        self.addValidators()
+
+    def addDefaults(self):
+        """
+        Fill out textedits with default values
+        """
+        zscale_default = 1.0
+        xmin_default = -0.3
+        xmax_default = 0.3
+        ymin_default = -0.3
+        ymax_default = 0.3
+
+        self.txtZmax.setText(str(zscale_default))
+        self.txtXmin.setText(str(xmin_default))
+        self.txtXmax.setText(str(xmax_default))
+        self.txtYmin.setText(str(ymin_default))
+        self.txtYmax.setText(str(ymax_default))
+
+    def addValidators(self):
+        """
+        Define simple validators on line edits
+        """
+        self.txtXmin.setValidator(GuiUtils.DoubleValidator())
+        self.txtXmax.setValidator(GuiUtils.DoubleValidator())
+        self.txtYmin.setValidator(GuiUtils.DoubleValidator())
+        self.txtYmax.setValidator(GuiUtils.DoubleValidator())
+        zvalidator = GuiUtils.DoubleValidator()
+        zvalidator.setBottom(0.0)
+        zvalidator.setTop(255.0)
+        self.txtZmax.setValidator(zvalidator)
+
+    def getState(self):
+        """
+        return current state of the widget
+        """
+        zscale = float(self.txtZmax.text())
+        xmin = float(self.txtXmin.text())
+        xmax = float(self.txtXmax.text())
+        ymin = float(self.txtYmin.text())
+        ymax = float(self.txtYmax.text())
+
+        return (xmin, xmax, ymin, ymax, zscale)
Index: src/sas/qtgui/Utilities/ReportDialog.py
===================================================================
--- src/sas/qtgui/Utilities/ReportDialog.py	(revision cb90b65b5c3464b4b73f28e8c3863bbbfdd3f599)
+++ src/sas/qtgui/Utilities/ReportDialog.py	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -10,4 +10,5 @@
 
 import sas.qtgui.Utilities.GuiUtils as GuiUtils
+import sas.qtgui.Utilities.ObjectLibrary as ObjectLibrary
 
 from sas.qtgui.Utilities.UI.ReportDialogUI import Ui_ReportDialogUI
@@ -27,4 +28,7 @@
 
         self.data_html, self.data_txt, self.data_images = report_list
+        #self.save_location = None
+        #if 'ReportDialog_directory' in ObjectLibrary.listObjects():
+        self.save_location = ObjectLibrary.getObject('ReportDialog_directory')
 
         # Fill in the table from input data
@@ -70,5 +74,8 @@
         """
         # Choose user's home directory
-        location = os.path.expanduser('~')
+        if self.save_location is None:
+            location = os.path.expanduser('~')
+        else:
+            location = self.save_location
         # Use a sensible filename default
         default_name = os.path.join(location, 'fit_report.pdf')
@@ -78,5 +85,5 @@
             'caption'  : 'Save Report',
             # don't use 'directory' in order to remember the previous user choice
-            #'directory': default_name,
+            'directory': default_name,
             'filter'   : 'PDF file (*.pdf);;HTML file (*.html);;Text file (*.txt)',
             'options'  : QtWidgets.QFileDialog.DontUseNativeDialog}
@@ -87,4 +94,7 @@
             return
         extension = filename_tuple[1]
+        self.save_location = os.path.dirname(filename)
+        # lifetime of this widget is short - keep the reference elsewhere
+        ObjectLibrary.addObject('ReportDialog_directory', self.save_location)
 
         try:
Index: src/sas/qtgui/Utilities/UI/ImageViewerOptionsUI.ui
===================================================================
--- src/sas/qtgui/Utilities/UI/ImageViewerOptionsUI.ui	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
+++ src/sas/qtgui/Utilities/UI/ImageViewerOptionsUI.ui	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ImageViewerOptionsUI</class>
+ <widget class="QDialog" name="ImageViewerOptionsUI">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>454</width>
+    <height>246</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Image Conversion Options</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_4">
+   <item row="0" column="0">
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>GroupBox</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_3">
+      <item row="0" column="0">
+       <layout class="QGridLayout" name="gridLayout_2">
+        <item row="0" column="0">
+         <layout class="QGridLayout" name="gridLayout">
+          <item row="0" column="0">
+           <widget class="QLabel" name="label">
+            <property name="text">
+             <string>x values from pixel # to:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1">
+           <widget class="QLabel" name="label_6">
+            <property name="text">
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;X&lt;span style=&quot; vertical-align:sub;&quot;&gt;min&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="2">
+           <widget class="QLineEdit" name="txtXmin"/>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_2">
+            <property name="text">
+             <string>y values from pixel # to:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="QLabel" name="label_7">
+            <property name="text">
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Y&lt;span style=&quot; vertical-align:sub;&quot;&gt;min&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="2">
+           <widget class="QLineEdit" name="txtYmin"/>
+          </item>
+          <item row="2" column="0">
+           <widget class="QLabel" name="label_3">
+            <property name="text">
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;z value range&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="1">
+           <widget class="QLabel" name="label_8">
+            <property name="text">
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;From 0 to:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="2">
+           <widget class="QLineEdit" name="txtZmax"/>
+          </item>
+         </layout>
+        </item>
+        <item row="0" column="1">
+         <layout class="QFormLayout" name="formLayout_2">
+          <item row="0" column="0">
+           <widget class="QLabel" name="label_4">
+            <property name="text">
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;X&lt;span style=&quot; vertical-align:sub;&quot;&gt;max&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1">
+           <widget class="QLineEdit" name="txtXmax"/>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_5">
+            <property name="text">
+             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Y&lt;span style=&quot; vertical-align:sub;&quot;&gt;max&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="QLineEdit" name="txtYmax"/>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QTextBrowser" name="textBrowser">
+     <property name="enabled">
+      <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:'MS Shell Dlg 2'; font-size:7.8pt; 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:8pt; font-weight:600;&quot;&gt;Note&lt;/span&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;:&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:8pt;&quot;&gt;Recommend to use an image with 8 bit Grey scale (and with No. of pixels &amp;lt; 300 x 300).&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:8pt;&quot;&gt;Otherwise, z = 0.299R + 0.587G + 0.114B.&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:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <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="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+  <zorder>groupBox</zorder>
+  <zorder>buttonBox</zorder>
+  <zorder>textBrowser</zorder>
+  <zorder>horizontalSpacer</zorder>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>ImageViewerOptionsUI</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>ImageViewerOptionsUI</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
Index: src/sas/qtgui/Utilities/UI/ImageViewerUI.ui
===================================================================
--- src/sas/qtgui/Utilities/UI/ImageViewerUI.ui	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
+++ src/sas/qtgui/Utilities/UI/ImageViewerUI.ui	(revision 133812c7ffae92cd47338d64c4d757261f78a6a6)
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ImageViewerUI</class>
+ <widget class="QMainWindow" name="ImageViewerUI">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>492</width>
+    <height>418</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Image Viewer</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="0" column="0">
+     <widget class="QFrame" name="imgFrame">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="frameShape">
+       <enum>QFrame::StyledPanel</enum>
+      </property>
+      <property name="frameShadow">
+       <enum>QFrame::Raised</enum>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>492</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionLoad_Image"/>
+    <addaction name="actionSave_Image"/>
+    <addaction name="actionPrint_Image"/>
+   </widget>
+   <widget class="QMenu" name="menuEdit">
+    <property name="title">
+     <string>Edit</string>
+    </property>
+    <addaction name="actionCopy_Image"/>
+   </widget>
+   <widget class="QMenu" name="menuImage">
+    <property name="title">
+     <string>Image</string>
+    </property>
+    <addaction name="actionConvert_to_Data"/>
+   </widget>
+   <widget class="QMenu" name="menuHelp">
+    <property name="layoutDirection">
+     <enum>Qt::RightToLeft</enum>
+    </property>
+    <property name="title">
+     <string>Help</string>
+    </property>
+    <addaction name="actionHow_To"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuEdit"/>
+   <addaction name="menuImage"/>
+   <addaction name="menuHelp"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionLoad_Image">
+   <property name="text">
+    <string>Load Image</string>
+   </property>
+  </action>
+  <action name="actionSave_Image">
+   <property name="text">
+    <string>Save Image</string>
+   </property>
+  </action>
+  <action name="actionPrint_Image">
+   <property name="text">
+    <string>Print Image</string>
+   </property>
+  </action>
+  <action name="actionCopy_Image">
+   <property name="text">
+    <string>Copy</string>
+   </property>
+  </action>
+  <action name="actionConvert_to_Data">
+   <property name="text">
+    <string>Convert to Data</string>
+   </property>
+  </action>
+  <action name="actionHow_To">
+   <property name="text">
+    <string>How To</string>
+   </property>
+  </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
