Can't connect button in QListWidgetItem
-
Hi !
I have a derived QListWidget in which my QListWidgetItem have buttons. Somehow I can't find how to connect the buttons (see code below and errors).
If someone can tell me what I'm doing wrong it would really help me out.
Thanks !class ElementItem : public QListWidgetItem { public: ElementItem(): QListWidgetItem(), isLineSelected(false) { button0 = new QPushButton(); button0->setCheckable(true); //this gives warning on VS : no instance of overloaded function "QObject::connect" matches the argument list QObject::connect(button0, SIGNAL(clicked(bool)), this, SLOT(button0Func(bool))); //this doesn't give warning but doesn't build and give error : '&': illegal operation on bound member function expression QObject::connect(button0, &QPushButton::clicked, this, &button0Func); QHBoxLayout* widgetLayout = new QHBoxLayout(); widgetLayout->addWidget(button0); QWidget* widget = new QWidget(); widget->setLayout(widgetLayout); } ~ElementItem() {} bool isLineSelected; QPushButton* button0; public Q_SLOTS: void button0Func(bool val) { isLineSelected = val; } };
-
QListWidgetItem is not a QObject, so it can't be connected to. You can derive from QObject if you want, but honestly your item seems a bit heavy, especially if you plan on having a lot of them.
-
@Paddle Don't get me wrong, if there's only couple of items like in the picture then it should be fine.
If you plan to have a lot of them I would not add buttons at all and instead implement a custom delegate and handle paining of the buttons in QStyledItemDelegate::paint and mouse clicks in QStyledItemDelegate::editorEvent. -
The star delegate example shows how to make custom delegates. It's a bit too much for your case because you don't need the editing functionality, but it shows the general idea.
All you really have to do is override 2 methods. The QStyledItemDelegate::paint method to draw the buttons and QStyledItemDelegate::editorEvent to check for mouse click event and handle it when cursor position of the event is above a button area. Nothing fancy.
-
Nothing fancy it should be indeed, yet when you don't know Qt by profession it becomes a daunting task.
Ok so I created MyDelegate and applied it to my list
ui->listWidgetElements->setItemDelegate(new MyDelegate);
I can even draw the shape of the buttons.
void MyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { //Buttons QStyleOptionButton opt0; opt0.state |= QStyle::State_Enabled; int btnsize = option.rect.height(); opt0.rect.setRect(option.rect.x() + 1, option.rect.y() + 1, btnsize , btnsize ); //opt0.icon = ???; QApplication::style()->drawControl(QStyle::CE_PushButton, &opt0, painter, 0); //Label : painter->drawText(x0 + height * 4 + 3 * border, option.rect.y() + height + 3, QString::fromLatin1("Edge")); }
But my problem is: how do I access my item data? My items are ElementItem which derive from QListWidgetItem. This custom class holds some data, including the item icons, labels and booleans that I want to toggle with the buttons.
class ElementItem : public QListWidgetItem { public: ElementItem(int elementnr, int startingVertex, int midVertex, int endVertex, Base::Type geometryType, bool construction, bool external) : QListWidgetItem() , ElementNbr(elementnr) , isLineSelected(false) , isStartingPointSelected(false) , isEndPointSelected(false) , isMidPointSelected(false) , GeometryType(geometryType) , isConstruction(construction) , isExternal(external) {} ~ElementItem() {} int ElementNbr; bool isLineSelected; bool isStartingPointSelected; bool isEndPointSelected; bool isMidPointSelected; Base::Type GeometryType; bool isConstruction; bool isExternal; QIcon icon0; QIcon icon1; QIcon icon2; QIcon icon3; QString label; };
Thanks.
-
Ok I seem to understand that using a subclass to QListWidgetItem was not proper. I should probably try to change the code to use a custom type (https://doc.qt.io/qt-6/qtcore-tools-customtype-example.html)
Then pass the data using 'setData'. -
when you don't know Qt by profession it becomes a daunting task
No worries, you'll make it. We're here to help.
ui->listWidgetElements->setItemDelegate(new MyDelegate);
Just a note here - the view does not take ownership of the delegate, so don't forget to delete it later or you'll leak memory.
how do I access my item data?
Items hold data for a list of predefined roles like
Qt::DisplayRole
,Qt::FontRole
etc. You can also define your custom roles by giving them valuesQt::UserRole + 1
,Qt::UserRole + 2
and so on.
In your custom item class override QListWidgetItem::data and return your data for these roles. Then you can access them in the delegate via index.
Or, to have less code, don't subclass the item class at all and just use QListWidgetItem::setData on the vanilla items to store your custom data. Something like this:enum MyCustomRoles { ElementNbrRole = Qt::UserRole +1, LineSelectedRole, StartingPointSelectedRole, ... };
then set the values:
QListWidgetItem item; item.setData(ElementNbrRole, 42); item.setData(LineSelectedRole, false); ...
and similarly you can then retrieve the data in the delgate:
int elemNbr = index.data(ElementNbrRole).toInt(); ...
-
Thanks for the return ! It's getting clearer for me.
So what I'm doing is that I create a custom QVariant data type
class ElementData { public: ElementData() = default; ~ElementData() = default; ElementData(const ElementData&) = default; ElementData(int elementnr, int startingVertex, int midVertex, int endVertex, Base::Type geometryType, bool construction, bool external, QIcon ic0,QIcon ic1,QIcon ic2,QIcon ic3, QString lab ) : ElementNbr(elementnr) , StartingVertex(startingVertex) , MidVertex(midVertex) , EndVertex(endVertex) , isLineSelected(false) , isStartingPointSelected(false) , isEndPointSelected(false) , isMidPointSelected(false) , GeometryType(geometryType) , isConstruction(construction) , isExternal(external) , icon0(ic0) , icon1(ic1) , icon2(ic2) , icon3(ic3) , label(lab) {} int ElementNbr; int StartingVertex; int MidVertex; int EndVertex; bool isLineSelected; bool isStartingPointSelected; bool isEndPointSelected; bool isMidPointSelected; Base::Type GeometryType; bool isConstruction; bool isExternal; QIcon icon0; QIcon icon1; QIcon icon2; QIcon icon3; QString label; }; Q_DECLARE_METATYPE(ElementData);
Then I can do on item creation :
QListWidgetItem* itemN = new QListWidgetItem(); ElementData elementData(i, .................... QVariant dataInVariant; dataInVariant.setValue(elementData); itemN->setData(Qt::UserRole, dataInVariant);
Then on Paint function :
void MyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { if (index.data(Qt::UserRole).canConvert<ElementData>()) { ElementData itemData = qvariant_cast<ElementData>(index.data(Qt::UserRole)); if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); /*bool isLineSelected = index.data(Qt::UserRole + 4).toBool(); bool isStartingPointSelected = index.data(Qt::UserRole + 5).toBool(); bool isEndPointSelected = index.data(Qt::UserRole + 6).toBool(); bool isMidPointSelected = index.data(Qt::UserRole + 7).toBool(); Base::Type type = Base::Type::fromKey(index.data(Qt::UserRole + 8).toInt()); bool construction = index.data(Qt::UserRole + 9).toBool(); bool isExternal = index.data(Qt::UserRole + 10).toBool(); bool isNamingBoxChecked = index.data(Qt::UserRole + 11).toBool();*/ int border = 1; int height = option.rect.height(); int x0 = option.rect.x(); int buttonSize = height - 2 * border; int btny = option.rect.y() + border; //Buttons QStyleOptionButton opt0; opt0.state |= QStyle::State_Enabled; opt0.rect.setRect(x0 + border, btny, buttonSize, buttonSize); opt0.icon = itemData.icon0; QApplication::style()->drawControl(QStyle::CE_PushButton, &opt0, painter, 0); //Label : painter->drawText(x0 + height * 4 + 3 * border, option.rect.y() + height + 3, itemData.label); } }
This works for the label, however the icons doesn't appear. I just have empty buttons. Do you know why perhaps?
Thanks! -
@Paddle said in Can't connect button in QListWidgetItem:
opt0.icon = itemData.icon0
The obvious first question is are you sure what is in
itemData.icon0
?Do you need to set
itemData.iconSize
? I don't know. If you do set (after settingicon
) do you see any sign of an icon being there?Oh, I see No icon with QStyleOptionButton
I solved this problem by setting
button.iconSize=QSize(16,16);
as the defaulticonsize
is (-1,-1) so the icon is invisible.Is this relevant to your situation?
Yep, same in https://www.qtcentre.org/threads/58313-No-icon-with-QStyleOptionButton, looks like this is common :)