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. tableView not updated after calling dataChanged
Forum Updated to NodeBB v4.3 + New Features

tableView not updated after calling dataChanged

Scheduled Pinned Locked Moved Solved Qt for Python
12 Posts 2 Posters 5.8k 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.
  • Carlos DiazC Offline
    Carlos DiazC Offline
    Carlos Diaz
    wrote on last edited by
    #3

    After taking a closer look at the log we can see the method data isn't being called after setData.

    [SET_DATA] DATA: 0x0800C000 index row: 0; column 0 # Here i load the first item of the list into the model
    data changed signal
    ['0x0800C000']
    Setting model
    Showing model
    [DATA] Column: 0; Row: 0 # Here the data method is being called
    0x0800C000
    [DATA] Column: 0; Row: 0
    0x0800C000
    Updating view, removing 1 rows
    ['0x0800C000']
    Removing 1 rows from 0 to 1
    []
    Currently we have 0 rows
    [SET_DATA] DATA: 0x0800C000 index row: 0; column 0 # Updating the data in the model, but the data method isn't being called after this
    data changed signal
    ['0x0800C000']
    [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
    data changed signal
    ['0x0800C000', '0x0800C004']
    [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
    data changed signal
    ['0x0800C000', '0x0800C004', '0x0800C008']
    [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
    data changed signal
    ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
    [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
    data changed signal
    ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
    [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
    data changed signal
    ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
    Currently we have 6 rows
    
    JonBJ 1 Reply Last reply
    0
    • Carlos DiazC Carlos Diaz

      After taking a closer look at the log we can see the method data isn't being called after setData.

      [SET_DATA] DATA: 0x0800C000 index row: 0; column 0 # Here i load the first item of the list into the model
      data changed signal
      ['0x0800C000']
      Setting model
      Showing model
      [DATA] Column: 0; Row: 0 # Here the data method is being called
      0x0800C000
      [DATA] Column: 0; Row: 0
      0x0800C000
      Updating view, removing 1 rows
      ['0x0800C000']
      Removing 1 rows from 0 to 1
      []
      Currently we have 0 rows
      [SET_DATA] DATA: 0x0800C000 index row: 0; column 0 # Updating the data in the model, but the data method isn't being called after this
      data changed signal
      ['0x0800C000']
      [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
      data changed signal
      ['0x0800C000', '0x0800C004']
      [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
      data changed signal
      ['0x0800C000', '0x0800C004', '0x0800C008']
      [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
      data changed signal
      ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
      [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
      data changed signal
      ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
      [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
      data changed signal
      ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
      Currently we have 6 rows
      
      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #4

      @Carlos-Diaz
      As I said above.

      I believe you will find your problem is because: having removed all rows via removeRows() you simply use setData() to put stuff into the model rows. You do not call insertRows(). If that is not called, the view won't get the signal and know there are any new rows.

      Put a debug in to show it's not being called. Then put in calls during your address population loop, and see how the view looks.

      1 Reply Last reply
      0
      • Carlos DiazC Offline
        Carlos DiazC Offline
        Carlos Diaz
        wrote on last edited by
        #5

        Hi @JonB,

        Here's the updated test code with the print statements updated:

        #!/usr/bin/env python3
        
        from fbs_runtime.application_context.PySide2 import ApplicationContext
        
        from PySide2.QtCore import *
        from PySide2.QtWidgets import *
        from PySide2.QtGui import QIntValidator
        from PySide2.QtUiTools import QUiLoader
        
        import sys
        
        class TestModel(QAbstractTableModel):
            def __init__(self):
                QAbstractTableModel.__init__(self)
                
                # Here we keep the data
                self.display = []
        
            def rowCount(self, parent=QModelIndex()):
                return len(self.display)
        
            def columnCount(self, parent=QModelIndex()):
                COLUMNS_WIDTH_SIZE = 1
                return COLUMNS_WIDTH_SIZE
        
            def setData(self, index, value, role=Qt.EditRole):
                '''
                Adjust the data (set it to value <value>) depending on the given index
                and role
                '''
        
                if role != Qt.EditRole:
                    return False
        
                if index.isValid() and 0 <= index.row(): # < len(self.addresses):
                    
                    print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                    if index.column() == 0:
                        self.display.append(value)
                    else:
                        return False
                    
                    print(f'data changed signal')
                    print(self.display)
                    # self.layoutChanged.emit()
                    self.dataChanged.emit(index, index)
                    return True
        
                return False
        
            def flags(self, index):
                '''
                Set the item flags at the given index. Seems like we're implementing
                this function just to see ho it's done, as we manually adjust each
                tableView to have NoEditTriggers.
                '''
        
                if not index.isValid():
                    return Qt.ItemIsEnabled
                return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)
        
            def insertRows(self, position, rows=1, index=QModelIndex()):
                '''
                Insert a row from the model.
                '''
                
                self.beginInsertRows(QModelIndex(), position, position + rows - 1)
        
                self.display.append(' ')
                print(f'insert {rows} rows at {position}')
        
                self.endInsertRows()
        
                return True
        
            def removeRows(self, position, rows=1, index=QModelIndex()):
                '''
                Remove a row from the model.
                '''
                
                self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
        
                print(self.display)
                
                print(f'Removing {rows} rows from {position} to {position+rows}')
                del self.display[position:position+rows]
        
                print(self.display)
        
                self.endRemoveRows()
        
                return True
        
            def insertColumns(self, column, count, parent):
                print(f'insert {count} columns at {column}')
        
            def removeColumns(self):
                pass
        
            def data(self, index, role=Qt.DisplayRole):
        
                if not index.isValid():
                    print(f'invalid index {index}')
                    return None
                
                if role != Qt.DisplayRole and role != Qt.EditRole:
                    # print(f'invalid role {role}')
                    return None
        
                column = index.column()
                row = index.row()
        
                print(f'[DATA] Column: {column}; Row: {row}')
                
                if column == 0:
                    tmp = self.display[index.row()]
                    print(tmp)
                    return tmp
                else:
                    return None
        
            def headerData(self, section, orientation, role):
                if role == Qt.DisplayRole and orientation == Qt.Horizontal:
                    if section == 0:
                        return 'Address'
        
        class AppContext(ApplicationContext):
            def run(self):
        
                self.app.setStyle('Fusion')
        
                ui_file = self.get_resource("mainwindow.ui")
                self.file = QFile(ui_file)
                self.file.open(QFile.ReadOnly)
                self.loader = QUiLoader()
                self.window = self.loader.load(self.file)
        
                self.window.updateView.clicked.connect(self.onUpdateView)
        
                self.address_list = ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
        
                self.model = TestModel()
        
                first_index = self.model.createIndex(0, 0)
        
                self.model.setData(first_index, self.address_list[0], Qt.EditRole)
        
                print(f'Setting model')
                self.window.tableView.setModel(self.model)
                print(f'Showing model')
                self.window.tableView.show()
                
                # Show the application to the user        
                self.window.show()
                return self.app.exec_()
        
            def onUpdateView(self):
        
                current_rows = self.model.rowCount()
        
                print(f'Updating view, removing {current_rows} rows')
                self.model.removeRows(0, current_rows)
        
                current_rows = self.model.rowCount()
                print(f'Currently we have {current_rows} rows')
                
                for row, address in enumerate(self.address_list):
                    idx = self.model.createIndex(row, 0)
                    tmp = self.model.setData(idx, address, Qt.EditRole)
                    print(f'Result of setData: {tmp}')
                
                current_rows = self.model.rowCount()
                print(f'Currently we have {current_rows} rows')
        
        if __name__ == '__main__':
            appctxt = AppContext()
            exit_code = appctxt.run()
            sys.exit(exit_code)
        

        The return value from setData is True, here's the log output:

        [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
        data changed signal
        ['0x0800C000']
        Setting model
        Showing model
        [DATA] Column: 0; Row: 0
        0x0800C000
        [DATA] Column: 0; Row: 0
        0x0800C000
        Updating view, removing 1 rows
        ['0x0800C000']
        Removing 1 rows from 0 to 1
        []
        Currently we have 0 rows
        [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
        data changed signal
        ['0x0800C000']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
        data changed signal
        ['0x0800C000', '0x0800C004']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
        data changed signal
        ['0x0800C000', '0x0800C004', '0x0800C008']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
        data changed signal
        ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
        data changed signal
        ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
        data changed signal
        ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
        Result of setData: True
        Currently we have 6 rows
        

        If in setData we uncomment the self.layoutChanged.emit() line the view works as expected:

            def setData(self, index, value, role=Qt.EditRole):
                '''
                Adjust the data (set it to value <value>) depending on the given index
                and role
                '''
        
                if role != Qt.EditRole:
                    return False
        
                if index.isValid() and 0 <= index.row(): # < len(self.addresses):
                    
                    print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                    if index.column() == 0:
                        self.display.append(value)
                    else:
                        return False
                    
                    print(f'data changed signal')
                    print(self.display)
                    self.layoutChanged.emit()
                    self.dataChanged.emit(index, index)
                    return True
        
                return False
        

        log:

        [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
        data changed signal
        ['0x0800C000']
        Setting model
        Showing model
        [DATA] Column: 0; Row: 0
        0x0800C000
        [DATA] Column: 0; Row: 0
        0x0800C000
        Updating view, removing 1 rows
        ['0x0800C000']
        Removing 1 rows from 0 to 1
        []
        Currently we have 0 rows
        [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
        data changed signal
        ['0x0800C000']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
        data changed signal
        ['0x0800C000', '0x0800C004']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
        data changed signal
        ['0x0800C000', '0x0800C004', '0x0800C008']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
        data changed signal
        ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
        data changed signal
        ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
        Result of setData: True
        [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
        data changed signal
        ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
        Result of setData: True
        Currently we have 6 rows
        [DATA] Column: 0; Row: 0
        0x0800C000
        [DATA] Column: 0; Row: 1
        0x0800C004
        [DATA] Column: 0; Row: 2
        0x0800C008
        [DATA] Column: 0; Row: 3
        0x0800C00C
        [DATA] Column: 0; Row: 4
        0x0800C010
        [DATA] Column: 0; Row: 5
        0x0800C014
        

        Updated tableView: https://imgur.com/zvQgS1s

        I guess adding self.layoutChanged.emit() is what i was missing or it isn't the proper way to solve it?

        JonBJ 2 Replies Last reply
        0
        • Carlos DiazC Carlos Diaz

          Hi @JonB,

          Here's the updated test code with the print statements updated:

          #!/usr/bin/env python3
          
          from fbs_runtime.application_context.PySide2 import ApplicationContext
          
          from PySide2.QtCore import *
          from PySide2.QtWidgets import *
          from PySide2.QtGui import QIntValidator
          from PySide2.QtUiTools import QUiLoader
          
          import sys
          
          class TestModel(QAbstractTableModel):
              def __init__(self):
                  QAbstractTableModel.__init__(self)
                  
                  # Here we keep the data
                  self.display = []
          
              def rowCount(self, parent=QModelIndex()):
                  return len(self.display)
          
              def columnCount(self, parent=QModelIndex()):
                  COLUMNS_WIDTH_SIZE = 1
                  return COLUMNS_WIDTH_SIZE
          
              def setData(self, index, value, role=Qt.EditRole):
                  '''
                  Adjust the data (set it to value <value>) depending on the given index
                  and role
                  '''
          
                  if role != Qt.EditRole:
                      return False
          
                  if index.isValid() and 0 <= index.row(): # < len(self.addresses):
                      
                      print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                      if index.column() == 0:
                          self.display.append(value)
                      else:
                          return False
                      
                      print(f'data changed signal')
                      print(self.display)
                      # self.layoutChanged.emit()
                      self.dataChanged.emit(index, index)
                      return True
          
                  return False
          
              def flags(self, index):
                  '''
                  Set the item flags at the given index. Seems like we're implementing
                  this function just to see ho it's done, as we manually adjust each
                  tableView to have NoEditTriggers.
                  '''
          
                  if not index.isValid():
                      return Qt.ItemIsEnabled
                  return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)
          
              def insertRows(self, position, rows=1, index=QModelIndex()):
                  '''
                  Insert a row from the model.
                  '''
                  
                  self.beginInsertRows(QModelIndex(), position, position + rows - 1)
          
                  self.display.append(' ')
                  print(f'insert {rows} rows at {position}')
          
                  self.endInsertRows()
          
                  return True
          
              def removeRows(self, position, rows=1, index=QModelIndex()):
                  '''
                  Remove a row from the model.
                  '''
                  
                  self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
          
                  print(self.display)
                  
                  print(f'Removing {rows} rows from {position} to {position+rows}')
                  del self.display[position:position+rows]
          
                  print(self.display)
          
                  self.endRemoveRows()
          
                  return True
          
              def insertColumns(self, column, count, parent):
                  print(f'insert {count} columns at {column}')
          
              def removeColumns(self):
                  pass
          
              def data(self, index, role=Qt.DisplayRole):
          
                  if not index.isValid():
                      print(f'invalid index {index}')
                      return None
                  
                  if role != Qt.DisplayRole and role != Qt.EditRole:
                      # print(f'invalid role {role}')
                      return None
          
                  column = index.column()
                  row = index.row()
          
                  print(f'[DATA] Column: {column}; Row: {row}')
                  
                  if column == 0:
                      tmp = self.display[index.row()]
                      print(tmp)
                      return tmp
                  else:
                      return None
          
              def headerData(self, section, orientation, role):
                  if role == Qt.DisplayRole and orientation == Qt.Horizontal:
                      if section == 0:
                          return 'Address'
          
          class AppContext(ApplicationContext):
              def run(self):
          
                  self.app.setStyle('Fusion')
          
                  ui_file = self.get_resource("mainwindow.ui")
                  self.file = QFile(ui_file)
                  self.file.open(QFile.ReadOnly)
                  self.loader = QUiLoader()
                  self.window = self.loader.load(self.file)
          
                  self.window.updateView.clicked.connect(self.onUpdateView)
          
                  self.address_list = ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
          
                  self.model = TestModel()
          
                  first_index = self.model.createIndex(0, 0)
          
                  self.model.setData(first_index, self.address_list[0], Qt.EditRole)
          
                  print(f'Setting model')
                  self.window.tableView.setModel(self.model)
                  print(f'Showing model')
                  self.window.tableView.show()
                  
                  # Show the application to the user        
                  self.window.show()
                  return self.app.exec_()
          
              def onUpdateView(self):
          
                  current_rows = self.model.rowCount()
          
                  print(f'Updating view, removing {current_rows} rows')
                  self.model.removeRows(0, current_rows)
          
                  current_rows = self.model.rowCount()
                  print(f'Currently we have {current_rows} rows')
                  
                  for row, address in enumerate(self.address_list):
                      idx = self.model.createIndex(row, 0)
                      tmp = self.model.setData(idx, address, Qt.EditRole)
                      print(f'Result of setData: {tmp}')
                  
                  current_rows = self.model.rowCount()
                  print(f'Currently we have {current_rows} rows')
          
          if __name__ == '__main__':
              appctxt = AppContext()
              exit_code = appctxt.run()
              sys.exit(exit_code)
          

          The return value from setData is True, here's the log output:

          [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
          data changed signal
          ['0x0800C000']
          Setting model
          Showing model
          [DATA] Column: 0; Row: 0
          0x0800C000
          [DATA] Column: 0; Row: 0
          0x0800C000
          Updating view, removing 1 rows
          ['0x0800C000']
          Removing 1 rows from 0 to 1
          []
          Currently we have 0 rows
          [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
          data changed signal
          ['0x0800C000']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
          data changed signal
          ['0x0800C000', '0x0800C004']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
          data changed signal
          ['0x0800C000', '0x0800C004', '0x0800C008']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
          data changed signal
          ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
          data changed signal
          ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
          data changed signal
          ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
          Result of setData: True
          Currently we have 6 rows
          

          If in setData we uncomment the self.layoutChanged.emit() line the view works as expected:

              def setData(self, index, value, role=Qt.EditRole):
                  '''
                  Adjust the data (set it to value <value>) depending on the given index
                  and role
                  '''
          
                  if role != Qt.EditRole:
                      return False
          
                  if index.isValid() and 0 <= index.row(): # < len(self.addresses):
                      
                      print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                      if index.column() == 0:
                          self.display.append(value)
                      else:
                          return False
                      
                      print(f'data changed signal')
                      print(self.display)
                      self.layoutChanged.emit()
                      self.dataChanged.emit(index, index)
                      return True
          
                  return False
          

          log:

          [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
          data changed signal
          ['0x0800C000']
          Setting model
          Showing model
          [DATA] Column: 0; Row: 0
          0x0800C000
          [DATA] Column: 0; Row: 0
          0x0800C000
          Updating view, removing 1 rows
          ['0x0800C000']
          Removing 1 rows from 0 to 1
          []
          Currently we have 0 rows
          [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
          data changed signal
          ['0x0800C000']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
          data changed signal
          ['0x0800C000', '0x0800C004']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
          data changed signal
          ['0x0800C000', '0x0800C004', '0x0800C008']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
          data changed signal
          ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
          data changed signal
          ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
          Result of setData: True
          [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
          data changed signal
          ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
          Result of setData: True
          Currently we have 6 rows
          [DATA] Column: 0; Row: 0
          0x0800C000
          [DATA] Column: 0; Row: 1
          0x0800C004
          [DATA] Column: 0; Row: 2
          0x0800C008
          [DATA] Column: 0; Row: 3
          0x0800C00C
          [DATA] Column: 0; Row: 4
          0x0800C010
          [DATA] Column: 0; Row: 5
          0x0800C014
          

          Updated tableView: https://imgur.com/zvQgS1s

          I guess adding self.layoutChanged.emit() is what i was missing or it isn't the proper way to solve it?

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by JonB
          #6

          @Carlos-Diaz
          Our posts keep crossing! Please read the answer I have just posted above your new post! I hope you will find it is the reason/solution.

          Carlos DiazC 1 Reply Last reply
          0
          • Carlos DiazC Carlos Diaz

            Hi @JonB,

            Here's the updated test code with the print statements updated:

            #!/usr/bin/env python3
            
            from fbs_runtime.application_context.PySide2 import ApplicationContext
            
            from PySide2.QtCore import *
            from PySide2.QtWidgets import *
            from PySide2.QtGui import QIntValidator
            from PySide2.QtUiTools import QUiLoader
            
            import sys
            
            class TestModel(QAbstractTableModel):
                def __init__(self):
                    QAbstractTableModel.__init__(self)
                    
                    # Here we keep the data
                    self.display = []
            
                def rowCount(self, parent=QModelIndex()):
                    return len(self.display)
            
                def columnCount(self, parent=QModelIndex()):
                    COLUMNS_WIDTH_SIZE = 1
                    return COLUMNS_WIDTH_SIZE
            
                def setData(self, index, value, role=Qt.EditRole):
                    '''
                    Adjust the data (set it to value <value>) depending on the given index
                    and role
                    '''
            
                    if role != Qt.EditRole:
                        return False
            
                    if index.isValid() and 0 <= index.row(): # < len(self.addresses):
                        
                        print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                        if index.column() == 0:
                            self.display.append(value)
                        else:
                            return False
                        
                        print(f'data changed signal')
                        print(self.display)
                        # self.layoutChanged.emit()
                        self.dataChanged.emit(index, index)
                        return True
            
                    return False
            
                def flags(self, index):
                    '''
                    Set the item flags at the given index. Seems like we're implementing
                    this function just to see ho it's done, as we manually adjust each
                    tableView to have NoEditTriggers.
                    '''
            
                    if not index.isValid():
                        return Qt.ItemIsEnabled
                    return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)
            
                def insertRows(self, position, rows=1, index=QModelIndex()):
                    '''
                    Insert a row from the model.
                    '''
                    
                    self.beginInsertRows(QModelIndex(), position, position + rows - 1)
            
                    self.display.append(' ')
                    print(f'insert {rows} rows at {position}')
            
                    self.endInsertRows()
            
                    return True
            
                def removeRows(self, position, rows=1, index=QModelIndex()):
                    '''
                    Remove a row from the model.
                    '''
                    
                    self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
            
                    print(self.display)
                    
                    print(f'Removing {rows} rows from {position} to {position+rows}')
                    del self.display[position:position+rows]
            
                    print(self.display)
            
                    self.endRemoveRows()
            
                    return True
            
                def insertColumns(self, column, count, parent):
                    print(f'insert {count} columns at {column}')
            
                def removeColumns(self):
                    pass
            
                def data(self, index, role=Qt.DisplayRole):
            
                    if not index.isValid():
                        print(f'invalid index {index}')
                        return None
                    
                    if role != Qt.DisplayRole and role != Qt.EditRole:
                        # print(f'invalid role {role}')
                        return None
            
                    column = index.column()
                    row = index.row()
            
                    print(f'[DATA] Column: {column}; Row: {row}')
                    
                    if column == 0:
                        tmp = self.display[index.row()]
                        print(tmp)
                        return tmp
                    else:
                        return None
            
                def headerData(self, section, orientation, role):
                    if role == Qt.DisplayRole and orientation == Qt.Horizontal:
                        if section == 0:
                            return 'Address'
            
            class AppContext(ApplicationContext):
                def run(self):
            
                    self.app.setStyle('Fusion')
            
                    ui_file = self.get_resource("mainwindow.ui")
                    self.file = QFile(ui_file)
                    self.file.open(QFile.ReadOnly)
                    self.loader = QUiLoader()
                    self.window = self.loader.load(self.file)
            
                    self.window.updateView.clicked.connect(self.onUpdateView)
            
                    self.address_list = ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
            
                    self.model = TestModel()
            
                    first_index = self.model.createIndex(0, 0)
            
                    self.model.setData(first_index, self.address_list[0], Qt.EditRole)
            
                    print(f'Setting model')
                    self.window.tableView.setModel(self.model)
                    print(f'Showing model')
                    self.window.tableView.show()
                    
                    # Show the application to the user        
                    self.window.show()
                    return self.app.exec_()
            
                def onUpdateView(self):
            
                    current_rows = self.model.rowCount()
            
                    print(f'Updating view, removing {current_rows} rows')
                    self.model.removeRows(0, current_rows)
            
                    current_rows = self.model.rowCount()
                    print(f'Currently we have {current_rows} rows')
                    
                    for row, address in enumerate(self.address_list):
                        idx = self.model.createIndex(row, 0)
                        tmp = self.model.setData(idx, address, Qt.EditRole)
                        print(f'Result of setData: {tmp}')
                    
                    current_rows = self.model.rowCount()
                    print(f'Currently we have {current_rows} rows')
            
            if __name__ == '__main__':
                appctxt = AppContext()
                exit_code = appctxt.run()
                sys.exit(exit_code)
            

            The return value from setData is True, here's the log output:

            [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
            data changed signal
            ['0x0800C000']
            Setting model
            Showing model
            [DATA] Column: 0; Row: 0
            0x0800C000
            [DATA] Column: 0; Row: 0
            0x0800C000
            Updating view, removing 1 rows
            ['0x0800C000']
            Removing 1 rows from 0 to 1
            []
            Currently we have 0 rows
            [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
            data changed signal
            ['0x0800C000']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
            data changed signal
            ['0x0800C000', '0x0800C004']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
            data changed signal
            ['0x0800C000', '0x0800C004', '0x0800C008']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
            data changed signal
            ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
            data changed signal
            ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
            data changed signal
            ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
            Result of setData: True
            Currently we have 6 rows
            

            If in setData we uncomment the self.layoutChanged.emit() line the view works as expected:

                def setData(self, index, value, role=Qt.EditRole):
                    '''
                    Adjust the data (set it to value <value>) depending on the given index
                    and role
                    '''
            
                    if role != Qt.EditRole:
                        return False
            
                    if index.isValid() and 0 <= index.row(): # < len(self.addresses):
                        
                        print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                        if index.column() == 0:
                            self.display.append(value)
                        else:
                            return False
                        
                        print(f'data changed signal')
                        print(self.display)
                        self.layoutChanged.emit()
                        self.dataChanged.emit(index, index)
                        return True
            
                    return False
            

            log:

            [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
            data changed signal
            ['0x0800C000']
            Setting model
            Showing model
            [DATA] Column: 0; Row: 0
            0x0800C000
            [DATA] Column: 0; Row: 0
            0x0800C000
            Updating view, removing 1 rows
            ['0x0800C000']
            Removing 1 rows from 0 to 1
            []
            Currently we have 0 rows
            [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
            data changed signal
            ['0x0800C000']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
            data changed signal
            ['0x0800C000', '0x0800C004']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
            data changed signal
            ['0x0800C000', '0x0800C004', '0x0800C008']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
            data changed signal
            ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
            data changed signal
            ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
            Result of setData: True
            [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
            data changed signal
            ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
            Result of setData: True
            Currently we have 6 rows
            [DATA] Column: 0; Row: 0
            0x0800C000
            [DATA] Column: 0; Row: 1
            0x0800C004
            [DATA] Column: 0; Row: 2
            0x0800C008
            [DATA] Column: 0; Row: 3
            0x0800C00C
            [DATA] Column: 0; Row: 4
            0x0800C010
            [DATA] Column: 0; Row: 5
            0x0800C014
            

            Updated tableView: https://imgur.com/zvQgS1s

            I guess adding self.layoutChanged.emit() is what i was missing or it isn't the proper way to solve it?

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by JonB
            #7

            @Carlos-Diaz said in tableView not updated after calling dataChanged:

            if index.isValid() and 0 <= index.row(): # < len(self.addresses):

            I think your commented-out bit suggests you were onto the problem :) I admit I don't know why the index is valid, but you should check its row is less than the number of items/rows currently in the model. Then I think it would return false, the setData()s would fail, and you will have to call insertRows() as you should? And then it all works :) ?

            1 Reply Last reply
            0
            • JonBJ JonB

              @Carlos-Diaz
              Our posts keep crossing! Please read the answer I have just posted above your new post! I hope you will find it is the reason/solution.

              Carlos DiazC Offline
              Carlos DiazC Offline
              Carlos Diaz
              wrote on last edited by Carlos Diaz
              #8

              @JonB

              Sorry about that, I also was going to mention our posts were crossing. I now cleaned up the code, I have been working with it for a couple of days and kept garbage code.

              I did a tiny experiment by removing the line that remove rows on the table and:

              1. Keeping the line self.layoutChanged.emit()
              2. Removing the line self.layoutChanged.emit()

              Option 1 the tableView works as expected, i can see the rows being updated.
              Option 2 the tableView is not being updated.

              So I ended up with the following working code:

              #!/usr/bin/env python3
              
              from fbs_runtime.application_context.PySide2 import ApplicationContext
              
              from PySide2.QtCore import *
              from PySide2.QtWidgets import *
              from PySide2.QtGui import QIntValidator
              from PySide2.QtUiTools import QUiLoader
              
              import sys
              
              class TestModel(QAbstractTableModel):
                  def __init__(self):
                      QAbstractTableModel.__init__(self)
                      
                      # Here we keep the data
                      self.display = []
              
                  def rowCount(self, parent=QModelIndex()):
                      return len(self.display)
              
                  def columnCount(self, parent=QModelIndex()):
                      COLUMNS_WIDTH_SIZE = 1
                      return COLUMNS_WIDTH_SIZE
              
                  def setData(self, index, value, role=Qt.EditRole):
                      '''
                      Adjust the data (set it to value <value>) depending on the given index
                      and role
                      '''
              
                      if role != Qt.EditRole:
                          return False
              
                      if index.isValid() and 0 <= index.row():
                          
                          print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                          if index.column() == 0:
                              self.display.append(value)
                          else:
                              return False
                          
                          print(f'data changed signal')
                          print(self.display)
                          self.layoutChanged.emit()
                          self.dataChanged.emit(index, index)
                          return True
              
                      return False
              
                  def flags(self, index):
                      '''
                      Set the item flags at the given index. Seems like we're implementing
                      this function just to see ho it's done, as we manually adjust each
                      tableView to have NoEditTriggers.
                      '''
              
                      if not index.isValid():
                          return Qt.ItemIsEnabled
                      return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)
              
                  def insertRows(self, position, rows=1, index=QModelIndex()):
                      '''
                      Insert a row from the model.
                      '''
                      
                      self.beginInsertRows(QModelIndex(), position, position + rows - 1)
              
                      self.display.append(' ')
                      print(f'insert {rows} rows at {position}')
              
                      self.endInsertRows()
              
                      return True
              
                  def removeRows(self, position, rows=1, index=QModelIndex()):
                      '''
                      Remove a row from the model.
                      '''
                      
                      self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
              
                      print(self.display)
                      
                      print(f'Removing {rows} rows from {position} to {position+rows}')
                      del self.display[position:position+rows]
              
                      print(self.display)
              
                      self.endRemoveRows()
              
                      return True
              
                  def insertColumns(self, column, count, parent):
                      print(f'insert {count} columns at {column}')
              
                  def removeColumns(self):
                      pass
              
                  def data(self, index, role=Qt.DisplayRole):
              
                      if not index.isValid():
                          print(f'invalid index {index}')
                          return None
                      
                      if role != Qt.DisplayRole and role != Qt.EditRole:
                          # print(f'invalid role {role}')
                          return None
              
                      column = index.column()
                      row = index.row()
              
                      print(f'[DATA] Column: {column}; Row: {row}')
                      
                      if column == 0:
                          tmp = self.display[index.row()]
                          print(tmp)
                          return tmp
                      else:
                          return None
              
                  def headerData(self, section, orientation, role):
                      if role == Qt.DisplayRole and orientation == Qt.Horizontal:
                          if section == 0:
                              return 'Address'
              
              class AppContext(ApplicationContext):
                  def run(self):
              
                      self.app.setStyle('Fusion')
              
                      ui_file = self.get_resource("mainwindow.ui")
                      self.file = QFile(ui_file)
                      self.file.open(QFile.ReadOnly)
                      self.loader = QUiLoader()
                      self.window = self.loader.load(self.file)
              
                      self.window.updateView.clicked.connect(self.onUpdateView)
              
                      self.address_list = ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
              
                      self.model = TestModel()
              
                      first_index = self.model.createIndex(0, 0)
              
                      self.model.setData(first_index, self.address_list[0], Qt.EditRole)
              
                      print(f'Setting model')
                      self.window.tableView.setModel(self.model)
                      print(f'Showing model')
                      self.window.tableView.show()
                      
                      # Show the application to the user        
                      self.window.show()
                      return self.app.exec_()
              
                  def onUpdateView(self):
              
                      current_rows = self.model.rowCount()
                      print(f'Currently we have {current_rows} rows')
                      
                      for row, address in enumerate(self.address_list):
                          idx = self.model.createIndex(row, 0)
                          tmp = self.model.setData(idx, address, Qt.EditRole)
                          print(f'Result of setData: {tmp}')
                      
                      current_rows = self.model.rowCount()
                      print(f'Currently we have {current_rows} rows')
              
              if __name__ == '__main__':
                  appctxt = AppContext()
                  exit_code = appctxt.run()
                  sys.exit(exit_code)
              
              

              Working log:

              [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
              data changed signal
              ['0x0800C000']
              Setting model
              Showing model
              [DATA] Column: 0; Row: 0
              0x0800C000
              [DATA] Column: 0; Row: 0
              0x0800C000
              Currently we have 1 rows
              [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
              data changed signal
              ['0x0800C000', '0x0800C000']
              Result of setData: True
              [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
              data changed signal
              ['0x0800C000', '0x0800C000', '0x0800C004']
              Result of setData: True
              [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
              data changed signal
              ['0x0800C000', '0x0800C000', '0x0800C004', '0x0800C008']
              Result of setData: True
              [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
              data changed signal
              ['0x0800C000', '0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
              Result of setData: True
              [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
              data changed signal
              ['0x0800C000', '0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
              Result of setData: True
              [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
              data changed signal
              ['0x0800C000', '0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
              Result of setData: True
              Currently we have 7 rows
              [DATA] Column: 0; Row: 0
              0x0800C000
              [DATA] Column: 0; Row: 1
              0x0800C000
              [DATA] Column: 0; Row: 2
              0x0800C004
              [DATA] Column: 0; Row: 3
              0x0800C008
              [DATA] Column: 0; Row: 4
              0x0800C00C
              [DATA] Column: 0; Row: 5
              0x0800C010
              [DATA] Column: 0; Row: 6
              0x0800C014
              

              We can see the data function is being called and returning valid data so the view is updated. Updated tableView: https://imgur.com/zvQgS1s

              JonBJ 1 Reply Last reply
              0
              • Carlos DiazC Carlos Diaz

                @JonB

                Sorry about that, I also was going to mention our posts were crossing. I now cleaned up the code, I have been working with it for a couple of days and kept garbage code.

                I did a tiny experiment by removing the line that remove rows on the table and:

                1. Keeping the line self.layoutChanged.emit()
                2. Removing the line self.layoutChanged.emit()

                Option 1 the tableView works as expected, i can see the rows being updated.
                Option 2 the tableView is not being updated.

                So I ended up with the following working code:

                #!/usr/bin/env python3
                
                from fbs_runtime.application_context.PySide2 import ApplicationContext
                
                from PySide2.QtCore import *
                from PySide2.QtWidgets import *
                from PySide2.QtGui import QIntValidator
                from PySide2.QtUiTools import QUiLoader
                
                import sys
                
                class TestModel(QAbstractTableModel):
                    def __init__(self):
                        QAbstractTableModel.__init__(self)
                        
                        # Here we keep the data
                        self.display = []
                
                    def rowCount(self, parent=QModelIndex()):
                        return len(self.display)
                
                    def columnCount(self, parent=QModelIndex()):
                        COLUMNS_WIDTH_SIZE = 1
                        return COLUMNS_WIDTH_SIZE
                
                    def setData(self, index, value, role=Qt.EditRole):
                        '''
                        Adjust the data (set it to value <value>) depending on the given index
                        and role
                        '''
                
                        if role != Qt.EditRole:
                            return False
                
                        if index.isValid() and 0 <= index.row():
                            
                            print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                            if index.column() == 0:
                                self.display.append(value)
                            else:
                                return False
                            
                            print(f'data changed signal')
                            print(self.display)
                            self.layoutChanged.emit()
                            self.dataChanged.emit(index, index)
                            return True
                
                        return False
                
                    def flags(self, index):
                        '''
                        Set the item flags at the given index. Seems like we're implementing
                        this function just to see ho it's done, as we manually adjust each
                        tableView to have NoEditTriggers.
                        '''
                
                        if not index.isValid():
                            return Qt.ItemIsEnabled
                        return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)
                
                    def insertRows(self, position, rows=1, index=QModelIndex()):
                        '''
                        Insert a row from the model.
                        '''
                        
                        self.beginInsertRows(QModelIndex(), position, position + rows - 1)
                
                        self.display.append(' ')
                        print(f'insert {rows} rows at {position}')
                
                        self.endInsertRows()
                
                        return True
                
                    def removeRows(self, position, rows=1, index=QModelIndex()):
                        '''
                        Remove a row from the model.
                        '''
                        
                        self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
                
                        print(self.display)
                        
                        print(f'Removing {rows} rows from {position} to {position+rows}')
                        del self.display[position:position+rows]
                
                        print(self.display)
                
                        self.endRemoveRows()
                
                        return True
                
                    def insertColumns(self, column, count, parent):
                        print(f'insert {count} columns at {column}')
                
                    def removeColumns(self):
                        pass
                
                    def data(self, index, role=Qt.DisplayRole):
                
                        if not index.isValid():
                            print(f'invalid index {index}')
                            return None
                        
                        if role != Qt.DisplayRole and role != Qt.EditRole:
                            # print(f'invalid role {role}')
                            return None
                
                        column = index.column()
                        row = index.row()
                
                        print(f'[DATA] Column: {column}; Row: {row}')
                        
                        if column == 0:
                            tmp = self.display[index.row()]
                            print(tmp)
                            return tmp
                        else:
                            return None
                
                    def headerData(self, section, orientation, role):
                        if role == Qt.DisplayRole and orientation == Qt.Horizontal:
                            if section == 0:
                                return 'Address'
                
                class AppContext(ApplicationContext):
                    def run(self):
                
                        self.app.setStyle('Fusion')
                
                        ui_file = self.get_resource("mainwindow.ui")
                        self.file = QFile(ui_file)
                        self.file.open(QFile.ReadOnly)
                        self.loader = QUiLoader()
                        self.window = self.loader.load(self.file)
                
                        self.window.updateView.clicked.connect(self.onUpdateView)
                
                        self.address_list = ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
                
                        self.model = TestModel()
                
                        first_index = self.model.createIndex(0, 0)
                
                        self.model.setData(first_index, self.address_list[0], Qt.EditRole)
                
                        print(f'Setting model')
                        self.window.tableView.setModel(self.model)
                        print(f'Showing model')
                        self.window.tableView.show()
                        
                        # Show the application to the user        
                        self.window.show()
                        return self.app.exec_()
                
                    def onUpdateView(self):
                
                        current_rows = self.model.rowCount()
                        print(f'Currently we have {current_rows} rows')
                        
                        for row, address in enumerate(self.address_list):
                            idx = self.model.createIndex(row, 0)
                            tmp = self.model.setData(idx, address, Qt.EditRole)
                            print(f'Result of setData: {tmp}')
                        
                        current_rows = self.model.rowCount()
                        print(f'Currently we have {current_rows} rows')
                
                if __name__ == '__main__':
                    appctxt = AppContext()
                    exit_code = appctxt.run()
                    sys.exit(exit_code)
                
                

                Working log:

                [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
                data changed signal
                ['0x0800C000']
                Setting model
                Showing model
                [DATA] Column: 0; Row: 0
                0x0800C000
                [DATA] Column: 0; Row: 0
                0x0800C000
                Currently we have 1 rows
                [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
                data changed signal
                ['0x0800C000', '0x0800C000']
                Result of setData: True
                [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
                data changed signal
                ['0x0800C000', '0x0800C000', '0x0800C004']
                Result of setData: True
                [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
                data changed signal
                ['0x0800C000', '0x0800C000', '0x0800C004', '0x0800C008']
                Result of setData: True
                [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
                data changed signal
                ['0x0800C000', '0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C']
                Result of setData: True
                [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
                data changed signal
                ['0x0800C000', '0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010']
                Result of setData: True
                [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
                data changed signal
                ['0x0800C000', '0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
                Result of setData: True
                Currently we have 7 rows
                [DATA] Column: 0; Row: 0
                0x0800C000
                [DATA] Column: 0; Row: 1
                0x0800C000
                [DATA] Column: 0; Row: 2
                0x0800C004
                [DATA] Column: 0; Row: 3
                0x0800C008
                [DATA] Column: 0; Row: 4
                0x0800C00C
                [DATA] Column: 0; Row: 5
                0x0800C010
                [DATA] Column: 0; Row: 6
                0x0800C014
                

                We can see the data function is being called and returning valid data so the view is updated. Updated tableView: https://imgur.com/zvQgS1s

                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by JonB
                #9

                @Carlos-Diaz said in tableView not updated after calling dataChanged:

                print(f'insert {rows} rows at {position}')

                Where is that called in the output? You are not calling insertRows(). Your code for setData() is wrong. As I have said in above posts.

                Your call to index.isValid() is insufficient. That only says:

                Returns true if this model index is valid; otherwise returns false.

                A valid index belongs to a model, and has non-negative row and column numbers.That's what I think. It would be interesting to hear your comment?

                You have to check for number of rows in model in setData(), your line needs to be:

                 if index.isValid() and 0 <= index.row() < self.rowCount():
                

                Your self.layoutChanged.emit() hides the incorrect code by causing the whole view to refresh.

                I really think that is your problem. It would be nice to hear your comment on this specific area?

                Carlos DiazC 1 Reply Last reply
                0
                • JonBJ JonB

                  @Carlos-Diaz said in tableView not updated after calling dataChanged:

                  print(f'insert {rows} rows at {position}')

                  Where is that called in the output? You are not calling insertRows(). Your code for setData() is wrong. As I have said in above posts.

                  Your call to index.isValid() is insufficient. That only says:

                  Returns true if this model index is valid; otherwise returns false.

                  A valid index belongs to a model, and has non-negative row and column numbers.That's what I think. It would be interesting to hear your comment?

                  You have to check for number of rows in model in setData(), your line needs to be:

                   if index.isValid() and 0 <= index.row() < self.rowCount():
                  

                  Your self.layoutChanged.emit() hides the incorrect code by causing the whole view to refresh.

                  I really think that is your problem. It would be nice to hear your comment on this specific area?

                  Carlos DiazC Offline
                  Carlos DiazC Offline
                  Carlos Diaz
                  wrote on last edited by
                  #10

                  @JonB Thanks for being so patient with my mistakes.

                  I think I had added everything you suggested and removed the self.layoutChanged.emit() call and the view keeps working!

                  I don't know if it's possible to add the new rows inside the setData function, so it's not handled on the application level?

                  Here's the updated code:

                  #!/usr/bin/env python3
                  
                  from fbs_runtime.application_context.PySide2 import ApplicationContext
                  
                  from PySide2.QtCore import *
                  from PySide2.QtWidgets import *
                  from PySide2.QtGui import QIntValidator
                  from PySide2.QtUiTools import QUiLoader
                  
                  import sys
                  
                  '''
                  QAbstractTableModel provides a standard interface for models that represent their
                  data as a two-dimensional array of items. It is not used directly, but must be
                  subclassed.
                  
                  Since the model provides a more especialized interface than QAbstractItemModel,
                  it is not suitable for use with tree views, although it can be used to provide
                  data to a QListView.
                  If you need to represent a simple list of items, and only need a model to contain
                  a single column of data, subclassing the QAbstractListModel may be more appropiate.
                  
                  The rowCount() and columnCount() functions return the dimensions of the table.
                  To retrieve a model index corresponding to an item in the model, use index()
                  and provide only the row and column numbers.
                  
                  Editable models need to implement setData(), and implement flags() to return a value
                  containing ItemsIsEditable.
                  
                  Models that provide interfaces to resizable data structures can provide implementations of:
                   - insertRows()
                   - removeRows()
                   - insertColumns()
                   - removeColumns()
                   
                  When implementing these functions, it is important to call the appropiate functions
                  so that all connected views are aware of any changes.
                  
                  Role enum:
                  https://doc.qt.io/qt-5/qt.html#ItemDataRole-enum
                  
                  Useful links:
                  https://doc.qt.io/qtforpython/PySide2/QtCore/QAbstractTableModel.html
                  https://doc.qt.io/qtforpython/tutorials/datavisualize/index.html
                  https://stackoverflow.com/questions/50391050/how-to-remove-row-from-qtreeview-using-qabstractitemmodel
                  https://github.com/pyside/Examples/blob/master/examples/itemviews/editabletreemodel/editabletreemodel.py
                  '''
                  class TestModel(QAbstractTableModel):
                      def __init__(self):
                          QAbstractTableModel.__init__(self)
                          
                          # Here we keep the data
                          self.display = []
                  
                      def rowCount(self, parent=QModelIndex()):
                          return len(self.display)
                  
                      def columnCount(self, parent=QModelIndex()):
                          COLUMNS_WIDTH_SIZE = 1
                          return COLUMNS_WIDTH_SIZE
                  
                      def setData(self, index, value, role=Qt.EditRole):
                          '''
                          Adjust the data (set it to value <value>) depending on the given index
                          and role
                          '''
                  
                          if role != Qt.EditRole:
                              print(f'[SET_DATA] Role is not EditRole')
                              return False
                  
                          # We must be sure we add the new value into a valid index of the model
                          # So we validate the index, we make sure the index row isn't 0 or less
                          # and that the amount of current rows is more than the index row we are
                          # adding the value to.
                          if index.isValid() and 0 <= index.row() < self.rowCount():
                              
                              print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                              if index.column() == 0:
                                  print(f'Before setting the new value {self.display}')
                                  self.display[index.row()] = value
                                  print(f'After setting the new value {self.display}')
                              else:
                                  return False
                              
                              self.dataChanged.emit(index, index)
                              return True
                  
                          return False
                  
                      def flags(self, index):
                          '''
                          Set the item flags at the given index. Seems like we're implementing
                          this function just to see ho it's done, as we manually adjust each
                          tableView to have NoEditTriggers.
                          '''
                  
                          if not index.isValid():
                              return Qt.ItemIsEnabled
                          return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)
                  
                      def insertRows(self, position, rows=1, index=QModelIndex()):
                          '''
                          Insert a row from the model.
                          '''
                          
                          self.beginInsertRows(QModelIndex(), position, position + rows - 1)
                  
                          print(f'Inserting {rows} rows at {position}')
                  
                          # We extend the list with rows elements
                          self.display.extend([''] * rows)
                  
                          self.endInsertRows()
                  
                          return True
                  
                      def removeRows(self, position, rows=1, index=QModelIndex()):
                          '''
                          Remove a row from the model.
                          '''
                          
                          self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
                  
                          print(self.display)
                          
                          print(f'Removing {rows} rows from {position} to {position+rows}')
                          del self.display[position:position+rows]
                  
                          print(self.display)
                  
                          self.endRemoveRows()
                  
                          return True
                  
                      def insertColumns(self, column, count, parent):
                          print(f'insert {count} columns at {column}')
                  
                      def removeColumns(self):
                          pass
                  
                      def data(self, index, role=Qt.DisplayRole):
                  
                          if not index.isValid():
                              print(f'invalid index {index}')
                              return None
                          
                          if role != Qt.DisplayRole and role != Qt.EditRole:
                              return None
                  
                          column = index.column()
                          row = index.row()
                  
                          print(f'Fetching data from column: {column}; row: {row}')
                          
                          if column == 0:
                              tmp = self.display[index.row()]
                              print(f'Data: {tmp}')
                              return tmp
                          else:
                              return None
                  
                      def headerData(self, section, orientation, role):
                          if role == Qt.DisplayRole and orientation == Qt.Horizontal:
                              if section == 0:
                                  return 'Address'
                  
                  class AppContext(ApplicationContext):
                      def run(self):
                  
                          self.app.setStyle('Fusion')
                  
                          ui_file = self.get_resource("mainwindow.ui")
                          self.file = QFile(ui_file)
                          self.file.open(QFile.ReadOnly)
                          self.loader = QUiLoader()
                          self.window = self.loader.load(self.file)
                  
                          self.window.updateView.clicked.connect(self.onUpdateView)
                  
                          self.address_list = ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
                  
                          self.model = TestModel()
                          self.window.tableView.setModel(self.model)
                  
                          # Add a row for the default data..
                          self.model.insertRows(0)
                          first_index = self.model.createIndex(0, 0)
                          self.model.setData(first_index, self.address_list[0], Qt.EditRole)
                          
                          self.window.tableView.show()
                          
                          # Show the application to the user        
                          self.window.show()
                          return self.app.exec_()
                  
                      def onUpdateView(self):
                          print(f'Updating view...')
                          
                          print(f'We have {self.model.rowCount()} rows')
                  
                          # Insert rows for new data
                          rows_to_add = len(self.address_list)
                          current_rows = self.model.rowCount()
                  
                          rows_to_add -= current_rows
                          print(f'Inserting {rows_to_add} new rows for the new data')
                          self.model.insertRows(0, rows_to_add)
                          print(f'We now have {self.model.rowCount()} rows')
                  
                          print(f'Inserting new data into the model...')
                          for row, address in enumerate(self.address_list):
                              column = 0
                              idx = self.model.createIndex(row, column)
                              tmp = self.model.setData(idx, address, Qt.EditRole)
                              print(f'Result of setData: {tmp}')
                  
                          print('Done')
                  
                  if __name__ == '__main__':
                      appctxt = AppContext()
                      exit_code = appctxt.run()
                      sys.exit(exit_code)
                  

                  Output log:

                  Inserting 1 rows at 0
                  [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
                  Before setting the new value ['']
                  After setting the new value ['0x0800C000']
                  Fetching data from column: 0; row: 0
                  Data: 0x0800C000
                  Fetching data from column: 0; row: 0
                  Data: 0x0800C000
                  Updating view...
                  We have 1 rows
                  Inserting 5 new rows for the new data
                  Inserting 5 rows at 0
                  We now have 6 rows
                  Inserting new data into the model...
                  [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
                  Before setting the new value ['0x0800C000', '', '', '', '', '']
                  After setting the new value ['0x0800C000', '', '', '', '', '']
                  Result of setData: True
                  [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
                  Before setting the new value ['0x0800C000', '', '', '', '', '']
                  After setting the new value ['0x0800C000', '0x0800C004', '', '', '', '']
                  Result of setData: True
                  [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
                  Before setting the new value ['0x0800C000', '0x0800C004', '', '', '', '']
                  After setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '', '', '']
                  Result of setData: True
                  [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
                  Before setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '', '', '']
                  After setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '', '']
                  Result of setData: True
                  [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
                  Before setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '', '']
                  After setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '']
                  Result of setData: True
                  [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
                  Before setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '']
                  After setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
                  Result of setData: True
                  Done
                  Fetching data from column: 0; row: 0
                  Data: 0x0800C000
                  Fetching data from column: 0; row: 1
                  Data: 0x0800C004
                  Fetching data from column: 0; row: 2
                  Data: 0x0800C008
                  Fetching data from column: 0; row: 3
                  Data: 0x0800C00C
                  Fetching data from column: 0; row: 4
                  Data: 0x0800C010
                  Fetching data from column: 0; row: 5
                  Data: 0x0800C014
                  

                  Current tableView: https://imgur.com/5HMxhrx

                  What do you think?

                  JonBJ 1 Reply Last reply
                  1
                  • Carlos DiazC Carlos Diaz

                    @JonB Thanks for being so patient with my mistakes.

                    I think I had added everything you suggested and removed the self.layoutChanged.emit() call and the view keeps working!

                    I don't know if it's possible to add the new rows inside the setData function, so it's not handled on the application level?

                    Here's the updated code:

                    #!/usr/bin/env python3
                    
                    from fbs_runtime.application_context.PySide2 import ApplicationContext
                    
                    from PySide2.QtCore import *
                    from PySide2.QtWidgets import *
                    from PySide2.QtGui import QIntValidator
                    from PySide2.QtUiTools import QUiLoader
                    
                    import sys
                    
                    '''
                    QAbstractTableModel provides a standard interface for models that represent their
                    data as a two-dimensional array of items. It is not used directly, but must be
                    subclassed.
                    
                    Since the model provides a more especialized interface than QAbstractItemModel,
                    it is not suitable for use with tree views, although it can be used to provide
                    data to a QListView.
                    If you need to represent a simple list of items, and only need a model to contain
                    a single column of data, subclassing the QAbstractListModel may be more appropiate.
                    
                    The rowCount() and columnCount() functions return the dimensions of the table.
                    To retrieve a model index corresponding to an item in the model, use index()
                    and provide only the row and column numbers.
                    
                    Editable models need to implement setData(), and implement flags() to return a value
                    containing ItemsIsEditable.
                    
                    Models that provide interfaces to resizable data structures can provide implementations of:
                     - insertRows()
                     - removeRows()
                     - insertColumns()
                     - removeColumns()
                     
                    When implementing these functions, it is important to call the appropiate functions
                    so that all connected views are aware of any changes.
                    
                    Role enum:
                    https://doc.qt.io/qt-5/qt.html#ItemDataRole-enum
                    
                    Useful links:
                    https://doc.qt.io/qtforpython/PySide2/QtCore/QAbstractTableModel.html
                    https://doc.qt.io/qtforpython/tutorials/datavisualize/index.html
                    https://stackoverflow.com/questions/50391050/how-to-remove-row-from-qtreeview-using-qabstractitemmodel
                    https://github.com/pyside/Examples/blob/master/examples/itemviews/editabletreemodel/editabletreemodel.py
                    '''
                    class TestModel(QAbstractTableModel):
                        def __init__(self):
                            QAbstractTableModel.__init__(self)
                            
                            # Here we keep the data
                            self.display = []
                    
                        def rowCount(self, parent=QModelIndex()):
                            return len(self.display)
                    
                        def columnCount(self, parent=QModelIndex()):
                            COLUMNS_WIDTH_SIZE = 1
                            return COLUMNS_WIDTH_SIZE
                    
                        def setData(self, index, value, role=Qt.EditRole):
                            '''
                            Adjust the data (set it to value <value>) depending on the given index
                            and role
                            '''
                    
                            if role != Qt.EditRole:
                                print(f'[SET_DATA] Role is not EditRole')
                                return False
                    
                            # We must be sure we add the new value into a valid index of the model
                            # So we validate the index, we make sure the index row isn't 0 or less
                            # and that the amount of current rows is more than the index row we are
                            # adding the value to.
                            if index.isValid() and 0 <= index.row() < self.rowCount():
                                
                                print(f'[SET_DATA] DATA: {value} index row: {index.row()}; column {index.column()}')
                                if index.column() == 0:
                                    print(f'Before setting the new value {self.display}')
                                    self.display[index.row()] = value
                                    print(f'After setting the new value {self.display}')
                                else:
                                    return False
                                
                                self.dataChanged.emit(index, index)
                                return True
                    
                            return False
                    
                        def flags(self, index):
                            '''
                            Set the item flags at the given index. Seems like we're implementing
                            this function just to see ho it's done, as we manually adjust each
                            tableView to have NoEditTriggers.
                            '''
                    
                            if not index.isValid():
                                return Qt.ItemIsEnabled
                            return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable)
                    
                        def insertRows(self, position, rows=1, index=QModelIndex()):
                            '''
                            Insert a row from the model.
                            '''
                            
                            self.beginInsertRows(QModelIndex(), position, position + rows - 1)
                    
                            print(f'Inserting {rows} rows at {position}')
                    
                            # We extend the list with rows elements
                            self.display.extend([''] * rows)
                    
                            self.endInsertRows()
                    
                            return True
                    
                        def removeRows(self, position, rows=1, index=QModelIndex()):
                            '''
                            Remove a row from the model.
                            '''
                            
                            self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
                    
                            print(self.display)
                            
                            print(f'Removing {rows} rows from {position} to {position+rows}')
                            del self.display[position:position+rows]
                    
                            print(self.display)
                    
                            self.endRemoveRows()
                    
                            return True
                    
                        def insertColumns(self, column, count, parent):
                            print(f'insert {count} columns at {column}')
                    
                        def removeColumns(self):
                            pass
                    
                        def data(self, index, role=Qt.DisplayRole):
                    
                            if not index.isValid():
                                print(f'invalid index {index}')
                                return None
                            
                            if role != Qt.DisplayRole and role != Qt.EditRole:
                                return None
                    
                            column = index.column()
                            row = index.row()
                    
                            print(f'Fetching data from column: {column}; row: {row}')
                            
                            if column == 0:
                                tmp = self.display[index.row()]
                                print(f'Data: {tmp}')
                                return tmp
                            else:
                                return None
                    
                        def headerData(self, section, orientation, role):
                            if role == Qt.DisplayRole and orientation == Qt.Horizontal:
                                if section == 0:
                                    return 'Address'
                    
                    class AppContext(ApplicationContext):
                        def run(self):
                    
                            self.app.setStyle('Fusion')
                    
                            ui_file = self.get_resource("mainwindow.ui")
                            self.file = QFile(ui_file)
                            self.file.open(QFile.ReadOnly)
                            self.loader = QUiLoader()
                            self.window = self.loader.load(self.file)
                    
                            self.window.updateView.clicked.connect(self.onUpdateView)
                    
                            self.address_list = ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
                    
                            self.model = TestModel()
                            self.window.tableView.setModel(self.model)
                    
                            # Add a row for the default data..
                            self.model.insertRows(0)
                            first_index = self.model.createIndex(0, 0)
                            self.model.setData(first_index, self.address_list[0], Qt.EditRole)
                            
                            self.window.tableView.show()
                            
                            # Show the application to the user        
                            self.window.show()
                            return self.app.exec_()
                    
                        def onUpdateView(self):
                            print(f'Updating view...')
                            
                            print(f'We have {self.model.rowCount()} rows')
                    
                            # Insert rows for new data
                            rows_to_add = len(self.address_list)
                            current_rows = self.model.rowCount()
                    
                            rows_to_add -= current_rows
                            print(f'Inserting {rows_to_add} new rows for the new data')
                            self.model.insertRows(0, rows_to_add)
                            print(f'We now have {self.model.rowCount()} rows')
                    
                            print(f'Inserting new data into the model...')
                            for row, address in enumerate(self.address_list):
                                column = 0
                                idx = self.model.createIndex(row, column)
                                tmp = self.model.setData(idx, address, Qt.EditRole)
                                print(f'Result of setData: {tmp}')
                    
                            print('Done')
                    
                    if __name__ == '__main__':
                        appctxt = AppContext()
                        exit_code = appctxt.run()
                        sys.exit(exit_code)
                    

                    Output log:

                    Inserting 1 rows at 0
                    [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
                    Before setting the new value ['']
                    After setting the new value ['0x0800C000']
                    Fetching data from column: 0; row: 0
                    Data: 0x0800C000
                    Fetching data from column: 0; row: 0
                    Data: 0x0800C000
                    Updating view...
                    We have 1 rows
                    Inserting 5 new rows for the new data
                    Inserting 5 rows at 0
                    We now have 6 rows
                    Inserting new data into the model...
                    [SET_DATA] DATA: 0x0800C000 index row: 0; column 0
                    Before setting the new value ['0x0800C000', '', '', '', '', '']
                    After setting the new value ['0x0800C000', '', '', '', '', '']
                    Result of setData: True
                    [SET_DATA] DATA: 0x0800C004 index row: 1; column 0
                    Before setting the new value ['0x0800C000', '', '', '', '', '']
                    After setting the new value ['0x0800C000', '0x0800C004', '', '', '', '']
                    Result of setData: True
                    [SET_DATA] DATA: 0x0800C008 index row: 2; column 0
                    Before setting the new value ['0x0800C000', '0x0800C004', '', '', '', '']
                    After setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '', '', '']
                    Result of setData: True
                    [SET_DATA] DATA: 0x0800C00C index row: 3; column 0
                    Before setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '', '', '']
                    After setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '', '']
                    Result of setData: True
                    [SET_DATA] DATA: 0x0800C010 index row: 4; column 0
                    Before setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '', '']
                    After setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '']
                    Result of setData: True
                    [SET_DATA] DATA: 0x0800C014 index row: 5; column 0
                    Before setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '']
                    After setting the new value ['0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014']
                    Result of setData: True
                    Done
                    Fetching data from column: 0; row: 0
                    Data: 0x0800C000
                    Fetching data from column: 0; row: 1
                    Data: 0x0800C004
                    Fetching data from column: 0; row: 2
                    Data: 0x0800C008
                    Fetching data from column: 0; row: 3
                    Data: 0x0800C00C
                    Fetching data from column: 0; row: 4
                    Data: 0x0800C010
                    Fetching data from column: 0; row: 5
                    Data: 0x0800C014
                    

                    Current tableView: https://imgur.com/5HMxhrx

                    What do you think?

                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by JonB
                    #11

                    @Carlos-Diaz
                    Please no more log output, it's not telling us anything! :)

                    You need have the following:

                    In setData() you must return False if the row is greater than the number of rows currently in the model:

                    if index.isValid() and 0 <= index.row() < self.rowCount():
                        ....
                    else:
                        return False
                    

                    I see you have that now.

                    In onUpdateView you must call insertRows(), either to insert at the start or at the end or the existing rows, and then setData() on each of the newly inserted rows.

                    You also seem to have that now.

                    So I would say what you have looks correct!

                    It may or may not be possible to call the insertRows() from within setData(), but I don't think that is setData()'s job anyway. It is correct that it should instead return False, and the coder must explicitly call insertRows() outside himself to add new rows. This is the way the model is designed to behave. That is why you are now able to say

                    removed the self.layoutChanged.emit() call and the view keeps working!

                    :)

                    Carlos DiazC 1 Reply Last reply
                    0
                    • JonBJ JonB

                      @Carlos-Diaz
                      Please no more log output, it's not telling us anything! :)

                      You need have the following:

                      In setData() you must return False if the row is greater than the number of rows currently in the model:

                      if index.isValid() and 0 <= index.row() < self.rowCount():
                          ....
                      else:
                          return False
                      

                      I see you have that now.

                      In onUpdateView you must call insertRows(), either to insert at the start or at the end or the existing rows, and then setData() on each of the newly inserted rows.

                      You also seem to have that now.

                      So I would say what you have looks correct!

                      It may or may not be possible to call the insertRows() from within setData(), but I don't think that is setData()'s job anyway. It is correct that it should instead return False, and the coder must explicitly call insertRows() outside himself to add new rows. This is the way the model is designed to behave. That is why you are now able to say

                      removed the self.layoutChanged.emit() call and the view keeps working!

                      :)

                      Carlos DiazC Offline
                      Carlos DiazC Offline
                      Carlos Diaz
                      wrote on last edited by
                      #12

                      @JonB Thanks for the help, now I need to migrate this into the final application.
                      I will mark the issue as solved.

                      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