Determine if QTableView has an open editor
Asked Answered
W

6

10

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?

Whitish answered 6/8, 2010 at 18:59 Comment(0)
T
2

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.

Trichosis answered 6/8, 2010 at 20:52 Comment(3)
Just a note, I think you meant mutable, not volatile.Ruthanneruthe
@Caleb - You're right. Changed -- and thanks for pointing that out.Trichosis
As already pointed out by Florian Kusche in his answer, this doesn't work because setModelData() is only called if the editing is committed, but not if it's canceled.Whitver
P
10

Just check whether the return value of

State QAbstractItemView::state () const

is

QTableView::EditingState
Pneumatics answered 13/3, 2014 at 14:4 Comment(1)
I am not sure that if its on that state is enough to determine that it is for the current cellClassieclassification
A
4

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.

Anarchy answered 29/8, 2013 at 12:1 Comment(2)
Nice. Just a few comments: better use mutable int instead of int*. Also, no need to reimplement setEditorData() and setModelData() in your provided solution, so you could have omitted them for clarity.Whitver
Yes you're right. Mutable ints would be a bit better.Anarchy
C
3

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.

Classieclassification answered 11/12, 2012 at 10:32 Comment(0)
T
2

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.

Trichosis answered 6/8, 2010 at 20:52 Comment(3)
Just a note, I think you meant mutable, not volatile.Ruthanneruthe
@Caleb - You're right. Changed -- and thanks for pointing that out.Trichosis
As already pointed out by Florian Kusche in his answer, this doesn't work because setModelData() is only called if the editing is committed, but not if it's canceled.Whitver
B
1

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
}
Bethany answered 31/12, 2016 at 8:45 Comment(0)
C
0

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);
Cyclone answered 8/9, 2016 at 3:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.