Index: calculatorview/src/sans/perspectives/calculator/calculator.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/calculator.py	(revision 49a88432b232020e345c0338d12a61270e609198)
+++ calculatorview/src/sans/perspectives/calculator/calculator.py	(revision 3e001f98892a6dd98b2b904148628e4df8c63238)
@@ -66,4 +66,5 @@
         gensans_help = "Generic SANS"
         pyconsole_help = "Python Console."
+        imageviewer_help = "Load an image file and display the image."
         #data_editor_help = "Meta Data Editor"
         return [("Data Operation", 
@@ -80,5 +81,6 @@
                 ("Generic Scattering Calculator", 
                         gensans_help, self.on_gen_model),
-                ("Python Shell/Editor", pyconsole_help, self.on_python_console)]
+                ("Python Shell/Editor", pyconsole_help, self.on_python_console),
+                ("Image Viewer", imageviewer_help, self.on_image_viewer),]
               
     def on_edit_data(self, event):
@@ -162,4 +164,14 @@
         self.put_icon(frame)
         frame.Show(True) 
+
+    def on_image_viewer(self, event):
+        """
+        Get choose an image file dialog
+        
+        :param event: menu event
+        """
+        from sans.perspectives.calculator.image_viewer import ImageView
+        image_view = ImageView(parent=self.parent)
+        image_view.load()
         
     def on_python_console(self, event):
@@ -193,4 +205,2 @@
                 except:
                     pass      
-  
-    
Index: calculatorview/src/sans/perspectives/calculator/gen_scatter_panel.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/gen_scatter_panel.py	(revision 968f72d1d11729248c82139ffacea034e9f6aa03)
+++ calculatorview/src/sans/perspectives/calculator/gen_scatter_panel.py	(revision 3e001f98892a6dd98b2b904148628e4df8c63238)
@@ -662,5 +662,5 @@
         output = self.sld_data  
         #frame_size = wx.Size(470, 470)    
-        self.plot_frame = PlotFrame(self, -1, 'testView')
+        self.plot_frame = PlotFrame(self.parent.parent, -1, 'testView')
         frame = self.plot_frame
         frame.Show(False)
@@ -1790,5 +1790,5 @@
         return flag
 
-class SasGenWindow(wx.Frame):
+class SasGenWindow(wx.MDIChildFrame):
     """
     GEN SAS main window
@@ -1802,5 +1802,5 @@
         kwds['title'] = title
         
-        wx.Frame.__init__(self, parent, *args, **kwds)
+        wx.MDIChildFrame.__init__(self, parent, *args, **kwds)
         self.parent = parent
         self.omfpanel = OmfPanel(parent=self)
@@ -2002,5 +2002,5 @@
         """
         """
-        frame = PlotFrame(self, -1, 'testView', self.scale2d)
+        frame = PlotFrame(self.parent, -1, 'testView', self.scale2d)
         add_icon(self.parent, frame)
         frame.add_plot(plot)
Index: calculatorview/src/sans/perspectives/calculator/help_panel.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/help_panel.py	(revision 318b5bbbcb046db18a8e953dccae04fc7b8a71e0)
+++ calculatorview/src/sans/perspectives/calculator/help_panel.py	(revision 3e001f98892a6dd98b2b904148628e4df8c63238)
@@ -84,4 +84,6 @@
             <li><a href ="pycrust_help.html" 
             target ="showframe">Python Shell</a><br></li>
+            <li><a href ="load_image_help.html" 
+            target ="showframe">Image Viewer</a><br></li>
             </ul>
             </body>
Index: calculatorview/src/sans/perspectives/calculator/image_viewer.py
===================================================================
--- calculatorview/src/sans/perspectives/calculator/image_viewer.py	(revision 3e001f98892a6dd98b2b904148628e4df8c63238)
+++ calculatorview/src/sans/perspectives/calculator/image_viewer.py	(revision 3e001f98892a6dd98b2b904148628e4df8c63238)
@@ -0,0 +1,457 @@
+import os
+import sys
+import wx
+import numpy as np
+import matplotlib
+matplotlib.interactive(False)
+#Use the WxAgg back end. The Wx one takes too long to render
+matplotlib.use('WXAgg')
+from sans.guiframe.local_perspectives.plotting.SimplePlot import PlotFrame
+#import matplotlib.pyplot as plt
+import matplotlib.image as mpimg
+import matplotlib.colors as colors
+from sans.guiframe.events import StatusEvent
+from sans.perspectives.calculator.calculator_widgets import InputTextCtrl
+from sans.dataloader.data_info import Data2D
+from sans.dataloader.data_info import Detector
+from sans.dataloader.manipulations import reader2D_converter
+_BOX_WIDTH = 60
+if sys.platform.count("win32") > 0:
+    _DIALOG_WIDTH = 400
+else:
+    _DIALOG_WIDTH = 480
+
+class ImageView:
+    """
+    Open a file dialog to allow the user to select a given file.
+    Display the loaded data if available.
+    """
+    def __init__(self, parent=None):
+        """
+        Init
+        """
+        self.parent = parent
+        
+    def load(self):
+        """
+        load image files
+        """
+        parent = self.parent
+        if parent == None:
+            location = os.getcwd()
+        else:
+            location = parent._default_save_location
+        path_list = self.choose_data_file(location=location)
+        if path_list == None:
+            return
+        if len(path_list) >= 0 and not(path_list[0]is None):
+            if parent != None:
+                parent._default_save_location = os.path.dirname(path_list[0])
+        err_msg = ''
+        for file_path in path_list:
+            basename = os.path.basename(file_path)
+            _, extension = os.path.splitext(basename)
+            try:
+                img = mpimg.imread(file_path)
+                is_png = extension.lower() == '.png'
+                plot_frame = ImageFrame(parent, -1, basename, img)
+                plot_frame.Show(False)
+                #plot_frame.im_show(img)
+                ax = plot_frame.plotpanel
+                if not is_png:
+                    ax.subplot.set_ylim(ax.subplot.get_ylim()[::-1])
+                ax.subplot.set_xlabel('x [pixel]')
+                ax.subplot.set_ylabel('y [pixel]')
+                ax.figure.subplots_adjust(left=0.15, bottom=0.1, 
+                                          right=0.95, top=0.95)
+                plot_frame.SetTitle('Picture -- %s --'% basename)
+                plot_frame.Show(True)
+                if parent != None:
+                    parent.put_icon(plot_frame)
+            except:
+                err_msg += "Failed to load '%s'.\n"% basename
+        if err_msg:
+            if parent is not None:
+                wx.PostEvent(parent, StatusEvent(status=err_msg, info="error"))
+            else:
+                print err_msg
+
+        
+    def choose_data_file(self, location=None):
+        """
+        Open a file dialog to allow loading a file
+        """
+        parent = self.parent
+        path = None
+        if location == None:
+            location = os.getcwd()
+        wlist = ''
+        elist = ["All images (*.png, *.bmp, *.gif, *.jpg, *.tif, *.tiff) | \
+                *.png; *.bmp; *.gif; *.jpg; *.tif; *.tiff", 
+                "PNG files (*.png) | *.png", 
+                "BMP files (*.bmp) | *.bmp", 
+                "GIF files (*.gif) | *.gif",
+                "JPG files (*.jpg) | *.jpg",
+                "TIF files (*.tif) | *.tif",
+                "TIFF files (*.tiff) | *.tiff"]
+        wlist = '|'.join(elist)        
+        style = wx.OPEN|wx.FD_MULTIPLE
+        dlg = wx.FileDialog(parent, "Image Viewer: Choose a image file", 
+                            location, "", wlist, style=style)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPaths()
+        else:
+            return None
+        dlg.Destroy()
+        return path
+
+class ImageFrame(PlotFrame):
+    """
+    Frame for simple plot
+    """
+    def __init__(self, parent, id, title, image=None, scale='log_{10}', 
+                 size=wx.Size(550, 470)): 
+        """
+        comment
+        :Param data: image array got from imread() of matplotlib [narray]
+        :param parent: parent panel/container
+        """
+        # Initialize the Frame object
+        PlotFrame.__init__(self, parent, id, title, scale, size)
+        self.parent = parent
+        self.data = image 
+        self.file_name = title
+                   
+        menu = wx.Menu()
+        id = wx.NewId()
+        item = wx.MenuItem(menu, id, "&Convert to Data")
+        menu.AppendItem(item)
+        wx.EVT_MENU(self, id, self.on_set_data)
+        self.menu_bar.Append(menu, "&Image")
+
+        menu_help = wx.Menu()
+        id = wx.NewId()
+        item = wx.MenuItem(menu_help, id, "&HowTo")
+        menu_help.AppendItem(item)
+        wx.EVT_MENU(self, id, self.on_help)
+        self.menu_bar.Append(menu_help, "&Help")
+        
+        self.SetMenuBar(self.menu_bar)
+        self.im_show(image)
+    
+    def on_set_data(self, event):
+        """
+        Rescale the x y range, make 2D data and send it to data explore
+        """
+        title = self.file_name
+        self.panel = SetDialog(parent=self, title=title, image=self.data)
+        self.panel.ShowModal()
+
+    def on_help(self, event):    
+        """
+        Image Viewer help panel
+        """
+        from sans.perspectives.calculator.help_panel import  HelpWindow
+        # Get models help model_function path
+        import sans.perspectives.calculator as calmedia
+
+        media = calmedia.get_data_path(media='media')
+        path = os.path.join(media,"load_image_help.html") 
+        name = "Image Viewer"
+        frame = HelpWindow(self, -1, title=' Help: Image Viewer',  
+                           pageToOpen=path, size=(640, 450))   
+        try: 
+            frame.splitter.DetachWindow(frame.lpanel)
+            # Display only the right side one
+            frame.lpanel.Hide() 
+            frame.Show(True)
+        except:
+            frame.Destroy() 
+            msg = 'Display Error\n'
+            info = "Info"
+            wx.MessageBox(msg, info)
+            
+class SetDialog(wx.Dialog):
+    """
+    Dialog for Data Set
+    """
+    def __init__(self, parent, id=-1, title="Convert to Data", image=None, 
+                 size=(_DIALOG_WIDTH, 270)):
+        wx.Dialog.__init__(self, parent, id, title, size)
+        # parent
+        self.parent = parent
+        self.base = parent.parent
+        self.title = title
+        self.image = np.array(image)
+        self.z_ctrl = None
+        self.xy_ctrls = []
+        self.is_png = self._get_is_png()
+        self._build_layout()
+        my_title = "Convert Image to Data - %s -"% self.title
+        self.SetTitle(my_title)
+        self.SetSize(size)
+    
+    def _get_is_png(self):
+        """
+        Get if the image file is png
+        """
+        _, extension = os.path.splitext(self.title)
+        return extension.lower() == '.png'
+            
+    def _build_layout(self):
+        """
+        Layout
+        """
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        zbox = wx.BoxSizer(wx.HORIZONTAL)
+        xbox = wx.BoxSizer(wx.HORIZONTAL)
+        ybox = wx.BoxSizer(wx.HORIZONTAL)
+        btnbox = wx.BoxSizer(wx.VERTICAL)
+        
+        sb_title = wx.StaticBox(self, -1, 'Transform Axes')
+        boxsizer = wx.StaticBoxSizer(sb_title, wx.VERTICAL)
+        z_title = wx.StaticText(self, -1, 'z values (range: 0 - 255) to:')
+        ztime_title = wx.StaticText(self, -1, 'z *')
+        x_title = wx.StaticText(self, -1, 'x values from pixel # to:')
+        xmin_title = wx.StaticText(self, -1, 'xmin:')
+        xmax_title = wx.StaticText(self, -1, 'xmax:')
+        y_title = wx.StaticText(self, -1, 'y values from pixel # to:')
+        ymin_title = wx.StaticText(self, -1, 'ymin: ')
+        ymax_title = wx.StaticText(self, -1, 'ymax:')
+        z_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH , 20),
+                                style=wx.TE_PROCESS_ENTER)
+        
+        xmin_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                style=wx.TE_PROCESS_ENTER)
+        xmax_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                style=wx.TE_PROCESS_ENTER)
+        ymin_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                style=wx.TE_PROCESS_ENTER)
+        ymax_ctl = InputTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                style=wx.TE_PROCESS_ENTER)
+        z_ctl.SetValue('1.0')
+        xmin_ctl.SetValue('-0.3')
+        xmax_ctl.SetValue('0.3')
+        ymin_ctl.SetValue('-0.3')
+        ymax_ctl.SetValue('0.3')
+        z_ctl.Bind(wx.EVT_TEXT, self._on_z_enter)
+        xmin_ctl.Bind(wx.EVT_TEXT, self._onparam)
+        xmax_ctl.Bind(wx.EVT_TEXT, self._onparam)
+        ymin_ctl.Bind(wx.EVT_TEXT, self._onparam)
+        ymax_ctl.Bind(wx.EVT_TEXT, self._onparam)
+        xbox.AddMany([(x_title , 0, wx.LEFT, 0), 
+                      (xmin_title , 0, wx.LEFT, 10), 
+                      (xmin_ctl , 0, wx.LEFT, 10), 
+                      (xmax_title , 0, wx.LEFT, 10), 
+                      (xmax_ctl , 0, wx.LEFT, 10)])
+        ybox.AddMany([(y_title , 0, wx.LEFT, 0), 
+                      (ymin_title , 0, wx.LEFT, 10), 
+                      (ymin_ctl , 0, wx.LEFT, 10), 
+                      (ymax_title , 0, wx.LEFT, 10), 
+                      (ymax_ctl , 0, wx.LEFT, 10)])     
+        zbox.AddMany([(z_title , 0, wx.LEFT, 0), 
+                      (ztime_title, 0, wx.LEFT, 10),
+                      (z_ctl , 0, wx.LEFT, 7), 
+                      ])   
+        msg = "The data rescaled will show up in the Data Explorer. \n"
+        msg += "*Note: Recommend to use an image with 8 bit Grey \n"
+        msg += "  scale (and with No. of pixels < 300 x 300).\n"
+        msg += "  Otherwise, z = 0.299R + 0.587G + 0.114B."
+        note_txt = wx.StaticText(self, -1, msg)
+        note_txt.SetForegroundColour("black")
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        okButton = wx.Button(self, -1, 'OK')
+        okButton.Bind(wx.EVT_BUTTON, self.on_set)
+        cancelButton = wx.Button(self, -1, 'Cancel')
+        cancelButton.Bind(wx.EVT_BUTTON, self.OnClose)
+        btnbox.Add(okButton, 0, wx.LEFT | wx.BOTTOM, 5)
+        btnbox.Add(cancelButton, 0, wx.LEFT | wx.TOP, 5)
+        hbox.Add(note_txt, 0, wx.LEFT, 5)
+        hbox.Add(btnbox, 0, wx.LEFT, 15)
+        vbox.Add((10, 15))
+        boxsizer.Add(xbox, 1, wx.LEFT | wx.BOTTOM, 5)
+        boxsizer.Add(ybox, 1, wx.LEFT | wx.BOTTOM, 5)
+        boxsizer.Add(zbox, 1, wx.LEFT | wx.BOTTOM, 5)
+        vbox.Add(boxsizer, 0, wx.LEFT, 20)
+        vbox.Add(hbox, 0, wx.LEFT | wx.TOP, 15)
+        okButton.SetFocus()
+        # set sizer
+        self.SetSizer(vbox)
+        #pos = self.parent.GetPosition()
+        #self.SetPosition(pos)
+        self.z_ctrl = z_ctl
+        self.xy_ctrls = [[xmin_ctl, xmax_ctl], [ymin_ctl, ymax_ctl]]
+
+    def _onparamEnter(self, event=None):
+        """
+        By pass original txtcrl binding
+        """
+        pass    
+        
+    def _onparam(self, event=None):
+        """
+        Set to default
+        """
+        item = event.GetEventObject()
+        self._check_ctrls(item)
+
+    def _check_ctrls(self, item, is_button=False):
+        """
+        """
+        flag = True
+        item.SetBackgroundColour("white")
+        try:
+            val = float(item.GetValue())  
+            if val < -10.0 or val > 10.0:
+                item.SetBackgroundColour("pink")
+                item.Refresh()
+                flag = False
+        except:
+            item.SetBackgroundColour("pink")
+            item.Refresh()
+            flag = False
+        if not flag and is_button:
+            err_msg = "The allowed range of the min and max values are \n"
+            err_msg += "between -10 and 10."
+            if self.base is not None:
+                wx.PostEvent(self.base, StatusEvent(status=err_msg, 
+                                                    info="error"))
+            else:
+                print err_msg
+        return flag
+
+    def _on_z_enter(self, event= None): 
+        """
+        On z factor enter
+        """
+        item = event.GetEventObject()
+        self._check_z_ctrl(item)
+        
+    def _check_z_ctrl(self, item, is_button=False):
+        """
+        """
+        flag = True
+        item.SetBackgroundColour("white")
+        try:
+            val = float(item.GetValue())  
+            if val <= 0:
+                item.SetBackgroundColour("pink")
+                item.Refresh()
+                flag = False
+        except:
+            item.SetBackgroundColour("pink")
+            item.Refresh() 
+            flag = False
+        if not flag and is_button:
+            err_msg = "The z scale value should be larger than 0."
+            if self.base is not None:
+                wx.PostEvent(self.base, StatusEvent(status=err_msg, 
+                                                    info="error"))
+            else:
+                print err_msg
+        return flag 
+    
+    def on_set(self, event):
+        """
+        Set image as data   
+        """
+        event.Skip()
+        # Check the textctrl values
+        for item_list in self.xy_ctrls:
+            for item in item_list:
+                 if not self._check_ctrls(item, True):
+                     return
+        if not self._check_z_ctrl(self.z_ctrl, True):
+            return
+        try:
+            image = self.image
+            xmin = float(self.xy_ctrls[0][0].GetValue())
+            xmax = float(self.xy_ctrls[0][1].GetValue())
+            ymin = float(self.xy_ctrls[1][0].GetValue())
+            ymax = float(self.xy_ctrls[1][1].GetValue())
+            zscale = float(self.z_ctrl.GetValue())
+            self.convert_image(image, xmin, xmax, ymin, ymax, zscale)
+        except:
+            err_msg = "Error occurred while converting Image to Data."
+            if self.base is not None:
+                wx.PostEvent(self.base, StatusEvent(status=err_msg, 
+                                                    info="error"))
+            else:
+                print err_msg
+            
+        self.OnClose(event)
+    
+    def convert_image(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 = os.path.basename(self.title)
+        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}}', '\AA^{-1}')
+        output.yaxis('\\rm{Q_{y}}', '\AA^{-1}')
+        # Store loading process information
+        output.meta_data['loader'] = self.title.split('.')[-1] + "Reader"
+        output.is_data = True
+        output = reader2D_converter(output)
+        if self.base != None:
+            data = self.base.create_gui_data(output, self.title)
+            self.base.add_data({data.id:data})
+            
+    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)
+                      
+    def OnClose(self, event):
+        """
+        Close event
+        """
+        # clear event
+        event.Skip()
+        self.Destroy()
+                       
+if __name__ == "__main__":
+    app  = wx.App()
+    ImageView(None).load()
+    app.MainLoop()   
+    
+ 
Index: calculatorview/src/sans/perspectives/calculator/media/load_image_help.html
===================================================================
--- calculatorview/src/sans/perspectives/calculator/media/load_image_help.html	(revision 3e001f98892a6dd98b2b904148628e4df8c63238)
+++ calculatorview/src/sans/perspectives/calculator/media/load_image_help.html	(revision 3e001f98892a6dd98b2b904148628e4df8c63238)
@@ -0,0 +1,48 @@
+<body>
+<h4>Image Viewer</h4>
+<ul>
+<li><a href="#Description">Description</a></li>
+<li><a href="#HowTo">HowTo?</a></li>
+</ul>
+<h5><a name="Description">Description</a></h5>
+<p>
+This tool is to load image files (in the format of png, bmp, gif, jpg, or tif) 
+and to display it in the x-y coordinate (against the pixel number). The displayed plot then
+ can be saved, printed, and copied. 
+ The plot can also be resized by dragging the corner of the panel. 
+</p>
+
+
+<h5><a name="HowTo">HowTo?</a></h5>
+<p>
+1. Select 'Image Viewer' under 'Tool' menu in the menubar.
+</p>
+<p>
+2. Select a file type from the drop-box at the bottom of the file dialog panel, 
+choose a file of interest, and then click the 'Open' button (See Picture below).</p>
+<p>
+<img src="load_image.bmp">
+</p>
+<p>
+3. If the loading is successful, the image will be displayed in the x-y coordinate. 
+The file name will be shown in the title bar (See Picture below). 
+</p>
+<p>
+4. Some options such as Saving, printing, and copying are available from the menubar, 
+or in the popup-menu by right-mouse-clicking anywhere in the plot (See Picture below).
+</p>
+<p><img src="pic_plot.bmp">
+</p>
+<p>
+5. If the image is taken from a 2D detector, it can be converted to our 2D data where 
+the z values are computed by the Eq. z = 0.299R + 0.587G + 0.114B unless the picture file
+is formatted in an 8 bit Grey-Scale tif. In the "Convert to Data" dialog, users set 
+the proper values in the text boxes and then click the OK button to close and to send the data generated to the Data
+ Explorer in the SasView main window. The "Cancel" button will just close the dialog 
+ (See Picture below).
+</p>
+
+<p><img src="pic_convert.bmp">
+</p>
+<br>
+</body>
