Python Forum
[PyQt] QTableWidget cell validation
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyQt] QTableWidget cell validation
#1
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.
Reply
#2
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_())
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] QTableWidget print problem JokerSob 8 4,685 Jan-28-2022, 06:08 PM
Last Post: Axel_Erfurt
  [PyQt] How do I display the DB table I will choose from the QComboBox in QTableWidget JokerSob 2 2,273 Aug-05-2021, 03:00 PM
Last Post: JokerSob
  Displaying database info in QTableWidget thewolf 6 5,183 Apr-03-2021, 02:49 PM
Last Post: thewolf
  [PyQt] Help: check content of combobox in horizontal header of QTableWidget mart79 1 3,267 Jul-26-2020, 06:18 PM
Last Post: deanhystad
  [PyQt] Add validation (regex) to QTableWidget cells mart79 0 2,690 May-05-2020, 12:51 PM
Last Post: mart79
  [PyQt] QTableWidget stylesheet error mart79 3 6,326 Feb-26-2020, 04:54 PM
Last Post: Denni
  [PyQt] Pyqt5: How do you make a button that adds new row with data to a Qtablewidget YoshikageKira 6 6,877 Jan-02-2020, 04:32 PM
Last Post: Denni
  Printing QTableWidget to an DIN A4 page Mady 2 7,145 Dec-18-2018, 06:56 PM
Last Post: Axel_Erfurt
  [PyQt]Find item or text in Qtablewidget maziio 0 11,211 Aug-06-2018, 01:37 PM
Last Post: maziio

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020