Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to apply validation on cells in a QTableWidget?



  • I'm trying to program two types of validation in different columns of a QTableWidget:

    1. any string without quotes, up to 32 characters
    2. any integer, up to a maximum size

    I've looked for information on validating cells in a QTableWidget, but I haven't found anything comprehensive. I understand that this maybe possible with a delegate, but I'm not sure how that works. Similarly with regular expressions. I'm looking for some pointers.



  • @Guerrian
    If you Google "validation on cells in a QTableWidget" there are several hits. Would that be a good start?



  • @JonB
    There doesn't appear to be whole lot about this on Google.





  • @JonB OK it's of some use, but I'm using C++ not Python.


  • Lifetime Qt Champion

    Hi,

    Use the doc Luke.

    More seriously, the example linked can server as a base for number 2.
    And for number 1, return a QLineEdit with a regular expression validator that allows the chars you want.



  • @Guerrian said in How to apply validation on cells in a QTableWidget?:

    @JonB OK it's of some use, but I'm using C++ not Python.

    Just so you know: I have to program with Qt using Python/PyQt, and about 99% of all Qt example code/web posts are for C++. It really is not hard to read either C++ or Python examples and if necessary come up with the equivalent in the opposite language. It's the principles of what to do in Qt that's important, the few lines of code are very easily translatable from one language to another.



  • @SGaist said in How to apply validation on cells in a QTableWidget?:

    QLineEdit

    For 1 here is what I have so far:

    void MainWindow::addSub(int row, int col)
    {
        auto *edit = new QLineEdit;
        QRegExp regExp("/^[^'\"]*$/");
        auto *validator = new QRegExpValidator(regExp, this);
        edit->setValidator(validator);
        auto item = new QTableWidgetItem();
        ui->tableWidget->setItem(row, col, item);
    }
    
    void MainWindow::addRow(int &last)
    {
        last = ui->tableWidget->rowCount();
        ui->tableWidget->insertRow(last);
        addSub(last, 0);
    }
    

    How do I actually connect the QLineEdit object called "edit" to the QTableWidget object?


  • Lifetime Qt Champion

    @Guerrian
    Hi
    you cannot connect external QLineEdit.
    You must do as in example and make delegate.
    http://doc.qt.io/qt-5/qtwidgets-itemviews-spinboxdelegate-example.html
    However, instead of spin box, you returns you LineEdit with validator ( as code is now)
    in CreateEditor.
    Its important to read about how delegates works.
    http://doc.qt.io/qt-5/qstyleditemdelegate.html



  • @mrjj Do I have to use a model / view based architecture (QTableView) to go down this path?


  • Lifetime Qt Champion

    @Guerrian
    no, it works the same via setItemDelegateForColumn and friends.
    The *Widgets version are also views, but comes wrapped up with own internal model and
    its meant for convenience.
    However, in the long run, you might be more happy with *Views.


  • Lifetime Qt Champion

    To add to @mrjj, use QRegularExpression and not QRegExp, that one is deprecated.



  • When I add the delegate to the QTableWidget the cells disappear i.e. there is no text editor in the table. I assume this is because I have not implemented a paint method for the delegate. I do no know how to do this:

    class TextDelegate : public QStyledItemDelegate
    {
        Q_OBJECT
    public:
        TextDelegate(QObject *parent = 0);
        QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const override;
        void setEditorData(QWidget *editor, const QModelIndex &index) const override;
        void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
        void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    };
    
    TextDelegate::TextDelegate(QObject *parent): QStyledItemDelegate(parent)
    {
    }
    
    QWidget *TextDelegate::createEditor(QWidget *parent,
        const QStyleOptionViewItem &,
        const QModelIndex &) const
    {
        QLineEdit *editor = new QLineEdit(parent);
        editor->setFrame(false);
        QRegularExpression regExp("/^[^'\"]*$/");
        auto *validator = new QRegularExpressionValidator(regExp, parent);
        editor->setValidator(validator);
        return editor;
    }
    
    void TextDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
    {
        QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
        lineEdit->setText("test value");
    }
    
    void TextDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
    {
    }
    
    void TextDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        editor->setGeometry(option.rect);
    }
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        ui->tableWidget->setHorizontalHeaderLabels(QStringList() << tr("Name"));
        TextDelegate delegate;
        ui->tableWidget->setItemDelegate(&delegate);
    }
    

  • Lifetime Qt Champion

    Hi
    Do you mean when not in edit mode ?



  • @mrjj I mean the cells have disappeared from the screen in all modes. There is nothing in the table except for the column and row text.

    0_1526680347205_Qt Table.jpg


  • Lifetime Qt Champion

    @Guerrian
    Hi
    There is a natural explanation to that.
    You delegate runs out of scope and gets deleted. :)

    TextDelegate delegate; // local var, wont survive the constructor
    ui->tableWidget->setItemDelegate(&delegate);
    

    do like this
    ui->tableWidget->setItemDelegate(new TextDelegate(this));

    and it will draw as expected.



  • @mrjj Nice one. I got further with this, but two problems still remain:

    1. The editor still allows quotes in the input.
    2. The text in the QTableWidget is not being updated when I leave the cell.

    In the case of 2 I added a debug line, but the function doesn't get called:

    void TextDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
    {
        QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
        QString value = lineEdit->text();
        qDebug() << "value: " << value;
        model->setData(index, value, Qt::EditRole);
    }
    

    This is what my createEditor function looks like now:

    QWidget *TextDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const
    {
        QLineEdit *lineEdit = new QLineEdit(parent);
        lineEdit->setFrame(false);
        QRegularExpression regExp("/^[^'\"]*$/");
        QRegularExpressionValidator *validator = new QRegularExpressionValidator(regExp, parent);
        lineEdit->setValidator(validator);
        QSignalMapper *mapper = new QSignalMapper(lineEdit);
        mapper->setMapping(lineEdit, 0);
        QObject::connect(lineEdit, SIGNAL(textEdited(bool)), mapper, SLOT(map()));
        QObject::connect(mapper, SIGNAL(mapped(QWidget *)), this, SIGNAL(commitData(QWidget *)));
        return lineEdit;
    }
    

    I'm not sure about the id parameter in the mapping.


  • Lifetime Qt Champion

    What are you doing with that QSignalMapper ?

    By the way, use qobject_cast and not static_case with QObject based classes.



  • @SGaist said in How to apply validation on cells in a QTableWidget?:

    What are you doing with that QSignalMapper ?

    I'm not sure, this is all new to me.

    By the way, use qobject_cast and not static_case with QObject based classes.

    Thanks


  • Lifetime Qt Champion

    In that case, don't use it. Follow the SpinBox Delegate example as suggested and just replace the QSpinBox editor by the QLineEdit + QRegularExpressionValidator. And IIRC, you even just have to re-implement only the createEditor method



  • @SGaist
    OK, but why is my setModelData not being executed?


  • Lifetime Qt Champion

    Did you remove the mapper ?
    Are you sure that your editor is used in the first place ?



  • @SGaist said in How to apply validation on cells in a QTableWidget?:

    Did you remove the mapper ?

    Yes.

    Are you sure that your editor is used in the first place ?

    Well yes, because createEditor is called when I click on the QTableWidget to edit a cell.


  • Lifetime Qt Champion

    Can you show your current call and a minimal QTableWidget setup that uses it ?



  • @Guerrian said in How to apply validation on cells in a QTableWidget?:

    The editor still allows quotes in the input.

    That's because your regular expression is wrongly escaped, raw string literals are a blessing in cases like this one: QRegularExpression regExp(QStringLiteral(R"**(^[^'\"]*$)**"));

    The text in the QTableWidget is not being updated when I leave the cell.

    Make sure you use override in the declaration of setModelData to avoid the usual pitfall.



  • @VRonin
    After I corrected the regular expression, my code worked.


Log in to reply