Search code examples
c++qtqtreeviewqtreewidgetqtreewidgetitem

QTreeWidget catch item editing finished with no text change


I'm developing a Qt application with QTreeWidget on Qt Designer form. User can press add-new-item button and new item will appear with default name, after that user must enter item's name.

So this is my code:

void MyFormClass::on_addNewItemButton_clicked()
{
    auto newItem = new QTreeWidgetItem({ _defaultName });
    newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
    ui->tree->addTopLevelItem(newItem);
    ui->tree->editItem(newItem);
}

In MyFormClass i also catch itemChanged signal to do some operations with newly created item and it's name:

void MyFormClass::on_tree_itemChanged(QTreeWidgetItem *item, int column)
{
    if (item->text(0).isEmpty()) {
        ...
    } else {
        ...
    }
}

And everything works fine except case when user doesn't change anything and just press Enter/left-click somewhere. This case QTreeWidget [probably] check that item has not been actually changed and do not emits proper signal.

So any ideas how i can create new item, then allow user to edit it immediately and finally catch any edit result (i.e. same with default)? Maybe using QTreeView will help?

Qt 5.4.2, C++11, Linux/Windows platforms

Bonus questions: why Qt designed this way? I mean, isn't it my affair to check whether item has changed or not? OK, signal is called itemChanged, but why there is no editFinished signal or something?


Solution

  • First i've followed @AlexanderVX advice and catched events. After user done editing in any way, QEvent::ChildRemoved will come in all cases. I wrote some code to catch this event end handle it.

    But after few days of researching i've learned some information about item delegates, which can handle user editing, so this is my solution:

    class MyEditingDelegate : public QItemDelegate
    {
        Q_OBJECT
    
    public:
        MyEditingDelegate(QObject *parent = nullptr) : QItemDelegate(parent) {}
        virtual void setModelData(QWidget *editor, 
                                  QAbstractItemModel *model, 
                                  const QModelIndex &index) const
        {
            QItemDelegate::setModelData(editor, model, index);
            if (index.column() == 0) {
                emit editingFinished(index);
            }
        }
    
    signals:
        void editingFinished(const QModelIndex &) const;
    };
    
    class MyTreeWidget : public QTreeWidget
    {
    public:
        MyTreeWidget(QWidget *parent = nullptr) : QTreeWidget(parent) {}
        QTreeWidgetItem *getItemFromIndex(const QModelIndex &index) const 
        {
            return itemFromIndex(index);
        }
    };
    
    MyFormClass::MyFormClass()
    {
        ...
    
        auto editDelegate = new MyEditingDelegate(this);
        ui->tree->setItemDelegate(editDelegate);
        connect(editDelegate, 
                &MyEditingDelegate::editingFinished, 
                [this] (const QModelIndex &index) {
            auto item = ui->tree->getItemFromIndex(index);
            onItemEditingFinished(item); // this is my handler
        });
    
        ...
    }