Reputation: 311
Recommended approach for cell validation on dynamic table.
The scenario: I have a QDialog where based on different dropdown selections 1 or more tables are dynamically added. Because tables are dynamically added, the standard cell clicked signal is not enough. It only provides the row and column, and I need to know which table was clicked in addition to the row and column. More specifically, I have 2 columns with integer values. When a cell is changed in one of the columns, they must be within a valid range, and the value of the cell in the 2nd column must be >= value of the cell in the first column.
I'm fairly new to Python, but my thinking is that I need to create a class that extends the QTableWidgetItem with the additional information I need and sends a custom signal, which I can then wire up to a slot within the dialog. I've tried several variations of the following code, but can't get things quite right:
class SmartCell(QtCore.QObject):
valueChanged = QtCore.pyqtSignal(str) # Signal to be emitted when value changes.
def __init__(self, tbl, rowname, colname, value):
QtGui.QTableWidgetItem.__init__(self)
self.tbl_name = tbl
self.row_name = rowname
self.col_name = colname
# self.setText(value)
self.__value = value
@property
def value(self):
return self.__value
@value.setter
def value(self, value):
if self.__value != value:
self.__value = value
# self.setText(value)
signal = self.tbl_name + ":" + self.row_name + ":" + self.col_name + ":" + self.text()
self.valueChanged.emit(signal)
and then in the dialog, after importing the SmartCell reference as sCell:
item = sCell(obj_name, f.part_name, "start_frame", str(f.start_frame))
item.valueChanged.connect(self.frame_cell_changed)
tbl.setItem(rowcounter, 1, item)
item = sCell(obj_name, f.part_name, "end_frame", str(f.end_frame))
item.valueChanged.connect(self.frame_cell_changed)
tbl.setItem(rowcounter, 2, item)
Upvotes: 1
Views: 230
Reputation: 243887
You're getting too complicated, the task of validating what a delegate should do instead of using create a post-validation logic with the QTableWidgetItems.
import random
from PyQt4 import QtCore, QtGui
class LimistDelegate(QtGui.QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = super(LimistDelegate, self).createEditor(parent, option, index)
if index.column() in (1, 2):
editor = QtGui.QSpinBox(parent)
return editor
def setEditorData(self, editor, index):
if index.column() in (1, 2):
m = 0 if index.column() == 1 else index.sibling(index.row(), 1).data()
M = index.sibling(index.row(), 2).data() if index.column() == 1 else 360
if hasattr(m, 'toPyObject'):
m = m.toPyObject()
if hasattr(M, 'toPyObject'):
M = M.toPyObject()
editor.setMinimum(m)
editor.setMaximum(M)
super(LimistDelegate, self).setEditorData(editor, index)
def create_table():
nrows, ncols = random.randrange(3, 6), 3
table = QtGui.QTableWidget(nrows, ncols)
for r in range(nrows):
text = "description {}".format(r)
a = random.randrange(0, 180)
b = random.randrange(a, 360)
for c, val in enumerate([text, a, b]):
it = QtGui.QTableWidgetItem()
it.setData(QtCore.Qt.DisplayRole, val) # set data on item
table.setItem(r, c, it)
return table
class Widget(QtGui.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
vlay = QtGui.QVBoxLayout(self)
for _ in range(4):
table = create_table()
delegate = LimistDelegate(table) # create delegate
table.setItemDelegate(delegate) # set delegate
vlay.addWidget(table)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Upvotes: 2