Signal model when content in view changes



  • I want the model to change QCheckState when a linked QLineEdit's content changes. I've subclassed QStyledItemDelegate and tried to connect signals for textEdited() to a method in the delegate that checks a QCheckbox in another column using setData(..,Qt::CheckStateRole).

    I realized it won't be so easy, because I only have a QModelIndex in createEditor, which gives me only a QAbstractItemModel const and then I cannot call setData().

    I've already reimplemented editorEvent(), but it is not triggered for my (persistent) editors. I feel like I have run out of options, because there's no other method that receives a non-const model. Hence I am asking before I have to break the model/view encapsulation or try to cast the const away.



  • Why don't you let the model itself do that work then in the setData method?



  • Yeah, that works when the editing is finished. AFAIK the model's setData() and the delegate's setModelData() only get called when the editor loses focus, e.g. when you click somewhere else or press enter. What I want is a checked checkbox whenever the editing started.

    Another problem with this is, that when you edit sth. then click on the checkbox, the default editorEvent() implementation of QStyledItemDelegate will just flip the checked state, that you modified in setData() yourself.

    What I ended up doing is to use the QTableView's indexWidget() method -- that always returns the correct editor for my persistent editors -- for every editor and connect it's signal to a method that looks up the QModelIndex for a specific QWidget and then calls the model's setData() method.



  • Hi kossmoboleat
    I also need to get a signal when editing of a cell starts (in my case in a QTreeView and a QListView). I like your idea to use QAbstractItemView::indexWidget() and understand that you connect an appropriate signal such as QLineEdit::textEdited() to a slot.

    • Do you establish the connection in a custom delegate? Is overriding QAbstractItemDelegate::editorEvent() suitable to connect textEdited() to a slot?
    • In which function (of your delegate) do you disconnect the signal? Or do you leave the disconnect to Qt?

    Alternatively, I believe QTreeView (and QListView) could be derived and the two virtual functions edit() and closeEditor() overridden to connect and disconnect the textEdited() of the editor widget to a slot. No custom delegate would be needed (but derived view classes).

    I appreciate to learn these details as it will help me to encounter issues that you may have solved already.
    Best
    Al_



  • Hi Al,

    I actually had to inherit from the view because you don't have a non-const model in the delegate and I wanted to change data in the model when editing in a cell started. If you don't need to do this, you could possibly do it in the delegate.

    editorEvent() might be suitable, but I'd prefer edit() as it seems to be "more logical" place. I actually didn't have to bother with that, because I'm using persistent editors and I'm connecting the signals when all the editors have to opened with openPersistentEditor().

    I left it to Qt to disconnect the signals when the editors are destroyed.

    I still don't like the solution so very much, because I have to use dynamic casts. When an editor is changed I have to try to cast it to several editor widgets and react accordingly. I've looked into the implementation of Qt and they do it the same way, so I'm not sure if there is a better solution.

    regards,
    tim



  • Hi tim

    Thanks for the clarifications. I ended up overriding QStyledItemDelegate::editorEvent() and connect the appropriate 'value is edited' signal (signal name, as you also note, depends on the editor widget) with my startEdit() signal. This works nicely if the editor widget is a QLineEdit as this has a signal textEdited(const QString&).

    For all other widgets, my views notably also use comboboxes, startEdit() is already emitted when the editor is loaded with the current value, i.e., before the user actually has modified the value. Thus, in my application the close button of the dialog is disabled too early and the Cancel and the Save buttons are enabled too early (i.e., even if the user only attempts to edit but eventually decides to keep the value as is).

    Below my code in case someone has a similar need. (Ignore the paint function: my delegate does also something completely unrelated; it crosses out deleted items that are not yet committed to the model)

    Best

    Al_

    header file
    @#ifndef QXCROSSOUTDELEGATE_H
    #define QXCROSSOUTDELEGATE_H

    #include <QStyledItemDelegate>

    class QXCrossoutDelegate : public QStyledItemDelegate{
    Q_OBJECT
    public:
    explicit QXCrossoutDelegate(QObject parent = 0);
    virtual void paint (QPainter
    painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
    virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const;
    signals:
    void startEdit() const;};

    #endif // QXCROSSOUTDELEGATE_H@

    implementation file
    @#include <QLineEdit>
    #include <QDateTimeEdit>
    #include <QDoubleSpinBox>
    #include <QSpinBox>
    #include <QComboBox>
    #include "qxcrossoutdelegate.h"

    QXCrossoutDelegate::QXCrossoutDelegate(QObject *parent) :
    QStyledItemDelegate(parent){}

    void QXCrossoutDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
    QStyleOptionViewItem newOption(option);
    if (index.model()->headerData(index.row(), Qt::Vertical, Qt::DisplayRole).toString() == QLatin1String("!"))
    newOption.font.setStrikeOut(true);
    QStyledItemDelegate::paint(painter, newOption, index);}

    QWidget* QXCrossoutDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const {
    QWidget* editorWidget = QStyledItemDelegate::createEditor(parent, option, index);
    bool ok(true);
    if (const QLineEdit* editorWidget_1 = qobject_cast<const QLineEdit*>(editorWidget))
    ok = connect(editorWidget_1, SIGNAL(textEdited(const QString&)), this, SIGNAL(startEdit()));
    /* The following widgets have no signal emitted only upon user change of value. As setEditorData will be called
    even before the user actually modifies the value, we can as well emit startEdit() already now
    else if (const QDateTimeEdit* editorWidget_1 = qobject_cast<const QDateTimeEdit*>(editorWidget))
    ok = connect(editorWidget_1, SIGNAL(dateTimeChanged(QDateTime)), this, SIGNAL(startEdit()));
    else if (const QDoubleSpinBox* editorWidget_1 = qobject_cast<const QDoubleSpinBox*>(editorWidget))
    ok = connect(editorWidget_1, SIGNAL(valueChanged(double)), this, SIGNAL(startEdit()));
    else if (const QSpinBox* editorWidget_1 = qobject_cast<const QSpinBox*>(editorWidget))
    ok = connect(editorWidget_1, SIGNAL(valueChanged(int)), this, SIGNAL(startEdit()));
    else if (const QComboBox* editorWidget_1 = qobject_cast<const QComboBox*>(editorWidget))
    ok = connect(editorWidget_1, SIGNAL(currentIndexChanged(int)), this, SIGNAL(startEdit())); */
    else emit startEdit(); // unknown editor widget, emit startEdit now: better too early than never
    Q_ASSERT(ok);
    return editorWidget;}
    @


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.