Question

Is there any way to determine if a QTableView has an open editor in the current cell? I need to handle the following situation:

  • A user double-clicks a cell and edits the data, but leaves the cell in the "edit" state.
  • On another part of the UI, an action is taken that changes the selected row of the underlying model.
  • Back on my view, I want to determine if the newly selected row is the same as the open row. If not, I need to take an action. (Prompt the user? Commit automatically? Revert?)

I see how to get the current item, and can get the delegate on that item, but I don't see any isEditMode() property I was hoping to find.

Can someone point me in the right direction?

Was it helpful?

Solution

Subclass your delegate so that it includes an accessor that tells you when it's editing:

void MyDelegate::setEditorData ( QWidget * editor, const QModelIndex & index ) const {
    // _isEditing  will have to be mutable because this method is const
    _isEditing = true; 
    QStyledItemDelegate::setEditorData(editor, index);
}

void MyDelegate::setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const {
    QStyledItemDelegate::setModelData(editor, model, index);
    _isEditing = false;
}

bool MyDelegate::isEditing() const { return _isEditing; }

Then you can just check the delegate to see what's going on. Alternatively and/or if you don't like the mutable, you can emit signals so you know what state the delegate is in.

OTHER TIPS

Just check whether the return value of

State QAbstractItemView::state () const

is

QTableView::EditingState

Connect to underlying model dataChanged signal

void QAbstractItemModel::dataChanged ( const QModelIndex & topLeft, const QModelIndex & bottomRight )

You can check if the cell where data has changed is the same than the currentIndex

QModelIndex QAbstractItemView::currentIndex () const

You cannot know if the current cell had an open editor straight, but can check if the view is in QAbstractItemView::EditingState

State QAbstractItemView::state () const

It should be enough to do what you want.

You can subclass QTableView in order to be able to access the state() function, which is unfortunately protected. However, I did not try that.

If you already have an QStyledItemDelegate subclass, you can use it to track whether an editor is currently open. However, you can't just use setEditorData/setModelData, because setModelData won't be called, when the user cancels editing. Instead, you can track the creation and destruction of the editor itself.

class MyItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    MyItemDelegate( QObject* parent = nullptr );
    ~MyItemDelegate();

    QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
    void setEditorData( QWidget* editor, const QModelIndex& index ) const;
    void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const;

    bool isEditorOpen() const   { return *m_editorCount > 0; }

protected:
    int* m_editorCount;

protected slots:
    void onEditorDestroyed( QObject* obj );
};

Implementation:

MyItemDelegate::MyItemDelegate( QObject* parent ) :
    QStyledItemDelegate( parent )
{
    m_editorCount = new int;
    *m_editorCount = 0;
}

MyItemDelegate::~MyItemDelegate()
{
    delete m_editorCount;
}

QWidget* MyItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
    // create an editor, can be changed as needed
    QWidget* editor = QStyledItemDelegate::createEditor( parent, option, index );

    connect( editor, SIGNAL(destroyed(QObject*)), SLOT(onEditorDestroyed(QObject*)));
    printf( "editor %p created\n", (void*) editor );
    (*m_editorCount)++;

    return editor;
}

void MyItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    ...
}

void MyItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    ...
}

void MyItemDelegate::onEditorDestroyed( QObject* obj )
{
    printf( "editor %p destroyed\n", (void*) obj );
    (*m_editorCount)--;
}

On some occasions, e.g. when moving to the next item in the tree using the cursor keys, Qt will create the new editor first and then destroy the old one. Hence, m_editorCount must be an integer instead of a bool.

Unfortunately, createEditor() is a const function. Therefore, you cannot create an int-member. Instead, create a pointer to an int and use that.

If you know the index of the item being edited, you can call indexWidget() and attempt to cast it. If it's valid, you not only know you're editing, but you also have your editor widget handy.

EditWidget *editWidget = qobject_cast<EditWidget*>(tableView->indexWidget(tableView->currentIndex()));
if(editWidget)
{
    //yep, ur editing bro
}

Here is an idea, its even helpful to get the edit/combo widget before the edit begins...

just emit a signal and consume it in the mainwindow... this is what I used one to get combo box in QTableWidget before editing...

first create a signal in ComoBoxItemDelegate...

signals:
   void OnComboEdit(QComboBox* pCombo) const;

then emit the signal in the createEditor method...

QWidget* ComboBoxItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    // Create the combobox and populate it
    QComboBox* cb = new QComboBox(parent);
    emit OnComboEdit(cb);
    return cb;
}

and in the MainWindow declare a function to receive the singal...

void MainWindow::OnComboEidt(QComboBox *pCB) const
{
    qDebug() << "Combo Eidt Singal Received";
}

Then finally in the constructor of MainWindow connect it...

ComboBoxItemDelegate* cbid = new ComboBoxItemDelegate(ui->tableWidget);
connect(cbid, &ComboBoxItemDelegate::OnComboEdit, this, &MainWindow::OnComboEidt);
ui->tableWidget->setItemDelegateForColumn(0, cbid);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top