![]() |
[PyQt] QTableWidget cell validation - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: [PyQt] QTableWidget cell validation (/thread-15472.html) |
QTableWidget cell validation - littleGreenDude - Jan-18-2019 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) Am I on the right path? Any guidance is greatly appreciated. RE: QTableWidget cell validation - littleGreenDude - Jan-18-2019 It turns out I was heading in the wrong direction. Delegates work nicely: 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_()) |