tableView not updated after calling dataChanged
-
wrote on 22 Nov 2019, 17:25 last edited by
Hi
I'm doing a toy project to understand the QAbstractTableModel and display it's data into a tableView widget. I have been reading some of the old posts here but most of them aren't solved.
Expected:
At the begginning i expect the string '0x0800C000' being displayed on column 0, row 0, after pressing the button I remove all the rows to clean the view and after that have the following list displayed on column 0, each element on it's own row: '0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014'.What I get:
The first string is properly displayed, the rows are being removed but the view isn't being updated.Here's what I have tried so far:
#!/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()): return 1 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') # Let Qt know there's new data to be displayed 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.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): pass def removeColumns(self): pass def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None if role != Qt.DisplayRole and role != Qt.EditRole: return None column = index.column() row = index.row() print(f'[DATA] Column: {column}; Row: {row}') if column == 0: return self.display[index.row()] 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) self.window.tableView.setModel(self.model) self.window.tableView.show() print(self.model.rowCount()) # 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') row = 0 for address in self.address_list: idx = self.model.createIndex(row, 0) self.model.setData(idx, address, Qt.EditRole) row += 1 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 tableView starts like this:
Table view when launching the application: https://imgur.com/eIfwwxmAfter pressing the button i get the following log data:
[SET_DATA] DATA: 0x0800C000 index row: 0; column0 data changed signal [SET_DATA] DATA: 0x0800C004 index row: 1; column0 data changed signal [SET_DATA] DATA: 0x0800C008 index row: 2; column0 data changed signal [SET_DATA] DATA: 0x0800C00C index row: 3; column0 data changed signal [SET_DATA] DATA: 0x0800C010 index row: 4; column0 data changed signal [SET_DATA] DATA: 0x0800C014 index row: 5; column0 data changed signal Currently we have 6 rows
So the data is being inserted in the
self.display
list.
But the widget looks still empty: https://imgur.com/moPD7x3You can find the complete repo here, I'm using fbs for the application so you should clone the repo, enter the cloned directory and do
fbs run
to get it running.Regards
-
After taking a closer look at the log we can see the method
data
isn't being called aftersetData
.[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
wrote on 22 Nov 2019, 18:40 last edited by JonB@Carlos-Diaz
As I said above.I believe you will find your problem is because: having removed all rows via
removeRows()
you simply usesetData()
to put stuff into the model rows. You do not callinsertRows()
. 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.
-
Hi
I'm doing a toy project to understand the QAbstractTableModel and display it's data into a tableView widget. I have been reading some of the old posts here but most of them aren't solved.
Expected:
At the begginning i expect the string '0x0800C000' being displayed on column 0, row 0, after pressing the button I remove all the rows to clean the view and after that have the following list displayed on column 0, each element on it's own row: '0x0800C000', '0x0800C004', '0x0800C008', '0x0800C00C', '0x0800C010', '0x0800C014'.What I get:
The first string is properly displayed, the rows are being removed but the view isn't being updated.Here's what I have tried so far:
#!/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()): return 1 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') # Let Qt know there's new data to be displayed 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.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): pass def removeColumns(self): pass def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None if role != Qt.DisplayRole and role != Qt.EditRole: return None column = index.column() row = index.row() print(f'[DATA] Column: {column}; Row: {row}') if column == 0: return self.display[index.row()] 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) self.window.tableView.setModel(self.model) self.window.tableView.show() print(self.model.rowCount()) # 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') row = 0 for address in self.address_list: idx = self.model.createIndex(row, 0) self.model.setData(idx, address, Qt.EditRole) row += 1 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 tableView starts like this:
Table view when launching the application: https://imgur.com/eIfwwxmAfter pressing the button i get the following log data:
[SET_DATA] DATA: 0x0800C000 index row: 0; column0 data changed signal [SET_DATA] DATA: 0x0800C004 index row: 1; column0 data changed signal [SET_DATA] DATA: 0x0800C008 index row: 2; column0 data changed signal [SET_DATA] DATA: 0x0800C00C index row: 3; column0 data changed signal [SET_DATA] DATA: 0x0800C010 index row: 4; column0 data changed signal [SET_DATA] DATA: 0x0800C014 index row: 5; column0 data changed signal Currently we have 6 rows
So the data is being inserted in the
self.display
list.
But the widget looks still empty: https://imgur.com/moPD7x3You can find the complete repo here, I'm using fbs for the application so you should clone the repo, enter the cloned directory and do
fbs run
to get it running.Regards
wrote on 22 Nov 2019, 18:23 last edited by@Carlos-Diaz said in tableView not updated after calling dataChanged:
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') row = 0 for address in self.address_list: idx = self.model.createIndex(row, 0) self.model.setData(idx, address, Qt.EditRole) row += 1
Why doesn't your output show those
print
lines?Don't they show that after removing all rows from the model, you just go
setData()
over all the rows which no longer exist? What does thebool
returned by yourself.model.setData()
return? -
wrote on 22 Nov 2019, 18:24 last edited by
After taking a closer look at the log we can see the method
data
isn't being called aftersetData
.[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
-
After taking a closer look at the log we can see the method
data
isn't being called aftersetData
.[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
wrote on 22 Nov 2019, 18:40 last edited by JonB@Carlos-Diaz
As I said above.I believe you will find your problem is because: having removed all rows via
removeRows()
you simply usesetData()
to put stuff into the model rows. You do not callinsertRows()
. 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.
-
wrote on 22 Nov 2019, 18:40 last edited by
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 theself.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? -
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 theself.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?wrote on 22 Nov 2019, 18:41 last edited by 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. -
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 theself.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?wrote on 22 Nov 2019, 18:50 last edited by JonB@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, butyou should check its row is less than the number of items/rows currently in the model. Then I think it would return false, thesetData()
s would fail, and you will have to callinsertRows()
as you should? And then it all works :) ? -
@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.wrote on 22 Nov 2019, 18:54 last edited by Carlos DiazSorry 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:
- Keeping the line
self.layoutChanged.emit()
- 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 - Keeping the line
-
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:
- Keeping the line
self.layoutChanged.emit()
- 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/zvQgS1swrote on 22 Nov 2019, 19:02 last edited by 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 forsetData()
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?
- Keeping the line
-
@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 forsetData()
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?
wrote on 22 Nov 2019, 19:59 last edited by@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?
-
@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?
wrote on 23 Nov 2019, 10:24 last edited by JonB@Carlos-Diaz
Please no more log output, it's not telling us anything! :)You need have the following:
In
setData()
you must returnFalse
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 callinsertRows()
, either to insert at the start or at the end or the existing rows, and thensetData()
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 withinsetData()
, but I don't think that issetData()
's job anyway. It is correct that it should instead returnFalse
, and the coder must explicitly callinsertRows()
outside himself to add new rows. This is the way the model is designed to behave. That is why you are now able to sayremoved the
self.layoutChanged.emit()
call and the view keeps working!:)
-
@Carlos-Diaz
Please no more log output, it's not telling us anything! :)You need have the following:
In
setData()
you must returnFalse
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 callinsertRows()
, either to insert at the start or at the end or the existing rows, and thensetData()
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 withinsetData()
, but I don't think that issetData()
's job anyway. It is correct that it should instead returnFalse
, and the coder must explicitly callinsertRows()
outside himself to add new rows. This is the way the model is designed to behave. That is why you are now able to sayremoved the
self.layoutChanged.emit()
call and the view keeps working!:)
wrote on 23 Nov 2019, 16:06 last edited by@JonB Thanks for the help, now I need to migrate this into the final application.
I will mark the issue as solved.
1/12