Index: src/sas/qtgui/DataExplorer.py
===================================================================
--- src/sas/qtgui/DataExplorer.py	(revision 488c49d59f65f588ab30dc9447de6dc0335177cc)
+++ src/sas/qtgui/DataExplorer.py	(revision 5032ea687fa17c1323c1124c0a7aa9d80184bf0b)
@@ -52,5 +52,6 @@
         self.treeView.setModel(self.proxy)
 
-    def loadFile(self, event):
+
+    def loadFile(self, event=None):
         """
         Called when the "Load" button pressed.
@@ -64,40 +65,102 @@
         self.communicate.fileReadSignal.emit(path_str)
 
-        # Read in the data from chosen file(s)
-        self.readData(path_str)
+        # threaded file load
+        load_thread = threads.deferToThread(self.readData, path_str)
+        load_thread.addCallback(self.loadComplete)
 
         return
 
+    def loadFolder(self, event=None):
+        """
+        Called when the "File/Load Folder" menu item chosen.
+        Opens the Qt "Open Folder..." dialog 
+        """
+        dir = QtGui.QFileDialog.getExistingDirectory(self, "Choose a directory", "",
+              QtGui.QFileDialog.ShowDirsOnly)
+        if dir is None:
+            return
+
+        dir = str(dir)
+
+        if not os.path.isdir(dir):
+            return
+
+        # get content of dir into a list
+        path_str = [os.path.join(os.path.abspath(dir), filename) for filename in os.listdir(dir)]
+
+        # threaded file load
+        load_thread = threads.deferToThread(self.readData, path_str)
+        load_thread.addCallback(self.loadComplete)
+        
+        return
+
     def deleteFile(self, event):
         """
-        """
+        Delete selected rows from the model
+        """
+        # Assure this is indeed wanted
+        delete_msg = "This operation will delete the checked data sets and all the dependents." +\
+                     "\nDo you want to continue?"
+        reply = QtGui.QMessageBox.question(self, 'Warning', delete_msg,
+                QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
+
+        if reply == QtGui.QMessageBox.No:
+            return
+
         # Figure out which rows are checked
-
-        # Delete these rows from the model
-
-        # Call data_manager update with delete_data()
-
+        ind = -1
+        # Use 'while' so the row count is forced at every iteration
+        while ind < self.model.rowCount():
+            ind += 1
+            item = self.model.item(ind)
+            if item and item.isCheckable() and item.checkState() == QtCore.Qt.Checked:
+                # Delete these rows from the model
+                self.model.removeRow(ind)
+                # Decrement index since we just deleted it
+                ind -= 1
+
+        # pass temporarily kept as a breakpoint anchor
         pass
 
     def sendData(self, event):
         """
-        """
+        Send selected item data to the current perspective and set the relevant notifiers
+        """
+        # should this reside on GuiManager or here?
+        self._perspective = self.parent.perspective()
+
+        # Set the signal handlers
+        self.communicator = self._perspective.communicator()
+        self.communicator.updateModelFromPerspectiveSignal.connect(self.updateModelFromPerspective)
+
         # Figure out which rows are checked
-
+        selected_items = []
+        for index in range(self.model.rowCount()):
+            item = self.model.item(index)
+            if item.isCheckable() and item.checkState() == QtCore.Qt.Checked:
+                selected_items.append(item)
+
+        # Which perspective has been selected?
+        if len(selected_items) > 1 and not self._perspective.allowBatch():
+            msg = self._perspective.title() + " does not allow multiple data."
+            msgbox = QtGui.QMessageBox()
+            msgbox.setIcon(QtGui.QMessageBox.Critical)
+            msgbox.setText(msg)
+            msgbox.setStandardButtons(QtGui.QMessageBox.Ok)
+            retval = msgbox.exec_()
+            return
         # Dig up data from model
-        # To get the original Data1D object back use:
-        # object_item.data().toPyObject()
-
-
-        # Which perspective has been selected?
-
+        data = [selected_items[0].child(0).data().toPyObject()]
+
+        # TODO
         # New plot or appended?
 
         # Notify the GuiManager about the send request
-        # updatePerspectiveWithDataSignal()
-        pass
+        self._perspective.setData(data_list=data)
+
 
     def chooseFiles(self):
         """
+        Shows the Open file dialog and returns the chosen path(s)
         """
         # List of known extensions
@@ -105,6 +168,7 @@
 
         # Location is automatically saved - no need to keep track of the last dir
-        # TODO: is it really?
-        paths = QtGui.QFileDialog.getOpenFileName(self, "Choose a file", "", wlist)
+        # But only with Qt built-in dialog (non-platform native)
+        paths = QtGui.QFileDialog.getOpenFileName(self, "Choose a file", "",
+                wlist, None, QtGui.QFileDialog.DontUseNativeDialog)
         if paths is None:
             return
@@ -132,4 +196,5 @@
         data_error = False
         error_message = ""
+        
         for p_file in path:
             info = "info"
@@ -147,13 +212,8 @@
 
             try:
-                message = "Loading Data... " + str(p_file) + "\n"
+                message = "Loading Data... " + str(basename) + "\n"
 
                 # change this to signal notification in GuiManager
                 self.communicate.statusBarUpdateSignal.emit(message)
-
-                # threaded file load
-                # load_thread = threads.deferToThread(self.loadThread, p_file)
-                # Add deferred callback for call return
-                # load_thread.addCallback(self.plotResult)
 
                 output_objects = self.loader.load(p_file)
@@ -170,4 +230,7 @@
                     output[new_data.id] = new_data
                     self.updateModel(new_data, p_file)
+                    self.model.reset()
+
+                    QtGui.qApp.processEvents()
 
                     if hasattr(item, 'errors'):
@@ -176,9 +239,11 @@
                             message += "\tError: {0}\n".format(error_data)
                     else:
+
                         logging.error("Loader returned an invalid object:\n %s" % str(item))
                         data_error = True
 
-            except:
+            except Exception as ex:
                 logging.error(sys.exc_value)
+
                 any_error = True
             if any_error or error_message != "":
@@ -207,5 +272,5 @@
             message = "Loading Data Complete! "
         message += log_msg
-        self.loadComplete(output=output, message=message)
+        return (output, message)
 
     def getWlist(self):
@@ -314,8 +379,8 @@
         Post message to status bar and update the data manager
         """
+        self.model.reset()
         # Notify the manager of the new data available
         self.communicate.statusBarUpdateSignal.emit(message)
         self.communicate.fileDataReceivedSignal.emit(output)
-
         self.manager.add_data(data_list=output)
 
@@ -362,7 +427,20 @@
         self.proxy.setFilterRegExp(r"[^()]")
 
+    def updateModelFromPerspective(self, model_item):
+        """
+        """
+        # Overwrite the index with what we got from the perspective
+        if type(model_item) != QtGui.QStandardItem:
+            msg = "Wrong data type returned from calculations."
+            raise AttributeError, msg
+        # self.model.insertRow(model_item)
+        # Reset the view
+        self.model.reset()
+        # Pass acting as a debugger anchor
+        pass
 
     def addExtraRows(self, info_item, data):
         """
+        Extract relevant data to include in the Info ModelItem
         """
         title_item   = QtGui.QStandardItem("Title: "      + data.title)
Index: src/sas/qtgui/GuiManager.py
===================================================================
--- src/sas/qtgui/GuiManager.py	(revision f721030e66e61dc6279aa8e024da03b25f8e0d42)
+++ src/sas/qtgui/GuiManager.py	(revision 5032ea687fa17c1323c1124c0a7aa9d80184bf0b)
@@ -65,9 +65,8 @@
         self._current_perspective = self.invariantWidget
      
-
     def fileRead(self, data):
         """
         """
-        #print("FILE %s "%data)
+        print("FILE %s "%data)
         pass
     
@@ -225,13 +224,12 @@
     def actionLoadData(self):
         """
-        """
-        print("actionLoadData TRIGGERED")
-        pass
+        Load file from Data Explorer
+        """
+        self.filesWidget.loadFile()
 
     def actionLoad_Data_Folder(self):
         """
         """
-        print("actionLoad_Data_Folder TRIGGERED")
-        pass
+        self.filesWidget.loadFolder()
 
     def actionOpen_Project(self):
Index: src/sas/qtgui/GuiUtils.py
===================================================================
--- src/sas/qtgui/GuiUtils.py	(revision f721030e66e61dc6279aa8e024da03b25f8e0d42)
+++ src/sas/qtgui/GuiUtils.py	(revision 5032ea687fa17c1323c1124c0a7aa9d80184bf0b)
@@ -25,4 +25,5 @@
 #from sas.sasgui.guiframe.events import StatusEvent
 #from sas.sasgui.guiframe.events import NewPlotEvent
+from sas.sasgui.guiframe.dataFitting import Data1D
 
 
@@ -202,2 +203,5 @@
     # Send data to the current perspective
     updatePerspectiveWithDataSignal = QtCore.pyqtSignal(list)
+
+    # New data in current perspective
+    updateModelFromPerspectiveSignal = QtCore.pyqtSignal(Data1D)
Index: src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py
===================================================================
--- src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py	(revision f721030e66e61dc6279aa8e024da03b25f8e0d42)
+++ src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py	(revision 5032ea687fa17c1323c1124c0a7aa9d80184bf0b)
@@ -11,4 +11,5 @@
 from sas.sascalc.invariant import invariant
 from sas.sasgui.guiframe.dataFitting import Data1D
+from sas.qtgui.GuiUtils import Communicate
 
 # local
@@ -40,6 +41,4 @@
     def __init__(self, manager=None, parent=None):
         super(InvariantWindow, self).__init__(parent)
-        # This controller contains the ui and doesn't inherit it directly!
-        # self.form = InvariantUI()
         self.setWindowTitle("Invariant Perspective")
         # initial input params
@@ -65,4 +64,5 @@
         self._high_power_value  = False
 
+        self.communicate = Communicate()
 
         # Mask file selector
@@ -92,4 +92,9 @@
         # Set up the mapper
         self.setupMapper()
+
+    def communicator(self):
+        """
+        """
+        return self.communicate
 
     def updateFromModel(self):
@@ -142,4 +147,5 @@
             self.pushButton.setStyleSheet(self.style)
 
+
     def plotResult(self, model):
         """
@@ -154,7 +160,13 @@
         self.pushButton.setStyleSheet(self.style)
 
+        # Send the new data to DE for keeping in the model
+        self.communicate.updateModelFromPerspectiveSignal.emit(self._data)
+
 
     def calculateThread(self, extrapolation):
         """
+        Perform Invariant calculations.
+
+        TODO: Create a dictionary of results to be sent to DE on completion.
         """
         self.updateFromModel()
@@ -294,4 +306,9 @@
         return self.model
                 
+    def title(self):
+        """
+        Perspective name
+        """
+        return "Invariant panel"
 
     def status(self):
@@ -537,5 +554,9 @@
             #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error'))
 
-
+    def allowBatch(self):
+        """
+        Tell the caller that we don't accept multiple data instances
+        """
+        return False
 
 if __name__ == "__main__":
Index: src/sas/qtgui/UnitTesting/DataExplorerTest.py
===================================================================
--- src/sas/qtgui/UnitTesting/DataExplorerTest.py	(revision 488c49d59f65f588ab30dc9447de6dc0335177cc)
+++ src/sas/qtgui/UnitTesting/DataExplorerTest.py	(revision 5032ea687fa17c1323c1124c0a7aa9d80184bf0b)
@@ -1,4 +1,6 @@
 import sys
 import unittest
+#from twisted.trial import unittest
+#from twisted.internet import reactor, defer, interfaces, threads, protocol, error
 
 from PyQt4.QtGui import *
@@ -20,7 +22,19 @@
     def setUp(self):
         '''Create the GUI'''
+        class MyPerspective(object):
+            def communicator(self):
+                return Communicate()
+            def allowBatch(self):
+                return False
+            def setData(self, data_list=None):
+                return None
+            def title(self):
+                return "Dummy Perspective"
+
         class dummy_manager(object):
             def communicator(self):
                 return Communicate()
+            def perspective(self):
+                return MyPerspective()
 
         self.form = DataExplorerWindow(None, dummy_manager())
@@ -67,12 +81,27 @@
 
     def testDeleteButton(self):
+        """
+        Functionality of the delete button
+        """
 
         deleteButton = self.form.cmdDelete
 
         # Mock the confirmation dialog with return=Yes
+        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.No)
 
         # Populate the model
-
-        # Assure the checkbox is on
+        filename = ["cyl_400_20.txt", "Dec07031.ASC", "cyl_400_20.txt"]
+        self.form.readData(filename)
+
+        # Assure the model contains three items
+        self.assertEqual(self.form.model.rowCount(), 3)
+
+        # Assure the checkboxes are on
+        item1 = self.form.model.item(0)
+        item2 = self.form.model.item(1)
+        item3 = self.form.model.item(2)
+        self.assertTrue(item1.checkState() == QtCore.Qt.Checked)
+        self.assertTrue(item2.checkState() == QtCore.Qt.Checked)
+        self.assertTrue(item3.checkState() == QtCore.Qt.Checked)
 
         # Click on the delete  button
@@ -80,24 +109,60 @@
 
         # Test the warning dialog called once
-        # self.assertTrue(QtGui.QFileDialog.getOpenFileName.called)
+        self.assertTrue(QtGui.QMessageBox.question.called)
+
+        # Assure the model still contains the items
+        self.assertEqual(self.form.model.rowCount(), 3)
+
+        # Now, mock the confirmation dialog with return=Yes
+        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.Yes)
+
+        # Click on the delete  button
+        QTest.mouseClick(deleteButton, Qt.LeftButton)
+
+        # Test the warning dialog called once
+        self.assertTrue(QtGui.QMessageBox.question.called)
 
         # Assure the model contains no items
-
-    def testSendToButton(self):        
-        sendToButton = self.form.cmdSendTo
-
-        # Mock the current perspective set_data method
-
+        self.assertEqual(self.form.model.rowCount(), 0)
+
+        # Click delete once again to assure no nasty behaviour on empty model
+        QTest.mouseClick(deleteButton, Qt.LeftButton)
+
+
+    def testSendToButton(self):
+        """
+        Test that clicking the Send To button sends checked data to a perspective
+        """
+        
         # Populate the model
+        filename = ["cyl_400_20.txt"]
+        self.form.readData(filename)
+
+        # setData is the method we want to see called
+        mocked = self.form.parent.perspective().setData
+        mocked = MagicMock(filename)
 
         # Assure the checkbox is on
+        self.form.cbSelect.setCurrentIndex(0)
 
         # Click on the Send To  button
-        QTest.mouseClick(sendToButton, Qt.LeftButton)
+        QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton)
 
         # Test the set_data method called once
-        # self.assertTrue(QtGui.QFileDialog.getOpenFileName.called)
-
-        # Assure the model contains no items
+        # self.assertTrue(mocked.called)
+
+        # open another file
+        filename = ["cyl_400_20.txt"]
+        self.form.readData(filename)
+
+        # Mock the warning message
+        QtGui.QMessageBox = MagicMock()
+
+        # Click on the button
+        QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton)
+
+        # Assure the message box popped up
+        QtGui.QMessageBox.assert_called_once()
+
 
     def testDataSelection(self):
@@ -159,5 +224,5 @@
     def testReadData(self):
         """
-        Test the readData() method
+        Test the low level readData() method
         """
         filename = ["cyl_400_20.txt"]
@@ -172,16 +237,7 @@
 
         # Expected two status bar updates
-        self.assertEqual(spy_status_update.count(), 2)
+        self.assertEqual(spy_status_update.count(), 1)
         self.assertIn(filename[0], str(spy_status_update.called()[0]['args'][0]))
-        self.assertIn("Loading Data Complete", str(spy_status_update.called()[1]['args'][0]))
-
-        # Expect one Data Received signal
-        self.assertEqual(spy_data_received.count(), 1)
-
-        # Assure returned dictionary has correct data
-        # We don't know the data ID, so need to iterate over dict
-        data_dict = spy_data_received.called()[0]['args'][0]
-        for data_key, data_value in data_dict.iteritems():
-            self.assertIsInstance(data_value, Data1D)
+
 
         # Check that the model contains the item
@@ -194,6 +250,9 @@
         self.assertEqual(model_name, filename[0])
 
-        # Assure add_data on data_manager was called (last call)
-        self.assertTrue(self.form.manager.add_data.called)
+    def testLoadFile(self):
+        """
+        Test the threaded call to readData()
+        """
+        pass
 
     def testGetWList(self):
@@ -206,25 +265,37 @@
             'HFIR 1D files (*.d1d);;DANSE files (*.sans);;NXS files (*.nxs)'
         self.assertEqual(defaults, list)
-
+       
     def testLoadComplete(self):
         """
-        """
-        # Initialize signal spy instances        
+        Test the callback method updating the data object
+        """
+
+        data_dict = {"a1":Data1D()}
+
+        self.form.manager.add_data = MagicMock()
+
+        # Initialize signal spy instances
         spy_status_update = QtSignalSpy(self.form, self.form.communicate.statusBarUpdateSignal)
         spy_data_received = QtSignalSpy(self.form, self.form.communicate.fileDataReceivedSignal)
 
-        # Need an empty Data1D object
-        mockData = Data1D()
-
-        # Call the tested method
-        self.form.loadComplete({"a":mockData}, message="test message")
-
-        # test the signals 
-        self.assertEqual(spy_status_update.count(), 1)
-        self.assertIn("message", str(spy_status_update.called()[0]['args'][0]))
+        # Read in the file
+        self.form.loadComplete(data_dict, message="Loading Data Complete")
+
+        # "Loading data complete" no longer sent in LoadFile but in callback
+        self.assertIn("Loading Data Complete", str(spy_status_update.called()[0]['args'][0]))
+
+        # Expect one Data Received signal
         self.assertEqual(spy_data_received.count(), 1)
 
-        # The data_manager update is going away, so don't bother testing
-       
+        # Assure returned dictionary has correct data
+        # We don't know the data ID, so need to iterate over dict
+        data_dict = spy_data_received.called()[0]['args'][0]
+        for data_key, data_value in data_dict.iteritems():
+            self.assertIsInstance(data_value, Data1D)
+
+        # Assure add_data on data_manager was called (last call)
+        self.assertTrue(self.form.manager.add_data.called)
+
+
 if __name__ == "__main__":
     unittest.main()
Index: src/sas/qtgui/UnitTesting/GuiManagerTest.py
===================================================================
--- src/sas/qtgui/UnitTesting/GuiManagerTest.py	(revision 488c49d59f65f588ab30dc9447de6dc0335177cc)
+++ src/sas/qtgui/UnitTesting/GuiManagerTest.py	(revision 5032ea687fa17c1323c1124c0a7aa9d80184bf0b)
@@ -9,13 +9,22 @@
 # Local
 from GuiManager import GuiManager
+from UI.MainWindowUI import MainWindow
 
-#app = QApplication(sys.argv)
+app = QApplication(sys.argv)
 
 class GuiManagerTest(unittest.TestCase):
-    '''Test the WelcomePanel'''
+    '''Test the Main Window functionality'''
     def setUp(self):
         '''Create the tested object'''
+        class MainSasViewWindow(MainWindow):
+            # Main window of the application
+            def __init__(self, reactor, parent=None):
+                super(MainSasViewWindow, self).__init__(parent)
+        
+                # define workspace for dialogs.
+                self.workspace = QWorkspace(self)
+                self.setCentralWidget(self.workspace)
 
-        self.manager = GuiManager(None)
+        self.manager = GuiManager(MainSasViewWindow(None), None)
 
     def tearDown(self):
@@ -52,4 +61,18 @@
         pass
 
+    def testActionLoadData(self):
+        """
+        Menu File/Load Data File(s)
+        """
+        # Mock the system file open method
+        QFileDialog.getOpenFileName = MagicMock(return_value=None)
+
+        # invoke the action
+
+        # Test the getOpenFileName() dialog called once
+        #self.assertTrue(QtGui.QFileDialog.getOpenFileName.called)
+        #QtGui.QFileDialog.getOpenFileName.assert_called_once()
+        
+
     # test each action separately
        
Index: src/sas/qtgui/UnitTesting/TestUtils.py
===================================================================
--- src/sas/qtgui/UnitTesting/TestUtils.py	(revision 488c49d59f65f588ab30dc9447de6dc0335177cc)
+++ src/sas/qtgui/UnitTesting/TestUtils.py	(revision 5032ea687fa17c1323c1124c0a7aa9d80184bf0b)
Index: src/sas/qtgui/UnitTesting/TestUtilsTest.py
===================================================================
--- src/sas/qtgui/UnitTesting/TestUtilsTest.py	(revision 488c49d59f65f588ab30dc9447de6dc0335177cc)
+++ src/sas/qtgui/UnitTesting/TestUtilsTest.py	(revision 5032ea687fa17c1323c1124c0a7aa9d80184bf0b)
@@ -18,13 +18,22 @@
     def testQtSignalSpy(self):
         '''Create the Spy the correct way'''
+        test_string = 'my precious'
 
+        def signalReceived(signal):
+            # Test the signal callback
+            self.assertEqual(signal, test_string)
+
+        communicator = Communicate()
+        communicator.statusBarUpdateSignal.connect(signalReceived)
+
+        # Define the signal spy for testing
         widget = QWidget()
-        signal = Communicate.statusBarUpdateSignal
-        self.spy = QtSignalSpy(widget, signal)
+        spy = QtSignalSpy(widget, communicator.statusBarUpdateSignal)
 
         # Emit a signal
-        signal.emit('aa')
+        communicator.statusBarUpdateSignal.emit(test_string)
 
-        # Test the spy object
+        # Was the signal caught by the signal spy?
+        self.assertEqual(spy.count(), 1)
 
 if __name__ == "__main__":
Index: src/sas/qtgui/run_tests.sh
===================================================================
--- src/sas/qtgui/run_tests.sh	(revision 488c49d59f65f588ab30dc9447de6dc0335177cc)
+++ src/sas/qtgui/run_tests.sh	(revision 5032ea687fa17c1323c1124c0a7aa9d80184bf0b)
@@ -1,5 +1,6 @@
-python -m UnitTesting.TestUtilsTest
-python -m UnitTesting.WelcomePanelTest
+# python -m UnitTesting.TestUtilsTest
+# python -m UnitTesting.WelcomePanelTest
 python -m UnitTesting.DataExplorerTest
-python -m UnitTesting.MainWindowTest
+# python -m UnitTesting.GuiManagerTest
+# python -m UnitTesting.MainWindowTest
 
