Access pointer to QWidget(Combobox) of the customdelegate - qt

I have derived a class from QStyledItemDelegate. I am using a QComboBox in this delegate. This delegate is used in QTableView.
My question is, how can i change the index of the Combobox in the delegate programatically i.e how to access the pointer to that widget outside the delegate class ?
I have checked that CreateEditor, SetEditorData, SetModelData functions (of QStyledItemDelegate) are called automatically when we click on the combobox and we cannot call them manually to maniplate the data in the model.

afaik any time you start editing and the combobox is shown, it will allocate a new one. if you want to have a permanent combobox, you should look at
QTableView::setIndexWidget(const QModelIndex&, QWidget*)
so you could access the combobox with the following code:
const QMoodelIndex idx = model->index(row, column);
QWidget* wid = view->indexWidget(idx);
QComboBox* box = qobject_cast<QComboBox*>(wid);
if (box)
// do your thing

You can have the contents of your combobox as a class member of your delegate in a QStringList. Your item delegate can be like :
#include <QStyledItemDelegate>
#include <QComboBox>
class ComboBoxDelegate: public QStyledItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(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;
QStringList comboItems;
mutable QComboBox *combo;
private slots:
void setData(int val);
};
ComboBoxDelegate::ComboBoxDelegate(QObject *parent ):QStyledItemDelegate(parent)
{
}
QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
combo = new QComboBox( parent );
QObject::connect(combo,SIGNAL(currentIndexChanged(int)),this,SLOT(setData(int)));
combo->addItems(comboItems);
combo->setMaxVisibleItems(comboItems.count());
return combo;
}
void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString text = index.model()->data( index, Qt::DisplayRole ).toString();
int comboIndex = comboItems.indexOf(QRegExp(text));
if(comboIndex>=0)
(static_cast<QComboBox*>( editor ))->setCurrentIndex(comboIndex);
}
void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
model->setData( index, static_cast<QComboBox*>( editor )->currentText() );
}
void ComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry( option.rect );
}
void ComboBoxDelegate::setData(int val)
{
emit commitData(combo);
//emit closeEditor(combo);
}
When you want to update the items in combobox somewhere in your code just get a pointer to the item delegate by calling itemDelegateForColumn and access the comboItems member :
ComboBoxDelegate * itemDelegate = qobject_cast<ComboBoxDelegate *>(ui->tableView->itemDelegateForColumn(columnIndex));
//Updating combobox items
itemDelegate->comboItems.append("newItem");
...

Related

Remove CellWidget from QTableWidget when focus out

I have a QTableWidget in which some cells have QComboBox as cellWidget. I want to create the comboBox only when the user clicks in the cell. For this, I have caught itemClicked signal and created the comboBox and set it as cellWidget.
Now, I want to delete this comboBox in two scenarios - if the user selects some value from the comboBox, or the user simply focusses out (click anywhere in the dialog).
For the first case, I have use the signal QComboBox::activated which is invoked when something is selected in the drop-down. There I delete the comboBox and it works fine.
However, I am not sure how to proceed for the second case (delete the comboBox when the user focusses out of the drop-down without selecting anything).
I tried capturing eventFilter for focusOut for the tablewidget as well as the comboBox, but it didn't help.
You can use QItemDelegate for this purpose:
comboboxdelegate.h
class ComboBoxDelegate : public QItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(QObject *parent = nullptr) :
QItemDelegate(parent)
{
}
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox *editor = new QComboBox(parent);
editor->addItems(QStringList() << "1" << "2" << "3" << "4"); // fill it with your own values
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const
{
QString text = index.model()->data(index, Qt::EditRole).toString();
QComboBox *cb = static_cast<QComboBox*>(editor);
cb->setCurrentText(text);
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox *cb = static_cast<QComboBox*>(editor);
QString text = cb->currentText();
model->setData(index, text, Qt::EditRole);
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
};
mainwindow.cpp
...
ui->tableWidget->setItemDelegate(new ComboBoxDelegate);
...

Resizing a QTableView column horizontally to the content of an item delegated column which is painted with a new text in Qt

I want to show my database tabel content in a QTableView. I use the following codes to do that:
QSqlDatabase test = QSqlDatabase::addDatabase("QMYSQL");
test.setDatabaseName("dbText");
test.setHostName("localhost");
test.setUserName("***");
test.setPassword("***");
if (!test.open())
qDebug()<<"ERROR ";
QSqlTableModel *model = new QSqlTableModel(this,test);
model->setTable("textTable");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->tableView->setModel(model);
ui->tableView->show();
As it is depicted in the following picture, ui->tableView columns are resized to the content of the database table columns.
Now, I want to clear the display text of Description column and paint it with new text and color. For this propose, I have used the function QTableView::setItemDelegateForColumn as follow:
ui->tableView->setItemDelegateForColumn(2,new PowerColorDelegate(this));
And here are PowerColorDelegate header and source file content:
PowerColorDelegate.h
#include <QStyledItemDelegate>
class PowerColorDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
PowerColorDelegate(QObject *parent = 0);
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QString displayText(const QVariant &value, const QLocale &locale) const;
};
PowerColorDelegate.cpp
#include <QApplication>
#include <QPainter>
#include "powercolordelegate.h"
PowerColorDelegate::PowerColorDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}
void PowerColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
if(!index.isValid())
{
QStyledItemDelegate::paint(painter, option, index);
return;
}
// Position our pixmap
const int x = option.rect.left();
const int y = option.rect.top();
QString newColor = "#fcaf9e";
QString newText = "This is my new text which I want to paint!";
painter->fillRect(option.rect, newColor);
painter->drawText(QRect(x, y, 80, 20), newText);
QStyledItemDelegate::paint(painter, opt, index);
}
QString PowerColorDelegate::displayText(const QVariant &value, const QLocale &locale) const
{
return "";
}
The result of using PowerColorDelegate on ui->tableView is shown in this picture:
How can I resize the third ui->tableView column (Description) horizontally to the content of the painted column?
Implement sizeHint for your delegate according to required text format

Using custom delegate with the class derived from QTableView

I have derived a class from QTableView. and I have Promoted the Widget QTableViewto the derived class in Qt creator. and try to implement the Custom delegate in the derived class, which does not seem to work. But when I demote the QTableView Widget to QTableView. Custom delegate works.
I tried to go through a documentation but could not find any solution. Am I missing anything?
Update:
I have checked the Paint() method is called but not createEditor method.
You can find the code below.
Geometry.cpp (constructor of the class derived from QTableView)
Geometry::Geometry(QWidget *parent) :
QTableView(parent)
{
this->setAcceptDrops(true);
this->setSelectionMode(QAbstractItemView::ExtendedSelection);
this->setContextMenuPolicy(Qt::CustomContextMenu);
this->setEditTriggers(QAbstractItemView::NoEditTriggers);
grpModel = new QStandardItemModel();
grpModel->setHorizontalHeaderItem(0,new QStandardItem ("Geometry part"));
grpModel->setHorizontalHeaderItem(0,new QStandardItem ("Surface property"));
this->setModel(grpModel);
}
MyDelegate.cpp (Definition of custom delegate)
QWidget* MyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
//Definition
this method is not called
}
void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
//definition
}
void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
//definition
}
void MyDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter,option,index); // This method is called
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->tableGeometry->setModel(grpModel); //tabelGeometry is promoted to Geometry
comboDelegate = new MyDelegate();
ui->tableGeometry->setItemDelegate(comboDelegate);
}
The solution was that
I have removed this->setEditTriggers(QAbstractItemView::NoEditTriggers); from the constructor of the class Geometry.cpp. What i have understood that delegate was attached but not called because Items must be editable for the delegate to be called.

checkbox and itemdelegate in a tableview

I'm doing an implementation of a CheckBox that inherits from QitemDelegate, to put it into a QTableView.
the problem is that I get when inserted flush left and I need it centered.
As I understand the method that is responsible for the Paint. I have it written as follows:
void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
bool checkValue;
QStyleOptionButton BtnStyle;
BtnStyle.state = QStyle::State_Enabled;
if(index.model()->data(index, Qt::DisplayRole).toBool() == true)
{
BtnStyle.state |= QStyle::State_On;
checkValue = true;
}else{
BtnStyle.state |= QStyle::State_Off;
checkValue = false;
}
BtnStyle.direction = QApplication::layoutDirection();
BtnStyle.rect = option.rect;
QApplication::style()->drawControl(QStyle::CE_CheckBox,&BtnStyle,painter );
QApplication::style()->drawControl(QStyle::CE_CheckBox,&BtnStyle,painter );
}
what is missing to appear centered?
so I have the delegated:
.h
class BooleanWidget : public QWidget
{
Q_OBJECT
QCheckBox * checkBox;
public:
BooleanWidget(QWidget * parent = 0)
{
checkBox = new QCheckBox(this);
QHBoxLayout * layout = new QHBoxLayout(this);
layout->addWidget(checkBox,0, Qt::AlignCenter);
}
bool isChecked(){return checkBox->isChecked();}
void setChecked(bool value){checkBox->setChecked(value);}
};
class CheckBoxDelegate : public QItemDelegate
{
Q_OBJECT
private:
BooleanWidget *checkbox;
public:
CheckBoxDelegate(QObject *parent);
~CheckBoxDelegate();
void setEditorData( QWidget *editor,const QModelIndex &index )const;
void setModelData( QWidget *editor,QAbstractItemModel *model,const QModelIndex &index )const;
QWidget *createEditor( QWidget *parent,const QStyleOptionViewItem &/* option */,const QModelIndex &/* index */ )const;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
public slots:
void changed( bool );
};
.cpp
void CheckBoxDelegate::changed( bool value )
{
BooleanWidget *checkbox = static_cast<BooleanWidget*>( sender() );
emit commitData( checkbox );
emit closeEditor( checkbox );
}
QWidget *CheckBoxDelegate::createEditor( QWidget *parent,const QStyleOptionViewItem &/* option */,const QModelIndex &/* index */ ) const
{
BooleanWidget *editor = new BooleanWidget( parent );
connect( editor, SIGNAL( toggled ( bool ) ), this, SLOT( changed( bool ) ) );
return editor;
}
void CheckBoxDelegate::setEditorData( QWidget *editor,const QModelIndex &index ) const
{
int value = index.model()->data(index, Qt::DisplayRole).toInt();
BooleanWidget *checkbox = static_cast<BooleanWidget*>(editor);
if(value == 1)
{
checkbox->setChecked(true);
}
else
{
checkbox->setChecked(false);
}
}
void CheckBoxDelegate::setModelData( QWidget *editor,QAbstractItemModel *model,const QModelIndex &index ) const
{
BooleanWidget *checkBox = qobject_cast<BooleanWidget*>( editor );
Qt::CheckState value;
if(checkBox->isChecked())
value = Qt::Checked;
else
value = Qt::Unchecked;
model->setData( index, value, Qt::DisplayRole);
}
void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
drawCheck(painter, option, option.rect, index.data().toBool() ? Qt::Checked : Qt::Unchecked);
drawFocus(painter, option, option.rect);
}
If you are extending the QItemDelegate class, it has a drawCheck() function, what will draw you a nince, centered checkbox. You can use it in the paint() function.
Edit:
Here is an example, assuming you have a class called BooleanEditor, what inherits from QItemDelegate:
void BooleanEditor::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
drawCheck(painter, option, option.rect, index.data().toBool() ? Qt::Checked : Qt::Unchecked);
drawFocus(painter, option, option.rect);
}
For keeping the checkbox centered when entering the edit mode, you can do something like this:
class BooleanWidget : public QWidget
{
Q_OBJECT
QCheckBox * checkBox;
public:
BooleanWidget(QWidget * parent = 0)
{
checkBox = new QCheckBox(this);
QHBoxLayout * layout = new QHBoxLayout(this);
layout->addWidget(checkBox,0, Qt::AlignCenter);
}
bool isChecked(){return checkBox->isChecked();}
void setChecked(bool value){checkBox->setChecked(value);}
};
And in your ItemDelegates createEditor() method return an instance of this BooleanWidget class. In your setModelData() and setEditorData() you can now cast your input widget to this BooleanWidget:
BooleanWidget * widget = qobject_cast<BooleanWidget*>(editor);
and then use the is/setChecked method.
I solve this problem with the following item delegate:
booleanitemdelegate.h
#ifndef BOOLEANITEMDELEGATE_H
#define BOOLEANITEMDELEGATE_H
#include <QItemDelegate>
class BooleanItemDelegate : public QItemDelegate
{
public:
BooleanItemDelegate(QObject *parent);
public:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;public:
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);
};
#endif // BOOLEANITEMDELEGATE_H
booleanitemdelegate.cpp
#include "booleanitemdelegate.h"
BooleanItemDelegate::BooleanItemDelegate(QObject *parent):
QItemDelegate(parent)
{
}
void BooleanItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
drawCheck(painter, option, option.rect, index.data().toBool() ? Qt::Checked : Qt::Unchecked);
drawFocus(painter, option, option.rect);
}
bool BooleanItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
if(event->type() == QEvent::MouseButtonRelease){
model->setData(index, !model->data(index).toBool());
event->accept();
}
return QItemDelegate::editorEvent(event, model, option, index);
}
I hope, I can help.
that's what I did to center-allign the editor control:
QWidget *checkBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QCheckBox *editor = new QCheckBox(parent);
editor->setTristate(allowTriState); //my class' variable
// this does the trick :)
editor->setStyleSheet("QCheckBox {margin-left: 43%; margin-right: 57%;}");
// this should do it better
// editor->setStyleSheet("QCheckBox {margin-left: auto; margin-right: auto;}");
// but in my case the checkbox is slightly to the left of original
return editor;
}
solved the problem as follows:
believes that inherits from the itemDelegate QStyledItemDelegate and reimplemented methods "paint" and "editorEvent".
In the method "editorEvent" emit a signal that indicates which row was selected.
Here is the code
class ItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
signals:
void clickSignal(int);
public:
ItemDelegate(QObject *parent = 0)
: QStyledItemDelegate(parent)
{
}
void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
QStyleOptionViewItemV4 viewItemOption(option);
if (index.column() == 0) {
const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
QRect newRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
QSize(option.decorationSize.width() + 5,option.decorationSize.height()),
QRect(option.rect.x() + textMargin, option.rect.y(),
option.rect.width() - (2 * textMargin), option.rect.height()));
viewItemOption.rect = newRect;
}
QStyledItemDelegate::paint(painter, viewItemOption, index);
}
virtual bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
const QModelIndex &index)
{
Q_ASSERT(event);
Q_ASSERT(model);
// make sure that the item is checkable
Qt::ItemFlags flags = model->flags(index);
if (!(flags & Qt::ItemIsUserCheckable) || !(flags & Qt::ItemIsEnabled))
return false;
// make sure that we have a check state
QVariant value = index.data(Qt::CheckStateRole);
if (!value.isValid())
return false;
// make sure that we have the right event type
if (event->type() == QEvent::MouseButtonRelease) {
const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
QRect checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
option.decorationSize,
QRect(option.rect.x() + (2 * textMargin), option.rect.y(),
option.rect.width() - (2 * textMargin),
option.rect.height()));
} else {
return false;
}
Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
? Qt::Unchecked : Qt::Checked);
emit(clickSignal(index.row()));
return model->setData(index, state, Qt::CheckStateRole);
}
};
the class where I have a connect QTableView do this:
connect(check,SIGNAL(clickSignal(int)),this,SLOT(CheckMark(int))); //check the itemDelegate
performed the operation with the check that were marked
Without too much fussing instead of directly setting rect:
BtnStyle.rect = option.rect;
Construct a new QRect and move x coordinate to the right as much as you like:
QRect r = option.rect;
QRect r1(r.x() + 34, r.y(), r.width(), r.height());
BtnStyle.rect = r1;
The solution in Python to center checkbox and allow user check/uncheck here

QStandardItem + QComboBox

I am trying to put a QComboBox into a QStandardItem to be used in a QStandardItemModel. I have been looking around and I cannot find an answer, any ideas?
You don't store a QComboBox in a QStandardItemModel. Let's say you have the following choices:
A
B
C
D
and you have a list with two items in a QListView, the first value being A the second being D:
QListView* pView = new QListView();
QStandardItemModel* pModel = new QStandardItemModel();
pView->setModel(pModel);
pModel->appendRow(new QStandardItem("A"));
pModel->appendRow(new QStandardItem("D"));
What we created above is a list widget which will display the values of "A" and "D". Now, to the QComboBox. I assume you want that to edit the values of "A" and "D" in the list. For this, you need to create a QItemDelegate.
See http://doc.qt.io/qt-4.8/qitemdelegate.html
An attempt:
class ComboBoxDelegate : public QItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(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;
};
ComboBoxDelegate::ComboBoxDelegate(QObject *parent)
: QItemDelegate(parent)
{
}
QWidget *ComboBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QComboBox *editor = new QComboBox(parent);
editor->addItem("A");
editor->addItem("B");
editor->addItem("C");
editor->addItem("D");
return editor;
}
void ComboBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString value = index.model()->data(index, Qt::EditRole).toString();
QComboBox *cBox = static_cast<QComboBox*>(editor);
cBox->setCurrentIndex(cBox->findText(value));
}
void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *cBox = static_cast<QComboBox*>(editor);
QString value = cBox->currentText();
model->setData(index, value, Qt::EditRole);
}
void ComboBoxDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
And then you need to set the delegate on the QListView to make it work, see:
pView->setItemDelegate(new ComboBoxDelegate(pView));

Resources