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. How to create a sliding window view over a dataframe in PyQt5 QTableView, QAbstractTableModel?
Forum Updated to NodeBB v4.3 + New Features

How to create a sliding window view over a dataframe in PyQt5 QTableView, QAbstractTableModel?

Scheduled Pinned Locked Moved Unsolved Qt for Python
3 Posts 1 Posters 854 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.
  • P Offline
    P Offline
    plmoknij
    wrote on last edited by plmoknij
    #1

    The following code will let me scroll down through a 200 by 4 table fetching 5 new rows each time. How do I modify this such that the table will open at a certain index (say 100) and I can scroll either up or down like a sliding window?

    user @d_stranz mentioned this idea in this thread but didnt show how to implement - https://www.qtcentre.org/threads/62807-PyQt-QTableView-displying-100-000-000-rows

    ```
    One possible solution is to implement a sliding window onto your underlying data that contains say, twice as many rows as can be viewed onscreen. When you scroll up or down, you slide the window along, moving the first and last index rows accordingly. This should also significantly improve your UI response time, since the view s only have to base their calculations on a few hundred rows instead of thousands or millions. This could probably be done with a proxy model. If having an accurate row number on the vertical header is important, you can re-implement your model's headerData() method to return a string containing the actual row number from the underlying data. "

    Here is the code I am working with

    import sys
    from PyQt5 import QtWidgets, QtCore
    import pandas as pd
    import numpy as np
    
    
    class DelayedFetchingTableModel(QtCore.QAbstractTableModel):
        def __init__(self, batch_size=5, max_num_nodes=200):
            QtCore.QAbstractTableModel.__init__(self)
            self.batch_size = batch_size
    
            self.df = pd.DataFrame(np.random.randint(0,200,size=(200, 4)))
            self.nodes = self.df[:5]
            self.max_num_nodes = max(self.batch_size, max_num_nodes)
    
        def rowCount(self, index):
            return len(self.nodes)
    
        def columnCount(self, index):
            return 4
    
        def data(self, index, role):
            if not index.isValid():
                return None
            if role != QtCore.Qt.DisplayRole:
                return None
            if index.row() < 0 or index.row() >= len(self.nodes):
                return None
            else:
                return QtCore.QVariant(str(self.nodes.iloc[index.row(), index.column()]))
    
        def canFetchMore(self, index):
            if index.isValid():
                return False
            return (len(self.nodes) < self.max_num_nodes)
    
        def fetchMore(self, index):
            print('fetch more')
            if index.isValid():
                print('not valid')
                return QtCore.QVariant()
            current_len = len(self.nodes)
            target_len = min(current_len + self.batch_size, self.max_num_nodes)
            self.beginInsertRows(index, current_len, target_len - 1)
            for i in range(current_len, target_len):
                self.nodes = self.nodes.append(self.df.iloc[i])
    
            self.endInsertRows()
    
    class MainForm(QtWidgets.QMainWindow):
        def __init__(self, parent=None):
            QtWidgets.QMainWindow.__init__(self, parent)
            self.model = DelayedFetchingTableModel(batch_size=self.batch_size, max_num_nodes=self.max_num_nodes)
    
            self.view = QtWidgets.QTableView()
            self.view.setModel(self.model)
    
            self.layout = QtWidgets.QVBoxLayout()
            self.layout.addWidget(self.view)
    
            self.window = QtWidgets.QWidget()
            self.window.setLayout(self.layout)
            self.setCentralWidget(self.window)
    
    
    def main():
        app = QtWidgets.QApplication(sys.argv)
        form = MainForm()
        form.show()
        app.exec_()
    
    if __name__ == '__main__':
        main()
    

    Cheers

    1 Reply Last reply
    0
    • P Offline
      P Offline
      plmoknij
      wrote on last edited by
      #2

      I have tried this and can at least filter the table by the specific rows I want. How do I implement fetchMore function to insert rows when I scroll up and append rows when I scroll down. It is not obvious how fetchMore works and the conditions where the function is called. Who is calling this function?

      import sys
      from PyQt5 import QtWidgets, QtCore
      import pandas as pd
      import numpy as np
      
      class TableModel(QtCore.QAbstractTableModel):
          def __init__(self, parent=None, visible_range = [2,5]):
              super().__init__(parent)
              self.df = pd.DataFrame(np.random.randint(0,1000000,size=(200, 4)))
              print(self.df)
              self.scope = self.df[visible_range[0]:visible_range[1] + 1]
              self.scope_range = visible_range
              #print(self.df)
      
          def rowCount(self, parent=QtCore.QModelIndex()):
              return len(self.scope)
      
          def columnCount(self, parent=QtCore.QModelIndex()):
              return 4
              #return len(self.df.columns)
      
          def data(self, index, role=QtCore.Qt.DisplayRole):
      
              if not index.isValid():
                  return None
              if role != QtCore.Qt.DisplayRole:
                  return None
              if index.row() < 0 or index.row() >= len(self.df):
                  return None
              else:
                  return QtCore.QVariant(str(self.scope.iloc[index.row(), index.column()]))
              
          def canFetchMore(self, index):
              if index.isValid():
                  return False
              return (len(self.scope) < len(self.df))
      
          def fetchMore(self, index):
              print('fetchmore')
              pass
              """
              if scroll up table:
                  #prepend 20 rows
                  self.scope_range = [max(self.scope_range[0] - 20, 0), self.scope_range[1]] 
                  self.scope = self.df[self.scope_range[0]:self.scope_range[1]+1]
              elif scroll down table:
                  #append 20 rows
                  self.scope_range = [self.scope_range[0], min(self.scope_range[1] + 20, len(self.df))]
                  self.scope = self.df[self.scope_range[0]:self.scope_range[1]+1]
              """
      
             
      class MainForm(QtWidgets.QMainWindow):
          def __init__(self, parent=None):
              QtWidgets.QMainWindow.__init__(self, parent)
              # Setup the model
              self.max_num_nodes = 200
              self.batch_size = 5
              self.model = TableModel()
              # Setup the view
              self.view = QtWidgets.QTableView()
              self.view.setModel(self.model)
              # Update the currently selected row in the spinbox
      
              # Collect all this stuff into a vertical layout
              self.layout = QtWidgets.QVBoxLayout()
              self.layout.addWidget(self.view)
      
              self.window = QtWidgets.QWidget()
              self.window.setLayout(self.layout)
              self.setCentralWidget(self.window)
      
      
      def main():
          app = QtWidgets.QApplication(sys.argv)
          form = MainForm()
          form.show()
          app.exec_()
      
      if __name__ == '__main__':
          main()
      
      1 Reply Last reply
      0
      • P Offline
        P Offline
        plmoknij
        wrote on last edited by
        #3

        Nevermind my nonsense. QTableView does exactly what I want out of the box. It will only update the view for what is currently visible

        1 Reply Last reply
        0

        • Login

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