QListWidget which signal?
-
I am using QListWidget, the height of the control displays one item, I want to use a signal that is emitted when the selection is changed to another, I started using itemEntered, this is emitted the instant the mouse moves into the control. I tried itemSelectionChanged, I thought this would be the one to use but it does not get emitted when the selection is changed, I've tried various other signals and I can't find one that is emitted when I change the selection.
Is there anything else required to enable this?
-
Ok, in doing this I found the problem, my slot was expecting the selected item to be in the response, it isn't, the signal is being emitted and the slot was being called but due to a bug in the slot it wasn't doing what I wanted....I will sort it out now.
-
@JonB , @jsulm , this is my class:
Protototype:
class clsQtListWidget : public QListWidget { Q_OBJECT friend class clsXMLnode; private: static const char mscszQtSignalCurItemChgd[]; static const char mscszQtSignalCurRowChgd[]; static const char mscszQtSignalTextChgd[]; static const char mscszQtSignalIndxesMoved[]; static const char mscszQtSignalItemActvd[]; static const char mscszQtSignalItemChgd[]; static const char mscszQtSignalItemClicked[]; static const char mscszQtSignalItemDblClicked[]; static const char mscszQtSignalItemEntered[]; static const char mscszQtSignalItemPressed[]; static const char mscszQtSignalItemSelChgd[]; static mpSignals mscmpSignals; clsXMLnode* mpobjNode; QJsonArray aryModelIndexes(const QModelIndexList& crobjIndexes); QMetaObject::Connection connect(clsSignal* pobjSignal); void setProperty(QString strProperty, QString strValue); void listWidgetItemToJSON(const QListWidgetItem* cpobjItem ,const QString& crstrKey ,QJsonObject& robjResult); private slots: void rptrCurrentItemChanged(QListWidgetItem* pobjCurrent ,QListWidgetItem* pobjPrevious); void rptrCurrentRowChanged(int intCurrentRow); void rptrCurrentTextChanged(const QString& crstrCurrentText); void rptrIndexesMoved(const QModelIndexList& crobjIndexes); void rptrItemActivated(QListWidgetItem* pobjItem); void rptrItemChanged(QListWidgetItem* pobjItem); void rptrItemClicked(QListWidgetItem* pobjItem); void rptrItemDoubleClicked(QListWidgetItem* pobjItem); void rptrItemEntered(QListWidgetItem* pobjItem); void rptrItemPressed(QListWidgetItem* pobjItem); void rptrItemSelectionChanged(); void setupControl(const QJsonObject& crobjJSON); public: explicit clsQtListWidget(clsXMLnode* pobjNode ,QString* pstrCSS ,QStringList* pslstProperties ,QWidget* pParent); ~clsQtListWidget(); static bool blnValidSignal(QString strSignal); static mpSignals* pmpGetSignals() { return &clsQtListWidget::mscmpSignals; } };
Implementation:
/** * File: clsQtListWidget.cpp * Notes: Contains implementation of the overriden signal/slot * helper class for the QListWidget control * History: 2021/02/12 Created by Simon Platten */ #include <clsCNT.h> #include <clsQtListWidget.h> #include <clsScriptHelper.h> //Static members const char clsQtListWidget::mscszQtSignalCurItemChgd[] = "currentItemChanged"; const char clsQtListWidget::mscszQtSignalCurRowChgd[] = "currentRowChanged"; const char clsQtListWidget::mscszQtSignalTextChgd[] = "currentTextChanged"; const char clsQtListWidget::mscszQtSignalIndxesMoved[] = "indexesMoved"; const char clsQtListWidget::mscszQtSignalItemActvd[] = "itemActivated"; const char clsQtListWidget::mscszQtSignalItemChgd[] = "itemChanged"; const char clsQtListWidget::mscszQtSignalItemClicked[] = "itemClicked"; const char clsQtListWidget::mscszQtSignalItemDblClicked[] = "itemDoubleClicked"; const char clsQtListWidget::mscszQtSignalItemEntered[] = "itemEntered"; const char clsQtListWidget::mscszQtSignalItemPressed[] = "itemPressed("; const char clsQtListWidget::mscszQtSignalItemSelChgd[] = "itemSelectionChanged"; mpSignals clsQtListWidget::mscmpSignals = { std::make_pair(QString(clsQtListWidget::mscszQtSignalCurItemChgd) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalCurItemChgd)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalCurRowChgd) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalCurRowChgd)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalTextChgd) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalTextChgd)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalIndxesMoved) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalIndxesMoved)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalItemActvd) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalItemActvd)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalItemChgd) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalItemChgd)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalItemClicked) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalItemClicked)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalItemDblClicked) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalItemDblClicked)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalItemEntered) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalItemEntered)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalItemPressed) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalItemPressed)) ,std::make_pair(QString(clsQtListWidget::mscszQtSignalItemSelChgd) ,new clsSignal(clsCNT::mscszListWidget ,clsQtListWidget::mscszQtSignalItemSelChgd)) }; /** * @brief clsQtListWidget::clsQtListWidget * @param pobjNode : Pointer to XML node * @param pstrCSS : Pointer to CSS * @param pParent : Pointer to parent widget */ clsQtListWidget::clsQtListWidget(clsXMLnode* pobjNode ,QString* pstrCSS ,QStringList* pslstProperties ,QWidget* pParent) : QListWidget(pParent), mpobjNode(pobjNode) { clsXMLnode::addSignalsToMap(&clsQtListWidget::mscmpSignals); if ( *pstrCSS != nullptr && pstrCSS->isEmpty() != true ) { setStyleSheet(*pstrCSS); } //Any properties if ( pslstProperties != nullptr ) { foreach( QString strProperty, *pslstProperties ) { QStringList slstProperty = strProperty.split(clsXMLnode::msccPropertyDelimiter); if ( slstProperty.length() != 2 ) { continue; } setProperty(slstProperty[0], slstProperty[1]); } } if ( mpobjNode != nullptr ) { //Process any attributes QString strRows = mpobjNode->strGetAttribute(clsXMLnode::mscszAttrRows); QMargins margins = contentsMargins(); if ( strRows.isEmpty() != true ) { QRect rctBounds = QFontMetrics(font()).boundingRect("X"); int intHeight = (strRows.toInt() * rctBounds.height()) + margins.top() + margins.bottom() + 3; setFixedHeight(intHeight); } QString strColumns = mpobjNode->strGetAttribute(clsXMLnode::mscszAttrColumns); if ( strColumns.isEmpty() != true ) { int intCharWidth = QFontMetrics(font()).maxWidth() ,intWidth = strColumns.toInt() * intCharWidth; setFixedWidth(intWidth); } mpobjNode->setWidget(this); } //Connect XMLnode set-up signal QObject::connect(mpobjNode, &clsXMLnode::setupControl, this, &clsQtListWidget::setupControl); //Connect native signals to repeater signals static const char scszQtListWidget[] = "clsQtListWidget::clsQtListWidget"; static const QString scstrAssert("Cannot connect %1 to repeater!"); QMetaObject::Connection cn; cn = QObject::connect(this, &clsQtListWidget::currentItemChanged ,this, &clsQtListWidget::rptrCurrentItemChanged); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalCurItemChgd) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::currentRowChanged ,this, &clsQtListWidget::rptrCurrentRowChanged); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalCurRowChgd) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::currentTextChanged ,this, &clsQtListWidget::rptrCurrentTextChanged); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalTextChgd) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::indexesMoved ,this, &clsQtListWidget::rptrIndexesMoved); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalIndxesMoved) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::itemActivated ,this, &clsQtListWidget::rptrItemActivated); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalItemActvd) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::itemChanged ,this, &clsQtListWidget::rptrItemChanged); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalItemChgd) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::itemClicked ,this, &clsQtListWidget::rptrItemClicked); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalItemClicked) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::itemDoubleClicked ,this, &clsQtListWidget::rptrItemDoubleClicked); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalItemDblClicked) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::itemEntered ,this, &clsQtListWidget::rptrItemEntered); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalItemEntered) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::itemPressed ,this, &clsQtListWidget::rptrItemPressed); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalItemPressed) .toLatin1().data()); cn = QObject::connect(this, &clsQtListWidget::itemSelectionChanged ,this, &clsQtListWidget::rptrItemSelectionChanged); Q_ASSERT_X(cn, scszQtListWidget ,scstrAssert.arg(clsQtListWidget::mscszQtSignalItemSelChgd) .toLatin1().data()); } /** * @brief clsQtListWidget::~clsQtListWidget */ clsQtListWidget::~clsQtListWidget() { for(int i=0; i<this->count(); i++ ) { QListWidgetItem* pobjItem = item(i); if ( pobjItem != nullptr ) { delete pobjItem; } } } /** * @brief clsQtListWidget::aryModelIndexes * @param crobjIndexes : Constant reference to mode indexes * @return JSON array representing items */ QJsonArray clsQtListWidget::aryModelIndexes(const QModelIndexList& crobjIndexes) { QJsonArray aryJSON; for( const QModelIndex& crobjModelIndex : crobjIndexes ) { QJsonObject objModelIndex; aryJSON.append(objModelIndex); } return aryJSON; } /** * @brief clsQtListWidget::eValidSignal * @param strSignal : The signal to validate * @return true if the signal is valid else false */ bool clsQtListWidget::blnValidSignal(QString strSignal) { return clsXMLnode::blnValidSignal(&clsQtListWidget::mscmpSignals, strSignal); } /** * @brief clsQtListWidget::listWidgetItemToJSON * @param cpobjItem : Constant pointer to list widget item * @param crstrKey : Constant reference to key to store under in robjResult * @param robjResult : Reference to JSON to populate * @return JSON object representing item */ void clsQtListWidget::listWidgetItemToJSON(const QListWidgetItem* cpobjItem ,const QString& crstrKey ,QJsonObject& robjResult) { Q_ASSERT_X(cpobjItem, "clsQtListWidget::listWidgetItemToJSON" , "Invalid pointer passed!"); QJsonObject objJSON; objJSON.insert(clsXMLnode::mscszAttrHidden, cpobjItem->isHidden()); objJSON.insert(clsXMLnode::mscszAttrSelected, cpobjItem->isSelected()); //0 = Unchecked, 1 = Partially checked, 2 = Checked objJSON.insert(clsXMLnode::mscszAttrChecked, cpobjItem->checkState()); objJSON.insert(clsXMLnode::mscszAttrText, cpobjItem->text()); //Anything to append? QVariant objExtra = cpobjItem->data(Qt::UserRole); if ( objExtra.isValid() == true ) { //Yes QJsonValue objExtraJSON(objExtra.toJsonValue()); //Add the additional data to the JSON robjResult.insert(clsXMLnode::mscszAttrData, objExtraJSON); } robjResult.insert(crstrKey, objJSON); } /** * @brief clsQtListWidget::rptrClicked * @param blnChecked : true = checked */ void clsQtListWidget::rptrCurrentItemChanged(QListWidgetItem* pobjCurrent ,QListWidgetItem* pobjPrevious) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalCurItemChgd ,&objJSON) == true ) { if ( pobjCurrent != nullptr ) { listWidgetItemToJSON(pobjCurrent, clsXMLnode::mscszAttrCurrent, objJSON); } if ( pobjPrevious != nullptr ) { listWidgetItemToJSON(pobjPrevious, clsXMLnode::mscszAttrPrevious, objJSON); } emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrCurrentRowChanged * @param intCurrentRow : Current row number */ void clsQtListWidget::rptrCurrentRowChanged(int intCurrentRow) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalCurRowChgd ,&objJSON) == true ) { objJSON.insert(clsXMLnode::mscszAttrCurrent, intCurrentRow); emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrCurrentTextChanged * @param crstrCurrentText : Constant pointer to current text */ void clsQtListWidget::rptrCurrentTextChanged(const QString& crstrCurrentText) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalTextChgd ,&objJSON) == true ) { objJSON.insert(clsXMLnode::mscszAttrCurrent, crstrCurrentText); emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrIndexesMoved * @param crobjIndexes : Constant reference to indexes that have moved */ void clsQtListWidget::rptrIndexesMoved(const QModelIndexList& crobjIndexes) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalIndxesMoved ,&objJSON) == true ) { objJSON.insert(clsXMLnode::mscszAttrIndexes, aryModelIndexes(crobjIndexes)); emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrItemActivated * @param pobjItem : Pointer to activated item */ void clsQtListWidget::rptrItemActivated(QListWidgetItem* pobjItem) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalItemActvd ,&objJSON) == true ) { listWidgetItemToJSON(pobjItem, clsXMLnode::mscszAttrActive, objJSON); emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrItemChanged * @param pobjItem : Pointer to the changed item */ void clsQtListWidget::rptrItemChanged(QListWidgetItem* pobjItem) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalItemChgd ,&objJSON) == true ) { listWidgetItemToJSON(pobjItem, clsXMLnode::mscszAttrChange, objJSON); emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrItemClicked * @param pobjItem : Pointer to the clicked item */ void clsQtListWidget::rptrItemClicked(QListWidgetItem* pobjItem) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalItemClicked ,&objJSON) == true ) { listWidgetItemToJSON(pobjItem, clsXMLnode::mscszAttrClicked, objJSON); emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrItemDoubleClicked * @param pobjItem : Pointer to the double clicked item */ void clsQtListWidget::rptrItemDoubleClicked(QListWidgetItem* pobjItem) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalItemDblClicked ,&objJSON) == true ) { listWidgetItemToJSON(pobjItem, clsXMLnode::mscszAttrDblClicked, objJSON); emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrItemEntered * @param pobjItem : Pointer to the entered item */ void clsQtListWidget::rptrItemEntered(QListWidgetItem* pobjItem) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalItemEntered ,&objJSON) == true ) { listWidgetItemToJSON(pobjItem, clsXMLnode::mscszAttrEntered, objJSON); emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrItemPressed * @param pobjItem : Pointer to the item pressed */ void clsQtListWidget::rptrItemPressed(QListWidgetItem* pobjItem) { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalItemPressed ,&objJSON) == true ) { listWidgetItemToJSON(pobjItem, clsXMLnode::mscszAttrPressed, objJSON); emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::rptrItemSelectionChanged */ void clsQtListWidget::rptrItemSelectionChanged() { QJsonObject objJSON; if ( mpobjNode != nullptr && mpobjNode->blnCheckSubscribers(clsQtListWidget::mscszQtSignalItemSelChgd ,&objJSON) == true ) { emit mpobjNode->commonRptdSignal(objJSON); } } /** * @brief clsQtListWidget::setProperty * @param strProperty : The property name * @param strValue : Value to assign to property */ void clsQtListWidget::setProperty(QString strProperty, QString strValue) { //For later (void)strProperty; (void)strValue; } /** * @brief clsQtListWidget::setupControl * @param crobjJSON : Constant reference to JSON set-up information */ void clsQtListWidget::setupControl(const QJsonObject& crobjJSON) { QJsonObject::const_iterator citFound = crobjJSON.find(clsXMLnode::mscszAttrData); if ( citFound == crobjJSON.end() ) { return; } //Process data QJsonArray aryData = citFound.value().toArray(); int intRow = 0; foreach( QJsonValue objRef, aryData ) { QJsonValue::Type type = objRef.type(); if ( type == QJsonValue::Object ) { QJsonObject objData(objRef.toObject()); citFound = objData.find(clsXMLnode::mscszAttrRec); if ( citFound == objData.end() ) { continue; } QListWidgetItem* pobjItem = item(intRow); if ( pobjItem == nullptr ) { pobjItem = new QListWidgetItem(this); } if ( pobjItem != nullptr ) { pobjItem->setData(Qt::UserRole, QVariant(objRef)); pobjItem->setText(citFound->toString()); } } intRow++; } }
I connect all the signals available to a slot I call a repeater as this then emits another signal which has all the data from the original signal and identifies the signal and slot it is coming from.
-
Ok, in doing this I found the problem, my slot was expecting the selected item to be in the response, it isn't, the signal is being emitted and the slot was being called but due to a bug in the slot it wasn't doing what I wanted....I will sort it out now.
-
@SPlatten
...which is why I suggested knocking up a small demo for these sorts of "inexplicable" issues before posting, always... :) Plus, always put aqDebug()
into slots to see if they are actually called, not rely on some behaviour you expect to be performed. -
From my experience I can say, you can solve some of your problems on your own, when trying to explain them (and your code) to others. A couple of times, I was about to post something here, because I couldn't find any solution for my issue... then, while I wrote the topic and tried to eplain my issue, things became clear and the problem got resolved in like 5mins.
These "Oh-gosh-I-am-such-an-idiot-Moments"... ;-) -
@Pl45m4 Its called Rubber Duck Debugging
🤓