Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/sas/qtgui/MainWindow/DataExplorer.py

    r67346f9 re5ae812  
    238238        Called when the "Open Project" menu item chosen. 
    239239        """ 
     240        # check if any items loaded and warn about data deletion 
     241        if self.model.rowCount() > 0: 
     242            msg = "This operation will set remove all data, plots and analyses from" 
     243            msg += " SasView before loading the project. Do you wish to continue?" 
     244            msgbox = QtWidgets.QMessageBox(self) 
     245            msgbox.setIcon(QtWidgets.QMessageBox.Warning) 
     246            msgbox.setText(msg) 
     247            msgbox.setWindowTitle("Project Load") 
     248            # custom buttons 
     249            button_yes = QtWidgets.QPushButton("Yes") 
     250            msgbox.addButton(button_yes, QtWidgets.QMessageBox.YesRole) 
     251            button_no = QtWidgets.QPushButton("No") 
     252            msgbox.addButton(button_no, QtWidgets.QMessageBox.RejectRole) 
     253            retval = msgbox.exec_() 
     254            if retval == QtWidgets.QMessageBox.RejectRole: 
     255                # cancel fit 
     256                return 
     257 
    240258        kwargs = { 
    241259            'parent'    : self, 
    242260            'caption'   : 'Open Project', 
    243             'filter'    : 'Project (*.json);;All files (*.*)', 
    244             'options'   : QtWidgets.QFileDialog.DontUseNativeDialog, 
    245             'directory' : self.default_project_location 
     261            'filter'    : 'Project Files (*.json);;Old Project Files (*.svs);;All files (*.*)', 
     262            'options'   : QtWidgets.QFileDialog.DontUseNativeDialog 
    246263        } 
    247264        filename = QtWidgets.QFileDialog.getOpenFileName(**kwargs)[0] 
    248265        if filename: 
    249266            self.default_project_location = os.path.dirname(filename) 
    250             load_thread = threads.deferToThread(self.readProject, filename) 
    251             load_thread.addCallback(self.readProjectComplete) 
    252             load_thread.addErrback(self.readProjectFailed) 
    253  
    254     def loadFailed(self, reason): 
    255         """ 
    256         """ 
    257         print("file load FAILED: ", reason) 
    258         pass 
    259  
    260     def readProjectFailed(self, reason): 
    261         """ 
    262         """ 
    263         print("readProjectFailed FAILED: ", reason) 
    264         pass 
    265  
    266     def readProject(self, filename): 
    267         self.communicator.statusBarUpdateSignal.emit("Loading Project... %s" % os.path.basename(filename)) 
    268         try: 
    269             manager = DataManager() 
    270             with open(filename, 'r') as infile: 
    271                 manager.load_from_readable(infile) 
    272  
    273             self.communicator.statusBarUpdateSignal.emit("Loaded Project: %s" % os.path.basename(filename)) 
    274             return manager 
    275  
    276         except: 
    277             self.communicator.statusBarUpdateSignal.emit("Failed: %s" % os.path.basename(filename)) 
    278             raise 
    279  
    280     def readProjectComplete(self, manager): 
    281         self.model.clear() 
    282  
    283         self.manager.assign(manager) 
    284         self.model.beginResetModel() 
    285         for id, item in self.manager.get_all_data().items(): 
    286             self.updateModel(item.data, item.path) 
    287  
    288         self.model.endResetModel() 
     267            self.deleteAllItems() 
     268            self.readProject(filename) 
     269 
     270    def loadAnalysis(self): 
     271        """ 
     272        Called when the "Open Analysis" menu item chosen. 
     273        """ 
     274        kwargs = { 
     275            'parent'    : self, 
     276            'caption'   : 'Open Analysis', 
     277            'filter'    : 'Project (*.fitv);;All files (*.*)', 
     278            'options'   : QtWidgets.QFileDialog.DontUseNativeDialog 
     279        } 
     280        filename = QtWidgets.QFileDialog.getOpenFileName(**kwargs)[0] 
     281        if filename: 
     282            self.readProject(filename) 
    289283 
    290284    def saveProject(self): 
     
    301295        name_tuple = QtWidgets.QFileDialog.getSaveFileName(**kwargs) 
    302296        filename = name_tuple[0] 
    303         if filename: 
    304             self.default_project_location = os.path.dirname(filename) 
    305             _, extension = os.path.splitext(filename) 
    306             if not extension: 
    307                 filename = '.'.join((filename, 'json')) 
    308             self.communicator.statusBarUpdateSignal.emit("Saving Project... %s\n" % os.path.basename(filename)) 
    309             with open(filename, 'w') as outfile: 
    310                 self.manager.save_to_writable(outfile) 
     297        if not filename: 
     298            return 
     299        self.default_project_location = os.path.dirname(filename) 
     300        _, extension = os.path.splitext(filename) 
     301        if not extension: 
     302            filename = '.'.join((filename, 'json')) 
     303        self.communicator.statusBarUpdateSignal.emit("Saving Project... %s\n" % os.path.basename(filename)) 
     304 
     305        return filename 
     306 
     307    def saveAsAnalysisFile(self, tab_id=1): 
     308        """ 
     309        Show the save as... dialog and return the chosen filepath 
     310        """ 
     311        default_name = "FitPage"+str(tab_id)+".fitv" 
     312 
     313        wildcard = "fitv files (*.fitv)" 
     314        kwargs = { 
     315            'caption'   : 'Save As', 
     316            'directory' : default_name, 
     317            'filter'    : wildcard, 
     318            'parent'    : None, 
     319        } 
     320        # Query user for filename. 
     321        filename_tuple = QtWidgets.QFileDialog.getSaveFileName(**kwargs) 
     322        filename = filename_tuple[0] 
     323        return filename 
     324 
     325    def saveAnalysis(self, data, tab_id=1): 
     326        """ 
     327        Called when the "Save Analysis" menu item chosen. 
     328        """ 
     329        filename = self.saveAsAnalysisFile(tab_id) 
     330        if not filename: 
     331            return 
     332        _, extension = os.path.splitext(filename) 
     333        if not extension: 
     334            filename = '.'.join((filename, 'fitv')) 
     335        self.communicator.statusBarUpdateSignal.emit("Saving analysis... %s\n" % os.path.basename(filename)) 
     336 
     337        with open(filename, 'w') as outfile: 
     338            GuiUtils.saveData(outfile, data) 
     339 
     340        self.communicator.statusBarUpdateSignal.emit('Analysis saved.') 
     341 
     342    def allDataForModel(self, model): 
     343        # data model 
     344        all_data = {} 
     345        for i in range(model.rowCount()): 
     346            properties = {} 
     347            item = model.item(i) 
     348            data = GuiUtils.dataFromItem(item) 
     349            if data is None: continue 
     350            # Now, all plots under this item 
     351            filename = data.filename 
     352            is_checked = item.checkState() 
     353            properties['checked'] = is_checked 
     354            other_datas = [] 
     355            # no need to save other_datas - things will be refit on read 
     356            #other_datas = GuiUtils.plotsFromFilename(filename, model) 
     357            # skip the main plot 
     358            #other_datas = list(other_datas.values())[1:] 
     359            all_data[data.id] = [data, properties, other_datas] 
     360        return all_data 
     361 
     362    def getDataForID(self, id): 
     363        # return the dataset with the given ID 
     364        all_data = [] 
     365        for model in (self.model, self.theory_model): 
     366            for i in range(model.rowCount()): 
     367                properties = {} 
     368                item = model.item(i) 
     369                data = GuiUtils.dataFromItem(item) 
     370                if data is None: continue 
     371                if data.id != id: continue 
     372                # We found the dataset - save it. 
     373                filename = data.filename 
     374                is_checked = item.checkState() 
     375                properties['checked'] = is_checked 
     376                other_datas = GuiUtils.plotsFromFilename(filename, model) 
     377                # skip the main plot 
     378                other_datas = list(other_datas.values())[1:] 
     379                all_data = [data, properties, other_datas] 
     380                break 
     381        return all_data 
     382 
     383    def getItemForID(self, id): 
     384        # return the model item with the given ID 
     385        item = None 
     386        for model in (self.model, self.theory_model): 
     387            for i in range(model.rowCount()): 
     388                properties = {} 
     389                item = model.item(i) 
     390                data = GuiUtils.dataFromItem(item) 
     391                if data is None: continue 
     392                if data.id != id: continue 
     393                # We found the item - return it 
     394                break 
     395        return item 
     396 
     397    def getAllData(self): 
     398        """ 
     399        converts all datasets into serializable dictionary 
     400        """ 
     401        data = self.allDataForModel(self.model) 
     402        theory = self.allDataForModel(self.theory_model) 
     403 
     404        all_data = {} 
     405        all_data['is_batch'] = str(self.chkBatch.isChecked()) 
     406 
     407        for key, value in data.items(): 
     408            all_data[key] = value 
     409        for key, value in theory.items(): 
     410            if key in all_data: 
     411                raise ValueError("Inconsistent data in Project file.") 
     412            all_data[key] = value 
     413        return all_data 
     414 
     415    def saveDataToFile(self, outfile): 
     416        """ 
     417        Save every dataset to a json file 
     418        """ 
     419        all_data = self.getAllData() 
     420        # save datas 
     421        GuiUtils.saveData(outfile, all_data) 
     422 
     423    def readProject(self, filename): 
     424        """ 
     425        Read out datasets and fitpages from file 
     426        """ 
     427        # Find out the filetype based on extension 
     428        ext = os.path.splitext(filename)[1] 
     429        all_data = {} 
     430        if 'svs' in ext.lower(): 
     431            # backward compatibility mode. 
     432            try: 
     433                datasets = GuiUtils.readProjectFromSVS(filename) 
     434            except Exception as ex: 
     435                # disregard malformed SVS and try to recover whatever 
     436                # is available 
     437                msg = "Error while reading the project file: "+str(ex) 
     438                logging.error(msg) 
     439                pass 
     440            # Convert fitpage properties and update the dict 
     441            try: 
     442                all_data = GuiUtils.convertFromSVS(datasets) 
     443            except Exception as ex: 
     444                # disregard malformed SVS and try to recover regardless 
     445                msg = "Error while converting the project file: "+str(ex) 
     446                logging.error(msg) 
     447                pass 
     448        else: 
     449            with open(filename, 'r') as infile: 
     450                try: 
     451                    all_data = GuiUtils.readDataFromFile(infile) 
     452                except Exception as ex: 
     453                    logging.error("Project load failed with " + str(ex)) 
     454                    return 
     455        for key, value in all_data.items(): 
     456            if key=='is_batch': 
     457                self.chkBatch.setChecked(True if value=='True' else False) 
     458                continue 
     459            if 'cs_tab' in key: 
     460                continue 
     461            # send newly created items to the perspective 
     462            self.updatePerspectiveWithProperties(key, value) 
     463 
     464        # See if there are any batch pages defined and create them, if so 
     465        self.updateWithBatchPages(all_data) 
     466 
     467        # Only now can we create/assign C&S pages. 
     468        for key, value in all_data.items(): 
     469            if 'cs_tab' in key: 
     470                self.updatePerspectiveWithProperties(key, value) 
     471 
     472    def updateWithBatchPages(self, all_data): 
     473        """ 
     474        Checks all properties and see if there are any batch pages defined. 
     475        If so, pull out relevant indices and recreate the batch page(s) 
     476        """ 
     477        batch_pages = [] 
     478        for key, value in all_data.items(): 
     479            if 'fit_params' not in value: 
     480                continue 
     481            params = value['fit_params'] 
     482            for page in params: 
     483                if page['is_batch_fitting'][0] != 'True': 
     484                    continue 
     485                batch_ids = page['data_id'][0] 
     486                # check for duplicates 
     487                batch_set = set(batch_ids) 
     488                if batch_set in batch_pages: 
     489                    continue 
     490                # Found a unique batch page. Send it away 
     491                items = [self.getItemForID(i) for i in batch_set] 
     492                # Update the batch page list 
     493                batch_pages.append(batch_set) 
     494                # Assign parameters to the most recent (current) page. 
     495                self._perspective().setData(data_item=items, is_batch=True) 
     496                self._perspective().updateFromParameters(page) 
     497        pass 
     498 
     499    def updatePerspectiveWithProperties(self, key, value): 
     500        """ 
     501        """ 
     502        if 'fit_data' in value: 
     503            data_dict = {key:value['fit_data']} 
     504            # Create new model items in the data explorer 
     505            items = self.updateModelFromData(data_dict) 
     506 
     507        if 'fit_params' in value: 
     508            params = value['fit_params'] 
     509            # Make the perspective read the rest of the read data 
     510            if not isinstance(params, list): 
     511                params = [params] 
     512            for page in params: 
     513                # Check if this set of parameters is for a batch page 
     514                # if so, skip the update 
     515                if page['is_batch_fitting'][0] == 'True': 
     516                    continue 
     517                # Send current model item to the perspective 
     518                self.sendItemToPerspective(items[0]) 
     519                # Assign parameters to the most recent (current) page. 
     520                self._perspective().updateFromParameters(page) 
     521        if 'cs_tab' in key and 'is_constraint' in value: 
     522            # Create a C&S page 
     523            self._perspective().addConstraintTab() 
     524            # Modify the tab 
     525            self._perspective().updateFromParameters(value) 
     526 
     527        pass # debugger 
     528 
     529    def updateModelFromData(self, data): 
     530        """ 
     531        Given data from analysis/project file, 
     532        create indices and populate data/theory models 
     533        """ 
     534        # model items for top level datasets 
     535        items = [] 
     536        for key, value in data.items(): 
     537            # key - cardinal number of dataset 
     538            # value - main dataset, [dependant filesets] 
     539            # add the main index 
     540            if not value: continue 
     541            #if key=='is_batch': 
     542            #    self.chkBatch.setChecked(True if value=='True' else False) 
     543            #    continue 
     544            new_data = value[0] 
     545            from sas.sascalc.dataloader.data_info import Data1D as old_data1d 
     546            from sas.sascalc.dataloader.data_info import Data2D as old_data2d 
     547            if isinstance(new_data, (old_data1d, old_data2d)): 
     548                new_data = self.manager.create_gui_data(value[0], new_data.filename) 
     549            assert isinstance(new_data, (Data1D, Data2D)) 
     550            properties = value[1] 
     551            is_checked = properties['checked'] 
     552            new_item = GuiUtils.createModelItemWithPlot(new_data, new_data.filename) 
     553            new_item.setCheckState(is_checked) 
     554            items.append(new_item) 
     555            model = self.theory_model 
     556            if new_data.is_data: 
     557                model = self.model 
     558                # Caption for the theories 
     559                new_item.setChild(2, QtGui.QStandardItem("FIT RESULTS")) 
     560 
     561            model.appendRow(new_item) 
     562            self.manager.add_data(data_list={new_data.id:new_data}) 
     563 
     564            # Add the underlying data 
     565            if not value[2]: 
     566                continue 
     567            for plot in value[2]: 
     568                assert isinstance(plot, (Data1D, Data2D)) 
     569                GuiUtils.updateModelItemWithPlot(new_item, plot, plot.name) 
     570        return items 
    311571 
    312572    def deleteFile(self, event): 
     
    424684            retval = msgbox.exec_() 
    425685 
     686    def sendItemToPerspective(self, item): 
     687        """ 
     688        Send the passed item data to the current perspective and set the relevant notifiers 
     689        """ 
     690        # Set the signal handlers 
     691        self.communicator.updateModelFromPerspectiveSignal.connect(self.updateModelFromPerspective) 
     692        selected_items = [item] 
     693        # Notify the GuiManager about the send request 
     694        try: 
     695            self._perspective().setData(data_item=selected_items, is_batch=False) 
     696        except Exception as ex: 
     697            msg = "%s perspective returned the following message: \n%s\n" %(self._perspective().name, str(ex)) 
     698            logging.error(msg) 
     699            msg = str(ex) 
     700            msgbox = QtWidgets.QMessageBox() 
     701            msgbox.setIcon(QtWidgets.QMessageBox.Critical) 
     702            msgbox.setText(msg) 
     703            msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok) 
     704            retval = msgbox.exec_() 
    426705 
    427706    def freezeCheckedData(self): 
     
    10591338        self.actionQuick3DPlot.triggered.connect(self.quickData3DPlot) 
    10601339        self.actionEditMask.triggered.connect(self.showEditDataMask) 
    1061         self.actionDelete.triggered.connect(self.deleteItem) 
     1340        self.actionDelete.triggered.connect(self.deleteSelectedItem) 
    10621341        self.actionFreezeResults.triggered.connect(self.freezeSelectedItems) 
    10631342 
     
    12711550                self.freezeItem(item_to_copy) 
    12721551 
    1273     def deleteItem(self): 
     1552    def deleteAllItems(self): 
     1553        """ 
     1554        Deletes all datasets from both model and theory_model 
     1555        """ 
     1556        deleted_items = [self.model.item(row) for row in range(self.model.rowCount()) 
     1557                         if self.model.item(row).isCheckable()] 
     1558        deleted_names = [item.text() for item in deleted_items] 
     1559        # Let others know we deleted data 
     1560        self.communicator.dataDeletedSignal.emit(deleted_items) 
     1561        # update stored_data 
     1562        self.manager.update_stored_data(deleted_names) 
     1563 
     1564        # Clear the model 
     1565        self.model.clear() 
     1566 
     1567    def deleteSelectedItem(self): 
    12741568        """ 
    12751569        Delete the current item 
     
    12881582            return 
    12891583 
     1584        indices = self.current_view.selectedIndexes() 
     1585        self.deleteIndices(indices) 
     1586 
     1587    def deleteIndices(self, indices): 
     1588        """ 
     1589        Delete model idices from the current view 
     1590        """ 
     1591        proxy = self.current_view.model() 
     1592        model = proxy.sourceModel() 
     1593 
     1594        deleted_items = [] 
     1595        deleted_names = [] 
     1596 
    12901597        # Every time a row is removed, the indices change, so we'll just remove 
    12911598        # rows and keep calling selectedIndexes until it returns an empty list. 
    1292         indices = self.current_view.selectedIndexes() 
    1293  
    1294         proxy = self.current_view.model() 
    1295         model = proxy.sourceModel() 
    1296  
    1297         deleted_items = [] 
    1298         deleted_names = [] 
    1299  
    13001599        while len(indices) > 0: 
    13011600            index = indices[0] 
    1302             row_index = proxy.mapToSource(index) 
    1303             item_to_delete = model.itemFromIndex(row_index) 
     1601            #row_index = proxy.mapToSource(index) 
     1602            #item_to_delete = model.itemFromIndex(row_index) 
     1603            item_to_delete = model.itemFromIndex(index) 
    13041604            if item_to_delete and item_to_delete.isCheckable(): 
    1305                 row = row_index.row() 
     1605                #row = row_index.row() 
     1606                row = index.row() 
    13061607 
    13071608                # store the deleted item details so we can pass them on later 
     
    14301731        checkbox_item.setCheckable(True) 
    14311732        checkbox_item.setCheckState(QtCore.Qt.Checked) 
    1432         checkbox_item.setText(os.path.basename(p_file)) 
     1733        if p_file is not None: 
     1734            checkbox_item.setText(os.path.basename(p_file)) 
    14331735 
    14341736        # Add the actual Data1D/Data2D object 
Note: See TracChangeset for help on using the changeset viewer.