Changeset d4dac80 in sasview for src/sas/qtgui/Utilities


Ignore:
Timestamp:
Apr 25, 2018 6:59:33 AM (7 years ago)
Author:
Piotr Rozyczko <rozyczko@…>
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:
017b285
Parents:
93c79b5
git-author:
Piotr Rozyczko <rozyczko@…> (03/22/18 16:46:19)
git-committer:
Piotr Rozyczko <rozyczko@…> (04/25/18 06:59:33)
Message:

Merge branch 'ESS_GUI' into ESS_GUI_better_batch

Location:
src/sas/qtgui/Utilities
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • src/sas/qtgui/Utilities/GridPanel.py

    r8b480d27 rd4dac80  
    11import os 
     2import sys 
    23import time 
    34import logging 
    45import webbrowser 
    56 
    6 from PyQt5 import QtCore, QtWidgets 
     7from PyQt5 import QtCore, QtWidgets, QtGui 
    78 
    89import sas.qtgui.Utilities.GuiUtils as GuiUtils 
    9  
     10from sas.qtgui.Plotting.PlotterData import Data1D 
    1011from sas.qtgui.Utilities.UI.GridPanelUI import Ui_GridPanelUI 
    1112 
     
    1516    Class for stateless grid-like printout of model parameters for mutiple models 
    1617    """ 
     18    ERROR_COLUMN_CAPTION = " (Err)" 
     19    IS_WIN = (sys.platform == 'win32') 
    1720    def __init__(self, parent = None, output_data=None): 
    1821 
    19         super(BatchOutputPanel, self).__init__() 
     22        super(BatchOutputPanel, self).__init__(parent._parent) 
    2023        self.setupUi(self) 
    2124 
    22         self.data = output_data 
    2325        self.parent = parent 
    2426        if hasattr(self.parent, "communicate"): 
     
    3032        self.grid_filename = "" 
    3133 
     34        self.has_data = False if output_data is None else True 
     35        # Tab numbering 
     36        self.tab_number = 1 
     37 
     38        # System dependent menu items 
     39        if not self.IS_WIN: 
     40            self.actionOpen_with_Excel.setVisible(False) 
     41 
     42        # list of QTableWidgets, indexed by tab number 
     43        self.tables = [] 
     44        self.tables.append(self.tblParams) 
     45 
    3246        # context menu on the table 
    3347        self.tblParams.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 
    3448        self.tblParams.customContextMenuRequested.connect(self.showContextMenu) 
    3549 
    36         # Fill in the table from input data 
    37         self.setupTable(output_data) 
    38  
    3950        # Command buttons 
    4051        self.cmdHelp.clicked.connect(self.onHelp) 
     52        self.cmdPlot.clicked.connect(self.onPlot) 
     53 
     54        # Fill in the table from input data 
     55        self.setupTable(widget=self.tblParams, data=output_data) 
     56        if output_data is not None: 
     57            # Set a table tooltip describing the model 
     58            model_name = output_data[0][0].model.id 
     59            self.tabWidget.setTabToolTip(0, model_name) 
     60 
     61    def closeEvent(self, event): 
     62        """ 
     63        Overwrite QDialog close method to allow for custom widget close 
     64        """ 
     65        # Maybe we should just minimize 
     66        self.setWindowState(QtCore.Qt.WindowMinimized) 
     67        event.ignore() 
    4168 
    4269    def addToolbarActions(self): 
     
    6491 
    6592        self.setupTableFromCSV(lines) 
     93        self.has_data = True 
     94 
     95    def currentTable(self): 
     96        """ 
     97        Returns the currently shown QTabWidget 
     98        """ 
     99        return self.tables[self.tabWidget.currentIndex()] 
    66100 
    67101    def showContextMenu(self, position): 
     
    70104        """ 
    71105        menu = QtWidgets.QMenu() 
    72         rows = [s.row() for s in self.tblParams.selectionModel().selectedRows()] 
     106        rows = [s.row() for s in self.currentTable().selectionModel().selectedRows()] 
    73107        num_rows = len(rows) 
    74108        if num_rows <= 0: 
     
    84118 
    85119        # Define the callbacks 
    86         self.actionPlotResults.triggered.connect(self.plotFits) 
     120        self.actionPlotResults.triggered.connect(self.onPlot) 
    87121        try: 
    88             menu.exec_(self.tblParams.viewport().mapToGlobal(position)) 
     122            menu.exec_(self.currentTable().viewport().mapToGlobal(position)) 
    89123        except AttributeError as ex: 
    90124            logging.error("Error generating context menu: %s" % ex) 
    91125        return 
     126 
     127    def addTabPage(self): 
     128        """ 
     129        Add new tab page with QTableWidget 
     130        """ 
     131        layout = QtWidgets.QVBoxLayout() 
     132        tab_widget = QtWidgets.QTableWidget(parent=self) 
     133        # Same behaviour as the original tblParams 
     134        tab_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) 
     135        tab_widget.setAlternatingRowColors(True) 
     136        tab_widget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 
     137        tab_widget.setLayout(layout) 
     138        # Simple naming here. 
     139        # One would think naming the tab with current model name would be good. 
     140        # However, some models have LONG names, which doesn't look well on the tab bar. 
     141        self.tab_number += 1 
     142        tab_name = "Tab " + str(self.tab_number) 
     143        # each table needs separate slots. 
     144        tab_widget.customContextMenuRequested.connect(self.showContextMenu) 
     145        self.tables.append(tab_widget) 
     146        self.tabWidget.addTab(tab_widget, tab_name) 
     147        # Make the new tab active 
     148        self.tabWidget.setCurrentIndex(self.tab_number-1) 
     149 
     150    def addFitResults(self, results): 
     151        """ 
     152        Create a new tab with batch fitting results 
     153        """ 
     154        self.addTabPage() 
     155        # Update the new widget 
     156        # Fill in the table from input data in the last/newest page 
     157        assert(self.tables) 
     158        self.setupTable(widget=self.tables[-1], data=results) 
     159        self.has_data = True 
     160 
     161        # Set a table tooltip describing the model 
     162        model_name = results[0][0].model.id 
     163        self.tabWidget.setTabToolTip(self.tabWidget.count()-1, model_name) 
     164 
    92165 
    93166    @classmethod 
     
    103176            logging.warning("Cannot display help. %s" % ex) 
    104177 
    105     def plotFits(self): 
     178    def onPlot(self): 
    106179        """ 
    107180        Plot selected fits by sending signal to the parent 
    108181        """ 
    109         rows = [s.row() for s in self.tblParams.selectionModel().selectedRows()] 
    110         data = self.dataFromTable(self.tblParams) 
     182        rows = [s.row() for s in self.currentTable().selectionModel().selectedRows()] 
     183        if not rows: 
     184            msg = "Nothing to plot!" 
     185            self.parent.communicate.statusBarUpdateSignal.emit(msg) 
     186            return 
     187        data = self.dataFromTable(self.currentTable()) 
    111188        # data['Data'] -> ['filename1', 'filename2', ...] 
    112189        # look for the 'Data' column and extract the filename 
     
    141218            tmpfile = tempfile.NamedTemporaryFile(delete=False, mode="w+", suffix=".csv") 
    142219            self.grid_filename = tmpfile.name 
    143             data = self.dataFromTable(self.tblParams) 
     220            data = self.dataFromTable(self.currentTable()) 
    144221            t = time.localtime(time.time()) 
    145222            time_str = time.strftime("%b %d %H:%M of %Y", t) 
     
    181258        if not filename: 
    182259            return 
    183         data = self.dataFromTable(self.tblParams) 
     260        data = self.dataFromTable(self.currentTable()) 
    184261        details = "File generated by SasView\n" 
    185262        with open(filename, 'w') as csv_file: 
     
    190267        Create tablewidget items and show them, based on params 
    191268        """ 
    192         # Clear existing display 
    193         self.tblParams.clear() 
     269        # Is this an empty grid? 
     270        if self.has_data: 
     271            # Add a new page 
     272            self.addTabPage() 
     273            # Access the newly created QTableWidget 
     274            current_page = self.tables[-1] 
     275        else: 
     276            current_page = self.tblParams 
    194277        # headers 
    195278        param_list = csv_data[1].rstrip().split(',') 
     279        # need to remove the 2 header rows to get the total data row number 
     280        rows = len(csv_data) -2 
     281        assert(rows > -1) 
     282        columns = len(param_list) 
     283        current_page.setColumnCount(columns) 
     284        current_page.setRowCount(rows) 
     285 
    196286        for i, param in enumerate(param_list): 
    197             self.tblParams.setHorizontalHeaderItem(i, QtWidgets.QTableWidgetItem(param)) 
     287            current_page.setHorizontalHeaderItem(i, QtWidgets.QTableWidgetItem(param)) 
    198288 
    199289        # first - Chi2 and data filename 
    200290        for i_row, row in enumerate(csv_data[2:]): 
    201291            for i_col, col in enumerate(row.rstrip().split(',')): 
    202                 self.tblParams.setItem(i_row, i_col, QtWidgets.QTableWidgetItem(col)) 
    203  
    204         self.tblParams.resizeColumnsToContents() 
    205  
    206     def setupTable(self, data): 
     292                current_page.setItem(i_row, i_col, QtWidgets.QTableWidgetItem(col)) 
     293 
     294        current_page.resizeColumnsToContents() 
     295 
     296    def setupTable(self, widget=None, data=None): 
    207297        """ 
    208298        Create tablewidget items and show them, based on params 
    209299        """ 
    210         # headers 
     300        # quietly leave is nothing to show 
     301        if data is None or widget is None: 
     302            return 
     303 
     304        # Figure out the headers 
    211305        model = data[0][0] 
     306 
     307        # TODO: add a conditional for magnetic models 
    212308        param_list = [m for m in model.model.params.keys() if ":" not in m] 
    213309 
    214310        # Check if 2D model. If not, remove theta/phi 
     311        if isinstance(model.data.sas_data, Data1D): 
     312            param_list.remove('theta') 
     313            param_list.remove('phi') 
    215314 
    216315        rows = len(data) 
    217316        columns = len(param_list) 
    218         self.tblParams.setColumnCount(columns+2) 
    219         self.tblParams.setRowCount(rows) 
    220  
     317 
     318        widget.setColumnCount(columns+2) # add 2 initial columns defined below 
     319        widget.setRowCount(rows) 
     320 
     321        # Insert two additional columns 
    221322        param_list.insert(0, "Data") 
    222323        param_list.insert(0, "Chi2") 
    223324        for i, param in enumerate(param_list): 
    224             self.tblParams.setHorizontalHeaderItem(i, QtWidgets.QTableWidgetItem(param)) 
    225  
     325            widget.setHorizontalHeaderItem(i, QtWidgets.QTableWidgetItem(param)) 
     326 
     327        # dictionary of parameter errors for post-processing 
     328        # [param_name] = [param_column_nr, error_for_row_1, error_for_row_2,...] 
     329        error_columns = {} 
    226330        # first - Chi2 and data filename 
    227331        for i_row, row in enumerate(data): 
     
    231335            if hasattr(row[0].data, "sas_data"): 
    232336                filename = row[0].data.sas_data.filename 
    233             self.tblParams.setItem(i_row, 0, QtWidgets.QTableWidgetItem(GuiUtils.formatNumber(chi2, high=True))) 
    234             self.tblParams.setItem(i_row, 1, QtWidgets.QTableWidgetItem(str(filename))) 
     337            widget.setItem(i_row, 0, QtWidgets.QTableWidgetItem(GuiUtils.formatNumber(chi2, high=True))) 
     338            widget.setItem(i_row, 1, QtWidgets.QTableWidgetItem(str(filename))) 
    235339            # Now, all the parameters 
    236340            for i_col, param in enumerate(param_list[2:]): 
     
    238342                    # parameter is on the to-optimize list - get the optimized value 
    239343                    par_value = row[0].pvec[row[0].param_list.index(param)] 
    240                     # should we parse out errors here and store them? 
     344                    # parse out errors and store them for later use 
     345                    err_value = row[0].stderr[row[0].param_list.index(param)] 
     346                    if param in error_columns: 
     347                        error_columns[param].append(err_value) 
     348                    else: 
     349                        error_columns[param] = [i_col, err_value] 
    241350                else: 
    242351                    # parameter was not varied 
    243352                    par_value = row[0].model.params[param] 
    244                 self.tblParams.setItem(i_row, i_col+2, QtWidgets.QTableWidgetItem( 
     353 
     354                widget.setItem(i_row, i_col+2, QtWidgets.QTableWidgetItem( 
    245355                    GuiUtils.formatNumber(par_value, high=True))) 
    246356 
    247         self.tblParams.resizeColumnsToContents() 
     357        # Add errors 
     358        error_list = list(error_columns.keys()) 
     359        for error_param in error_list[::-1]: # must be reverse to keep column indices 
     360            # the offset for the err column: +2 from the first two extra columns, +1 to append this column 
     361            error_column = error_columns[error_param][0]+3 
     362            error_values = error_columns[error_param][1:] 
     363            widget.insertColumn(error_column) 
     364 
     365            column_name = error_param + self.ERROR_COLUMN_CAPTION 
     366            widget.setHorizontalHeaderItem(error_column, QtWidgets.QTableWidgetItem(column_name)) 
     367 
     368            for i_row, error in enumerate(error_values): 
     369                item = QtWidgets.QTableWidgetItem(GuiUtils.formatNumber(error, high=True)) 
     370                # Fancy, italic font for errors 
     371                font = QtGui.QFont() 
     372                font.setItalic(True) 
     373                item.setFont(font) 
     374                widget.setItem(i_row, error_column, item) 
     375 
     376        # resize content 
     377        widget.resizeColumnsToContents() 
    248378 
    249379    @classmethod 
  • src/sas/qtgui/Utilities/GuiUtils.py

    r27689dc rd4dac80  
    245245    customModelDirectoryChanged = QtCore.pyqtSignal() 
    246246 
     247    # Notify the gui manager about new data to be added to the grid view 
     248    sendDataToGridSignal = QtCore.pyqtSignal(list) 
     249 
     250 
    247251def updateModelItemWithPlot(item, update_data, name=""): 
    248252    """ 
     
    333337    """ 
    334338    assert isinstance(item, QtGui.QStandardItem) 
    335     #assert isinstance(update_data, list) 
    336339 
    337340    # Add the actual Data1D/Data2D object 
     
    465468 
    466469    return info_item 
     470 
     471def dataFromItem(item): 
     472    """ 
     473    Retrieve Data1D/2D component from QStandardItem. 
     474    The assumption - data stored in SasView standard, in child 0 
     475    """ 
     476    return item.child(0).data() 
    467477 
    468478def openLink(url): 
     
    813823    return (xLabel, yLabel, xscale, yscale) 
    814824 
    815 def dataFromItem(item): 
    816     """ 
    817     Retrieve Data1D/2D component from QStandardItem. 
    818     The assumption - data stored in SasView standard, in child 0 
    819     """ 
    820     return item.child(0).data() 
    821  
    822825def formatNumber(value, high=False): 
    823826    """ 
  • src/sas/qtgui/Utilities/LocalConfig.py

    r3b3b40b rd4dac80  
    134134 
    135135# Default threading model 
    136 USING_TWISTED = False 
     136USING_TWISTED = True 
    137137 
    138138# Logging levels to disable, if any 
  • src/sas/qtgui/Utilities/TabbedModelEditor.py

    r93c79b5 rd4dac80  
    3535        self.edit_only = edit_only 
    3636        self.is_modified = False 
     37        self.label = None 
    3738 
    3839        self.addWidgets() 
     
    240241        # Run the model test in sasmodels 
    241242        try: 
    242             _ = GuiUtils.checkModel(full_path) 
     243            model_results = self.checkModel(full_path) 
     244            logging.info(model_results) 
    243245        except Exception as ex: 
    244246            msg = "Error building model: "+ str(ex) 
  • src/sas/qtgui/Utilities/UI/GridPanelUI.ui

    r3b3b40b rd4dac80  
    88    <y>0</y> 
    99    <width>939</width> 
    10     <height>329</height> 
     10    <height>330</height> 
    1111   </rect> 
    1212  </property> 
     
    2121  </property> 
    2222  <widget class="QWidget" name="centralwidget"> 
    23    <layout class="QGridLayout" name="gridLayout"> 
     23   <layout class="QGridLayout" name="gridLayout_2"> 
     24    <item row="0" column="0"> 
     25     <widget class="QTabWidget" name="tabWidget"> 
     26      <property name="tabPosition"> 
     27       <enum>QTabWidget::South</enum> 
     28      </property> 
     29      <property name="currentIndex"> 
     30       <number>0</number> 
     31      </property> 
     32      <widget class="QWidget" name="tab"> 
     33       <attribute name="title"> 
     34        <string>Tab 1</string> 
     35       </attribute> 
     36       <layout class="QGridLayout" name="gridLayout"> 
     37        <item row="0" column="0"> 
     38         <widget class="QTableWidget" name="tblParams"> 
     39          <property name="contextMenuPolicy"> 
     40           <enum>Qt::CustomContextMenu</enum> 
     41          </property> 
     42          <property name="alternatingRowColors"> 
     43           <bool>true</bool> 
     44          </property> 
     45          <property name="selectionBehavior"> 
     46           <enum>QAbstractItemView::SelectRows</enum> 
     47          </property> 
     48         </widget> 
     49        </item> 
     50       </layout> 
     51      </widget> 
     52     </widget> 
     53    </item> 
    2454    <item row="1" column="0"> 
    2555     <layout class="QHBoxLayout" name="horizontalLayout"> 
     
    3666        </property> 
    3767       </spacer> 
     68      </item> 
     69      <item> 
     70       <widget class="QPushButton" name="cmdPlot"> 
     71        <property name="text"> 
     72         <string>Plot</string> 
     73        </property> 
     74       </widget> 
    3875      </item> 
    3976      <item> 
     
    5390     </layout> 
    5491    </item> 
    55     <item row="0" column="0"> 
    56      <widget class="QTableWidget" name="tblParams"> 
    57       <property name="contextMenuPolicy"> 
    58        <enum>Qt::CustomContextMenu</enum> 
    59       </property> 
    60       <property name="alternatingRowColors"> 
    61        <bool>true</bool> 
    62       </property> 
    63       <property name="selectionBehavior"> 
    64        <enum>QAbstractItemView::SelectRows</enum> 
    65       </property> 
    66      </widget> 
    67     </item> 
    6892   </layout> 
    6993  </widget> 
     
    7397     <x>0</x> 
    7498     <y>0</y> 
    75      <width>939</width> 
     99     <width>510</width> 
    76100     <height>26</height> 
    77101    </rect> 
  • src/sas/qtgui/Utilities/UnitTesting/GridPanelTest.py

    r3b3b40b rd4dac80  
    7070        self.assertIsInstance(self.widget, QtWidgets.QMainWindow) 
    7171        # Default title 
    72         self.assertEqual(self.widget.windowTitle(), "Grid Panel") 
     72        self.assertEqual(self.widget.windowTitle(), "Batch Fitting Results") 
    7373 
    7474        # non-modal window 
Note: See TracChangeset for help on using the changeset viewer.