Index: src/sas/qtgui/DataExplorer.py
===================================================================
--- src/sas/qtgui/DataExplorer.py	(revision ef01be48d93733bf0b5ac6ff7ccce2ac92d368b6)
+++ src/sas/qtgui/DataExplorer.py	(revision 55d89f89e773b2faf78331b3fdae988c7fe5f2ce)
@@ -768,5 +768,4 @@
             method_name='Plotter2D'
 
-        # TODO: Replace this with the proper MaskPlotPanel from plottools
         new_plot = globals()[method_name](self, quickplot=True)
         new_plot.data = data
@@ -782,7 +781,20 @@
     def quickData3DPlot(self):
         """
-        """
-        print "quickData3DPlot"
-        pass
+        Slowish 3D plot
+        """
+        index = self.treeView.selectedIndexes()[0]
+        model_item = self.model.itemFromIndex(self.data_proxy.mapToSource(index))
+        data = GuiUtils.dataFromItem(model_item)
+
+        new_plot = Plotter2D(self, quickplot=True, dimension=3)
+        new_plot.data = data
+        new_plot.plot(marker='o', linestyle='')
+
+        # Update the global plot counter
+        title = "Plot " + data.name
+        new_plot.setWindowTitle(title)
+
+        # Show the plot
+        new_plot.show()
 
     def showEditDataMask(self):
Index: src/sas/qtgui/Plotter2D.py
===================================================================
--- src/sas/qtgui/Plotter2D.py	(revision 6d05e1d115a38b7805a83cb480014af70b58ef75)
+++ src/sas/qtgui/Plotter2D.py	(revision 55d89f89e773b2faf78331b3fdae988c7fe5f2ce)
@@ -11,5 +11,6 @@
 
 class Plotter2D(PlotterBase):
-    def __init__(self, parent=None, quickplot=False):
+    def __init__(self, parent=None, quickplot=False, dimension=2):
+        self.dimension = dimension
         super(Plotter2D, self).__init__(parent, quickplot=quickplot)
 
@@ -31,5 +32,4 @@
         self.zmax=data.zmax
         self.label=data.name
-        self.dimension=2
         self.xLabel="%s(%s)"%(data._xaxis, data._xunit)
         self.yLabel="%s(%s)"%(data._yaxis, data._yunit)
@@ -41,33 +41,29 @@
         """
         # create an axis object
-        ax = self.ax
+        zmin_2D_temp = self.zmin
+        zmax_2D_temp = self.zmax
+        if self.scale == 'log_{10}':
+            self.scale = 'linear'
+            if not self.zmin is None:
+                zmin_2D_temp = numpy.pow(10, self.zmin)
+            if not self.zmax is None:
+                zmax_2D_temp = numpy.pow(10, self.zmax)
+        else:
+            self.scale = 'log_{10}'
+            if not self.zmin is None:
+                # min log value: no log(negative)
+                if self.zmin <= 0:
+                    zmin_2D_temp = -32
+                else:
+                    zmin_2D_temp = numpy.log10(self.zmin)
+            if not self.zmax is None:
+                zmax_2D_temp = numpy.log10(self.zmax)
 
-        # graph properties
-        ax.set_xlabel(self.x_label)
-        ax.set_ylabel(self.y_label)
-        # Title only for regular charts
-        if not self.quickplot:
-            ax.set_title(label=self._title)
-
-        # Re-adjust colorbar
-        self.figure.subplots_adjust(left=0.2, right=.8, bottom=.2)
-
-        output = PlotUtilities.build_matrix(self._data.data, self.qx_data, self.qy_data)
-
-        im = ax.imshow(output,
-                       interpolation='nearest',
-                       origin='lower',
-                       vmin=self.zmin, vmax=self.zmax,
-                       cmap=self.cmap,
-                       extent=(self.xmin, self.xmax,
-                               self.ymin, self.ymax))
-
-        cbax = self.figure.add_axes([0.84, 0.2, 0.02, 0.7])
-        cb = self.figure.colorbar(im, cax=cbax)
-        cb.update_bruteforce(im)
-        cb.set_label('$' + self.scale + '$')
-
-        # Schedule the draw for the next time the event loop is idle.
-        self.canvas.draw_idle()
+        self.image(data=self.data.data, qx_data=self.qx_data,
+                   qy_data=self.qy_data, xmin=self.xmin,
+                   xmax=self.xmax,
+                   ymin=self.ymin, ymax=self.ymax,
+                   cmap=self.cmap, zmin=zmin_2D_temp,
+                   zmax=zmax_2D_temp)
 
     def contextMenuQuickPlot(self):
@@ -81,6 +77,7 @@
         self.actionCopyToClipboard = self.contextMenu.addAction("Copy to Clipboard")
         self.contextMenu.addSeparator()
-        self.actionToggleGrid = self.contextMenu.addAction("Toggle Grid On/Off")
-        self.contextMenu.addSeparator()
+        if self.dimension == 2:
+            self.actionToggleGrid = self.contextMenu.addAction("Toggle Grid On/Off")
+            self.contextMenu.addSeparator()
         self.actionChangeScale = self.contextMenu.addAction("Toggle Linear/Log Scale")
 
@@ -89,5 +86,6 @@
         self.actionPrintImage.triggered.connect(self.onImagePrint)
         self.actionCopyToClipboard.triggered.connect(self.onClipboardCopy)
-        self.actionToggleGrid.triggered.connect(self.onGridToggle)
+        if self.dimension == 2:
+            self.actionToggleGrid.triggered.connect(self.onGridToggle)
         self.actionChangeScale.triggered.connect(self.onToggleScale)
 
@@ -95,32 +93,6 @@
         """
         Toggle axis and replot image
-
         """
-        zmin_2D_temp = self.zmin
-        zmax_2D_temp = self.zmax
-        if self.scale == 'log_{10}':
-            self.scale = 'linear'
-            if not self.zmin is None:
-                zmin_2D_temp = math.pow(10, self.zmin)
-            if not self.zmax is None:
-                zmax_2D_temp = math.pow(10, self.zmax)
-        else:
-            self.scale = 'log_{10}'
-            if not self.zmin is None:
-                # min log value: no log(negative)
-                if self.zmin <= 0:
-                    zmin_2D_temp = -32
-                else:
-                    zmin_2D_temp = math.log10(self.zmin)
-            if not self.zmax is None:
-                zmax_2D_temp = math.log10(self.zmax)
-
-        self.image(data=self.data.data, qx_data=self.qx_data,
-                   qy_data=self.qy_data, xmin=self.xmin,
-                   xmax=self.xmax,
-                   ymin=self.ymin, ymax=self.ymax,
-                   cmap=self.cmap, zmin=zmin_2D_temp,
-                   zmax=zmax_2D_temp)
-
+        self.plot()
 
     def image(self, data, qx_data, qy_data, xmin, xmax, ymin, ymax,
@@ -129,7 +101,5 @@
         """
         Render the current data
-
         """
-        #self.data = data
         self.qx_data = qx_data
         self.qy_data = qy_data
@@ -140,10 +110,8 @@
         self.zmin = zmin
         self.zmax = zmax
-        #c = self.color(color)
         # If we don't have any data, skip.
         if data == None:
             return
         if data.ndim == 1:
-            #output = self._build_matrix()
             output = PlotUtilities.build_matrix(data, self.qx_data, self.qy_data)
         else:
@@ -157,6 +125,4 @@
                     zmin_temp = self.zmin_2D
                     output[output > 0] = numpy.log10(output[output > 0])
-                    #In log scale Negative values are not correct in general
-                    #output[output<=0] = math.log(numpy.min(output[output>0]))
                 elif self.zmin <= 0:
                     zmin_temp = self.zmin
@@ -166,6 +132,4 @@
                     zmin_temp = self.zmin
                     output[output > 0] = numpy.log10(output[output > 0])
-                    #In log scale Negative values are not correct in general
-                    #output[output<=0] = math.log(numpy.min(output[output>0]))
             except:
                 #Too many problems in 2D plot with scale
@@ -193,6 +157,6 @@
             self.figure.subplots_adjust(left=0.1, right=.8, bottom=.1)
 
-            X = self.x_bins[0:-1]
-            Y = self.y_bins[0:-1]
+            X = self._data.x_bins[0:-1]
+            Y = self._data.y_bins[0:-1]
             X, Y = numpy.meshgrid(X, Y)
 
@@ -200,5 +164,4 @@
                 # mpl >= 1.0.0
                 ax = self.figure.gca(projection='3d')
-                #ax.disable_mouse_rotation()
                 cbax = self.figure.add_axes([0.84, 0.1, 0.02, 0.8])
                 if len(X) > 60:
@@ -218,5 +181,5 @@
             im = ax.plot_surface(X, Y, output, rstride=1, cstride=1, cmap=cmap,
                                  linewidth=0, antialiased=False)
-            self.set_axis_off()
+            self.ax.set_axis_off()
 
         if cbax == None:
Index: src/sas/qtgui/PlotterBase.py
===================================================================
--- src/sas/qtgui/PlotterBase.py	(revision 6d05e1d115a38b7805a83cb480014af70b58ef75)
+++ src/sas/qtgui/PlotterBase.py	(revision 55d89f89e773b2faf78331b3fdae988c7fe5f2ce)
@@ -52,5 +52,4 @@
         self.color=0
         self.symbol=0
-        self.dimension=1
         self.grid_on = False
         self.scale = 'linear'
Index: src/sas/qtgui/UnitTesting/Plotter2DTest.py
===================================================================
--- src/sas/qtgui/UnitTesting/Plotter2DTest.py	(revision 55d89f89e773b2faf78331b3fdae988c7fe5f2ce)
+++ src/sas/qtgui/UnitTesting/Plotter2DTest.py	(revision 55d89f89e773b2faf78331b3fdae988c7fe5f2ce)
@@ -0,0 +1,107 @@
+import sys
+import unittest
+
+from PyQt4 import QtGui
+from PyQt4 import QtCore
+from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
+from mock import MagicMock
+
+####### TEMP
+import LocalSetup
+#######
+from sas.sasgui.guiframe.dataFitting import Data1D
+from sas.sasgui.guiframe.dataFitting import Data2D
+
+# Tested module
+import sas.qtgui.Plotter2D as Plotter2D
+
+app = QtGui.QApplication(sys.argv)
+
+class Plotter2DTest(unittest.TestCase):
+    '''Test the Plotter 2D class'''
+    def setUp(self):
+        '''create'''
+        self.plotter = Plotter2D.Plotter2D(None, quickplot=True)
+
+        self.data = Data2D(image=[0.1]*4,
+                           qx_data=[1.0, 2.0, 3.0, 4.0],
+                           qy_data=[10.0, 11.0, 12.0, 13.0],
+                           dqx_data=[0.1, 0.2, 0.3, 0.4],
+                           dqy_data=[0.1, 0.2, 0.3, 0.4],
+                           q_data=[1,2,3,4],
+                           xmin=-1.0, xmax=5.0,
+                           ymin=-1.0, ymax=15.0,
+                           zmin=-1.0, zmax=20.0)
+
+        self.data.title="Test data"
+        self.data.id = 1
+
+    def tearDown(self):
+        '''destroy'''
+        self.plotter = None
+
+    def testDataProperty(self):
+        """ Adding data """
+        self.plotter.data = self.data
+
+        self.assertEqual(self.plotter.data, self.data)
+        self.assertEqual(self.plotter._title, self.data.title)
+        self.assertEqual(self.plotter.xLabel, "$\\rm{Q_{x}}(A^{-1})$")
+        self.assertEqual(self.plotter.yLabel, "$\\rm{Q_{y}}(A^{-1})$")
+
+    def testPlot(self):
+        """ Look at the plotting """
+        self.plotter.data = self.data
+        self.plotter.show()
+        FigureCanvas.draw = MagicMock()
+
+        self.plotter.plot()
+
+        #self.assertTrue(FigureCanvas.draw.called)
+
+    def testContextMenuQuickPlot(self):
+        """ Test the right click menu """
+        self.plotter.data = self.data
+        actions = self.plotter.contextMenu.actions()
+        self.assertEqual(len(actions), 7)
+
+        # Trigger Save Image and make sure the method is called
+        self.assertEqual(actions[0].text(), "Save Image")
+        self.plotter.toolbar.save_figure = MagicMock()
+        actions[0].trigger()
+        self.assertTrue(self.plotter.toolbar.save_figure.called)
+
+        # Trigger Print Image and make sure the method is called
+        self.assertEqual(actions[1].text(), "Print Image")
+        QtGui.QPrintDialog.exec_ = MagicMock(return_value=QtGui.QDialog.Rejected)
+        actions[1].trigger()
+        self.assertTrue(QtGui.QPrintDialog.exec_.called)
+
+        # Trigger Copy to Clipboard and make sure the method is called
+        self.assertEqual(actions[2].text(), "Copy to Clipboard")
+
+        # Spy on cliboard's dataChanged() signal
+        self.clipboard_called = False
+        def done():
+            self.clipboard_called = True
+        QtCore.QObject.connect(app.clipboard(), QtCore.SIGNAL("dataChanged()"), done)
+        actions[2].trigger()
+        QtGui.qApp.processEvents()
+        # Make sure clipboard got updated.
+        self.assertTrue(self.clipboard_called)
+
+        # Trigger Toggle Grid and make sure the method is called
+        self.assertEqual(actions[4].text(), "Toggle Grid On/Off")
+        self.plotter.ax.grid = MagicMock()
+        actions[4].trigger()
+        self.assertTrue(self.plotter.ax.grid.called)
+
+        # Trigger Change Scale and make sure the method is called
+        self.assertEqual(actions[6].text(), "Toggle Linear/Log Scale")
+        FigureCanvas.draw = MagicMock()
+        actions[6].trigger()
+        #self.assertTrue(FigureCanvas.draw.called)
+
+
+if __name__ == "__main__":
+    unittest.main()
Index: src/sas/qtgui/run_tests.bat
===================================================================
--- src/sas/qtgui/run_tests.bat	(revision 363fbfa07207fe7ae5f577b063028a6ad59c1435)
+++ src/sas/qtgui/run_tests.bat	(revision 55d89f89e773b2faf78331b3fdae988c7fe5f2ce)
@@ -12,2 +12,5 @@
 python -m UnitTesting.PlotHelperTest
 python -m UnitTesting.KiessigCalculatorTest
+python -m UnitTesting.PlotterTest
+python -m UnitTesting.Plotter2DTest
+python -m UnitTesting.ScalePropertiesTest
Index: src/sas/qtgui/run_tests.sh
===================================================================
--- src/sas/qtgui/run_tests.sh	(revision 363fbfa07207fe7ae5f577b063028a6ad59c1435)
+++ src/sas/qtgui/run_tests.sh	(revision 55d89f89e773b2faf78331b3fdae988c7fe5f2ce)
@@ -9,4 +9,7 @@
 python -m UnitTesting.PlotHelperTest
 python -m UnitTesting.SasviewLoggerTest
+python -m UnitTesting.PlotterTest
+python -m UnitTesting.Plotter2DTest
+python -m UnitTesting.ScalePropertiesTest
+python -m UnitTesting.KiessigCalculatorTest
 python -m UnitTesting.DensityCalculatorTest
-python -m UnitTesting.KiessigCalculatorTest
