After I change one or more cell contents of my qtableview, each changed cell loses its formatting.
Code:
...
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super(TableModel, self).__init__()
self._data = data
def data(self, index, role):
if index.isValid():
if role == Qt.DisplayRole or role == Qt.EditRole:
value =self._data[index.row()][index.column()]
return str(value)
if role == Qt.TextAlignmentRole:
value = self._data[index.row()][index.column()]
if isinstance(value, int):
return Qt.AlignVCenter + Qt.AlignRight
if role == Qt.ForegroundRole:
value = self._data[index.row()][index.column()]
if (isinstance(value, int)
and value < 0):
return QtGui.QColor('red')
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.EditRole:
self._data[index.row()][index.column()] = value
self.dataChanged.emit(index, index, (Qt.DisplayRole, Qt.TextAlignmentRole, Qt.ForegroundRole,))
return True
return False
def rowCount(self, index):
return len(self._data)
def columnCount(self, index):
return len(self._data[0])
def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
How can i change the code in order to keep formatting after changing cell content?
Related
So basically this is my PandasModel:
class pandasModel(QAbstractTableModel):
def __init__(self, data, editable=False):
QAbstractTableModel.__init__(self)
self._data = data
self.editable = editable
def rowCount(self, parent=None):
return self._data.shape[0]
def columnCount(self, parnet=None):
return self._data.shape[1]
def data(self, index, role=Qt.DisplayRole):
if index.isValid():
if role == Qt.DisplayRole:
return str(self._data.iloc[index.row(), index.column()])
elif role == Qt.TextAlignmentRole:
return Qt.AlignCenter
def setData(self, index, value, role):
if role == Qt.EditRole:
self._data.iloc[index.row(), index.column()] = value
return True
return False
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self._data.columns[col]
def flags(self, index):
if self.editable:
return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
else:
return Qt.ItemIsEnabled | Qt.ItemIsSelectable
def sort(self, col, order):
"""sort table by given column number col"""
self.layoutAboutToBeChanged.emit()
self._data = self._data.sort_values(
self._data.columns[col], ascending=order == Qt.AscendingOrder)
self.layoutChanged.emit()
And this is the way I'm updating my Table when the data is changed and received from the thread using API.
Basically my other Thread runs in a while loop constantly and if the data is changed it emits the new array of data to main thread and then the data is received and that is when the data is changed.
self.Table.setModel(pandasModel(self.myDataFrame))
I only wanted to update the new data but this updates whole table, due to that the selection is also lost!
Is there other way we can do it so that the values are updated but the table doesn't reset completely and also the selections of rows to be not lost???
P.S. the data is huge so cannot just pick one and update manually I want something dynamic
I have this TreeItem:
class QJsonTreeItem(object):
def __init__(self, data, parent=None):
self._parent = parent
self._key = ""
self._value = ""
self._type = None
self._children = list()
self.itemData = data
...
def data(self, column):
if column is 0:
return self.key
elif column is 1:
return self.value
def setData(self, column, value):
if column is 0:
self.key = value
if column is 1:
self.value = value
...
def insertChildren(self, position, rows, columns):
if position < 0 or position > len(self._children):
return False
for row in range(rows):
data = [None for v in range(columns)]
item = QJsonTreeItem(data, self)
self._children.insert(position, item)
return True
...
And custom QAbstractItemModel:
class QJsonTreeModel(QAbstractItemModel):
def __init__(self, parent=None):
super(QJsonTreeModel, self).__init__(parent)
self._rootItem = QJsonTreeItem(["Key", "Value"])
self._headers = ("Key", "Value")
...
def data(self, index, role):
if not index.isValid():
return None
if role != Qt.DisplayRole and role != Qt.EditRole:
return None
item = index.internalPointer()
if role == Qt.DisplayRole or role == Qt.EditRole:
if index.column() == 0:
return item.data(index.column())
if index.column() == 1:
return item.value
return None
def getItem(self, index):
if index.isValid():
item = index.internalPointer()
if item:
return item
return self._rootItem
def setData(self, index, value, role):
if role == Qt.EditRole:
item = index.internalPointer()
item.setData(index.column(), value)
self.dataChanged.emit(index, index, [Qt.EditRole])
return True
return False
def parent(self, index):
if not index.isValid():
return QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent()
if parentItem == self._rootItem:
return QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
...
def insertRows(self, position, rows, parent, *args, **kwargs):
parentItem = self.getItem(parent)
self.beginInsertRows(parent, position, position + rows - 1)
success = parentItem.insertChildren(position, rows, self._rootItem.columnCount())
self.endInsertRows()
return success
And in my MainWindow file there is a button for adding new items in QTreeView which looks like this:
self.treeView = QTreeView()
self.model = QJsonTreeModel()
self.treeView.setModel(self.model)
...
rightClickMenu = QMenu()
actionAddItem = rightClickMenu.addAction(self.tr("Add Item"))
actionAddItem.triggered.connect(partial(self.treeAddItem))
...
def treeAddItem(self):
try:
index = self.treeView.selectionModel().currentIndex()
parent = index.parent()
if self.model.data(parent, Qt.EditRole) == None:
if not self.model.insertRow(index.row() + 1, parent):
return
for column in range(self.model.columnCount(parent)):
child = self.model.index(index.row() + 1, column, parent)
self.model.setData(child, "[No data]", Qt.EditRole)
else:
pass
except Exception as exception:
QMessageBox.about(self, "Exception", "Exception in treeAddItem() function: " + str(exception))
return
The question is can I somehow add not an "[No data]" string for QtreeView, but fo example an empty dict() or list()? As far as I understand it only adds an empty strings to the QTreeView, but my task still needs dictionaries and list. If it`s not possible my main idea is to get full tree back to dictionary and to work directly with dictionary items and then load back changed dict to tree, but it seems kinda "bad style".
Can someone help me with this task or offer another idea?
The soultion I did is this:
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.EditRole:
item = index.internalPointer()
item.setData(index.column(), value)
self.dataChanged.emit(index, index, [Qt.EditRole])
return True
if role == Qt.DisplayRole:
item = index.internalPointer()
item.setData(index.column(), dict())
self.dataChanged.emit(index, index, [Qt.EditRole])
return True
if role == Qt.ToolTipRole:
item = index.internalPointer()
item.setData(index.column(), list())
self.dataChanged.emit(index, index, [Qt.EditRole])
return True
return False
I am trying to update my QTableView after I receive a notice via pydispatcher of a change in the system. I did create the following functions
def rowCount(self, parent=None):
return len(self.m_list)
def columnCount(self, parent=None):
return len(self.table_def)
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.table_def[col]['Header']
return QVariant()
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
if index.row() >= len(self.m_list) or index.row() < 0:
return None
row = index.row()
col = index.column()
print("Called for (%s, %s, %s)" % (row, col, role))
if role == Qt.DisplayRole:
return self.table_def[index.column()]['Function'](index.row())
elif role == Qt.BackgroundRole:
batch = (index.row() // 100) % 2
if batch == 0:
return QApplication.palette().base()
return QApplication.palette().alternateBase()
else:
return None
def flags(self, index):
if not index.isValid():
return None
return Qt.ItemIsEnabled
def update_model(self, data):
print('update_model')
index_1 = self.index(0, 0)
index_2 = self.index(0, 1)
self.dataChanged.emit(index_1, index_2, [Qt.DisplayRole])
The line self.dataChanged.emit(index_1, index_2, [Qt.DisplayRole]) does not seems to do anything; i.e. data(self, index, role=Qt.DisplayRole) is not called.
If I click on the table, then data(self, index, role=Qt.DisplayRole) is called and the table update.
The fix that I have right now is to call beginResetModel() and endResetModel(). That works, but it is not how it should work.
Any idea what could be happening?
I had the same problem and I fixed it just by calling self.headerDataChanged.emit instead. So, to do that, once you change something in the table, call the following:
self.headerDataChanged.emit(Qt.Horizontal, idx1, idx2)
where self._data is your data within the class. idx1 and idx2 are the first and last indexes of changed data, respectively. Qt.Horizontal is an example and it could be vertical depending on your table content.
I have a pandas dataframe that I would like to present in a QtableView and make it editable. I have create the below model, but for some reason the output has checkboxes in every field. How can I get rid of them?
The outout looks like this:
And this this is the model that is used to make the pandas dataframe shown in a qtavleview and make it editable (I'm using PySide)
class PandasModelEditable(QtCore.QAbstractTableModel):
def __init__(self, data, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self._data = data
def rowCount(self, parent=None):
return len(self._data.values)
def columnCount(self, parent=None):
return self._data.columns.size
def data(self, index, role=QtCore.Qt.DisplayRole):
if index.isValid():
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
return unicode(self._data.iloc[index.row(), index.column()])
return unicode()
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role != QtCore.Qt.DisplayRole:
return None
if orientation == QtCore.Qt.Horizontal:
try:
return '%s' % unicode(self._data.columns.tolist()[section])
except (IndexError,):
return unicode()
elif orientation == QtCore.Qt.Vertical:
try:
return '%s' % unicode(self._data.index.tolist()[section])
except (IndexError,):
return unicode()
def flags(self, index):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \
QtCore.Qt.ItemIsEditable
def setData(self, index, value, role=QtCore.Qt.EditRole):
if index.isValid():
self._data.iloc[index.row(), index.column()] = value
if self.data(index, QtCore.Qt.DisplayRole) == value:
self.dataChanged.emit(index, index)
return True
return unicode()
Removing QtCore.Qt.ItemIsSelectable does not solve the problem as it doesn't seem to have any effet.
You are returning the wrong default values from data and setaData. The former should return None (so you could just remove the last line), whilst the latter should return False.
Is there a way to edit specific item in qtableview given the row and col value? For example, I want to increment its value every second. Here is my tablemodel. Thanks
class MyTableModel(QAbstractTableModel):
def __init__(self, datain, headerdata, parent=None, *args):
""" datain: a list of lists
headerdata: a list of strings
"""
QAbstractTableModel.__init__(self, parent, *args)
self.arraydata = datain
self.headerdata = headerdata
def rowCount(self, parent):
return len(self.arraydata)
def columnCount(self, parent):
try:
return len(self.arraydata[0])
except:
return 0
def data(self, index, role):
if not index.isValid():
return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
return QVariant(self.arraydata[index.row()][index.column()])
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return QVariant(self.headerdata[col])
return QVariant()
def sort(self, Ncol, order):
"""Sort table by given column number.
"""
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self.arraydata = sorted(self.arraydata, key=operator.itemgetter(Ncol))
if order == Qt.DescendingOrder:
self.arraydata.reverse()
self.emit(SIGNAL("layoutChanged()"))
You could increment the value directly in your model, and emit a dataChanged signal from the model.
For example, add a method like this to the model class:
def incrementData(row, column):
self.arraydata[row][column] += 1
idx = self.index(row, column)
self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"), idx, idx)