PDA

View Full Version : Update the combobox delegate in runtime while the popup is opened



PinTxO
30th October 2015, 09:13
Hi everyone.

I have a QStyleItemDelegate for my QCombobox where I show an icon with a text and two more icons.
The items are displayed well but there is an incorrect behaviour when the user has opened the popup of the combobox. The icons change in runtime and when the popup is opened by the user, in spite of doing a "combobox.update" the icons don't refresh. They only refresh when I close the popup and I open it again. A curious thing too happens when I move the mouse on a combobox item because that item refreshes, but only that one.

¿Any help? Thanks

ChrisW67
30th October 2015, 19:06
Post the code of your delegate (or other relevant code) so we can have an idea how to help you.

PinTxO
2nd November 2015, 09:19
Thanks for your interest. Here is my delegate:

ComboUpDownDelegate::ComboUpDownDelegate(QObject* parent): AuniaComboDelegate_Base(parent) {
auto combobox = qobject_cast<QComboBox*>(parent);
initCombo(combobox);

// Icons
m_iconClock = Customisation::getImage("time", QSize(16, 16), QColor("#C8C8C8"));
m_iconWarningNoErrors = Customisation::getImage("warning", QSize(16, 16), QColor("#C8C8C8"));
...
...more icons
...
}

ComboUpDownDelegate::~ComboUpDownDelegate() {
}

void ComboUpDownDelegate::initCombo(QComboBox* combo) {
if (combo != nullptr) {
combo->view()->setEditTriggers(QAbstractItemView::NoEditTriggers) ;
combo->installEventFilter(this);
}
}

QSize ComboUpDownDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const {
auto combo = static_cast<QComboBox*>(this->parent());

const auto text = index.data(Qt::DisplayRole).toString();
const auto style = QApplication::style();

QStyleOptionButton opt;
opt.text = text;
opt.rect = option.rect;
opt.icon = index.data(Qt::DecorationRole).value<QIcon>();
if (combo != nullptr) {
opt.iconSize = combo->iconSize();
}

return style->sizeFromContents(QStyle::CT_CheckBox, &opt, QSize());
}

QWidget* ComboUpDownDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option,
const QModelIndex& index) const {
return nullptr;
}

void ComboUpDownDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
QComboBox* combo = static_cast<QComboBox*>(this->parent());

// Basic style
QStyleOptionViewItemV4 modifiedOption(option);
modifiedOption.text.clear();
modifiedOption.icon = QIcon();
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &modifiedOption, painter);

const auto topLeft = option.rect.topLeft();
const int margin = 3;

painter->setPen(combo->palette().color(QPalette::Text));

// Icon for the state of the device
const bool state1 = index.data(ICON1_ROLE).toBool();
QPixmap icon1(state1 ? m_iconDeviceConnected : m_iconDeviceDisconnected);
painter->drawPixmap(margin, topLeft.y() + margin + 4, icon1);

// Text to show
QFont normalFont(painter->font());
normalFont.setBold(false);
QFont boldFont(normalFont);
boldFont.setBold(true);
QFontMetrics fm(normalFont);
painter->setPen(QColor("#7C7C7C"));

QPoint p(topLeft + QPoint(icon1.width() + 2.5 * margin, fm.height() + 6));
const QString deviceText = index.data(Qt::DisplayRole).toString();
const QString UTText = " - " + index.data(UT_ROLE).toString();
p = drawText(painter, p, deviceText, boldFont);
p.setX(p.x() + 2);
p = drawText(painter, p, UTText, normalFont);

// Icon for the state of the last execution
const int state3 = index.data(ICON3_ROLE).toInt();
QPixmap icon3;
QPixmap noIcon3;
switch (state3) {
case 1:
icon3 = m_iconExecutionNoErrors;
break;
case 2:
icon3 = m_iconExecutionErrors;
break;
case 3:
icon3 = m_iconRunning;
break;
default:
noIcon3 = m_iconExecutionNoErrors;
break;
}
int w;
if(!icon3.isNull())
{
w = option.rect.width() - icon3.width() - margin;
painter->drawPixmap(w, topLeft.y() + margin + 8, icon3);
}
else
{
w = option.rect.width() - noIcon3.width() - margin;
}

// Icon to filter the traces of the devices
const int state2 = index.data(ICON2_ROLE).toInt();
QPixmap icon2;
QPixmap noIcon2;
switch (state2) {
case 1:
icon2 = m_iconEye;
break;
case 2:
icon2 = m_iconStrikethroughEye;
break;
default:
noIcon2 = m_iconEye;
break;
}
if(!icon2.isNull())
{
w -= icon2.width();
w -= 8;
painter->drawPixmap(w, topLeft.y() + margin + 8, icon2);
}
}

bool ComboUpDownDelegate::eventFilter(QObject *object, QEvent *event) {
auto combo = qobject_cast<QComboBox*>(object);

if (combo != nullptr && object == this->parent() && event->type() == QEvent::Paint) {
QStylePainter painter(combo);

painter.setPen(combo->palette().color(QPalette::Text));

QStyleOptionComboBox opt;
opt.initFrom(combo);
opt.editable = combo->isEditable();
opt.frame = combo->hasFrame();
if (combo->hasFocus() && !opt.editable) {
opt.state |= QStyle::State_Selected;
}
opt.subControls = QStyle::SC_All;

if (m_title.isEmpty()) {
opt.currentText = combo->currentText();
} else {
opt.currentText = m_title.arg(combo->count());
}
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);

return true;
}
return QStyledItemDelegate::eventFilter(object, event);
}

void ComboUpDownDelegate::setTitle(const QString& title) {
m_title = title;
}

QString ComboUpDownDelegate::title() const {
return m_title;
}

And my code where I change the data of the combo:

comboDevices->setItemData(index, 0, ComboUpDownDelegate::ICON2_ROLE);
// Show the running icon
comboDevices->setItemData(index, 3, ComboUpDownDelegate::ICON3_ROLE);
comboDevices->update();

This code when the popup of the combobox is opened, the icons don't refresh

PinTxO
2nd November 2015, 16:33
I solved the problem.

The thread that changed the value of each role was not the main thread. To solve it I've emitted a queued signal and without changing any line of code, the combo items refresh with the opened popup. I hope my solution will be useful for other developers.

Thanks