Slot called twice qt - c++

I have an editable list view inside a dock widget. I wanted to keep the track of the data before the user edits and the data after the user edits. The complete concerning code is:
void MainWindow :: createDock()
{
//initialize dockWidget
QDockWidget *dock = new QDockWidget("Tags", this);
dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
dock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
//widget to store all widgets placed inside dock because dock cannot set layout but can set widget
QWidget *tags = new QWidget(dock);
//initiazlize treeViewModel
listViewModel = new QSqlTableModel(this);
listViewModel->setTable("tags");
listViewModel->select();
listViewModel->setHeaderData(0, Qt::Horizontal, "Tags");
//set the model for treeView
listView = new QListView(dock);
listView->setModel(listViewModel);
connect(listView, &QListView::doubleClicked, this, &MainWindow::onListViewDoubleClicked, Qt::UniqueConnection);
connect(listViewModel, &QSqlTableModel::dataChanged, this, &MainWindow::onLVDataChanged, Qt::UniqueConnection);
//add treeView to the dock
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(listView);
tags->setLayout(layout);
//add the dock widget to the main window and show it
dock->setWidget(tags);
this->addDockWidget(Qt::LeftDockWidgetArea, dock);
//dock->show();
}
void MainWindow :: onLVDataChanged(const QModelIndex& index, const QModelIndex& index2, const QVector<int> & roles)
{
QMetaMethod metaMethod = sender()->metaObject()->method(senderSignalIndex());
QMessageBox::information(this, "", metaMethod.name());
afterUpdate = index.data().toString();
//do somethings
beforeUpdate = "";
afterUpdate = "";
}
void MainWindow :: onListViewDoubleClicked(const QModelIndex &index)
{
QMetaMethod metaMethod = sender()->metaObject()->method(senderSignalIndex());
QMessageBox::information(this, "", metaMethod.name());
beforeUpdate = index.data().toString();
}
I do this:
I double click an item so as to edit it. The onDoubleClick() is called only once (seen becuase of QMessageBox). I add a space to the data present (in my case it was "fiction", i changed it to "fiction "). But, after I press enter, dataChanged() is called twice (again seen through QMessageBox).
I don't emit the signal explicitly. It is emitted only by model.

The problem is caused by the editing strategy, by default it is QSqlTableModel::OnRowChange, this expects the row to be changed emitting a signal to update the item and another to update the entire row, that can be easily seen if we use the following:
void MainWindow::onListViewDoubleClicked(const QModelIndex &index)
{
qDebug()<<__FUNCTION__<<index;
}
void MainWindow::onLVDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
qDebug()<<__FUNCTION__<<topLeft<<bottomRight<<roles<<topLeft.data();
}
Output:
onListViewDoubleClicked QModelIndex(1,0,0x0,QSqlTableModel(0x562940043670))
onLVDataChanged QModelIndex(1,0,0x0,QSqlTableModel(0x562940043670)) QModelIndex(1,0,0x0,QSqlTableModel(0x562940043670)) QVector() QVariant(QString, "tag2 ")
onLVDataChanged QModelIndex(1,0,0x0,QSqlTableModel(0x562940043670)) QModelIndex(1,2,0x0,QSqlTableModel(0x562940043670)) QVector() QVariant(QString, "tag2 ")
The solution is to change the editing strategy to QSqlTableModel::OnManualSubmit:
...
listViewModel = new QSqlTableModel(this);
listViewModel->setTable("tags");
listViewModel->setEditStrategy(QSqlTableModel::OnManualSubmit); // <--
listViewModel->select();
listViewModel->setHeaderData(0, Qt::Horizontal, "Tags");
...

Related

Add QCombobox inside QTreeview specific cell

I was trying to insert a QCombobox only in some specific cells of my QTreeview. As I read, I think that I need to create my delegate (that I've created). But I don't understand how to insert this in my treeview.
I want to realize this:
This is my code:
#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>
#include "mainwindow.h"
#include "comboboxdelegate.h"
const int ROWS = 2;
const int COLUMNS = 3;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
treeView = new QTreeView(this);
setCentralWidget(treeView);
standardModel = new QStandardItemModel ;
standardModel->setColumnCount(2);
QStandardItem *root = new QStandardItem("ROOT");
root->setCheckable(true);
root->setCheckState(Qt::Checked);
root->setEditable(false);
standardModel->setItem(0, 0, root);
QList< QStandardItem * > listOne ;
QStandardItem *f1 = new QStandardItem( "Field_1" );
f1->setCheckable(true);
f1->setCheckState(Qt::Checked);
f1->setEditable(false);
listOne.append(f1) ;
listOne.append( new QStandardItem( "<Free text>" ) ) ;
root->appendRow(listOne);
QList< QStandardItem * > listTwo ;
QStandardItem *f2 = new QStandardItem( "Field_2" );
listTwo.append(f2) ;
listTwo.append( new QStandardItem( "<HERE COMBOBOX!>" ) ) ;
root->appendRow(listTwo);
treeView->setModel(standardModel);
treeView->expandAll();
}
I managed to create an entire column with QCombobox (using custom delegate). But I don't know how to set only specific cell. Anyone can help me?
QTreeWidget makes widget items convenient.
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
treeWidget = new QTreeWidget(this);
setCentralWidget(treeWidget);
treeWidget->setColumnCount(2);
auto root = new QTreeWidgetItem({"Root"});
root->setCheckState(0, Qt::Checked);
treeWidget->addTopLevelItem(root);
auto child1 = new QTreeWidgetItem({"Field 1", "<Free Text>"});
child1->setCheckState(0, Qt::Checked);
child1->setFlags(child1->flags() | Qt::ItemIsEditable);
root->addChild(child1);
auto child2 = new QTreeWidgetItem({"Field 2"});
child2->setFlags(child2->flags() | Qt::ItemIsEditable);
root->addChild(child2);
auto comboBox = new QComboBox();
comboBox->addItems({"Red", "Blue", "Yellow"});
treeWidget->setItemWidget(child2, 1, comboBox);
connect(treeWidget, &QTreeWidget::itemDoubleClicked, treeWidget, &QTreeWidget::editItem);
treeWidget->expandAll();
}
There are a few differences to note.
You'll need QTreeWidget* treeWidget; in your class declaration. And include the QTreeWidget header.
By default, TreeWidgetItems aren't checkable (no checkbox), but calling QTreeWidgetItem::setCheckState with Qt::Checked or Qt::Unchecked will make it checkable.
Items are not editable by default. Whole rows can be made editable by calling treeWidgetItem->setFlags(treeWidgetItem->flags() | Qt::ItemIsEditable). To filter what rows/columns can be edited, you can define your own itemDoubleClicked slot and use an if-statement (example).
You need to store combobox items in model item, for example using Qt::UserRole
QStringList options = {"one", "two", "three"};
QStandardItem* item = new QStandardItem(options[0]);
item->setData(QVariant(options),Qt::UserRole);
listTwo.append(item);
Then you need assign delegate to view. You can assign it for the whole table and return default delegate if index.data(Qt::UserRole).isNull().
Delegate* delegate = new Delegate(treeView);
treeView->setItemDelegate(delegate);
It's probably a good idea to set edit triggers to all, so dropdown occurs not only on doubleclick but also on single click
treeView->setEditTriggers(QAbstractItemView::AllEditTriggers);
Delegate must implement createEditor, setEditorData and setModelData
QWidget *Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.data(Qt::UserRole).isNull()) {
return QStyledItemDelegate::createEditor(parent, option, index);
}
return new QComboBox(parent);
}
void Delegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
if (!comboBox) {
return QStyledItemDelegate::setEditorData(editor, index);
}
QStringList options = index.data(Qt::UserRole).toStringList();
comboBox->addItems(options);
QString value = index.data().toString();
int current = options.indexOf(value);
if (current > -1) {
comboBox->setCurrentIndex(current);
}
comboBox->showPopup();
}
void Delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
if (!comboBox) {
return QStyledItemDelegate::setModelData(editor, model, index);
}
model->setData(index, comboBox->currentText());
}
By default delegate doesn't change how item is displayed and show editor only if edit is triggered: no combobox is shown. But you can override it with custom paintEvent.
void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.data(Qt::UserRole).isNull()) {
return QStyledItemDelegate::paint(painter, option, index);
}
QStyle* style = qApp->style();
QStyleOptionComboBox opt;
opt.rect = option.rect;
opt.currentText = index.data().toString();
opt.palette = option.palette;
opt.state = option.state;
opt.subControls = QStyle::SC_All;
opt.activeSubControls = QStyle::SC_All;
opt.editable = false;
opt.frame = true;
style->drawComplexControl(QStyle::CC_ComboBox, &opt, painter, 0);
style->drawControl(QStyle::CE_ComboBoxLabel, &opt, painter, 0);
}
Full source here: combobox-delegate

Using QTreeWidgetItems setData to store a QStackedWidget or QVariant

I am trying to make a QTreeWidget such that each row contains a series of comboboxes. Depending on how the user interacts with the comboboxes I would like certain comboboxes to becomes line edits and some to become buttons.
It was suggested here that a QStackedWidget would serve my needs and its done a pretty good job except now I need a way to alter the QStackedWidget that is next to the one that contains the combobox sending me an indexChanged signal. (Basically I want to change the neighboring QStackWidgets index)
I thought that I would be able to simply store the QStackWidget in the childItem using setData and then retrieve it inside the indexChanged slot but for some reason it appears the QStackWidget is not set to the childItems data.
Any help is appreciated.
This is where I orginally setup my QTreeWidget and its Items
QTreeWidgetItem *childItem = new QTreeWidgetItem(itemParent);
QVariant itemParentVariant,widgetParentVarient;
widgetParentVarient.setValue(widgetParent);
itemParentVariant.setValue(itemParent);
QList<QVariant> stackWidgetList;
uint cycleSetup;
for(cycleSetup = 0;cycleSetup < methodBlocks.at(rowType).size()+2;cycleSetup++)
{
QComboBox *itemComboBox = new QComboBox;
itemComboBox->setProperty("rowType", rowType);
itemComboBox->setProperty("row", 0);
itemComboBox->setProperty("column",cycleSetup);
itemComboBox->setProperty("widgetParent",widgetParentVarient);
itemComboBox->setProperty("itemParent",itemParentVariant);
itemComboBox->addItems(methodBlocks.at(0).at(cycleSetup));
QObject::connect(itemComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnComboIndexChanged(const QString&)));
QLineEdit *itemLineEdit = new QLineEdit;
QPushButton *itemButton = new QPushButton;
itemButton->setText("Reset");
QComboBox *timeComboBox = new QComboBox;
timeComboBox->setProperty("rowType", rowType);
timeComboBox->setProperty("row", 0);
timeComboBox->setProperty("column",cycleSetup);
timeComboBox->setProperty("widgetParent",widgetParentVarient);
timeComboBox->setProperty("itemParent",itemParentVariant);
timeComboBox->addItems(QString("Seconds;MilliSeconds;Reset").split(";"));
QStackedWidget *masterItemWidget = new QStackedWidget;
masterItemWidget->addWidget(itemLineEdit);
masterItemWidget->addWidget(itemComboBox);
masterItemWidget->addWidget(itemButton);
masterItemWidget->addWidget(timeComboBox);
masterItemWidget->setCurrentIndex(1);
QVariant stackParent;
stackParent.setValue(masterItemWidget);
itemComboBox->setProperty("stackParent",stackParent);
childItem->setData(0,Qt::UserRole,stackParent);
stackWidgetList.push_back(stackParent);
widgetParent->setItemWidget(childItem,cycleSetup,masterItemWidget);
itemParent->addChild(childItem);
}
And this is inside the slot where I am trying to retrieve the data (The QStackWidget)
QStackedWidget *itemMaster = combo->property("stackParent").value<QStackedWidget*>(); //this works
itemMaster->setCurrentIndex(0);
QTreeWidget *widgetParent = combo->property("widgetParent").value<QTreeWidget*>();
QTreeWidgetItem *parentItem = combo->property("itemParent").value<QTreeWidgetItem*>();
QTreeWidgetItem *childItem = new QTreeWidgetItem(parentItem);
QList<QVariant> stackList = childItem->data(0,Qt::UserRole).value<QList<QVariant>>(); //this doesnt
QStackedWidget *itemsibMaster = childItem->data(0,Qt::UserRole).value<QStackedWidget*>(); //neither does this
itemsibMaster->setCurrentIndex(2);
EDIT:
I've tried to set the data like this
QFrame *stackFrame = new QFrame;
QStackedWidget *masterItemWidget = new QStackedWidget(stackFrame);
masterItemWidget->addWidget(itemLineEdit);
masterItemWidget->addWidget(itemComboBox);
masterItemWidget->addWidget(itemButton);
masterItemWidget->addWidget(timeComboBox);
masterItemWidget->setCurrentIndex(1);
QVariant stackParent;
stackParent.setValue(masterItemWidget);
itemComboBox->setProperty("stackParent",stackParent);
QVariant frameVariant;
frameVariant.setValue(stackFrame);
childItem->setData(0,Qt::UserRole,frameVariant);
stackWidgetList.push_back(stackParent);
widgetParent->setItemWidget(childItem,cycleSetup,masterItemWidget);
itemParent->addChild(childItem);
And retrieve it like this
QStackedWidget *itemMaster = combo->property("stackParent").value<QStackedWidget*>();
itemMaster->setCurrentIndex(0);
QTreeWidget *widgetParent = combo->property("widgetParent").value<QTreeWidget*>();
QTreeWidgetItem *parentItem = combo->property("itemParent").value<QTreeWidgetItem*>();
QTreeWidgetItem *childItem = new QTreeWidgetItem(parentItem);
QFrame *frameObject = childItem->data(0,Qt::UserRole).value<QFrame*>();
//QList<QVariant> stackList = childItem->data(0,Qt::UserRole).value<QList<QVariant>>();
QStackedWidget *itemFrameMaster = frameObject->findChild<QStackedWidget*>();
if(itemFrameMaster)
{
qDebug() << "itemFrame Exists";
}
else
{
qDebug() << "itemFrame is NULL";//It goes to here
}
So I'm still unable to get the desired functionality.
Check if it works.
As stacked widget QStackedWidget derived from QFrame, Create a Frame object and add your stacked widget to it. Set the frame to your child item.
QFrame *stackFrame = new QFrame(Parent);
QStackedWidget *masterItemWidget = new QStackedWidget(stackFrame);
While querying first query for the frame and get the stacked widget child from it.
QStackedWidget *stackedWidget = FrameObject->findChild<QStackedWidget *>();
Alright so I managed to get the functionality that I was after, so in case anyone else is trying to do this here's the fix.
Essentially everything I had done before was correct except for one error.
When retrieving the data stored in the comboboxes child I had originally gotten that child here.
QTreeWidgetItem *parentItem = combo->property("itemParent").value<QTreeWidgetItem*>();
QTreeWidgetItem *childItem = new QTreeWidgetItem(parentItem);
The issue was that since the parentItem had more than one child (I suppose in this case the minimum is two children) I was not referencing the correct child. This issue was solved by creating a custom combobox property which held the child rather than the parent (parent can be derived from the parent more accurately than visa versa).
Now where I populate the QTreeWidget with combobox items looks like this.
QTreeWidgetItem *childItem = new QTreeWidgetItem(itemParent);
QVariant childItemVariant,widgetParentVarient;
widgetParentVarient.setValue(widgetParent);
childItemVariant.setValue(childItem);
uint cycleSetup;
for(cycleSetup = 0;cycleSetup < methodBlocks.at(rowType).size();cycleSetup++)
{
if(cycleSetup < methodBlocks.at(rowType).size())
{
QComboBox *itemComboBox = new QComboBox;
itemComboBox->setProperty("rowType", rowType);
itemComboBox->setProperty("row", 0);
itemComboBox->setProperty("column",cycleSetup);
itemComboBox->setProperty("widgetParent",widgetParentVarient);
itemComboBox->setProperty("childItem",childItemVariant);
itemComboBox->addItems(methodBlocks.at(0).at(cycleSetup));
QObject::connect(itemComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnComboIndexChanged(const QString&)));
QLineEdit *itemLineEdit = new QLineEdit;
QFrame *stackFrame = new QFrame;
QStackedWidget *masterItemWidget = new QStackedWidget(stackFrame);
masterItemWidget->addWidget(itemLineEdit);
masterItemWidget->addWidget(itemComboBox);
//masterItemWidget->addWidget(timeComboBox);
masterItemWidget->setCurrentIndex(1);
QVariant stackParent;
stackParent.setValue(masterItemWidget);
itemComboBox->setProperty("stackParent",stackParent);
itemComboBox->setProperty("cycleSetupIT",cycleSetup);
QVariant frameVariant;
frameVariant.setValue(stackFrame);
childItem->setData(cycleSetup,Qt::UserRole,stackParent);
widgetParent->setItemWidget(childItem,cycleSetup,masterItemWidget);
itemParent->addChild(childItem);
}
}
}
And the code in the slot where I get the data from the childItem (which holds the combobox) looks like this.
QTreeWidget *widgetParent = combo->property("widgetParent").value<QTreeWidget*>();
widgetParent->setColumnCount(6);
QTreeWidgetItem *childItem = combo->property("childItem").value<QTreeWidgetItem*>();
QTreeWidgetItem *parentItem = childItem->parent();
int rowType = combo->property("rowType").toInt();
int cycleIT = combo->property("cycleSetupIT").toInt();
int offset = 0;
QStackedWidget *itemFrameMaster = childItem->data(cycleIT+1,Qt::UserRole).value<QStackedWidget*>();
itemFrameMaster->setCurrentIndex(0);
Hope this helps. Also if anyone knows how to get the number of data items in a QStandardItem::data() that'd be great albeit not super critical.
Cheers!

Signal when value is changed ( before return) of a field of a EnhTableWidget

I have a question concerning signals of a field of a EnhTableWidget:
when I click into a cell of that table -->
..currentCellChanged(int,int,int,int) is emitted
when I click return in a cell of a table -->
..cellChanged(int,int) is emitted
I need to start a calculation-method when the value of a cell is changed, but before return is pressed.Is there a signal for that, something like
when I change the value of a field ( no return yet !) of that table --> ..?? is emitted
Create a customized delegate that handles changes emitted by the cell editor:
MyDelegate::MyDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}
QWidget* MyDelegate::createEditor(QWidget* parent,const QStyleOptionViewItem &option,const QModelIndex &index) const
{
// Assume you want a QLineEdit editor for the QTableWidget cell
QLineEdit* editor = new QLineEdit(parent);
// Get notified when editor changes
QObject::connect(editor, &QLineEdit::textEdited, this, [=](const QString &newValue) {
qDebug() << "Cell has changed without pressing return: " << newValue;
}
return editor;
}
The itemChanged signal is emitted
void QTableWidget::itemChanged(QTableWidgetItem * item)
Also you could try to catch the dataChanged signal, which is inherited from the QAbstractItemView class
void QAbstractItemView::dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles = QVector<int> ())
Or you could subclass QTableWidget and reimplement keyPressEvent or use event filter with a custom keyPressHandler if you don't want to subclass:
tableWidget->installEventFilter(keyPressHandler);

QItemDelegate with custom widgets

I'm having problems with my QTableView and QItemDelegate classes. For one column my delegate creates a simple combo box and everything works just fine. For my 2nd column I need a widget that has two combo boxes in a single widget.
I've written the following code in my QItemDelegate, just to be clear this only shows code for my 2nd column, the one that doesn't work. The other simple Combo-box isn't shown as it works fine:
QWidget *UserDefinedUnitsDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem & option ,const QModelIndex & index ) const
{
//set up a simple widget with a layout
QWidget* pWidget = new QWidget(parent);
QHBoxLayout* hLayout = new QHBoxLayout(pWidget);
pWidget->setLayout(hLayout);
//add two combo boxes to the layout
QComboBox* comboEditor = new QComboBox(pWidget);
QComboBox* comboEditor2 = new QComboBox(pWidget);
//now add both editors to this
hLayout->addWidget(comboEditor);
hLayout->addWidget(comboEditor2);
return pWidget;
}
Now this displays just fine but when I edit it and click elsewhere it doesn't stop editing. Can anyone offer any pointers?
Edit: So i need to call CommitData() and closeEditor() at some point. Can anyone offer pointers on where to call these?
Thanks.
You can keep the editor widget as a member of class and emit commitData when the current index of one of the comboboxes has changed. So you can connect currentIndexChanged(int) to a slot and emit commitData from there:
QWidget *UserDefinedUnitsDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem & option ,const QModelIndex & index ) const
{
//set up a simple widget with a layout
pWidget = new QWidget(parent);
QHBoxLayout* hLayout = new QHBoxLayout(pWidget);
pWidget->setLayout(hLayout);
//add two combo boxes to the layout
QComboBox* comboEditor = new QComboBox(pWidget);
QComboBox* comboEditor2 = new QComboBox(pWidget);
connect(comboEditor,SIGNAL(currentIndexChanged(int)),this,SLOT(setData(int)));
connect(comboEditor2,SIGNAL(currentIndexChanged(int)),this,SLOT(setData(int)));
//now add both editors to this
hLayout->addWidget(comboEditor);
hLayout->addWidget(comboEditor2);
return pWidget;
}
void UserDefinedUnitsDelegate::setData(int val)
{
emit commitData(pWidget);
}

Qt: start editing of cell after one click

By default the cell in QTableView starts being edited after double click. How to change this behavior. I need it to start editing after one click.
I have set combo-box delegate to the cell. When clicking the cell it only selects it. When double clicking on the cell the QComboBox editor is activated but not expanded. I want it to expand after just one click as if I added QComboBox by setCellWidget function of QTableWidget. I need the same effect by using model-view-delegate.
You can just set edit trigger use this function setEditTriggers
C++
yourView->setEditTriggers(QAbstractItemView::AllEditTriggers)
Python:
yourView.setEditTriggers(QAbstractItemView.AllEditTriggers)
enum QAbstractItemView::EditTrigger
flags QAbstractItemView::EditTriggers
This enum describes actions which will initiate item editing.
Constant Value Description
QAbstractItemView::NoEditTriggers 0 No editing possible.
QAbstractItemView::CurrentChanged 1 Editing start whenever current item changes.
QAbstractItemView::DoubleClicked 2 Editing starts when an item is double clicked.
QAbstractItemView::SelectedClicked 4 Editing starts when clicking on an already selected item.
QAbstractItemView::EditKeyPressed 8 Editing starts when the platform edit key has been pressed over an item.
QAbstractItemView::AnyKeyPressed 16 Editing starts when any key is pressed over an item.
QAbstractItemView::AllEditTriggers 31 Editing starts for all above actions.
The EditTriggers type is a typedef for QFlags. It stores an OR combination of EditTrigger values.
Edit after one click
You can reimplement mousePressEvent in view you are using
void YourView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
QModelIndex index = indexAt(event->pos());
if (index.column() == 0) { // column you want to use for one click
edit(index);
}
}
QTreeView::mousePressEvent(event);
}
Expanded QCombobox when edit
You should imlement setEditorData in your subclass of QItemDelegate and at the end call showPopup.
But it has some unexpected behaviour. QComboBox disappears when mouse leave its area. But for me it is advantage.
I can select different item with single click and release.
void IconDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
Q_UNUSED(index);
QComboBox *comboBox = qobject_cast<QComboBox*>(editor);
// Add data
comboBox->addItem(QIcon(":/icons/information16.png"), "info");
comboBox->addItem(QIcon(":/icons/warning16.png"), "warning");
comboBox->addItem(QIcon(":/icons/send16.png"), "send");
comboBox->addItem(QIcon(":/icons/select16.png"), "select");
comboBox->showPopup(); // <<<< Show popup here
}
Together it works fast way. Click and hold to choose item and commit data on release ( Just one click and release )
If you want click to show expanded qcombobox and next click to choose/hide, I do not know solution for now.
Based on the idea provided by Jason, I came up with this solution.
To launch the editor on single click, I connected QAbstractItemView::clicked(const QModelIndex &index) signal of my view, to QAbstractItemView::edit(const QModelIndex &index) slot of that same view.
If you use Qt4, you need to create a slot in your delegate. Pass your combobox as an argument to this slot. In this slot you call QComboBox::showPopup. So it will look like this:
void MyDelegate::popUpComboBox(QComboBox *cb)
{
cb->showPopup();
}
But first we need to register the QComboBox* type. You can call this in the constructor of your delegate:
qRegisterMetaType<QComboBox*>("QComboBox*");
The reason we need this slot, is because we can't show the pop up straight away in MyDelegate::createEditor, because the position and the rect of the list view are unknown. So what we do is in MyDelegate::createEditor, we call this slot with a queued connection:
QComboBox *cb = new QComboBox(parent);
// populate your combobox...
QMetaObject::invokeMethod(const_cast<MyDelegate*>(this), "popUpComboBox", Qt::QueuedConnection, Q_ARG(QComboBox*, cb));
This will show the list view of the combobox correctly when the editor is activated.
Now if you are using Qt5, the slot is not needed. All you do is call QComboBox::showPopup with a queued connection from MyDelegate::createEditor. The easiest way to do this is with a QTimer:
QTimer::singleShot(0, cb, &QComboBox::showPopup);
For some extra information, this is how you can paint the combobox so it is shown all the time, not only when the editor is shown:
void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.column() == 1) // show combobox only in the second column
{
QStyleOptionComboBox box;
box.state = option.state;
box.rect = option.rect;
box.currentText = index.data(Qt::EditRole).toString();
QApplication::style()->drawComplexControl(QStyle::CC_ComboBox, &box, painter, 0);
QApplication::style()->drawControl(QStyle::CE_ComboBoxLabel, &box, painter, 0);
return;
}
QStyledItemDelegate::paint(painter, option, index);
}
This solution works perfeclty for me. Single click on a cell, and the combo pops up.
class GFQtComboEnumItemDelegate : public QStyledItemDelegate
{
void setEditorData(QWidget *editor, const QModelIndex &index) const
{
QComboBox* pE = qobject_cast<QComboBox*>(editor);
... // init the combo here
if(m_must_open_box)
{
m_must_open_box = false;
pE->showPopup();
}
}
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
if (event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent* pME = static_cast<QMouseEvent*>(event);
if(pME->button() == Qt::LeftButton)
{
QAbstractItemView* pView = qobject_cast<QAbstractItemView*>( const_cast<QWidget*>(option.widget) );
if(pView != nullptr)
{
emit pView->setCurrentIndex(index);
m_must_open_box = true;
emit pView->edit(index);
}
return true;
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
mutable bool m_must_open_box;
};
If you override QStyledItemDelegate::createEditor() then you can expand the combo box after it is created.