I have problem with QComboBox as item delegeate editor for QTableWidget.
QTableWidget uses SqlTypeDelegate as item delegate.
When I draw QTableWidget inside QGraphicsScene(through QGraphicsProxyWidget) then QComboBox popup list of available items is not shown.
But if I'm using QTableWidget as a normal widget(drawn not through QGraphicsScene\View) then QComboBox behavior is normal - it shows item list.
What should I do to force QComboBox to show its item list?
below sample code:
main.cpp:
#include <QtGui/QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QTableWidget>
#include "sqltypedelegate.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene scene;
QTableWidget *table = new QTableWidget(4,2);
table->setItemDelegate(new SqlTypeDelegate(table));
QGraphicsProxyWidget *proxy = scene.addWidget(table);
QGraphicsView view(&scene);
view.show();
return app.exec();
}
sqltypedelegate.h:
#include <QItemDelegate>
#include <QStyledItemDelegate>
#include <QModelIndex>
#include <QObject>
#include <QSize>
#include <QComboBox>
class SqlTypeDelegate : public QItemDelegate
{
Q_OBJECT
public:
SqlTypeDelegate(QObject *parent = 0);
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;
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
sqltypedelegate.cpp:
#include <QtGui>
#include "sqltypedelegate.h"
SqlTypeDelegate::SqlTypeDelegate(QObject *parent)
: QItemDelegate(parent)
{}
QWidget *SqlTypeDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QComboBox *editor = new QComboBox(parent);
editor->addItem(QString("decimal(50)"));
editor->addItem(QString("integer"));
editor->addItem(QString("varchar(50)"));
editor->addItem(QString("char"));
editor->setEditable(true);
return editor;
}
void SqlTypeDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString value = index.model()->data(index, Qt::EditRole).toString();
QComboBox *comboBox = static_cast<QComboBox*>(editor);
comboBox->setEditText(value);
}
void SqlTypeDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
model->setData(index, comboBox->currentText(), Qt::EditRole);
}
void SqlTypeDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
comboBox->setGeometry(option.rect);
}
Finaly I've found solution: event filters!
Just lurk for QEvent::MouseButtonRelease and then call showPopup.
bool SqlTypeDelegate::eventFilter(QObject *object, QEvent *event)
{
QComboBox * comboBox = dynamic_cast<QComboBox*>(object);
if (comboBox)
{
if (event->type() == QEvent::MouseButtonRelease)
{
comboBox->showPopup();
return true;
}
}
else
{
return QItemDelegate::eventFilter( object, event );
}
return false;
}
I got the same problem and my workaround is add two line codes to createEditor(...).
editor->move(option.rect.x(),option.rect.y());
editor->showPopup();
Then when double clicking the table item, the QComboBox will show the popup items triggered by the second mouse button pressing and hide the popup items triggered by the second mouse button releasing. This works for Qt 4.8.
I also tried the Qt5.0 and the application crashed with or without this workaround.
I have reported this issue as a bug.
Related
What i want to do is to start typing some data in a table cell and it shows completion suggestions but so far no success.
I tried adding QLineEdit in a cell but is there a way to accomplish that without using QLineEdit as a cellWidget?
Edit:
Made it work by overriding QItemDelegate class
Autocomplete_Delegate.h
#include <QItemDelegate>
#include <QModelIndex>
#include <QLineEdit>
#include <QCompleter>
class Autocomplete_Delegate : public QItemDelegate {
public:
Autocomplete_Delegate(QObject *parent, QStringList model);
~Autocomplete_Delegate();
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
private:
QStringList model;
};
Autocomplete_Delegate..cpp
Autocomplete_Delegate::Autocomplete_Delegate(QObject *parent, QStringList model) : QItemDelegate(parent), model(model) {}
Autocomplete_Delegate::~Autocomplete_Delegate() {
model.clear();
}
QWidget *Autocomplete_Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QWidget *editor = QItemDelegate::createEditor(parent, option, index); //* Create the editor so it looks native to the tablewidget
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); //* create a linedit so it behaves like a line edit and cast the editor to line edit
QCompleter *completer = new QCompleter(model, parent); //* make a completer and pass in the wordlist
completer->setCaseSensitivity(Qt::CaseInsensitive); //* set the case senstivity
lineEdit->setCompleter(completer); //* set the completor on line edit
return lineEdit;
}
void Autocomplete_Delegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
QString data = index.model()->data(index, Qt::EditRole).toString(); //* get the data from the model -> the cell
QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);
lineEdit->setText(data); //* set the data in the editor
}
Thanks to #eyllanesc for the idea.
You can use a role associated with the QTableWidgetItem, and use a delegate to create a QCompleter where your model is established and updated.
#include <QtWidgets>
enum CustomRoles{
ListCompleterRole = Qt::UserRole + 1000
};
class CompletedDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
QWidget* editor = QStyledItemDelegate::createEditor(parent, option, index);
if(QLineEdit *le = qobject_cast<QLineEdit *>(editor)){
QStringListModel *model = new QStringListModel(le);
QCompleter *completer = new QCompleter(le);
completer->setModel(model);
le->setCompleter(completer);
}
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const{
QStyledItemDelegate::setEditorData(editor, index);
if(QLineEdit *le = qobject_cast<QLineEdit *>(editor)){
if(QCompleter *completer = le->completer()){
if(QStringListModel *model = qobject_cast<QStringListModel *>(completer->model())){
QVariant v = index.data(CustomRoles::ListCompleterRole);
if (v.canConvert<QStringList>()){
QStringList options = v.value<QStringList>();
model->setStringList(options);
}
}
}
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStringList words = {
"astonishing",
"agreement",
"appeal",
"autonomy",
"accompany",
"articulate",
"article",
"amuse",
"advertise",
"admiration"
};
QTableWidget w(1, 1);
CompletedDelegate *delegate = new CompletedDelegate(&w);
w.setItemDelegate(delegate);
QTableWidgetItem *item = new QTableWidgetItem;
item->setData(CustomRoles::ListCompleterRole, words); // update options
w.setItem(0, 0, item);
w.show();
return a.exec();
}
I have a QListView and a QTableView both have the same QStandardItemModel. I have added a custom delegator to the QTableView. Now when I go to my QTableView and double-click an item I see the delegate editor widget, now if I go to my QListView and double-click the same item I see the delegate editor widget there as well. Point to be noted is that I see the editor widget only for those items in QListView which have been double-clicked in QTableView already. Whats going on here? Why do QListView items also showing the delegate editor widget even though the delegate is only added to the QTableView?
For reference, I am having below code:
#include <QtWidgets/QApplication>
#include <QtGui>
#include <QCombobox>
#include <QListview>
#include <QTableview>
#include <QLayout>
#include <QColor>
#include <QStyledItemDelegate>
#include <QSpinbox>
class SpinBoxDeligate : public QStyledItemDelegate {
public:
QWidget * createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override {
auto w = new QSpinBox(parent);
w->setFrame(false);
w->setMinimum(0);
w->setMaximum(100);
return w;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override {
static_cast<QSpinBox*>(editor)->setValue(index.data(Qt::EditRole).toInt());
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override {
model->setData(index, static_cast<QSpinBox*>(editor)->value(), Qt::EditRole);
}
};
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QStandardItemModel model(3, 1);
for (int r = 0; r < 3; ++r)
{
auto text = QString("%0").arg(r);
QStandardItem* item = new QStandardItem(text);
item->setFlags(Qt::ItemIsUserCheckable
| Qt::ItemIsEnabled
| Qt::ItemIsEditable
);
item->setData(Qt::Unchecked, Qt::CheckStateRole);
item->setData(text, Qt::ToolTipRole);
item->setData(QSize(100, 30), Qt::SizeHintRole);
item->setData(QIcon(":/QtMVC/Desert.jpg"), Qt::DecorationRole);
model.setItem(r, 0, item);
}
QComboBox* combo = new QComboBox();
combo->setModel(&model);
QListView* list = new QListView();
list->setModel(&model);
QTableView* table = new QTableView();
table->setModel(&model);
table->setItemDelegate(new SpinBoxDeligate());
QWidget w;
QVBoxLayout* containerLayout = new QVBoxLayout();
w.setLayout(containerLayout);
containerLayout->addWidget(combo);
containerLayout->addWidget(list);
containerLayout->addWidget(table);
w.show();
return app.exec();
}
The problem is really simple, if the data saved in the model are numbers the delegate is a QSpinBox by default, ie the delegate you see is the QListView is not the SpinBoxDeligate, but the delegate by default.
And why is it generated if you do not keep a number?
It's because the SpinBoxDeligate saves the data as a number.
So the solution is to save the data obtained by the SpinBoxDeligate as text:
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override {
model->setData(index, static_cast<QSpinBox*>(editor)->text(), Qt::EditRole);
}
I am setting a QStyledItemDelegate on my model for a particular field, and returning a QComboBox from QStyledItemDelegate::createEditor
QComboBox* createEditor(QWidget* parent)
{
QComboBox* cb = new QComboBox(parent);
cb->addItem("UNDEFINED");
cb->addItem("TEST");
cb->addItem("OSE");
cb->addItem("TSE");
return cb;
}
void setEditorData(QWidget* editor, const QModelIndex& index)
{
QComboBox* cb = qobject_cast<QComboBox*>(editor);
if (!cb)
throw std::logic_error("editor is not a combo box");
QString value = index.data(Qt::EditRole).toString();
int idx = cb->findText(value);
if (idx >= 0)
cb->setCurrentIndex(idx);
cb->showPopup();
}
This is working fine, and when I select the field in question I am shown a combo box.
When I select an option from the drop-down list, the combobox closes and the item is displayed with a drop-down icon next to it:
At this point I would like the QStyledItemDelegate::setModelData function to be called, so that selecting an item in the list commits the data to the model.
However, I am required to first press Enter to commit the data (whereby the drop-down icon disappears)
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index)
{
QComboBox* cb = qobject_cast<QComboBox*>(editor);
if (!cb)
throw std::logic_error("editor is not a combo box");
model->setData(index, cb->currentText(), Qt::EditRole);
}
Question:
How can I configure my QComboBox to automatically commit the data when the user selects an item in the list and the combobox list closes, rather than requiring the additional press of Enter?
You have to issue the signal commitData and closeEditor when an item is selected as shown in the following example:
#include <QApplication>
#include <QStandardItemModel>
#include <QListView>
#include <QStyledItemDelegate>
#include <QComboBox>
class ComboBoxDelegate: public QStyledItemDelegate{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
Q_UNUSED(option)
Q_UNUSED(index)
QComboBox* editor = new QComboBox(parent);
connect(editor, QOverload<int>::of(&QComboBox::activated),
this, &ComboBoxDelegate::commitAndCloseEditor);
editor->addItems({"UNDEFINED", "TEST", "OSE", "TSE"});
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const{
QComboBox* cb = qobject_cast<QComboBox*>(editor);
if (!cb)
throw std::logic_error("editor is not a combo box");
QString value = index.data(Qt::EditRole).toString();
int idx = cb->findText(value);
if (idx >= 0)
cb->setCurrentIndex(idx);
cb->showPopup();
}
private:
void commitAndCloseEditor(){
QComboBox *editor = qobject_cast<QComboBox *>(sender());
emit commitData(editor);
emit closeEditor(editor);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QListView view;
QStandardItemModel model;
for(int i=0; i<10; i++){
model.appendRow(new QStandardItem("UNDEFINED"));
}
view.setItemDelegate(new ComboBoxDelegate(&view));
view.setModel(&model);
view.show();
return a.exec();
}
I have a tableView composed of 4 rows and 4 columns. In the last column I have a Pushbutton for every cell. I am trying to click on the button of every column to open another .ui form. So for 4 Pushbutton I need to open 4 equal .ui form. To be specific I will open a table to edit. How do I click on the QPush button to open a .ui form?
Below is the buttoncolumndelegate.h
#include "buttoncolumndelegate.h"
ButtonColumnDelegate::ButtonColumnDelegate(QObject *parent) :
QItemDelegate(parent)
{
}
void ButtonColumnDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QPushButton detail(index.data().toString());
detail.setGeometry(option.rect);
detail.setText("Detail");
painter->save();
painter->translate(option.rect.topLeft());
detail.render(painter);
painter->restore();
}
void ButtonColumnDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QPushButton *detail = qobject_cast<QPushButton *>(editor);
detail->setProperty("Detail", "Detail");
detail->setText("Detail");
(void) index;
}
void ButtonColumnDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QPushButton *detail = qobject_cast<QPushButton *>(editor);
detail->setGeometry(20,20,20,20);
model->setData(index, detail->property("Detail"));
}
void ButtonColumnDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
editor->setGeometry(option.rect);
(void) index;
}
This is the dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include "buttoncolumndelegate.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
mybutton = new ButtonColumnDelegate(this);
mModel = new QStandardItemModel(4,4,this);
ui->tableView->setModel(mModel);
ui->tableView->setItemDelegateForColumn(3, mybutton);
}
Dialog::~Dialog()
{
delete ui;
}
Any clue on how to create the clickable connection as soon as I click the button and open the .ui (table) would be great.
You doing it wrong. Editor mode in delegates - is for handle editing. You should draw a button and hadle cmouse events manually via delegate.
Or you may use setIndexWidget on your view to create buttons.
I am trying to set delegate to my QTreeWidget. The problem is that delegate setModelData is never called. createEditor and setEditorData are called.
Since the editor that I create is simple QLineEdit, commitData() signal doesn't have to be emitted. Also I tried to emit this signal just in case is needed , when editLine editingFinished() was emitted, but that doesn't solve the problem.
As I understand documentaion say that for simple widgets like QLineEdit setModelData should be called without emitting commitData signal, so the following code should work :
MyDlg::MyDlg()
{
mTreeWdg->setItemDelegate(new TestDelegate( this ));
}
MyDlg::OnTreeItemDoubleCliked(QTreeeWidget* item,int column)
{
if(column != 1) return;
item->setFlags(Qt::ItemIsEditable);
mTreeWdg->editItem(item,column);
}
TestDelegate::TestDelegate(QObject *parent )
:QItemDelegate(parent)
{
}
QWidget* TestDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if(index.column() == 1) // value column
{
QLineEdit* edit = new QLineEdit(parent);
return edit;
}
else return 0; // no editor attached
}
void TestDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
if(index.column() == 1)
{
QLineEdit* edit = static_cast<QLineEdit*> (editor);
edit->setText("damn");
}
}
void TestDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index)
{
if(index.column()!= 1)
return;
}
void TestDelegate::updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
void TestDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) must be const:
void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const
Look at the declaration above.