I'm using Qt (C++), specifically I have a table that auto-completes using a QCompleter. I'm very new to Qt and struggling with this.
My problem is that even though the QCompleter shows the correct values, the data it returns is always the data for the first element.
For example, if I have the following data (fields separated by dashes, data made up):
1-David-197598713-Los Angeles
2-Daniel-1933398713-NYC
3-Don-1975555-Argentina
And I type D, the QCompleter shows the 3 options, but regardless of which I pick, this happens:
The actual cell edits "correctly" (the value reflects what's been picked)
The callback receives the WRONG value (always the ID of the first element)
So in this case I'd always get (Assuming the name is the autocomplete field):
1-Daniel-197598713-Los Angeles or 1-Don-197598713-Los Angeles or 1-David-197598713-Los Angeles
Also, for some motive the setEditorData procedure does nothing at all (if I comment it, the behavior doesn't change at all)
This is my entire delegate source:
productNameDelegate::productNameDelegate(QObject *parent) : QItemDelegate(parent)
{
}
QWidget *productNameDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QLineEdit *editor = new QLineEdit(parent);
QCompleter *q = new QCompleter(db::getProductsNames());
q->setCaseSensitivity(Qt::CaseInsensitive);
q->setCompletionMode(QCompleter::PopupCompletion);
editor->setCompleter(q);
return editor;
}
void productNameDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{ //TODO this procedure doesn't affect the code at all
QString value = index.model()->data(index, Qt::EditRole).toString();
QLineEdit *LineEdit = static_cast<QLineEdit*>(editor);
LineEdit->setText(value);
}
void productNameDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QLineEdit *LineEdit = static_cast<QLineEdit*>(editor);
QString value = LineEdit->text();
int code=LineEdit->completer()->currentIndex().data(Qt::UserRole).toInt(); //FIXME: siempre devuelve el primer elemento de la lista
if (index.column()==2 && index.row()>=6 && index.row() <= 25)
m->addProducto(code, index.row());
model->setData(index, value, Qt::EditRole);
}
void productNameDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
void productNameDelegate::setModel(factura* m) {
this->m=m;
}
Related
I have a tableView that has a column that is using a comboBox. I need to fill the comboBox using the delegate class with the data from the model class. I was using signals and slots for this task but I know there is a method using data.
This is how i create and fill the comboBox. I need to get the file row directly from the model class without already storing it in the delegate.
QWidget *CDelegate :: createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const
{
if(index.column() == COL_ComboBox)
{
QComboBox *editor = new QComboBox(parent);
for(int i=0; i<file.at(index.row()).size(); i++)
editor -> addItem(file.at(index.row()).at(i))
return editor;
}
...
}
As I understand, you want to fill QComboBox with data from model of your QTableView. As you see, const QModelIndex & index parameter in createEditor function provides you access to this model. Look for method model of QModelIndex class. Thats' why, your createEditor function may be like this:
QWidget *CDelegate :: createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const
{
if(index.column() == COL_ComboBox)
{
QComboBox *editor = new QComboBox(parent);
const QAbstractItemModel *model = index.model();
while(/*condition*/)
{
// take data from model
// QVariant dt = model->data(...);
// fill editor with data from dt
// editor->addItem(...)
}
return editor;
}
...
}
I have this slot and I want to be able to use the string that is at the index being passed through. How can I get to it?
void Dialog::on_list_Favorites_2_clicked(const QModelIndex &index)
{
}
Since you're using QListWidget instead of QListView you should also use the signal itemClicked(QListWidgetItem*) instead of clicked(const QModelIndex &).
void Dialog::on_list_Favorites_2_itemClicked(QListWidgetItem* item)
{
qDebug() << item->text();
}
You can use below function for this case.
QListWidgetItem * QListWidget::itemFromIndex(const QModelIndex & index) const
And then, the text of item can get using by QString QListWidgetItem::text() const
void Dialog::on_list_Favorites_2_clicked(const QModelIndex &index)
{
QListWidgetItem* pItem = m_listWidget->itemFromIndex( index );
Q_ASSERT( pItem );
if ( pItem )
{
QString itemName = pItem->text();
}
}
I followed the Spin Box Delegate tutorial, which Qt provides, to try to implement my own QItemDelegate. It would be used to specify a QComboBox to represent data in a QTableView cell but it is not working.
My biggest problem is that I don't know when my QItemDelegate is going to be utilized.
when itemModel->setData() is used or when itemModel->setItem(). I would suspect setItem() because I reimplemented a QItemDelegate (emphasis on the "Item") but the tutorial uses setData() and it works fine.
I know that if the specified QItemDelegate does not work it uses the default one but how do I now that the one I specified did not work?
when should I suspect for QTableView to use my delegate. I would like to specify which delegates to use for each cell. Is this possible or does the QTableView only use one delegate throughout?
How would I specify the items to populate the QComboBox once it gets displayed by the QTableView?
I implemented QItemDelegate here:
the part where I try to add the cell which is suppose to use the QComboBox is under the comment "Enabled" in mainwindow.cpp further down this post.
qcomboboxitemdelegate.h
#ifndef QCOMBOBOXITEMDELEGATE_H
#define QCOMBOBOXITEMDELEGATE_H
#include <QItemDelegate>
#include <QComboBox>
class QComboBoxItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit QComboBoxItemDelegate(QObject *parent = 0);
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index);
void setEditorData(QWidget *editor, const QModelIndex &index);
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index);
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index);
signals:
private:
};
#endif // QCOMBOBOXITEMDELEGATE_H
qcomboboxitemdelegate.cpp
#include "qcomboboxitemdelegate.h"
#include <QDebug>
QComboBoxItemDelegate::QComboBoxItemDelegate(QObject *parent)
: QItemDelegate(parent)
{
}
QWidget* QComboBoxItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) {
// create widget for use
QComboBox* comboBox = new QComboBox(parent);
return comboBox;
}
void QComboBoxItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) {
// update model widget
QString value = index.model()->data(index, Qt::EditRole).toString();
qDebug() << "Value:" << value;
QComboBox* comboBox = static_cast<QComboBox*>(editor);
comboBox->setCurrentIndex(comboBox->findText(value));
}
void QComboBoxItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) {
// store edited model data to model
QComboBox* comboBox = static_cast<QComboBox*>(editor);
QString value = comboBox->currentText();
model->setData(index, value, Qt::EditRole);
}
void QComboBoxItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) {
editor->setGeometry(option.rect);
}
mainwindow.cpp : this is where I initialize the QStandardItemModel
void MainWindow::init() {
itemModel = new QStandardItemModel(this);
}
void MainWindow::setupUi() {
this->setWindowTitle("QAlarmClock");
QStringList labelList;
labelList << "Alarm Name" << "Time" << "Enabled";
itemModel->setHorizontalHeaderLabels(labelList);
ui->tableView->setModel(itemModel);
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->tableView->setItemDelegate(comboBoxItemDelegate);
}
void MainWindow::on_actionNew_triggered() {
alarmDialog = new AlarmDialog(this);
connect(alarmDialog, SIGNAL(on_close()), this, SLOT(on_alarmDialog_close()));
alarmDialog->exec();
}
mainwindow.cpp : this is where I update QStandardItemModel
void MainWindow::on_alarmDialog_close() {
QString alarmName = alarmDialog->getAlarmName();
QDateTime alarmDateTime = alarmDialog->getDateTime();
itemModel->insertRow(itemModel->rowCount());
int rowCount = itemModel->rowCount();
// Alarm Name
QStandardItem* alarmItem = new QStandardItem(QIcon("res/alarmclock.ico"), alarmName);
itemModel->setItem(rowCount - 1 , 0, alarmItem);
// Date Time
QStandardItem* dateTimeItem = new QStandardItem();
dateTimeItem->setText(alarmDateTime.toString());
dateTimeItem->setEditable(false);
itemModel->setItem(rowCount - 1, 1, dateTimeItem);
// Enabled
QStandardItem* enabledItem = new QStandardItem();
QList<QStandardItem*> optionList;
optionList << new QStandardItem("Enabled") << new QStandardItem("Disabled");
enabledItem->appendRows(optionList);
itemModel->setItem(rowCount - 1, 2, enabledItem);
}
Edit 1
qcomboboxdelegate.cpp
QWidget* QComboBoxItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) {
// create widget for use
qDebug() << "Column: " << index.column();
if (index.column() == 2) {
QComboBox* comboBox = new QComboBox(parent);
QStringList values;
values << "Enabled" << "Disabled";
comboBox->addItems(values);
return comboBox;
} else {
return QItemDelegate::createEditor(parent, option, index);
}
}
mainwindow.cpp
void MainWindow::on_alarmDialog_close() {
QList<QStandardItem*> row;
QString alarmName = alarmDialog->getAlarmName();
QDateTime alarmDateTime = alarmDialog->getDateTime();
QString status = "Enabled";
// Alarm Name
QStandardItem* alarmItem = new QStandardItem(QIcon("res/alarmclock.ico"), alarmName);
row << alarmItem;
// Date Time
QStandardItem* dateTimeItem = new QStandardItem();
dateTimeItem->setText(alarmDateTime.toString());
dateTimeItem->setEditable(false);
row << dateTimeItem;
// Enabled
QStandardItem* statusItem = new QStandardItem(status);
row << statusItem;
itemModel->appendRow(row);
}
First, you should have a description of your model columns:
enum Columns
{
COL_NAME,
COL_TIME,
COL_STATUS
}
Your delegate should only work for the last column.
Here is an example of how you can populate your model:
for (int i = 0; i < 5; ++i)
{
QStandardItem *itemName = new QStandardItem(QString("name %1").arg(i));
QStandardItem *itemTime = new QStandardItem(QString("time %1").arg(i));
QString status;
if (i % 2 == 0)
{
status = "Enabled";
}
else
{
status = "Disabled";
}
QStandardItem *itemStatus = new QStandardItem(status);
QList<QStandardItem*> row;
row << itemName << itemTime << itemStatus;
model->appendRow(row);
}
As I said, your delegate should only work for the last column. So all methods you have reimplemented should have a column check like this:
QWidget* QComboBoxItemDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index)
{
if (index.column() == COL_STATUS)
{
QStringList values;
values << "Enabled" << "Disabled";
QComboBox* comboBox = new QComboBox(parent);
comboBox->addItems(values);
return comboBox;
}
else
{
return QItemDelegate::createEditor(parent, option, index);
}
}
You should add this check to the other methods: if the current column is not the status column, the base class (QItemDelegate) implementation should be used.
Then you set your delegate to your view:
ui->tableView->setItemDelegate(new ComboBoxDelegate);
If you do everything right, a combo Box will appear in the last column if you try to edit its values.
Even more simply; I found QTableView::setItemDelegateForColumn() to work admirably for a column. For example, in your MainWindow, you could make a member:
QComboBoxItemDelegate dgtComboDelegate;
Then, in your ctor, or init(), you could have
ui->tableView->setItemDelegateForColumn(2, dgtComboDelegate);
If you wanted that to happen for a single cell, that's when you need to test on the index.column() and index.row().
You know, you don't have to create a QTableView to do this either. E.g., see the ?:
Qt - Centering a checkbox in a QTable
The OP doesn't give the declaration for a table widget or view; but it does have the QTableView tag. It should work equally well for either.
In the former case, you can do ui->tableWidget->setItemDelegateForColumn(2, dgtComboDelegate); and never have to make your own model. Just use setData() on the items you create (or even later, for that matter,) to initialize their values.
So I figured out that I did not override the correct function prototypes..! I forgot that they
had const in the prototype meaning that I was not overriding any functions so it was using the default ones. Here are the correct virtual functions that have to be re-implemented: http://qt-project.org/doc/qt-5.0/qtwidgets/qitemdelegate.html
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.
I'm subclassing QAbstractItemDelegate. This is my code. Suggestions are welcome:
QWidget *ParmDelegate::createWidget(Parm *p, const QModelIndex &index) const {
QWidget *w;
if (index.column() == 0) {
w = new QLabel(p->getName().c_str());
} else {
if (p->isSection())
return NULL;
w = p->createControl();
}
return w;
}
QWidget *ParmDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
cout << "createEditor called" << endl;
Parm *p = reinterpret_cast<Parm*>(index.internalPointer());
QWidget *retval = createWidget(p, index);
retval->setFocusPolicy(Qt::StrongFocus);
retval->setParent(parent);
return retval;
}
void ParmDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QRect rect(option.rect);
editor->setGeometry(QRect(QPoint(0,0), rect.size()));
}
void ParmDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
Parm *p = reinterpret_cast<Parm*>(index.internalPointer());
scoped_ptr<QWidget> w(createWidget(p, index));
if (!w)
return;
QRect rect(option.rect);
w->setGeometry(QRect(QPoint(0,0), rect.size()));
w->render(painter, rect.topLeft());
}
QSize ParmDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
Parm *p = reinterpret_cast<Parm*>(index.internalPointer());
scoped_ptr<QWidget> w(createWidget(p, index));
if (!w)
return QSize(0,0);
return w->sizeHint();
}
bool ParmDelegate::editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index ) {
cout << "editorEvent called" << endl;
return false;
}
When this is run, I only see that editorEvent gets called twice for every edit event -- no createEditor!
From Qt's AbstractItemDelegate documentation:
To provide custom editing, there are two approaches that can be used. The first approach is to create an editor widget and display it directly on top of the item. To do this you must reimplement createEditor() to provide an editor widget, setEditorData() to populate the editor with the data from the model, and setModelData() so that the delegate can update the model with data from the editor.
The second approach is to handle user events directly by reimplementing editorEvent().
This appears to say that you are missing something to trigger the first approach. My guess is that your model's data() function isn't returning the proper value for the Qt::EditRole option.
I had implemented a TableView which i had inhertied from QItemDelegate. Then i had similar problem. I tracked it down to not calling 'return QItemDelegate::editorEvent(event, model, option, index);' in the editorEvent(...) method.
You can try this. Maybe it helps.