Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QTableVIew



  • Hi, if a user presses a button on my PyQt5 GUI, it runs a python script which dumps data into a .csv file and then I use pandas to create dataframe out of the .csv. Finally, the dataframe is displayed using QTableView in a new window without any problem.

    However, there is an additional checkbox option in my GUI. If checked, the python script will continuously run and overwrite the .csv file, meaning 5 different datasets. I want GUI to update the QTableView for every overwrite. For each overwrite, all data is overwritten (meaning all rows of data in dataframe)

    For debugging, rather than continuous run, I have altered the checkbox option code so that python script is ran only 5 times. Couple of issues I'm seeing:

    1. While the python script is running 5 times, the window with displayed table is not accessible until all 5 script runs are finished.
    2. Upon each python script run, the QTableVIew is not updated. I can verify because once the QTableView is accessible after 5th run, it is not matching the 5th output.

    Ideally I want only the data to update if checkbox option is clicked.

    Here is my code (kind of minimized/variables changed for privacy purposes):

    
    from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout
    from PyQt5.QtGui import QIcon
    
    from PyQt5 import QtWidgets, QtCore, uic, QtGui
    from PyQt5.QtWidgets import QMessageBox
    
    class TableModel(QtCore.QAbstractTableModel):
    
        def __init__(self, data, parent=None):
            QtCore.QAbstractTableModel.__init__(self, parent)
            self._data = data
    
        def rowCount(self, parent=None):
            return len(self._data.index)
        
        def columnCount(self, parent=None):
            return self._data.columns.size
    
        def data(self, index, role=QtCore.Qt.DisplayRole):
            if index.isValid():
                if role == QtCore.Qt.DisplayRole:
                    return str(self._data.iloc[index.row(), index.column()])
            return None    
    
    
        def headerData(self, rowcol, orientation, role):
            if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
                return self._data.columns[rowcol]
            if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
                return self._data.index[rowcol]
            return None
    
        def flags(self, index):
            flags = super(self.__class__, self).flags(index)
            flags |= QtCore.Qt.ItemIsEditable
            flags |= QtCore.Qt.ItemIsSelectable
            flags |= QtCore.Qt.ItemIsEnabled
            flags |= QtCore.Qt.ItemIsDragEnabled
            flags |= QtCore.Qt.ItemIsDropEnabled
            return flags
    
        def sort(self, Ncol, order):
            """Sort table by given column number.
            """
            try:
                self.layoutAboutToBeChanged.emit()
                self._data = self._data.sort_values(self._data.columns[Ncol], ascending=not order)
                self.layoutChanged.emit()
            except Exception as e:
                print(e)
    
    class App(QtWidgets.QMainWindow):
        def __init__(self):
            super(App, self).__init__()
            uic.loadUi('Example.ui', self)
    
            self.button.clicked.connect(self.run_script)
            self.checkboxbutton.stateChanged.connect(self.run_script_5times) 
    
        def run_script(self):
    
            self.script_cmd = 'python3.7 example.py > example_data.csv'
            os.system(self.script_cmd)
            df = pd.read_csv('example_data.csv', index_col=False)
            self.model = TableModel(df)
            self.table = QtWidgets.QTableView()
            self.table.setModel(self.model)
            self.table.show()
    
      def run_script_5times(self, state):
            i = 0
            while state == QtCore.Qt.Checked and i < 5:
                time.sleep(3)
                os.system(self.script_cmd)
     
                df2 = pd.read_csv('example_data.csv', index_col=False)
                self.model.data=df2
                self.model.layoutChanged.emit()
                i = i + 1
    ``
    

    End goal is to have a table update with each python run without the table reloading, only the data. RIght now table is not updating and secondly once it does, I have seen examples where refresh is choppy - would like to avoid that. Let me know if I can help clarify - if someone knows options I can try?



  • @worldsnexthero

    Upon each python script run, the QTableVIew is not updated. I can verify because once the QTableView is accessible after 5th run, it is not matching the 5th output.

    and secondly once it does, I have seen examples where refresh is choppy - would like to avoid that

    Not sure what exactly you mean by either of these, or how they are connected.

            self.model = TableModel(df)
            self.table = QtWidgets.QTableView()
            self.table.setModel(self.model)
    

    Not sure how your QTableView is shown since it's not parented by QMainWindow nor addWidget() -ed anywhere. You sure you're loading into the table you see?. Maybe it's right to recreate the model & view each time, but when you talk about "choppy" I would be thinking about having one constant view, and perhaps one constant model which gets reloaded from the script.

    Test your code without any of the Python scrip[t/csv stuff, and if any funnies also don't do any sorting and see if that is relevant.



  • @worldsnexthero said in QTableVIew:

    displayed table is not accessible until all 5 script runs are finished.

    Don't, and never, use time.sleep(), it blocks the event loop/updates. And your loop re-reads the file without allowing update even if you didn't have the sleep() there. Use a QTimer. Start with that, see whether that affects your second comment too.



  • Awesome, this does fix (1) qtableview window inaccessible. this is solved.

    After removing time.sleep() from the code, the existing code from original comment does not update qtableview, that problem still exists. Have looked at numerous sources online and have not seen much help regarding qtableview being overwritten with new data (same table format) from another pandas dataframe. Any thoughts on this front?



  • @worldsnexthero

    Upon each python script run, the QTableVIew is not updated. I can verify because once the QTableView is accessible after 5th run, it is not matching the 5th output.

    and secondly once it does, I have seen examples where refresh is choppy - would like to avoid that

    Not sure what exactly you mean by either of these, or how they are connected.

            self.model = TableModel(df)
            self.table = QtWidgets.QTableView()
            self.table.setModel(self.model)
    

    Not sure how your QTableView is shown since it's not parented by QMainWindow nor addWidget() -ed anywhere. You sure you're loading into the table you see?. Maybe it's right to recreate the model & view each time, but when you talk about "choppy" I would be thinking about having one constant view, and perhaps one constant model which gets reloaded from the script.

    Test your code without any of the Python scrip[t/csv stuff, and if any funnies also don't do any sorting and see if that is relevant.


Log in to reply