Note: I don't believe this is a duplicate of How to programmatically change/update data in Python PyQt4 TableView? as I don't want to insert new rows. I want to edit the table data.
I have changed my code below. The table holds integers in columns titled "2" and "3". When changing the value of the QSpinBox, I want the numbers in the table to get multiplied by the value from the QSpinBox programatically.
Question: How can I programmatically multiply the values in these columns by the spinbox multiplier value?
Note: if the UI doesn't exist, the model still needs to work (without this multiplier feature). Meaning; I believe that the spinbox must initiate model data edit.
import sys
try:
from PySide2 import QtWidgets
except ImportError:
from PyQt5 import QtWidgets
try:
from PySide2 import QtCore
except ImportError:
from PyQt5 import QtCore
class MyTableModel(QtCore.QAbstractTableModel):
def __init__(self, table_data, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.table_data = table_data
def rowCount(self, parent):
return len(self.table_data)
def columnCount(self, parent):
return len(self.table_data[0])
def flags(self, index):
original_flags = super(MyTableModel, self).flags(index)
return original_flags | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
item = index.internalPointer()
if item is not None:
print(item)
value = self.table_data[row][column]
return value
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
self.table_data[row][column] = value
self.dataChanged.emit(index, index)
return True
return QtCore.QAbstractTableModel.setData(self, index, value, role)
class Widget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
QtWidgets.QWidget.__init__(self, *args, **kwargs)
self.view = QtWidgets.QTableView()
self.setLayout(QtWidgets.QVBoxLayout())
self.layout().addWidget(self.view)
table_data = [['HD', '1920', '1080', 'other', 'stuff', 'here'], ['lowres', '640', '480', 'other', 'stuff', 'here']]
self.proxy_model = QtCore.QSortFilterProxyModel()
self.model = MyTableModel(table_data=table_data)
self.proxy_model.setSourceModel(self.model)
self.proxy_model.setDynamicSortFilter(True)
self.view.setModel(self.proxy_model)
self.proxy_model.dataChanged.connect(self.on_data_changed)
self.view.setSortingEnabled(True) # requires proxy model
self.view.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.view.horizontalHeader().setStretchLastSection(True)
self.view.horizontalHeader().setSectionsMovable(True)
def on_data_changed(self, _from, _to):
model = _from.model() # proxy model
model.blockSignals(True)
for index in self.view.selectionModel().selectedIndexes():
model.setData(index, _from.data())
model.blockSignals(False)
print('data was changed in table')
def change_value(self, col, multiplier):
print('Multiply the value (for all rows) in column %s with: %s' % (col, multiplier))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
m = QtWidgets.QWidget()
w = Widget()
e = QtWidgets.QSpinBox()
e.setValue(1)
e.valueChanged.connect(lambda: w.change_value(col=1, multiplier=e.value()))
e.valueChanged.connect(lambda: w.change_value(col=2, multiplier=e.value()))
l = QtWidgets.QHBoxLayout()
l.addWidget(w)
l.addWidget(e)
m.setLayout(l)
m.show()
sys.exit(app.exec_())
Am I supposed to execute self.proxy_mode.setData() and if so, how?
This is how I solved it. Not completely sure about getting the index via mapFromSource, but it works.
import sys
import copy
try:
from PySide2 import QtWidgets
except ImportError:
from PyQt5 import QtWidgets
try:
from PySide2 import QtCore
except ImportError:
from PyQt5 import QtCore
class MyTableModel(QtCore.QAbstractTableModel):
def __init__(self, table_data, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.table_data = table_data
def rowCount(self, parent):
return len(self.table_data)
def columnCount(self, parent):
return len(self.table_data[0])
def flags(self, index):
original_flags = super(MyTableModel, self).flags(index)
return original_flags | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
item = index.internalPointer()
if item is not None:
print(item)
value = self.table_data[row][column]
return value
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
self.table_data[row][column] = value
self.dataChanged.emit(index, index)
return True
return QtCore.QAbstractTableModel.setData(self, index, value, role)
class Widget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
QtWidgets.QWidget.__init__(self, *args, **kwargs)
self.view = QtWidgets.QTableView()
self.setLayout(QtWidgets.QVBoxLayout())
self.layout().addWidget(self.view)
table_data = [['HD', '1920', '1080', 'other', 'stuff', 'here'], ['lowres', '640', '480', 'other', 'stuff', 'here']]
self._original_table_data = copy.deepcopy(table_data)
self.proxy_model = QtCore.QSortFilterProxyModel()
self.model = MyTableModel(table_data=table_data)
self.proxy_model.setSourceModel(self.model)
self.proxy_model.setDynamicSortFilter(True)
self.view.setModel(self.proxy_model)
self.proxy_model.dataChanged.connect(self.on_data_changed)
self.view.setSortingEnabled(True) # requires proxy model
self.view.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.view.horizontalHeader().setStretchLastSection(True)
self.view.horizontalHeader().setSectionsMovable(True)
def on_data_changed(self, _from, _to):
model = _from.model() # proxy model
model.blockSignals(True)
for index in self.view.selectionModel().selectedIndexes():
model.setData(index, _from.data())
model.blockSignals(False)
print('data was changed in table')
def change_value(self, col, multiplier):
print('Multiply the value (for all rows) in column %s with: %s' % (col, multiplier))
index = self.proxy_model.mapFromSource(QtCore.QModelIndex())
row_count = self.proxy_model.rowCount(index)
for row in range(0, row_count):
original_value = self._original_table_data[row][col]
index = self.proxy_model.index(row, col)
self.proxy_model.setData(index, int(original_value)*int(multiplier))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
m = QtWidgets.QWidget()
w = Widget()
q = QtWidgets.QLabel('Multiplier:')
e = QtWidgets.QSpinBox()
e.setValue(1)
e.valueChanged.connect(lambda: w.change_value(col=1, multiplier=e.value()))
e.valueChanged.connect(lambda: w.change_value(col=2, multiplier=e.value()))
l = QtWidgets.QHBoxLayout()
l.addWidget(w)
l.addWidget(q)
l.addWidget(e)
m.setLayout(l)
m.show()
sys.exit(app.exec_())
Related
This code I have taken from https://bravo.hatenablog.jp/entry/2016/01/18/093059 .
In this I want to add header "Quantity" at run time if needed.
Before creating table it should ask for how many headers? If user supply 4 then create table with "Quantity" headers also. Otherwise create default headers (Three).
I have tried it but not found any solution.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Model(QAbstractItemModel):
headers = "Toppings", "Udon/Soba", "Hot/Cold"
def __init__(self, parent=None):
super(Model, self).__init__(parent)
self.items = []
def index(self, row, column, parent=QModelIndex()):
return self.createIndex(row, column, None)
def parent(self, child):
return QModelIndex()
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return len(self.headers)
def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
try:
return self.items[index.row()][index.column()]
except:
return
return
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role != Qt.DisplayRole:
return
if orientation == Qt.Horizontal:
return self.headers[section]
def addRow(self, topping, menkind, hotcold):
self.beginInsertRows(QModelIndex(), len(self.items), 1)
self.items.append([topping, menkind, hotcold])
self.endInsertRows()
def removeRows(self, rowIndexes):
for row in sorted(rowIndexes, reverse=True):
self.beginRemoveRows(QModelIndex(), row, row + 1)
del self.items[row]
self.endRemoveRows()
def flags(self, index):
return super(Model, self).flags(index) | Qt.ItemIsEditable
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.EditRole:
self.items[index.row()][index.column()] = value
return True
return False
class View(QTreeView):
def __init__(self, parent=None):
super(View, self).__init__(parent)
self.setItemsExpandable(False)
self.setIndentation(0)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
def drawBranches(self, painter, rect, index):
return
class InputWidget(QWidget):
def __init__(self, parent=None):
super(InputWidget, self).__init__(parent)
layout = QVBoxLayout()
toppings = ("Kitsune", "Tanuki", "Tempura", "Tsukimi", "Meat", "Curry")
self.toppingInput = QComboBox()
for topping in toppings:
self.toppingInput.addItem(topping)
layout.addWidget(self.toppingInput)
self.bgrp = QGroupBox()
udon = QRadioButton('Udon')
udon.setChecked(True)
soba = QRadioButton('Soba')
btnlayout = QHBoxLayout()
btnlayout.addWidget(udon)
btnlayout.addWidget(soba)
self.bgrp.setLayout(btnlayout)
layout.addWidget(self.bgrp)
self.udonsoba = udon, soba
self.bgrp_temp = QGroupBox()
hot = QRadioButton('Warm')
hot.setChecked(True)
cold = QRadioButton('Cold')
btnlayout_temp = QHBoxLayout()
btnlayout_temp.addWidget(hot)
btnlayout_temp.addWidget(cold)
self.bgrp_temp.setLayout(btnlayout_temp)
layout.addWidget(self.bgrp_temp)
self.hotcold = hot, cold
self.addButton = QPushButton('OK')
layout.addWidget(self.addButton)
layout.addStretch()
self.setLayout(layout)
def values(self):
topping = self.toppingInput.currentText()
udonsoba = '?'
for btn in self.udonsoba:
if btn.isChecked():
udonsoba = btn.text()
break
hotcold = '?'
for btn in self.hotcold:
if btn.isChecked():
hotcold = btn.text()
break
return topping, udonsoba, hotcold
class Delegate(QStyledItemDelegate):
def __init__(self, parent=None):
super(Delegate, self).__init__(parent)
def createEditor(self, parent, option, index):
return QLineEdit(parent)
def setEditorData(self, editor, index):
value = index.model().data(index, Qt.DisplayRole)
editor.setText(value)
def setModelData(self, editor, model, index):
model.setData(index, editor.text())
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.view = View(self)
self.model = Model(self)
self.view.setModel(self.model)
self.view.setItemDelegate(Delegate())
self.setCentralWidget(self.view)
self.inputWidget = InputWidget()
self.inputWidget.addButton.clicked.connect(self.addItem)
self.addDock = QDockWidget('Input', self)
self.addDock.setWidget(self.inputWidget)
self.addDock.setAllowedAreas(Qt.AllDockWidgetAreas)
self.addDockWidget(Qt.RightDockWidgetArea, self.addDock)
self.addDock.hide()
toolBar = QToolBar()
self.addToolBar(toolBar)
delButton = QPushButton('Delete')
delButton.clicked.connect(self.removeItems)
toolBar.addWidget(delButton)
self.addButton = QPushButton('Add')
self.addButton.clicked.connect(self.addDock.show)
toolBar.addWidget(self.addButton)
def addItem(self):
self.model.addRow(*self.inputWidget.values())
def selectedRows(self):
rows = []
for index in self.view.selectedIndexes():
if index.column() == 0:
rows.append(index.row())
return rows
def removeItems(self):
self.model.removeRows(self.selectedRows())
def main():
app = QApplication(sys.argv)
w = MainWindow()
w.show()
w.raise_()
app.exec_()
if __name__ == '__main__':
main()
I wish to add a checkbox to a cell with text in my QTableView:
when a cell is not activated we should see a text and a greyed out checkbox, like this
when we open a delegate editor, we should be able to edit text and to change state of a checkbox
Just for info - further I was planning to do next: when a checkbox is activated - a cell will reject signal for updating text in it, and if checkbox is unchecked - text will be updated when special signal is emitted.
I understand that I should subclass QStyledItemDelegate and re-implement paint method, but as far as I am a beginner I found it difficult to figure out this task.
You can use this example to write and test a code:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import pickle
class Mainwindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.setCentralWidget(self.table)
self.list = ['item_1', 'item_2','item_3']
self.data = [
[1, 'Blocks γ=500 GOST 31359-2007', self.list[0], 0.18, 0.22],
[2, 'Blocks γ=600 GOST 31359-2008', self.list[0], 0.25, 0.27],
[3, 'Insulation', self.list[0], 0.041, 0.042],
[3, 'Insulation', self.list[0], 0.041, 0.042]
]
self.model = Materials(self.data)
self.table.setModel(self.model)
self.table.setSelectionBehavior(self.table.SelectRows)
self.table.setSelectionMode(self.table.SingleSelection)
for row in range(len(self.model.materials)):
index = self.table.model().index(row,2)
self.table.setIndexWidget(index, self.setting_combobox(index))
def setting_combobox(self, index):
widget = QtWidgets.QComboBox()
list = self.list
widget.addItems(list)
widget.setCurrentIndex(0)
widget.currentTextChanged.connect(lambda value: self.model.setData(index, value))
return widget
class Materials(QtCore.QAbstractTableModel):
def __init__(self, materials = [[]], parent = None):
super(Materials, self).__init__()
self.materials = materials
def rowCount(self, parent):
return len(self.materials)
def columnCount(self, parent):
return len(self.materials[0])
def data(self, index, role):
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.FontRole:
if index.column() == 0:
boldfont = QtGui.QFont()
boldfont.setBold(True)
return boldfont
def setData(self, index, value, role = QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
self.materials[row][column] = value
self.dataChanged.emit(index, index)
return True
return False
def flags(self, index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
if __name__ == '__main__':
app = QtWidgets.QApplication([])
application = Mainwindow()
application.show()
sys.exit(app.exec())
A possible solution is to enable the role Qt.CheckStateRole and modify the position of the checkbox using the delegate. For this, the model must also be modified to store the state of the checkbox.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class CustomDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
value = index.data(QtCore.Qt.CheckStateRole)
if value is None:
model = index.model()
model.setData(index, QtCore.Qt.Unchecked, QtCore.Qt.CheckStateRole)
super().initStyleOption(option, index)
option.direction = QtCore.Qt.RightToLeft
option.displayAlignment = QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter
class Mainwindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.setCentralWidget(self.table)
self.list = ["item_1", "item_2", "item_3"]
self.data = [
[1, "Blocks γ=500 GOST 31359-2007", self.list[0], 0.18, 0.22],
[2, "Blocks γ=600 GOST 31359-2008", self.list[0], 0.25, 0.27],
[3, "Insulation", self.list[0], 0.041, 0.042],
[3, "Insulation", self.list[0], 0.041, 0.042],
]
self.model = Materials(self.data)
self.table.setModel(self.model)
self.table.setSelectionBehavior(self.table.SelectRows)
self.table.setSelectionMode(self.table.SingleSelection)
for row in range(len(self.model.materials)):
index = self.table.model().index(row, 2)
self.table.setIndexWidget(index, self.setting_combobox(index))
delegate = CustomDelegate(self.table)
self.table.setItemDelegateForColumn(4, delegate)
self.resize(640, 480)
def setting_combobox(self, index):
widget = QtWidgets.QComboBox()
list = self.list
widget.addItems(list)
widget.setCurrentIndex(0)
widget.currentTextChanged.connect(
lambda value: self.model.setData(index, value)
)
return widget
class Materials(QtCore.QAbstractTableModel):
def __init__(self, materials=[[]], parent=None):
super(Materials, self).__init__()
self.materials = materials
self.check_states = dict()
def rowCount(self, parent):
return len(self.materials)
def columnCount(self, parent):
return len(self.materials[0])
def data(self, index, role):
if role == QtCore.Qt.DisplayRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
value = self.materials[row][column]
return value
if role == QtCore.Qt.FontRole:
if index.column() == 0:
boldfont = QtGui.QFont()
boldfont.setBold(True)
return boldfont
if role == QtCore.Qt.CheckStateRole:
value = self.check_states.get(QtCore.QPersistentModelIndex(index))
if value is not None:
return value
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
self.materials[row][column] = value
self.dataChanged.emit(index, index, (role,))
return True
if role == QtCore.Qt.CheckStateRole:
self.check_states[QtCore.QPersistentModelIndex(index)] = value
self.dataChanged.emit(index, index, (role,))
return True
return False
def flags(self, index):
return (
QtCore.Qt.ItemIsEditable
| QtCore.Qt.ItemIsEnabled
| QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsUserCheckable
)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
application = Mainwindow()
application.show()
sys.exit(app.exec())
So i'm writing an application and using QTableView with QAbstractTableModel to display my data. In each row a checkbox is placed in the first column and when it is checked or unchecked, i want a pyqtsignal CheckBoxValue be emitted to MyTable. But it seems that the connected function self.checkboxchecked in MyTable is not called. Although I used to use pyqtSignal several times without any issue, i'm stuck here and couldn't solve it out.
Thank you all for spending time taking care of my question!
import operator # used for sorting
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import QtGui, QtCore
class MyTable(QWidget):
Checkboxcheckvalue = pyqtSignal(bool)
Rowselected = pyqtSignal(int)
doubleClicked = pyqtSignal(int)
def __init__(self, *args):
QWidget.__init__(self, *args)
# setGeometry(x_pos, y_pos, width, height)
# self.setGeometry(70, 150, 1326, 582)
self.dataList = []
self.header = []
self.currentrow = []
self.setWindowTitle("Click on the header to sort table")
self.table_model = MyTableModel(self, self.dataList, self.header)
self.table_view = QTableView()
#self.table_view.setSelectionMode(QAbstractItemView.SingleSelection)
self.table_view.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
self.table_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
# bind cell click to a method reference
self.table_view.clicked.connect(self.showSelection)
self.table_view.clicked.connect(self.selectRow)
self.table_view.doubleClicked.connect(self.doubleclickedaction)
self.table_model.CheckBoxValue.connect(self.checkboxchecked)
self.table_view.setModel(self.table_model)
# enable sorting
self.table_view.setSortingEnabled(True)
layout = QVBoxLayout(self)
layout.addWidget(self.table_view)
self.setLayout(layout)
def update_model(self):
self.table_model = MyTableModel(self, self.dataList, self.header)
self.table_view.setModel(self.table_model)
self.table_view.update()
def findLabelName(self,rowindex):
return self.dataList[rowindex][-1]
def doubleclickedaction(self,index):
self.doubleClicked.emit(index.row())
self.currentrow = index.row()
print ('doubleclicked',self.findLabelName(index.row()))
def checkboxchecked(self,value):
print ('table checkboxchecked',self.currentrow,value)
# self.currentrow = index.row()
if value == True:
self.Checkboxcheckvalue.emit(True)
else:
self.Checkboxcheckvalue.emit(False)
def selectedLabel(self,index):
return self.findLabelName(index.row())
def showSelection(self, item):
cellContent = item.data()
# print(cellContent) # test
sf = "You clicked on {}".format(cellContent)
# display in title bar for convenience
self.setWindowTitle(sf)
def selectRow(self, index):
self.Rowselected.emit(index.row())
self.currentrow = index.row()
print("current row is %d", index.row())
pass
class MyTableModel(QAbstractTableModel):
"""
keep the method names
they are an integral part of the model
"""
CheckBoxValue = pyqtSignal(bool)
def __init__(self, parent, mylist, header, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.mylist = mylist
self.header = header
def setDataList(self, mylist):
self.mylist = mylist
self.layoutAboutToBeChanged.emit()
self.dataChanged.emit(self.createIndex(0, 0), self.createIndex(self.rowCount(0), self.columnCount(0)))
self.layoutChanged.emit()
def rowCount(self, parent):
if self.mylist != []:
return len(self.mylist)
else:
return 0
def columnCount(self, parent):
if self.mylist != []:
return len(self.mylist[0])-1
else:
return 0
def data(self, index, role):
if not index.isValid():
return None
if (index.column() == 0):
value = self.mylist[index.row()][index.column()].text()
else:
value = self.mylist[index.row()][index.column()]
if role == QtCore.Qt.EditRole:
return value
elif role == QtCore.Qt.DisplayRole:
return value
elif role == QtCore.Qt.CheckStateRole:
if index.column() == 0:
if self.mylist[index.row()][index.column()].isChecked():
return QtCore.Qt.Checked
else:
return QtCore.Qt.Unchecked
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.header[col]
return None
def sort(self, col, order):
"""sort table by given column number col"""
if col != 0:
pass
else:
col = -1
self.emit(SIGNAL("layoutAboutToBeChanged()"))
self.mylist = sorted(self.mylist, key=operator.itemgetter(col))
if order == Qt.DescendingOrder:
self.mylist.reverse()
self.emit(SIGNAL("layoutChanged()"))
print 'sorted'
def flags(self, index):
if not index.isValid():
return None
if index.column() == 0:
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsUserCheckable
else:
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def setData(self, index, value, role):
if not index.isValid():
return False
if role == QtCore.Qt.CheckStateRole and index.column() == 0:
if value == QtCore.Qt.Checked:
self.mylist[index.row()][index.column()].setChecked(True)
print('checked',index.row())
self.CheckBoxValue.emit(True)
else:
self.mylist[index.row()][index.column()].setChecked(False)
print('unchecked',index.row())
self.CheckBoxValue.emit(False)
self.dataChanged.emit(index, index)
return True
if __name__ == '__main__':
app = QApplication([])
header = ['name', 'type', 'segment', 'exit', 'entry']
dataList = [
[QtGui.QCheckBox('line9'), 'line', '058176', '01', '1705','line9'],
[QtGui.QCheckBox('line3'), 'line', '058176', '02', '1705','line3'],
[QtGui.QCheckBox('line6'), 'line', '058176', '03', '1705','line6'],
[QtGui.QCheckBox('line1'), 'line', '058176', '04', '1705','line'],
[QtGui.QCheckBox('line4'), 'line', '058176', '01', '1705','line4'],
[QtGui.QCheckBox('area4'), 'area', '058176', '02', '1705','area4'],
[QtGui.QCheckBox('area2'), 'area', '058176', '02', '1705','area2'],
[QtGui.QCheckBox('area8'), 'area', '058176', '01', '1705','area8'],
]
win = MyTable()
win.dataList = dataList
win.header = header
win.update_model()
win.show()
app.exec_()
In your case you have created a table_model in the MyTable constructor, and with that object you have made the connection, but later in the update_model method you have created a new model so the previous model has been deleted and its connections as well.
def update_model(self):
self.table_model = MyTableModel(self, self.dataList, self.header)
self.table_view.setModel(self.table_model)
self.table_model.CheckBoxValue.connect(self.checkboxchecked)
self.table_view.update()
Although I prefer to update the model, instead of creating a new model for it, I would create a method to update the data:
class MyTableModel(QAbstractTableModel):
"""
keep the method names
they are an integral part of the model
"""
CheckBoxValue = pyqtSignal(bool)
def __init__(self, parent, mylist, header, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.mylist = mylist
self.header = header
def update_model(self, mylist, header):
self.beginResetModel()
self.mylist = mylist
self.header = header
self.endResetModel()
[...]
and in the update_model of the Table:
class MyTable(QWidget):
[...]
def update_model(self):
self.table_model.update_model(self.dataList, self.header)
I would like to modify any cell (except header) within given QTableView. Here below is my original code that does not allow for any changes:
import sys
import csv
from datetime import datetime, timedelta
import calendar
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sqlite3
from pandas import DataFrame
class TBWindow(QMainWindow):
def __init__(self, parent=None):
super(TBWindow, self).__init__(parent)
sql = "select * from stock"
args = []
conn = sqlite3.connect('DataBase.db')
c = conn.cursor()
c.execute(sql,args)
data = c.fetchall()
names = list(map(lambda x: x[0], c.description))
c.close()
conn.close()
data = DataFrame(data, columns = names)
MenuBar = self.menuBar()
saveFile = QAction("&Save File", self)
saveFile.setShortcut("Ctrl+S")
saveFile.setStatusTip('Save File')
saveFile.triggered.connect(self.file_save)
MenuBar.addAction(saveFile)
self.setWindowTitle('Aplikace Princezna Pampeliska')
self.centralwidget = QWidget(self)
self.lineEdit = QLineEdit(self.centralwidget)
self.view = QTableView(self.centralwidget)
self.comboBox = QComboBox(self.centralwidget)
self.label = QLabel(self.centralwidget)
self.gridLayout = QGridLayout(self.centralwidget)
self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
self.gridLayout.addWidget(self.view, 1, 0, 1, 3)
self.gridLayout.addWidget(self.comboBox, 0, 2, 1, 1)
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.setCentralWidget(self.centralwidget)
self.label.setText("Filter")
self.model = PandasModel(data)
self.proxy = QSortFilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.view.setModel(self.proxy)
for column in range(self.view.horizontalHeader().count()):
self.view.resizeColumnToContents(column)
for i in (0,1,3):
self.view.horizontalHeader().setSectionResizeMode(i, QHeaderView.Stretch)
self.comboBox.addItems(list(data.columns.values))
self.lineEdit.textChanged.connect(self.on_lineEdit_textChanged)
self.comboBox.currentIndexChanged.connect(self.on_comboBox_currentIndexChanged)
self.horizontalHeader = self.view.horizontalHeader()
def file_save(self):
newModel = self.view.model()
data = []
for row in range(newModel.rowCount()):
rowRes = []
for column in range(newModel.columnCount()):
index = newModel.index(row, column)
item = newModel.data(index)
if item != '':
rowRes.append(item)
data.append(rowRes)
dataFrame = DataFrame(data)
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
fileName, fEnd = QFileDialog.getSaveFileName(self, "Save File", "", ".csv")
if fileName:
address = fileName+fEnd
dataFrame.to_csv(address, index=False, header=False)
#pyqtSlot(str)
def on_lineEdit_textChanged(self, text):
search = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp)
self.proxy.setFilterRegExp(search)
#pyqtSlot(int)
def on_comboBox_currentIndexChanged(self, index):
self.proxy.setFilterKeyColumn(index)
class PandasModel(QAbstractTableModel):
def __init__(self, data, parent=None):
QAbstractTableModel.__init__(self, parent)
self._data = data
def rowCount(self, parent=None):
return self._data.shape[0]
def columnCount(self, parent=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()])
return None
def headerData(self, col, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self._data.columns[col]
return None
if __name__ == '__main__':
app = QApplication(sys.argv)
main = TBWindow()
main.showMaximized()
sys.exit(app.exec_())
I have read some articles about modifying cells within QTableView and found out that one needs to modify the model not the view. Hence the addition of this bit of code makes the cells editable:
def flags(self, index):
flags = super(self.__class__,self).flags(index)
flags |= Qt.ItemIsEditable
flags |= Qt.ItemIsSelectable
flags |= Qt.ItemIsEnabled
flags |= Qt.ItemIsDragEnabled
flags |= Qt.ItemIsDropEnabled
return flags
However, the changes do not persist, they disappear right after I click elsewhere. I tried to add function setData() in this manner with no success:
def setData(self, index, value, role=Qt.EditRole):
row = index.row()
col = index.column()
self._data[row][name] = value
self.emit(SIGNAL('dataChanged()'))
return True
The program exits with some key error. Any suggestions what should be changed? Thanks!
Your code has 2 errors:
the first is caused by a bad programming practice: you should not call the variables with names that you used with other variables, functions or classes. In your case flags is the name of the method and therefore you should not use it in the name of the variable:
def flags(self, index):
fl = super(self.__class__,self).flags(index)
fl |= Qt.ItemIsEditable
fl |= Qt.ItemIsSelectable
fl |= Qt.ItemIsEnabled
fl |= Qt.ItemIsDragEnabled
fl |= Qt.ItemIsDropEnabled
return fl
The second error is caused because you are not using the proper method to access and assign the data in pandas, this must be done through the iloc method. Another problem is that you are using a valid method for PyQt4 to notify the changes, you must use the new syntax plus the dataChanged signal to add new fields in Qt5 as you can see in the docs:
def setData(self, index, value, role=Qt.EditRole):
if index.isValid():
row = index.row()
col = index.column()
self._data.iloc[row][col] = float(value)
self.dataChanged.emit(index, index, (Qt.DisplayRole, ))
return True
return False
Plus:
I have also previously implemented a class that creates a model using pandas as you can see in the question How to display a Pandas data frame with PyQt5
I want to make the first column with checkbox, and get the check status of that, how can I do that? I override the flag(), but it seems do not work, I am sure where is the problem?
And I got this links, but it does work for my code either. And I don't want to use delegate, because it is too complicated.
data = [['00','01','02'],
['10','11','12'],
['20','21','22']]
class MainWindow(QWidget):
def __init__(self, parent=None, *args):
super(MainWindow, self).__init__(parent)
clipTableWidget = QTableWidget()
self.model = TModel(data, self)
clipTableView = QTableView()
clipTableView.setModel(self.model)
layout = QVBoxLayout()
layout.addWidget(clipTableView)
self.setLayout(layout)
class TModel(QAbstractTableModel):
def __init__(self, datain, parent=None):
super(TModel, self).__init__(parent)
self.arraydata = datain
def rowCount(self, parent=QModelIndex()):
return len(self.arraydata)
def columnCount(self, parent=QModelIndex()):
return len(self.arraydata[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 flags(self, index):
if not index.isValid():
return QVariant()
elif index.column() == 1:
return Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsUserCheckable
return QVariant()
Ok here is the solution to your problem. The reason Why your flags weren't working is because those flags dont work on text , they are meant for QItems .
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class MainWindow(QWidget):
def __init__(self, parent=None, *args):
super(MainWindow, self).__init__(parent)
data = [['00','01','02'],
['10','11','12'],
['20','21','22']]
clipTableWidget = QTableWidget()
self.model = QStandardItemModel(self)
clipTableView = QTableView()
count1=0
for row in data:
count2 = 0
for column in row:
if count2 == 0:
item = QStandardItem(column)
item.setCheckable(True)
item.setCheckState(False)
item.setFlags(Qt.ItemIsUserCheckable| Qt.ItemIsEnabled)
self.model.setItem(count1,count2,item)
count2+=1
else:
item = QStandardItem(column)
self.model.setItem(count1,count2,item)
count2+=1
count1+=1
clipTableView.setModel(self.model)
layout = QVBoxLayout()
layout.addWidget(clipTableView)
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()