Changeset 5032ea68 in sasview for src/sas/qtgui
- Timestamp:
- Jun 14, 2016 4:51:18 AM (8 years ago)
- Branches:
- ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
- Children:
- a281ab8
- Parents:
- 488c49d
- Location:
- src/sas/qtgui
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/DataExplorer.py
r488c49d r5032ea68 52 52 self.treeView.setModel(self.proxy) 53 53 54 def loadFile(self, event): 54 55 def loadFile(self, event=None): 55 56 """ 56 57 Called when the "Load" button pressed. … … 64 65 self.communicate.fileReadSignal.emit(path_str) 65 66 66 # Read in the data from chosen file(s) 67 self.readData(path_str) 67 # threaded file load 68 load_thread = threads.deferToThread(self.readData, path_str) 69 load_thread.addCallback(self.loadComplete) 68 70 69 71 return 70 72 73 def loadFolder(self, event=None): 74 """ 75 Called when the "File/Load Folder" menu item chosen. 76 Opens the Qt "Open Folder..." dialog 77 """ 78 dir = QtGui.QFileDialog.getExistingDirectory(self, "Choose a directory", "", 79 QtGui.QFileDialog.ShowDirsOnly) 80 if dir is None: 81 return 82 83 dir = str(dir) 84 85 if not os.path.isdir(dir): 86 return 87 88 # get content of dir into a list 89 path_str = [os.path.join(os.path.abspath(dir), filename) for filename in os.listdir(dir)] 90 91 # threaded file load 92 load_thread = threads.deferToThread(self.readData, path_str) 93 load_thread.addCallback(self.loadComplete) 94 95 return 96 71 97 def deleteFile(self, event): 72 98 """ 73 """ 99 Delete selected rows from the model 100 """ 101 # Assure this is indeed wanted 102 delete_msg = "This operation will delete the checked data sets and all the dependents." +\ 103 "\nDo you want to continue?" 104 reply = QtGui.QMessageBox.question(self, 'Warning', delete_msg, 105 QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) 106 107 if reply == QtGui.QMessageBox.No: 108 return 109 74 110 # Figure out which rows are checked 75 76 # Delete these rows from the model 77 78 # Call data_manager update with delete_data() 79 111 ind = -1 112 # Use 'while' so the row count is forced at every iteration 113 while ind < self.model.rowCount(): 114 ind += 1 115 item = self.model.item(ind) 116 if item and item.isCheckable() and item.checkState() == QtCore.Qt.Checked: 117 # Delete these rows from the model 118 self.model.removeRow(ind) 119 # Decrement index since we just deleted it 120 ind -= 1 121 122 # pass temporarily kept as a breakpoint anchor 80 123 pass 81 124 82 125 def sendData(self, event): 83 126 """ 84 """ 127 Send selected item data to the current perspective and set the relevant notifiers 128 """ 129 # should this reside on GuiManager or here? 130 self._perspective = self.parent.perspective() 131 132 # Set the signal handlers 133 self.communicator = self._perspective.communicator() 134 self.communicator.updateModelFromPerspectiveSignal.connect(self.updateModelFromPerspective) 135 85 136 # Figure out which rows are checked 86 137 selected_items = [] 138 for index in range(self.model.rowCount()): 139 item = self.model.item(index) 140 if item.isCheckable() and item.checkState() == QtCore.Qt.Checked: 141 selected_items.append(item) 142 143 # Which perspective has been selected? 144 if len(selected_items) > 1 and not self._perspective.allowBatch(): 145 msg = self._perspective.title() + " does not allow multiple data." 146 msgbox = QtGui.QMessageBox() 147 msgbox.setIcon(QtGui.QMessageBox.Critical) 148 msgbox.setText(msg) 149 msgbox.setStandardButtons(QtGui.QMessageBox.Ok) 150 retval = msgbox.exec_() 151 return 87 152 # Dig up data from model 88 # To get the original Data1D object back use: 89 # object_item.data().toPyObject() 90 91 92 # Which perspective has been selected? 93 153 data = [selected_items[0].child(0).data().toPyObject()] 154 155 # TODO 94 156 # New plot or appended? 95 157 96 158 # Notify the GuiManager about the send request 97 # updatePerspectiveWithDataSignal()98 pass 159 self._perspective.setData(data_list=data) 160 99 161 100 162 def chooseFiles(self): 101 163 """ 164 Shows the Open file dialog and returns the chosen path(s) 102 165 """ 103 166 # List of known extensions … … 105 168 106 169 # Location is automatically saved - no need to keep track of the last dir 107 # TODO: is it really? 108 paths = QtGui.QFileDialog.getOpenFileName(self, "Choose a file", "", wlist) 170 # But only with Qt built-in dialog (non-platform native) 171 paths = QtGui.QFileDialog.getOpenFileName(self, "Choose a file", "", 172 wlist, None, QtGui.QFileDialog.DontUseNativeDialog) 109 173 if paths is None: 110 174 return … … 132 196 data_error = False 133 197 error_message = "" 198 134 199 for p_file in path: 135 200 info = "info" … … 147 212 148 213 try: 149 message = "Loading Data... " + str( p_file) + "\n"214 message = "Loading Data... " + str(basename) + "\n" 150 215 151 216 # change this to signal notification in GuiManager 152 217 self.communicate.statusBarUpdateSignal.emit(message) 153 154 # threaded file load155 # load_thread = threads.deferToThread(self.loadThread, p_file)156 # Add deferred callback for call return157 # load_thread.addCallback(self.plotResult)158 218 159 219 output_objects = self.loader.load(p_file) … … 170 230 output[new_data.id] = new_data 171 231 self.updateModel(new_data, p_file) 232 self.model.reset() 233 234 QtGui.qApp.processEvents() 172 235 173 236 if hasattr(item, 'errors'): … … 176 239 message += "\tError: {0}\n".format(error_data) 177 240 else: 241 178 242 logging.error("Loader returned an invalid object:\n %s" % str(item)) 179 243 data_error = True 180 244 181 except :245 except Exception as ex: 182 246 logging.error(sys.exc_value) 247 183 248 any_error = True 184 249 if any_error or error_message != "": … … 207 272 message = "Loading Data Complete! " 208 273 message += log_msg 209 self.loadComplete(output=output, message=message)274 return (output, message) 210 275 211 276 def getWlist(self): … … 314 379 Post message to status bar and update the data manager 315 380 """ 381 self.model.reset() 316 382 # Notify the manager of the new data available 317 383 self.communicate.statusBarUpdateSignal.emit(message) 318 384 self.communicate.fileDataReceivedSignal.emit(output) 319 320 385 self.manager.add_data(data_list=output) 321 386 … … 362 427 self.proxy.setFilterRegExp(r"[^()]") 363 428 429 def updateModelFromPerspective(self, model_item): 430 """ 431 """ 432 # Overwrite the index with what we got from the perspective 433 if type(model_item) != QtGui.QStandardItem: 434 msg = "Wrong data type returned from calculations." 435 raise AttributeError, msg 436 # self.model.insertRow(model_item) 437 # Reset the view 438 self.model.reset() 439 # Pass acting as a debugger anchor 440 pass 364 441 365 442 def addExtraRows(self, info_item, data): 366 443 """ 444 Extract relevant data to include in the Info ModelItem 367 445 """ 368 446 title_item = QtGui.QStandardItem("Title: " + data.title) -
src/sas/qtgui/GuiManager.py
rf721030 r5032ea68 65 65 self._current_perspective = self.invariantWidget 66 66 67 68 67 def fileRead(self, data): 69 68 """ 70 69 """ 71 #print("FILE %s "%data)70 print("FILE %s "%data) 72 71 pass 73 72 … … 225 224 def actionLoadData(self): 226 225 """ 227 """228 print("actionLoadData TRIGGERED")229 pass226 Load file from Data Explorer 227 """ 228 self.filesWidget.loadFile() 230 229 231 230 def actionLoad_Data_Folder(self): 232 231 """ 233 232 """ 234 print("actionLoad_Data_Folder TRIGGERED") 235 pass 233 self.filesWidget.loadFolder() 236 234 237 235 def actionOpen_Project(self): -
src/sas/qtgui/GuiUtils.py
rf721030 r5032ea68 25 25 #from sas.sasgui.guiframe.events import StatusEvent 26 26 #from sas.sasgui.guiframe.events import NewPlotEvent 27 from sas.sasgui.guiframe.dataFitting import Data1D 27 28 28 29 … … 202 203 # Send data to the current perspective 203 204 updatePerspectiveWithDataSignal = QtCore.pyqtSignal(list) 205 206 # New data in current perspective 207 updateModelFromPerspectiveSignal = QtCore.pyqtSignal(Data1D) -
src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py
rf721030 r5032ea68 11 11 from sas.sascalc.invariant import invariant 12 12 from sas.sasgui.guiframe.dataFitting import Data1D 13 from sas.qtgui.GuiUtils import Communicate 13 14 14 15 # local … … 40 41 def __init__(self, manager=None, parent=None): 41 42 super(InvariantWindow, self).__init__(parent) 42 # This controller contains the ui and doesn't inherit it directly!43 # self.form = InvariantUI()44 43 self.setWindowTitle("Invariant Perspective") 45 44 # initial input params … … 65 64 self._high_power_value = False 66 65 66 self.communicate = Communicate() 67 67 68 68 # Mask file selector … … 92 92 # Set up the mapper 93 93 self.setupMapper() 94 95 def communicator(self): 96 """ 97 """ 98 return self.communicate 94 99 95 100 def updateFromModel(self): … … 142 147 self.pushButton.setStyleSheet(self.style) 143 148 149 144 150 def plotResult(self, model): 145 151 """ … … 154 160 self.pushButton.setStyleSheet(self.style) 155 161 162 # Send the new data to DE for keeping in the model 163 self.communicate.updateModelFromPerspectiveSignal.emit(self._data) 164 156 165 157 166 def calculateThread(self, extrapolation): 158 167 """ 168 Perform Invariant calculations. 169 170 TODO: Create a dictionary of results to be sent to DE on completion. 159 171 """ 160 172 self.updateFromModel() … … 294 306 return self.model 295 307 308 def title(self): 309 """ 310 Perspective name 311 """ 312 return "Invariant panel" 296 313 297 314 def status(self): … … 537 554 #wx.PostEvent(self.parent, StatusEvent(status=msg, info='error')) 538 555 539 556 def allowBatch(self): 557 """ 558 Tell the caller that we don't accept multiple data instances 559 """ 560 return False 540 561 541 562 if __name__ == "__main__": -
src/sas/qtgui/UnitTesting/DataExplorerTest.py
r488c49d r5032ea68 1 1 import sys 2 2 import unittest 3 #from twisted.trial import unittest 4 #from twisted.internet import reactor, defer, interfaces, threads, protocol, error 3 5 4 6 from PyQt4.QtGui import * … … 20 22 def setUp(self): 21 23 '''Create the GUI''' 24 class MyPerspective(object): 25 def communicator(self): 26 return Communicate() 27 def allowBatch(self): 28 return False 29 def setData(self, data_list=None): 30 return None 31 def title(self): 32 return "Dummy Perspective" 33 22 34 class dummy_manager(object): 23 35 def communicator(self): 24 36 return Communicate() 37 def perspective(self): 38 return MyPerspective() 25 39 26 40 self.form = DataExplorerWindow(None, dummy_manager()) … … 67 81 68 82 def testDeleteButton(self): 83 """ 84 Functionality of the delete button 85 """ 69 86 70 87 deleteButton = self.form.cmdDelete 71 88 72 89 # Mock the confirmation dialog with return=Yes 90 QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.No) 73 91 74 92 # Populate the model 75 76 # Assure the checkbox is on 93 filename = ["cyl_400_20.txt", "Dec07031.ASC", "cyl_400_20.txt"] 94 self.form.readData(filename) 95 96 # Assure the model contains three items 97 self.assertEqual(self.form.model.rowCount(), 3) 98 99 # Assure the checkboxes are on 100 item1 = self.form.model.item(0) 101 item2 = self.form.model.item(1) 102 item3 = self.form.model.item(2) 103 self.assertTrue(item1.checkState() == QtCore.Qt.Checked) 104 self.assertTrue(item2.checkState() == QtCore.Qt.Checked) 105 self.assertTrue(item3.checkState() == QtCore.Qt.Checked) 77 106 78 107 # Click on the delete button … … 80 109 81 110 # Test the warning dialog called once 82 # self.assertTrue(QtGui.QFileDialog.getOpenFileName.called) 111 self.assertTrue(QtGui.QMessageBox.question.called) 112 113 # Assure the model still contains the items 114 self.assertEqual(self.form.model.rowCount(), 3) 115 116 # Now, mock the confirmation dialog with return=Yes 117 QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.Yes) 118 119 # Click on the delete button 120 QTest.mouseClick(deleteButton, Qt.LeftButton) 121 122 # Test the warning dialog called once 123 self.assertTrue(QtGui.QMessageBox.question.called) 83 124 84 125 # Assure the model contains no items 85 86 def testSendToButton(self): 87 sendToButton = self.form.cmdSendTo 88 89 # Mock the current perspective set_data method 90 126 self.assertEqual(self.form.model.rowCount(), 0) 127 128 # Click delete once again to assure no nasty behaviour on empty model 129 QTest.mouseClick(deleteButton, Qt.LeftButton) 130 131 132 def testSendToButton(self): 133 """ 134 Test that clicking the Send To button sends checked data to a perspective 135 """ 136 91 137 # Populate the model 138 filename = ["cyl_400_20.txt"] 139 self.form.readData(filename) 140 141 # setData is the method we want to see called 142 mocked = self.form.parent.perspective().setData 143 mocked = MagicMock(filename) 92 144 93 145 # Assure the checkbox is on 146 self.form.cbSelect.setCurrentIndex(0) 94 147 95 148 # Click on the Send To button 96 QTest.mouseClick(se ndToButton, Qt.LeftButton)149 QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton) 97 150 98 151 # Test the set_data method called once 99 # self.assertTrue(QtGui.QFileDialog.getOpenFileName.called) 100 101 # Assure the model contains no items 152 # self.assertTrue(mocked.called) 153 154 # open another file 155 filename = ["cyl_400_20.txt"] 156 self.form.readData(filename) 157 158 # Mock the warning message 159 QtGui.QMessageBox = MagicMock() 160 161 # Click on the button 162 QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton) 163 164 # Assure the message box popped up 165 QtGui.QMessageBox.assert_called_once() 166 102 167 103 168 def testDataSelection(self): … … 159 224 def testReadData(self): 160 225 """ 161 Test the readData() method226 Test the low level readData() method 162 227 """ 163 228 filename = ["cyl_400_20.txt"] … … 172 237 173 238 # Expected two status bar updates 174 self.assertEqual(spy_status_update.count(), 2)239 self.assertEqual(spy_status_update.count(), 1) 175 240 self.assertIn(filename[0], str(spy_status_update.called()[0]['args'][0])) 176 self.assertIn("Loading Data Complete", str(spy_status_update.called()[1]['args'][0])) 177 178 # Expect one Data Received signal 179 self.assertEqual(spy_data_received.count(), 1) 180 181 # Assure returned dictionary has correct data 182 # We don't know the data ID, so need to iterate over dict 183 data_dict = spy_data_received.called()[0]['args'][0] 184 for data_key, data_value in data_dict.iteritems(): 185 self.assertIsInstance(data_value, Data1D) 241 186 242 187 243 # Check that the model contains the item … … 194 250 self.assertEqual(model_name, filename[0]) 195 251 196 # Assure add_data on data_manager was called (last call) 197 self.assertTrue(self.form.manager.add_data.called) 252 def testLoadFile(self): 253 """ 254 Test the threaded call to readData() 255 """ 256 pass 198 257 199 258 def testGetWList(self): … … 206 265 'HFIR 1D files (*.d1d);;DANSE files (*.sans);;NXS files (*.nxs)' 207 266 self.assertEqual(defaults, list) 208 267 209 268 def testLoadComplete(self): 210 269 """ 211 """ 212 # Initialize signal spy instances 270 Test the callback method updating the data object 271 """ 272 273 data_dict = {"a1":Data1D()} 274 275 self.form.manager.add_data = MagicMock() 276 277 # Initialize signal spy instances 213 278 spy_status_update = QtSignalSpy(self.form, self.form.communicate.statusBarUpdateSignal) 214 279 spy_data_received = QtSignalSpy(self.form, self.form.communicate.fileDataReceivedSignal) 215 280 216 # Need an empty Data1D object 217 mockData = Data1D() 218 219 # Call the tested method 220 self.form.loadComplete({"a":mockData}, message="test message") 221 222 # test the signals 223 self.assertEqual(spy_status_update.count(), 1) 224 self.assertIn("message", str(spy_status_update.called()[0]['args'][0])) 281 # Read in the file 282 self.form.loadComplete(data_dict, message="Loading Data Complete") 283 284 # "Loading data complete" no longer sent in LoadFile but in callback 285 self.assertIn("Loading Data Complete", str(spy_status_update.called()[0]['args'][0])) 286 287 # Expect one Data Received signal 225 288 self.assertEqual(spy_data_received.count(), 1) 226 289 227 # The data_manager update is going away, so don't bother testing 228 290 # Assure returned dictionary has correct data 291 # We don't know the data ID, so need to iterate over dict 292 data_dict = spy_data_received.called()[0]['args'][0] 293 for data_key, data_value in data_dict.iteritems(): 294 self.assertIsInstance(data_value, Data1D) 295 296 # Assure add_data on data_manager was called (last call) 297 self.assertTrue(self.form.manager.add_data.called) 298 299 229 300 if __name__ == "__main__": 230 301 unittest.main() -
src/sas/qtgui/UnitTesting/GuiManagerTest.py
r488c49d r5032ea68 9 9 # Local 10 10 from GuiManager import GuiManager 11 from UI.MainWindowUI import MainWindow 11 12 12 #app = QApplication(sys.argv)13 app = QApplication(sys.argv) 13 14 14 15 class GuiManagerTest(unittest.TestCase): 15 '''Test the WelcomePanel'''16 '''Test the Main Window functionality''' 16 17 def setUp(self): 17 18 '''Create the tested object''' 19 class MainSasViewWindow(MainWindow): 20 # Main window of the application 21 def __init__(self, reactor, parent=None): 22 super(MainSasViewWindow, self).__init__(parent) 23 24 # define workspace for dialogs. 25 self.workspace = QWorkspace(self) 26 self.setCentralWidget(self.workspace) 18 27 19 self.manager = GuiManager( None)28 self.manager = GuiManager(MainSasViewWindow(None), None) 20 29 21 30 def tearDown(self): … … 52 61 pass 53 62 63 def testActionLoadData(self): 64 """ 65 Menu File/Load Data File(s) 66 """ 67 # Mock the system file open method 68 QFileDialog.getOpenFileName = MagicMock(return_value=None) 69 70 # invoke the action 71 72 # Test the getOpenFileName() dialog called once 73 #self.assertTrue(QtGui.QFileDialog.getOpenFileName.called) 74 #QtGui.QFileDialog.getOpenFileName.assert_called_once() 75 76 54 77 # test each action separately 55 78 -
src/sas/qtgui/UnitTesting/TestUtilsTest.py
r488c49d r5032ea68 18 18 def testQtSignalSpy(self): 19 19 '''Create the Spy the correct way''' 20 test_string = 'my precious' 20 21 22 def signalReceived(signal): 23 # Test the signal callback 24 self.assertEqual(signal, test_string) 25 26 communicator = Communicate() 27 communicator.statusBarUpdateSignal.connect(signalReceived) 28 29 # Define the signal spy for testing 21 30 widget = QWidget() 22 signal = Communicate.statusBarUpdateSignal 23 self.spy = QtSignalSpy(widget, signal) 31 spy = QtSignalSpy(widget, communicator.statusBarUpdateSignal) 24 32 25 33 # Emit a signal 26 signal.emit('aa')34 communicator.statusBarUpdateSignal.emit(test_string) 27 35 28 # Test the spy object 36 # Was the signal caught by the signal spy? 37 self.assertEqual(spy.count(), 1) 29 38 30 39 if __name__ == "__main__": -
src/sas/qtgui/run_tests.sh
r488c49d r5032ea68 1 python -m UnitTesting.TestUtilsTest2 python -m UnitTesting.WelcomePanelTest1 # python -m UnitTesting.TestUtilsTest 2 # python -m UnitTesting.WelcomePanelTest 3 3 python -m UnitTesting.DataExplorerTest 4 python -m UnitTesting.MainWindowTest 4 # python -m UnitTesting.GuiManagerTest 5 # python -m UnitTesting.MainWindowTest 5 6
Note: See TracChangeset
for help on using the changeset viewer.