Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. can't get qtableview to update correctly
QtWS25 Last Chance

can't get qtableview to update correctly

Scheduled Pinned Locked Moved Unsolved Qt for Python
qtableviewpyqt5
10 Posts 2 Posters 2.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    El_Mo
    wrote on 7 Jul 2022, 17:33 last edited by El_Mo 7 Jul 2022, 17:34
    #1

    Hi @ all :) ,
    I want to make a qtableview widget correctly updating. I'm working on a calibration applet, where i wanna fill cell by cell of an (e. g.) 100 x 100 x 4 array.

    If my hardware reaches position 1, 2, 3, and so on, I will trigger a voltage measurement and gather those values with an i2c-read out-function.

    So issues a la "my qtableview is not updating" are omnipresent.

    But so far, I'm not able to adapt examples I have read, to make my code behaving as I want.

    So if you look at my screenshot:

    applet.png

    ..the problem is:

    • when I'm clicking on row or col +/-, the yellow highlighting is not changing instantly (should move to indexed cell)
    • when I'm clicking on store i²c, which is meant to put a dummy 0.0 in/on selected cell, this is also not changing instantly
    • i only see updated values, when i switch widgets tab back and forth (A4, A5 and so on). Then the yellow highlight moved or stored values are displayed.

    Several methods like telling the model that data has changed, I was not able to implement correctly so far.

    Could some of you help me to add a few lines just to force applet to update correctly?

    Following i will add simplified code, so you can run that applet, if you want to.

    ### libraries:
    import sys # to use e. g. exit function
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtCore import Qt
    import pandas as pd # to use pandas tables
    import numpy as np # to use numpy arrays
    
    ### user-defined header files / modules:
    from uLib_coloredWidget import Color    # import user-defined functions..                        
    from rndGen import i2c_read             # .. see folder
    
    
    ### initial settings:
     # general
    np.random.seed(4) # if using np.random, then pseudo random values will occure
    
    
    ### globals:
    nRow = 5; nCol = 5; nSht = 4 # table dimensions
    rowIdx = colIdx = shtIdx = 0 # aux vars to index array
    rndArray = np.random.rand(nSht, nRow, nCol) * 4.3 # auxilliary before integrating i2c
    tabNames = ["A4", "A5","A6","A7"] # array (list) with tab names
    rowIdx = 1; colIdx = 1 # aux vars to index selected cell
    
    ### declarations / definitions:
    class TableModel(QtCore.QAbstractTableModel):
    
        def __init__(self, data):
            super(TableModel, self).__init__()
            self._data = data
    
        def data(self, index, role):
    
            if role == Qt.BackgroundRole and index.column() == colIdx and index.row() == rowIdx:
                # See below for the data structure.
                return QtGui.QColor('yellow')
    
            if role == Qt.DisplayRole:
                value = self._data.iloc[index.row(), index.column()]
                if isinstance(value, float): # to set fixed DISPLAYED precision of floats
                    return "%.4f" % value
                return str(value)
    
        def rowCount(self, index):
            return self._data.shape[0]
    
        def columnCount(self, index):
            return self._data.shape[1]
    
        def headerData(self, section, orientation, role):
            # section is the index of the column/row.
            if role == Qt.DisplayRole:
                if orientation == Qt.Horizontal:
                    return str(self._data.columns[section])
    
                if orientation == Qt.Vertical:
                    return str(self._data.index[section])
    
    class App(QtWidgets.QMainWindow):
         # local variable's declarations
    
         # init
        def __init__(self):
            super().__init__() # default one
    
            self.setup_main_window() # using helper function to increase readability (function call within self scope)
                                     # setup main window
            self.createLayout() # function call to create layouts with widgets
            self.post_main_window() # pass edited layouts to main window
    
         # declaration / methods / helper functions
        def setup_main_window(self): # to set window's / applet's properties
            self.centralwidget = QtWidgets.QWidget()
            self.setCentralWidget(self.centralwidget)        
            self.resize( 800, 400  )
            self.setWindowTitle( "# disposition calibration #" )
    
        def post_main_window(self): # to publish edited layouts in app window
            self.centralwidget.setLayout(self.lyoOut)
            
        def createLayout(self): # to create layouts with widgets
            self.lyoOut = QtWidgets.QVBoxLayout() # declare different layouts
            self.lyoIn1 = QtWidgets.QHBoxLayout()
            self.lyoIn2 = QtWidgets.QGridLayout()
    
            self.createWidgets() # function call pass widgets to sub-layouts
    
            self.lyoOut.addLayout(self.lyoIn1) # inner layouts got widgets by self.createWidgets()
            self.lyoOut.addLayout(self.lyoIn2) # merge edited inner layout in/to outside layout here
    
        def createWidgets(self): # create master-layout's widgets (function calls)
             # fill 1st row of ouside layout
            self.lyoIn1 = self.createNestedTabs(self.lyoIn1) # function call to create master-tabs
             # fill 2nd row of outside layout
            self.lyoIn2 = self.createButtons(self.lyoIn2) # function call to create buttons 
    
        def createNestedTabs(self, layout2modify): # create 1st tab layer
            self.MstTabs = QtWidgets.QTabWidget() # create tabs-widget
            self.MstTabs.setTabPosition(QtWidgets.QTabWidget.North) # set it's location
            self.MstTabs.addTab(self.createChildTabs(), "data") # add several sub-tab layouts to that widget
            self.MstTabs.addTab(Color("orange"), "plot") # 
    
            stylesheet = """ 
                QTabBar::tab:selected {background: lightgreen;}
                QTabBar::tab:!selected {background: lightyellow;}
                """ 
            self.MstTabs.setStyleSheet(stylesheet)
    
            layout2modify.addWidget(self.MstTabs) # add this tabs-widget to passed-in layout
            return layout2modify # return edited layout
    
        def createChildTabs(self): # create 2nd tab layer
            self.ChdTabs = QtWidgets.QTabWidget() # create tabs-widget
            self.ChdTabs.setTabPosition(QtWidgets.QTabWidget.West) # set it's location
            self.ChdTabs.addTab(self.createPandasTables(0), "A4") 
            self.ChdTabs.addTab(self.createPandasTables(1), "A5") 
            self.ChdTabs.addTab(self.createPandasTables(2), "A6") 
            self.ChdTabs.addTab(self.createPandasTables(3), "A7")
    
            return self.ChdTabs # return created widgets
    
        def createPandasTables(self, shtIdx): # to creating and editing pandas tables-widgets
             # use indexed (pandas)dataframe sheet values
            Lbl = ["a","b","c","d","e"]
            self.df = pd.DataFrame(rndArray[shtIdx], columns = Lbl, index = Lbl)
             # .. to create a widget
            self.table_widget = QtWidgets.QTableView() # create QTableView-Widget
            self.model = TableModel(self.df) # make df to user defined table model to use in widgets
            self.table_widget.setModel(self.model) # pass created model to created widget
    
             # certain formatings
            self.table_widget.resizeColumnsToContents() # set column width to content
            self.table_widget.horizontalHeader().setStretchLastSection(True) # strech last column to frame width
            self.table_widget.verticalHeader().setStretchLastSection(True) # strech last row to frame height
            self.table_widget.setAlternatingRowColors(True) # switch on alternating row highlighting
    
            return self.table_widget # return created widgets
    
        def createButtons(self, layout2modify): # helper function - to create layout's buttons
            bStoreI2C = QtWidgets.QPushButton("Store i²c")
            bStoreI2C.clicked.connect(lambda:self.storeVal())        
            bStoreI2C.setStyleSheet("QPushButton::hover" 
                                 "{"
                                 "background-color : yellow;"
                                 "}")
            layout2modify.addWidget(bStoreI2C, 1, 3, 2, 1)
    
            self.lbl_1 = QtWidgets.QLabel()
            self.lbl_1.setText(str(rowIdx))
            self.lbl_1.setAlignment(QtCore.Qt.AlignCenter)
            layout2modify.addWidget(self.lbl_1, 1, 5, 2, 1)
    
            bRowAdd = QtWidgets.QPushButton("row +")
            bRowAdd.clicked.connect(lambda:self.rowAdd())
            layout2modify.addWidget(bRowAdd, 2, 6)
    
    
            bRowSub = QtWidgets.QPushButton("row -")
            bRowSub.clicked.connect(lambda:self.rowSub())
            layout2modify.addWidget(bRowSub, 1, 6)
    
            return layout2modify # return edited layout
    
        def storeVal(self):
    
            #i2c_vals = get_i2c_values(i2c_addrs)
            for i in range (0,4):
                #self.tbData[i, rowIdx, colIdx] = i2c_vals[i] # change cell entries with imported value
                rndArray[i, rowIdx, colIdx] = 0
            #self.tbData[sht, row, col] = 99 # change cell entry with imported value
    
            # try 1
            #self.table_widget.update()
            #self.table_widget.repaint()
             #self.model.select()
            #self.table_widget.select()
            # try 2
            
            # self.refreshModel() # not working so far
            #self.model = TableModel(self.df) # make df to user defined table model to use in widgets
            #self.table_widget.setModel(self.model) 
    
            # print(rndArray)
            print('i²c-value(s) stored')
    
        def rowAdd(self):
            global rowIdx
    
            rowIdx = (rowIdx + 1) % nRow # increment and modulo to cycle
            self.lbl_1.setText(str(rowIdx)) # update label's text
    
            print('row is ', rowIdx)
    
        def rowSub(self):
            global rowIdx
    
            rowIdx = (rowIdx - 1) % nRow # increment and modulo to cycle
            self.lbl_1.setText(str(rowIdx)) # update label's text
    
            print('row is ', rowIdx)
    
        
    ### main:
    def main():
        app = QtWidgets.QApplication(sys.argv) # instanciate app
        window = App() # instanciate window
    
        window.show() # show window 
        
        app.exec_() # stuck here 'til window is closed
        print('# window will be terminated.. #')
        time.sleep(2)
        print('# ..app execution closed #')
    
    
    # make file executable
    if __name__ == '__main__':
        main()
    

    rndGen.py: (is called in fillCSV_forum.py)

    import numpy as np
    
    def i2c_read():
        floats = np.random.rand(4,1,1) * 4.3
    
        return floats
    

    uLib_coloredWidget.py: (is called in fillCSV_forum.py)

    from PyQt5.QtGui import QColor, QPalette
    from PyQt5.QtWidgets import QWidget
    
    
    class Color(QWidget):
      def __init__(self, color):
        super().__init__()
        self.setAutoFillBackground(True)
        palette = self.palette()
        palette.setColor(QPalette.Window, QColor(color))
        self.setPalette(palette)
    

    pip freeze --local-output of virtual enviroment:

    numpy==1.23.0
    pandas==1.4.3
    PyQt5==5.15.7
    PyQt5-Qt5==5.15.2
    PyQt5-sip==12.11.0
    python-dateutil==2.8.2
    pytz==2022.1
    six==1.16.0
    
    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 7 Jul 2022, 17:59 last edited by
      #2

      Hi and welcome to devnet,

      From the looks of it, you forgot to implement setData in your model. Thus nothing will happen.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • E Offline
        E Offline
        El_Mo
        wrote on 7 Jul 2022, 21:35 last edited by
        #3

        Hi SGaist,

        thy vm for your hint, but

        1. if you test my code OR like i mentioned in the beginning: technically it updates and display the changed data... but only if i am swapping the tabs widgets
        2. could you please give a corresponding code example, because i already had read such tips, but i was not able to implement it correctly without detailed examples so far

        regards :)

        1 Reply Last reply
        0
        • S Offline
          S Offline
          SGaist
          Lifetime Qt Champion
          wrote on 8 Jul 2022, 19:28 last edited by
          #4

          The D'n'D documentation has an editable example.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          1
          • E Offline
            E Offline
            El_Mo
            wrote on 10 Jul 2022, 16:32 last edited by
            #5

            hmpf, yah, thanks for that link. but this is c++ and i'm coding this in python. further i am not familiar at all to easily swap between.

            i took another 3 h and i tried to adapt/integrate another python examples in my code... but jesus, i cannot get it working...

            e. g. i tried update functions like this link text

            isn't anybody out there, who had same problems with pandas dataframe, tab widgets and updates ^^ ?

            grml

            1 Reply Last reply
            0
            • S Offline
              S Offline
              SGaist
              Lifetime Qt Champion
              wrote on 10 Jul 2022, 17:11 last edited by
              #6

              As part of your class, something like:

              def setData(self, index: QModelIndex, value QVariant &value, role: int):
              
                  if role == Qt.EditRole) 
                      if not self.checkIndex(index):
                          return False;
                      # update your pandas frame
                      self.dataChanged.emit(index.row(), index.column())
                      return True
                  
                  return False
              

              Just in case, there's a read-only example of a model in Qt's python documentation.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              1 Reply Last reply
              1
              • E Offline
                E Offline
                El_Mo
                wrote on 12 Jul 2022, 08:45 last edited by
                #7

                [... additionally many hours of trial and error....]

                i think i finally got a dirty solution / work around..

                the problem i could determining, was e. g. if i am clicking the col+/- or store button, the focus of recently selected tab is vanishing.
                first when click again into any tab region or select another tabs those values are updating.

                so i tried to look for programmatically tab swap and did this as a dirty work around because i could not find a method like "reactivate tab again"

                i added ... :

                    def storeVal(self):
                
                        #i2c_vals = get_i2c_values(i2c_addrs)
                        for i in range (0,nSht):
                            self.df[i].iat[rowIdx, colIdx] = 99
                
                        print('i²c-value(s) stored')
                        self.show_data()
                    
                    def show_data(self):
                                           
                        x = self.ChdTabs.currentIndex()
                        print(x) # debugging
                        self.ChdTabs.setCurrentIndex(1)
                        self.ChdTabs.setCurrentIndex(x)
                

                ... a show method and called it at the end of the store-method.

                in this show method i programmatically swap the active tab back and forth. this is so fast, that i cannot see it

                now my values are correctly shown

                another tiny if else code is necessary to also swap if tab 1 is selected, but this is cosmetic thing

                1 Reply Last reply
                0
                • S Offline
                  S Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on 12 Jul 2022, 18:42 last edited by
                  #8

                  From what you wrote, it seems that you are modifying your data frame directly rather that through the model hence the model as no way to propagate the information to the upper layer.

                  Use the model as interface on top of your data frame.

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  1 Reply Last reply
                  0
                  • E Offline
                    E Offline
                    El_Mo
                    wrote on 13 Jul 2022, 06:04 last edited by
                    #9

                    yah i know,

                    and i would like to press your code snippets into a working model-will-change-data-interface-solution, but i didn't get it running for weeks. i am not familiar enough with object oriented coding and couldn't merge different code snippets to a working solution ...

                    unfortunately i m so busy at work, that i cannot enjoy weeks of youtube tutorials to learn this right.

                    so yeah, i change pandas data with a app/widget-class method directly and the model is correctly showing this but first when if give back the focus to tabs.

                    i would like to see a better / correct version with a model-class-method setData, but i was not able to...

                    =(

                    1 Reply Last reply
                    0
                    • S Offline
                      S Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote on 13 Jul 2022, 19:10 last edited by
                      #10

                      The short version would be to add a set_i2c_data method to your model, update the data frame in it and then emit the dataChanged signal as last step of that method.

                      Interested in AI ? www.idiap.ch
                      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                      1 Reply Last reply
                      0

                      2/10

                      7 Jul 2022, 17:59

                      topic:navigator.unread, 8
                      • Login

                      • Login or register to search.
                      2 out of 10
                      • First post
                        2/10
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved