PDA

View Full Version : How to implement a well behaved editor for QAbstractItem::createEditor()



spud
10th June 2015, 15:30
This is a repost of this (http://stackoverflow.com/questions/30752007/how-to-close-an-editor-created-by-a-custom-qitemdelegatecreateeditor).

I have created a custom item delegate which let's users edit a list of file paths:
11200
I have achieved this through a custom class DirEdit. Now the selected path is commited and the editor is closed when the user presses enter, but I would like to add two cases where the editor should be closed without the user having to press enter:

When the user selects a file by activating a combo box entry(by clicking or pressing return)
When the user selects a file by clicking the "ellipsis" tool button.


I have been experimenting with clearFocus() and other methods, but nothing seems to work. Below is a complete example:

#include <QtWidgets>

class DirEdit : public QWidget
{
QLineEdit* lineEdit=nullptr;
public:
DirEdit(QWidget* parent=nullptr)
: QWidget(parent)
{
new QHBoxLayout(this);
layout()->setMargin(0);
layout()->addWidget(lineEdit=new QLineEdit(this));

QCompleter *completer = new QCompleter(this);

auto model = new QDirModel(completer);
model->setFilter(QDir::AllDirs|QDir::NoDotAndDotDot);
completer->setModel(model);

lineEdit->setCompleter(completer);
connect(completer, static_cast<void (QCompleter::*)(const QString&)>(&QCompleter::activated), [this](const QString& text)
{
// >>>>>>>>>>>>>>>>>>>>>>> TODO: Make the editor close here <<<<<<<<<<<<<<<<<<<<<<<<<<<<
});

QToolButton* dotDotDot;
layout()->addWidget(dotDotDot=new QToolButton(this));
dotDotDot->setText("...");
connect(dotDotDot, &QToolButton::clicked, this, [this]()
{
QString dir = QFileDialog::getExistingDirectory(window(), "Select Directory", lineEdit->text());
if(dir!="")
{
lineEdit->setText(dir);
// >>>>>>>>>>>>>>>>>>>>>>> TODO: Make the editor close here <<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
});
setFocusProxy(lineEdit);
}
void setPath(const QString& path)
{
lineEdit->setText(path);
}
QString path()const
{
return lineEdit->text();
}
};

class MyDelegate : public QItemDelegate
{
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const
{
return new DirEdit(parent);
}

void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem& option, const QModelIndex &)const
{
editor->setGeometry(option.rect);
}

void setEditorData(QWidget *editor, const QModelIndex &index) const
{
QVariant value = index.model()->data(index, Qt::DisplayRole);

if (DirEdit *dirEdit = dynamic_cast<DirEdit *>(editor))
dirEdit->setPath(value.toString());
}

void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
if (DirEdit *dirEdit = dynamic_cast<DirEdit *>(editor))
model->setData(index, dirEdit->path());
}
};

int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QListWidget listWidget;

listWidget.setItemDelegate(new MyDelegate);

listWidget.addItem(QStandardPaths::writableLocatio n(QStandardPaths::DesktopLocation));
listWidget.addItem(QStandardPaths::writableLocatio n(QStandardPaths::MusicLocation));
listWidget.addItem(QStandardPaths::writableLocatio n(QStandardPaths::DocumentsLocation));
listWidget.addItem(QStandardPaths::writableLocatio n(QStandardPaths::DownloadLocation));

for (int i = 0; i<listWidget.count(); i++)
listWidget.item(i)->setFlags(listWidget.item(0)->flags()|Qt::ItemIsEditable);

listWidget.show();
return app.exec();
}

The only restraint is that DirEdit should be a standard element and not need to know about the Item View/Delegate framework.

thanks

Added after 1 20 minutes:

Ok, I found one solution for this myself. It consists of replacing the TODOs with
QApplication::postEvent(this, new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier));
See Stack Overflow for an explanation.