Layout problem
-
I have written an application for testing things and this works perfectly.
mainwindow.ui:
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"/> </widget> <resources/> <connections/> </ui>main.cpp:
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }mainwindow.h:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QDebug> #include <QButtonGroup> #include <QFormLayout> #include <QGroupBox> #include <QLabel> #include <QLayoutItem> #include <QMainWindow> #include <QPushButton> #include <QRadioButton> #include <QVBoxLayout> #include <QScrollArea> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT private: Ui::MainWindow *ui; public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); void makeScrollingArea(QVBoxLayout* pobjVbxLO ,QScrollArea** ppobjScrollArea ,const quint16 uint16FixedHeight ,const quint16 uint16FixedWidth ,QFormLayout** ppobjFormLayout ,QButtonGroup** ppobjBtnGroup = nullptr); }; #endif // MAINWINDOW_Hmainwindow.cpp:
#include "mainwindow.h" #include "ui_mainwindow.h" #include "clsQtLayout.h" void MainWindow::makeScrollingArea(QVBoxLayout* pobjVbxLO ,QScrollArea** ppobjScrollArea ,const quint16 uint16FixedHeight ,const quint16 uint16FixedWidth ,QFormLayout** ppobjFormLayout ,QButtonGroup** ppobjBtnGroup) { if ( pobjVbxLO == nullptr || ppobjFormLayout == nullptr ) { return; } QWidget* pobjWidget(new QWidget); pobjWidget->setObjectName(pobjWidget->metaObject()->className()); *ppobjFormLayout = new QFormLayout; (*ppobjFormLayout)->setObjectName((*ppobjFormLayout)->metaObject()->className()); (*ppobjFormLayout)->setContentsMargins(0,0,0,0); (*ppobjFormLayout)->setSpacing(0); pobjWidget->setLayout(*ppobjFormLayout); if ( ppobjBtnGroup != nullptr ) { *ppobjBtnGroup = new QButtonGroup(pobjWidget); (*ppobjBtnGroup)->setObjectName((*ppobjBtnGroup)->metaObject()->className()); } if ( ppobjScrollArea != nullptr ) { *ppobjScrollArea = new QScrollArea; (*ppobjScrollArea)->setObjectName((*ppobjScrollArea)->metaObject()->className()); (*ppobjScrollArea)->setWidget(pobjWidget); if ( uint16FixedHeight > 0 ) { (*ppobjScrollArea)->setFixedHeight(uint16FixedHeight); } if ( uint16FixedWidth > 0 ) { (*ppobjScrollArea)->setFixedWidth(uint16FixedWidth); } (*ppobjScrollArea)->setWidgetResizable(true); pobjVbxLO->addWidget(*ppobjScrollArea); } } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); int intFixedHeight(19 * 3/*fontMetrics().height() * 3*/); QVBoxLayout* pvbxLayout(new QVBoxLayout); QString strSA("background-color:#ffaaaa;") ,strSB("background-color:#aaffaa;") ,strSC("background-color:#aaaaff;") ,strSD("background-color:#ffaaff;"); clsQtLayout* pobjLA(new clsQtLayout(&strSA)) ,* pobjLB(new clsQtLayout(&strSB)) ,* pobjLC(new clsQtLayout(&strSC)) ,* pobjLD(new clsQtLayout(&strSD)); pobjLA->pobjGetScroller()->setFixedHeight(intFixedHeight); pobjLB->pobjGetScroller()->setFixedHeight(intFixedHeight); pobjLC->pobjGetScroller()->setFixedHeight(intFixedHeight); pobjLD->pobjGetScroller()->setFixedHeight(intFixedHeight); QRadioButton* pRadioButton; for( int i=1; i<=15; i++ ) { pRadioButton = new QRadioButton(QString("Radio %1").arg(i)); pRadioButton->setObjectName(pRadioButton->text()); if ( i < 6 ) { pobjLA->addButton(*pRadioButton); } else if ( i < 11 ) { pobjLB->addButton(*pRadioButton); } else { pobjLC->addButton(*pRadioButton); } QObject::connect(pRadioButton, &QRadioButton::clicked, [pRadioButton](bool blnChecked) { qDebug() << pRadioButton->group()->objectName() << ":" << pRadioButton->text() << " Checked: " << blnChecked; } ); } pvbxLayout->addWidget(pobjLA->pobjGetScroller()); pvbxLayout->addWidget(pobjLB->pobjGetScroller()); pvbxLayout->addWidget(pobjLC->pobjGetScroller()); pvbxLayout->addWidget(new QPushButton("HELLO")); QFormLayout* pfrmLayout; QScrollArea* pobjSA; makeScrollingArea(pvbxLayout, &pobjSA, 0, 0, &pfrmLayout); QHBoxLayout* phblo(new QHBoxLayout); for( int i=1; i<=10; i++ ) { QPushButton* pbtnTemp(new QPushButton(QString("Button %1").arg(i))); phblo->addWidget(pbtnTemp); } pfrmLayout->addRow(phblo); for ( int i=1; i<=5; i++ ) { pRadioButton = new QRadioButton(QString("Radio %1").arg(i)); pRadioButton->setObjectName(pRadioButton->text()); pobjLD->addButton(*pRadioButton); } pvbxLayout->addWidget(pobjLD->pobjGetScroller()); ui->centralwidget->setLayout(pvbxLayout); } MainWindow::~MainWindow() { delete ui; }clsQtLayout.h:
#ifndef CLSQTLAYOUT_H #define CLSQTLAYOUT_H #include <QAbstractButton> #include <QButtonGroup> #include <QFormLayout> #include <QGridLayout> #include <QHBoxLayout> #include <QLayout> #include <QScrollArea> class clsQtLayout : public QWidget { friend class clsXMLnode; private: QButtonGroup* mpobjButtonGroup; QFormLayout* mpobjFLO; // clsXMLnode* mpobjNode; QScrollArea* mpobjScroller; public: static const char mscszFormLayout[]; static const char mscszGridLayout[]; static const char mscszHBoxLayout[]; static const char mscszVBoxLayout[]; explicit clsQtLayout(/*clsXMLnode* pobjNode,*/QString* pstrCSS = nullptr , QWidget* pobjParent = nullptr); ~clsQtLayout(); void addButton(QAbstractButton& robjButton); QScrollArea* pobjGetScroller() { return mpobjScroller; } }; #endif // CLSQTLAYOUT_HclsQtLayout.cpp:
#include <clsQtLayout.h> //Static initialisation const char clsQtLayout::mscszFormLayout[] = "QFormLayout"; const char clsQtLayout::mscszGridLayout[] = "QGridLayout"; const char clsQtLayout::mscszHBoxLayout[] = "QHBoxLayout"; const char clsQtLayout::mscszVBoxLayout[] = "QVBoxLayout"; /** * @brief clsQtLayout::clsQtLayout * @param pobjNode : Pointer to XML node * @param pstrCSS : Pointer to CSS * @param pobjParent : Pointer to parent widget */ clsQtLayout::clsQtLayout(/*clsXMLnode* pobjNode,*/ QString* pstrCSS , QWidget* pobjParent) : QWidget(pobjParent) , mpobjButtonGroup(nullptr) { // Q_ASSERT_X(pobjNode!=nullptr, "clsQtLayout::clsQtLayout", "No node supplied!"); QString strButtonGroup("buttonGrp") ,strID("rdoTest") ,strHeight("64") ,strName(QString("%1: %2").arg("Layout").arg("Form")) ,strType("form") ,strWidth; if ( strID.isEmpty() != true ) { strName += QString(", %1: %2").arg("ID").arg(strID); } mpobjFLO = nullptr; setObjectName(strName); if ( strType.compare("form") == 0 ) { //Set-up form mpobjFLO = new QFormLayout; mpobjFLO->setObjectName(strName + ", QFormLayout"); mpobjFLO->setContentsMargins(0,0,0,0); mpobjFLO->setSpacing(0); setLayout(mpobjFLO); } if ( strButtonGroup.isEmpty() != true ) { mpobjButtonGroup = new QButtonGroup(this); mpobjButtonGroup->setObjectName(strName + ", QButtonGroup"); } QString strHorzSpacing("0"), strVertSpacing("0"); if ( mpobjFLO != nullptr ) { if ( strHorzSpacing.isEmpty() != true ) { int intHorzSpacing(strHorzSpacing.toInt()); if ( intHorzSpacing >= 0 ) { mpobjFLO->setHorizontalSpacing(intHorzSpacing); } } if ( strVertSpacing.isEmpty() != true ) { int intVertSpacing(strVertSpacing.toInt()); if ( intVertSpacing >= 0 ) { mpobjFLO->setVerticalSpacing(intVertSpacing); } } } mpobjScroller = new QScrollArea; mpobjScroller->setObjectName(strName + ", QScrollArea"); mpobjScroller->setWidget(this); if ( strHeight.isEmpty() != true ) { int intHeight(strHeight.toInt()); if ( intHeight >= 0 ) { mpobjScroller->setFixedHeight(intHeight); } } if ( strWidth.isEmpty() != true ) { int intWidth(strWidth.toInt()); if ( intWidth >= 0 ) { mpobjScroller->setFixedWidth(intWidth); } } if ( pstrCSS != nullptr && pstrCSS->isEmpty() != true ) { mpobjScroller->setStyleSheet(*pstrCSS); } mpobjScroller->setWidgetResizable(true); } /** * @brief clsQtLayout::~clsQtLayout */ clsQtLayout::~clsQtLayout() { if ( mpobjScroller != nullptr ) { mpobjScroller->deleteLater(); } } /** * @brief clsQtLayout::addButton * @param robjButton : Reference to button */ void clsQtLayout::addButton(QAbstractButton& robjButton) { if ( mpobjButtonGroup != nullptr ) { mpobjButtonGroup->addButton(&robjButton); } if ( mpobjFLO != nullptr ) { mpobjFLO->addRow("", &robjButton); } }The output from the above prototyping application:

In my actual application when I try to use the same class clsQtLayout I am getting very different results:

The Male and Female radio buttons should appear in the red box, but they don't. The red box the style set on the scroller. I'm not sure what I've done wrong, my configuration XML for this layout:
<layout id="rdoLO" buttongroup="enSEX" height="12" hspacing="0" type="form" vspacing="0" properties="QLayout { background-color:#ffaaaa;}"> <radiobutton id="rdoM" group="enSEX" text="Male" default="true" position="0,0"/> <radiobutton id="rdoF" group="enSEX" text="Female" position="1,0"/> </layout>In my code when I encounter a widget e.g. QRadioButton with a parent that has a layout I call the addButton member function of the layout, What I'm seeing is that the first time its called the Male is added, but on the second call Male is gone and Female is added, I just can't see why.
The following is called before the widget to add is added to the layout:
clsQtLayout* pobjLayout(nullptr); QString strGroup(strGetAttribute(clsXMLnode::mscszAttrGroup)); if ( strGroup.isEmpty() != true ) { QButtonGroup* pobjBtnGrp(qobject_cast<QButtonGroup*>(pobjGetGroup(strGroup))); if ( pobjBtnGrp != nullptr ) { pobjLayout = static_cast<clsQtLayout*>(pobjBtnGrp->parent()); } }After the widget to add is created:
if ( pobjWidget != nullptr ) { if ( pobjLayout != nullptr ) { QAbstractButton* pobjButton(qobject_cast<QAbstractButton*>(pobjWidget)); if ( pobjButton != nullptr ) { QString strName(pobjButton->objectName()); if ( strName.isEmpty() == true ) { pobjButton->setObjectName(pobjButton->text()); } pobjLayout->addButton(*pobjButton); } }[edit], I've stripped out the above two bits of code and in the radio button constructor (my overridden version of QRadioButton), I've added:
if ( strGroup.isEmpty() != true ) { QButtonGroup* pobjBtnGrp(qobject_cast<QButtonGroup*>( mpobjNode->pobjManageGroupType(strGroup))); if ( pobjBtnGrp != nullptr ) { clsQtLayout* pobjLayout(static_cast<clsQtLayout*>(pobjBtnGrp->parent())); if ( pobjLayout != nullptr ) { pobjLayout->addButton(*this); } } }Where strGroup is the group attribute as specified I the XML. The QButtonGroup is created by the clsQtLayout class when it processes the buttongroup attribute and sets the parent of the button group to be the layout widget which points to an instance of clsQtLayout. However the result is identical.
-
@JonB , fixed another bug which prevents renaming of object already assigned a name:
void clsXMLnode::setObjName(QObject& robjObject, const QString& crstrName) { if ( crstrName.isEmpty() != true ) { QString strCurrent(robjObject.objectName()); if ( strCurrent.isEmpty() != true ) { qdbg() << "Current name: " << strCurrent << ", Tried to change to: " << crstrName; } else { //Set the object name robjObject.setObjectName(crstrName); } } }Now the output is:
S000000000044E000000000659T14:37:04.373DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Male S000000000045E000000000659T14:37:04.373DL00000122F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Male S000000000046E000000000659T14:37:04.373DL00000125F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x60000002ac70, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000047E000000000659T14:37:04.373DL00000127F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=1 list:(clsQtRadioButton(0x600001723c00, name=Male)) S000000000048E000000000659T14:37:04.373DL00000128F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[0]: clsQtRadioButton(0x600001723c00, name=Male) S000000000049E000000000659T14:37:04.373DL00003730F../clsMainWnd.cpp[static void clsXMLnode::setObjName] Current name: Male S000000000050E000000000659T14:37:04.373DL00000051F../clsQtRadionButton.cpp[auto clsQtRadioButton::clsQtRadioButton] Female S000000000051E000000000659T14:37:04.373DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Female S000000000052E000000000659T14:37:04.373DL00000122F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Female S000000000053E000000000659T14:37:04.373DL00000125F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x60000002ac70, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000054E000000000659T14:37:04.373DL00000127F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=2 list:(clsQtRadioButton(0x600001723c00, name=Male), clsQtRadioButton(0x60000170f340, name=Female)) S000000000055E000000000660T14:37:04.373DL00000128F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[0]: clsQtRadioButton(0x600001723c00, name=Male) S000000000056E000000000660T14:37:04.373DL00000130F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[1]: clsQtRadioButton(0x60000170f340, name=Female)Finally, fixed!

When processing the widgets, if the type of widget is a layout then the actual widget connected to the parent layout is the scroll area not the layout itself.
I then had another issue which was to take the widgets that are children of any groups in the layout and adding these to the layouts.
Next issue which I'm now going to fix is the individual row height of the children so that have the correct height in the layout.
Final demo with row height fixed:

From XML:
<layout id="rdoLO" height="56" hspacing="0" type="form" vspacing="0" properties="QLayout { background-color:#ffaaaa;}"> <buttongroup id="enSEX"> <radiobutton id="rdoM" height="28" text="Male" default="true" position="0,0"/> <radiobutton id="rdoF" height="28" text="Female" position="1,0"/> </buttongroup> </layout> -
Hi,
There seems to be some reversed logic here. Why should à layout create widgets at all ?
Also, why a addButton when layouts have addWidgets that takes any QWidget based class ?Your code seems to try to auto-generate itself rather than have one builder class that does this from an "external" point of view.
-
Hi,
There seems to be some reversed logic here. Why should à layout create widgets at all ?
Also, why a addButton when layouts have addWidgets that takes any QWidget based class ?Your code seems to try to auto-generate itself rather than have one builder class that does this from an "external" point of view.
@SGaist , the layout doesn't create the widgets, the XML is free format, it represents that the radio buttons are contained in the layout. All widgets are created during parsing the XML, if they happen to be inside a layout then a layout is created and the widgets added to it.
My engine allows loads the design from the XML and this can the XML can be modified and reloaded into the engine with no re-compilation required, all the features of the widgets and layouts are managed by attributes, signals and slots can be connected by defining subscriber XML nodes.
addButton is just the name of the function I've added:
void clsQtLayout::addButton(QAbstractButton& robjButton) { if ( mpobjButtonGroup != nullptr ) { mpobjButtonGroup->addButton(&robjButton); } if ( mpobjFLO != nullptr ) { mpobjFLO->addRow("", &robjButton); } } -
Hi,
There seems to be some reversed logic here. Why should à layout create widgets at all ?
Also, why a addButton when layouts have addWidgets that takes any QWidget based class ?Your code seems to try to auto-generate itself rather than have one builder class that does this from an "external" point of view.
@SGaist , I've restructured the XML and code, the XML now looks like this:
<layout id="rdoLO" height="12" hspacing="0" type="form" vspacing="0" properties="QLayout { background-color:#ffaaaa;}"> <buttongroup id="enSEX"> <radiobutton id="rdoM" text="Male" default="true" position="0,0"/> <radiobutton id="rdoF" text="Female" position="1,0"/> </buttongroup> </layout>The prototype for the layout:
#ifndef CLSQTLAYOUT_H #define CLSQTLAYOUT_H #include <clsMainWnd.h> #include <clsSignal.h> #include <QAbstractButton> #include <QFormLayout> #include <QGridLayout> #include <QHBoxLayout> #include <QLayout> #include <QScrollArea> #include <QVBoxLayout> typedef std::map<QString, QButtonGroup*> mpGroups; class clsQtLayout : public QWidget { friend class clsXMLnode; private: mpGroups* mmpGroups; QFormLayout* mpobjFLO; clsXMLnode* mpobjNode; QScrollArea* mpobjScroller; public: static const char mscszFormLayout[]; static const char mscszGridLayout[]; static const char mscszHBoxLayout[]; static const char mscszVBoxLayout[]; explicit clsQtLayout(clsXMLnode* pobjNode, QString* pstrCSS , QWidget* pobjParent); ~clsQtLayout(); void addButton(QAbstractButton& robjButton, const QString& crstrGroup); void addGroup(const QString& crstrGroup); void addWidget(QWidget *pobjWidget); bool blnIsLayout() { return true; } QFormLayout* pobjGetFormLO() { return mpobjFLO; } QScrollArea* pobjGetScroller() { return mpobjScroller; } }; #endif // CLSQTLAYOUT_HThe implementation:
/** * File: clsQtLayout.cpp * Notes: Contains implementation of the class clsQtLayout * History: 2021/10/23 Created by Simon Platten */ #include <clsQtLayout.h> //Static initialisation const char clsQtLayout::mscszFormLayout[] = "QFormLayout"; const char clsQtLayout::mscszGridLayout[] = "QGridLayout"; const char clsQtLayout::mscszHBoxLayout[] = "QHBoxLayout"; const char clsQtLayout::mscszVBoxLayout[] = "QVBoxLayout"; /** * @brief clsQtLayout::clsQtLayout * @param pobjNode : Pointer to XML node * @param pstrCSS : Pointer to CSS * @param pobjParent : Pointer to parent widget */ clsQtLayout::clsQtLayout(clsXMLnode* pobjNode, QString* pstrCSS , QWidget* pobjParent) : QWidget(pobjParent), mpobjNode(pobjNode) { Q_ASSERT_X(pobjNode!=nullptr, "clsQtLayout::clsQtLayout" , "No node supplied!"); QString strID(pobjNode->strGetAttribute(clsXMLnode::mscszAttrID)) ,strHeight(mpobjNode->strGetAttribute(clsXMLnode::mscszAttrHeight)) ,strHorzSpacing(mpobjNode->strGetAttribute( clsXMLnode::mscszAttrSpacingH)) ,strName ,strType(pobjNode->strGetAttribute(clsXMLnode::mscszAttrType)) ,strVertSpacing(mpobjNode->strGetAttribute( clsXMLnode::mscszAttrSpacingV)) ,strWidth(mpobjNode->strGetAttribute(clsXMLnode::mscszAttrWidth)); mmpGroups = nullptr; mpobjFLO = nullptr; strName = QString("%1: %2").arg(clsXMLnode::mscszNameLayout).arg(strType); if ( strID.isEmpty() != true ) { strName += QString(", %1: %2").arg(clsXMLnode::mscszNameID).arg(strID); } clsXMLnode::setObjName(*this, strName); if ( strType.compare(clsXMLnode::mscszLayoutForm) == 0 ) { //Set-up form mpobjFLO = new QFormLayout; clsXMLnode::setObjName(*mpobjFLO, strName + ", QFormLayout"); mpobjFLO->setContentsMargins(0,0,0,0); mpobjFLO->setSpacing(0); setLayout(mpobjFLO); //Any spacing to apply? if ( strHorzSpacing.isEmpty() != true ) { int intHorzSpacing(strHorzSpacing.toInt()); if ( intHorzSpacing >= 0 ) { mpobjFLO->setHorizontalSpacing(intHorzSpacing); } } if ( strVertSpacing.isEmpty() != true ) { int intVertSpacing(strVertSpacing.toInt()); if ( intVertSpacing >= 0 ) { mpobjFLO->setVerticalSpacing(intVertSpacing); } } } mpobjScroller = new QScrollArea; clsXMLnode::setObjName(*mpobjScroller, strName + ", QScrollArea"); mpobjScroller->setWidget(this); if ( strHeight.isEmpty() != true ) { int intHeight(strHeight.toInt()); if ( intHeight >= 0 ) { mpobjScroller->setFixedHeight(intHeight); } } if ( strWidth.isEmpty() != true ) { int intWidth(strWidth.toInt()); if ( intWidth >= 0 ) { mpobjScroller->setFixedWidth(intWidth); } } if ( pstrCSS != nullptr && pstrCSS->isEmpty() != true ) { mpobjScroller->setStyleSheet(*pstrCSS); } mpobjScroller->setWidgetResizable(true); } /** * @brief clsQtLayout::~clsQtLayout */ clsQtLayout::~clsQtLayout() { if ( mmpGroups != nullptr ) { for( mpGroups::iterator itGrp=mmpGroups->begin(); itGrp!=mmpGroups->end(); itGrp++ ) { QButtonGroup* pobjGroup(itGrp->second); pobjGroup->deleteLater(); } delete mmpGroups; } if ( mpobjScroller != nullptr ) { mpobjScroller->deleteLater(); } } /** * @brief clsQtLayout::addButton * @param robjButton : Reference to button * @param crstrGroup : Constant reference to button group */ void clsQtLayout::addButton(QAbstractButton& robjButton, const QString& crstrGroup) { if ( crstrGroup.isEmpty() != true && mmpGroups != nullptr ) { mpGroups::iterator itGrp(mmpGroups->find(crstrGroup)); if ( itGrp != mmpGroups->end() ) { QButtonGroup* pobjBtnGrp(itGrp->second); pobjBtnGrp->addButton(&robjButton); } } if ( mpobjFLO != nullptr ) { mpobjFLO->addWidget(&robjButton); } } /** * @brief clsQtLayout::addGroup * @param crstrGroup : Group ID */ void clsQtLayout::addGroup(const QString& crstrGroup) { if ( crstrGroup.isEmpty() != true ) { QButtonGroup* pobjGroup(new QButtonGroup(this)); clsXMLnode::setObjName(*pobjGroup, QString("%1, %2: %3") .arg(objectName()) .arg(clsXMLnode::mscszTypeQButtonGroup) .arg(crstrGroup)); if ( mmpGroups == nullptr ) { mmpGroups = new mpGroups; } mmpGroups->insert(std::make_pair(crstrGroup, pobjGroup)); } } /** * @brief clsQtLayout::addWidget * @param pobjWidget Pointer to widget to add */ void clsQtLayout::addWidget(QWidget *pobjWidget) { if ( pobjWidget != nullptr && mpobjNode != nullptr ) { QLayout* pobjLayout(layout()); if ( pobjLayout != nullptr ) { pobjLayout->addWidget(pobjWidget); } } }When I step into the addButton function above, I can see the widget is added and the debugger shows:

So far so good, but on the next call of addButton:

I can see when entering addButton that Male isn't there anymore, I've checked the code and there isn't anything that clears or removes the item and it is the same layout, what is going on?I've checked the code and the button added each time is a different and new object created using new so it isn't the same being replaced.
-
@SPlatten said in Layout problem:
void addButton(QAbstractButton& robjButton, const QString& crstrGroup);
Why are you passing a reference here ? The usual way to pass QObject based class around is through pointers.
-
@SPlatten said in Layout problem:
void addButton(QAbstractButton& robjButton, const QString& crstrGroup);
Why are you passing a reference here ? The usual way to pass QObject based class around is through pointers.
-
Still trying to figure out what is going on, I have added some debug code to my addButton function:
void clsQtLayout::addButton(QAbstractButton* pobjButton, const QString& crstrGroup) { if ( pobjButton == nullptr ) { return; } if ( crstrGroup.isEmpty() != true ) { QButtonGroup* pobjBtnGrp(pobjAddGroup(crstrGroup)); if ( pobjBtnGrp != nullptr ) { {qdbg() << "BEFORE: " << pobjButton->text(); QButtonGroup* pobjCurGrp(pobjButton->group()); if ( pobjCurGrp != nullptr ) { qdbg() << "CURRENT GROUP: " << pobjCurGrp; QList<QAbstractButton*> lstButtons(pobjCurGrp->buttons()); qdbg() << QString("BUTTONS.length=%1 list:").arg(lstButtons.length()) << lstButtons; }} pobjBtnGrp->addButton(pobjButton); {qdbg() << "AFTER: " << pobjButton->text(); QButtonGroup* pobjCurGrp(pobjButton->group()); if ( pobjCurGrp != nullptr ) { qdbg() << "CURRENT GROUP: " << pobjCurGrp; QList<QAbstractButton*> lstButtons(pobjCurGrp->buttons()); qdbg() << QString("BUTTONS.length=%1 list:").arg(lstButtons.length()) << lstButtons; }} } } if ( mpobjFLO != nullptr ) { mpobjFLO->addWidget(pobjButton); } }The output of from the above:
S000000000036E000000000652T14:12:25.261DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Male S000000000037E000000000652T14:12:25.261DL00000121F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Male S000000000038E000000000652T14:12:25.262DL00000124F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x600000027fe0, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000039E000000000652T14:12:25.262DL00000126F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=1 list:(clsQtRadioButton(0x600001774c40, name=Male)) S000000000040E000000000652T14:12:25.262DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Female S000000000041E000000000652T14:12:25.262DL00000121F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Female S000000000042E000000000652T14:12:25.262DL00000124F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x600000027fe0, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000043E000000000652T14:12:25.262DL00000126F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=2 list:(clsQtRadioButton(0x600001774c40, name=Widget: radiobutton, id: rdoM), clsQtRadioButton(0x600001774e00, name=Female))Every line output is prefixed with S the sequence number, E elapsed time, T real time, D = Debug, L = Line number, F = function which includes the file, then the debug message.
From the output when the first button Male is Added to the group the debug output simply shows Male as it has no group, after adding Male to the group the buttons in the list shows simply clsQtRadioButton(0x60000170a180, name=Male. When the Female button is added the buttons in the group changes to clsQtRadioButton(0x60000170a180, name=Widget: radiobutton, id: rdoM), clsQtRadioButton(0x60000170c5c0, name=Female.
From the output after the Female button is added it looks like the Male button is still present but has changed from:
clsQtRadioButton(0x60000170a180, name=Male)
to:
clsQtRadioButton(0x60000170a180, name=Widget: radiobutton, id: rdoM)
Why and is this normal? -
Still trying to figure out what is going on, I have added some debug code to my addButton function:
void clsQtLayout::addButton(QAbstractButton* pobjButton, const QString& crstrGroup) { if ( pobjButton == nullptr ) { return; } if ( crstrGroup.isEmpty() != true ) { QButtonGroup* pobjBtnGrp(pobjAddGroup(crstrGroup)); if ( pobjBtnGrp != nullptr ) { {qdbg() << "BEFORE: " << pobjButton->text(); QButtonGroup* pobjCurGrp(pobjButton->group()); if ( pobjCurGrp != nullptr ) { qdbg() << "CURRENT GROUP: " << pobjCurGrp; QList<QAbstractButton*> lstButtons(pobjCurGrp->buttons()); qdbg() << QString("BUTTONS.length=%1 list:").arg(lstButtons.length()) << lstButtons; }} pobjBtnGrp->addButton(pobjButton); {qdbg() << "AFTER: " << pobjButton->text(); QButtonGroup* pobjCurGrp(pobjButton->group()); if ( pobjCurGrp != nullptr ) { qdbg() << "CURRENT GROUP: " << pobjCurGrp; QList<QAbstractButton*> lstButtons(pobjCurGrp->buttons()); qdbg() << QString("BUTTONS.length=%1 list:").arg(lstButtons.length()) << lstButtons; }} } } if ( mpobjFLO != nullptr ) { mpobjFLO->addWidget(pobjButton); } }The output of from the above:
S000000000036E000000000652T14:12:25.261DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Male S000000000037E000000000652T14:12:25.261DL00000121F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Male S000000000038E000000000652T14:12:25.262DL00000124F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x600000027fe0, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000039E000000000652T14:12:25.262DL00000126F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=1 list:(clsQtRadioButton(0x600001774c40, name=Male)) S000000000040E000000000652T14:12:25.262DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Female S000000000041E000000000652T14:12:25.262DL00000121F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Female S000000000042E000000000652T14:12:25.262DL00000124F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x600000027fe0, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000043E000000000652T14:12:25.262DL00000126F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=2 list:(clsQtRadioButton(0x600001774c40, name=Widget: radiobutton, id: rdoM), clsQtRadioButton(0x600001774e00, name=Female))Every line output is prefixed with S the sequence number, E elapsed time, T real time, D = Debug, L = Line number, F = function which includes the file, then the debug message.
From the output when the first button Male is Added to the group the debug output simply shows Male as it has no group, after adding Male to the group the buttons in the list shows simply clsQtRadioButton(0x60000170a180, name=Male. When the Female button is added the buttons in the group changes to clsQtRadioButton(0x60000170a180, name=Widget: radiobutton, id: rdoM), clsQtRadioButton(0x60000170c5c0, name=Female.
From the output after the Female button is added it looks like the Male button is still present but has changed from:
clsQtRadioButton(0x60000170a180, name=Male)
to:
clsQtRadioButton(0x60000170a180, name=Widget: radiobutton, id: rdoM)
Why and is this normal?@SPlatten
Bear in mind I have never used aQButtonGroup! Or if I have I didn't look at it, it just worked.A quick glance at https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qbuttongroup.cpp.html, I don't spot anything there changing object names.....
I assume you would know if your code ever generates an object name like
Widget: radiobutton, id: rdoM.Now that you say you (seem to) have a object name being changed (which I did not expect), use signal
QObject::objectNameChangedto monitor when that happens.Otherwise I guess it's possible the debug display of
QButtonGroupjust happens to output more than one button in the format you see, I don't know. (If you then remove the female button, does display output with just male in it revert to how it was originally or remain in the "changed" form?)You could examine what is in your
QButtonGroup::buttons()[0]in more detail to see what is really going there, compared against theQButtonGroup::buttons()[1]. -
@SPlatten
Bear in mind I have never used aQButtonGroup! Or if I have I didn't look at it, it just worked.A quick glance at https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qbuttongroup.cpp.html, I don't spot anything there changing object names.....
I assume you would know if your code ever generates an object name like
Widget: radiobutton, id: rdoM.Now that you say you (seem to) have a object name being changed (which I did not expect), use signal
QObject::objectNameChangedto monitor when that happens.Otherwise I guess it's possible the debug display of
QButtonGroupjust happens to output more than one button in the format you see, I don't know. (If you then remove the female button, does display output with just male in it revert to how it was originally or remain in the "changed" form?)You could examine what is in your
QButtonGroup::buttons()[0]in more detail to see what is really going there, compared against theQButtonGroup::buttons()[1].@JonB , thank you, I modified the function:
void clsQtLayout::addButton(QAbstractButton* pobjButton, const QString& crstrGroup) { if ( pobjButton == nullptr ) { return; } if ( crstrGroup.isEmpty() != true ) { QButtonGroup* pobjBtnGrp(pobjAddGroup(crstrGroup)); if ( pobjBtnGrp != nullptr ) { {qdbg() << "BEFORE: " << pobjButton->text(); QButtonGroup* pobjCurGrp(pobjButton->group()); if ( pobjCurGrp != nullptr ) { qdbg() << "CURRENT GROUP: " << pobjCurGrp; QList<QAbstractButton*> lstButtons(pobjCurGrp->buttons()); qdbg() << QString("BUTTONS.length=%1 list:").arg(lstButtons.length()) << lstButtons; qdbg() << "Button[0]: " << lstButtons[0]; }} pobjBtnGrp->addButton(pobjButton); {qdbg() << "AFTER: " << pobjButton->text(); QButtonGroup* pobjCurGrp(pobjButton->group()); if ( pobjCurGrp != nullptr ) { qdbg() << "CURRENT GROUP: " << pobjCurGrp; QList<QAbstractButton*> lstButtons(pobjCurGrp->buttons()); qdbg() << QString("BUTTONS.length=%1 list:").arg(lstButtons.length()) << lstButtons; qdbg() << "Button[0]: " << lstButtons[0]; if ( lstButtons.length() > 1 ) { qdbg() << "Button[1]: " << lstButtons[1]; } }} } } if ( mpobjFLO != nullptr ) { mpobjFLO->addWidget(pobjButton); } }The output is now:
S000000000036E000000000719T14:24:00.461DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Male S000000000037E000000000719T14:24:00.461DL00000122F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Male S000000000038E000000000719T14:24:00.461DL00000125F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x600000028f50, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000039E000000000719T14:24:00.461DL00000127F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=1 list:(clsQtRadioButton(0x60000176f140, name=Male)) S000000000040E000000009972T14:24:09.714DL00000128F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[0]: clsQtRadioButton(0x60000176f140, name=Male) S000000000041E000000012687T14:24:12.429DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Female S000000000042E000000012687T14:24:12.429DL00000122F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Female S000000000043E000000012687T14:24:12.429DL00000125F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x600000028f50, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000044E000000012687T14:24:12.429DL00000127F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=2 list:(clsQtRadioButton(0x60000176f140, name=Widget: radiobutton, id: rdoM), clsQtRadioButton(0x600001756d00, name=Female)) S000000000045E000000015192T14:24:14.934DL00000128F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[0]: clsQtRadioButton(0x60000176f140, name=Widget: radiobutton, id: rdoM) S000000000046E000000018239T14:24:17.981DL00000130F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[1]: clsQtRadioButton(0x600001756d00, name=Female) -
@SPlatten
Bear in mind I have never used aQButtonGroup! Or if I have I didn't look at it, it just worked.A quick glance at https://code.woboq.org/qt5/qtbase/src/widgets/widgets/qbuttongroup.cpp.html, I don't spot anything there changing object names.....
I assume you would know if your code ever generates an object name like
Widget: radiobutton, id: rdoM.Now that you say you (seem to) have a object name being changed (which I did not expect), use signal
QObject::objectNameChangedto monitor when that happens.Otherwise I guess it's possible the debug display of
QButtonGroupjust happens to output more than one button in the format you see, I don't know. (If you then remove the female button, does display output with just male in it revert to how it was originally or remain in the "changed" form?)You could examine what is in your
QButtonGroup::buttons()[0]in more detail to see what is really going there, compared against theQButtonGroup::buttons()[1].@JonB said in Layout problem:
I assume you would know if your code ever generates an object name like
Widget: radiobutton, id: rdoM.^^^^^ ?????
Now that you say you (seem to) have a object name being changed (which I did not expect), use signal
QObject::objectNameChangedto monitor when that happens.You could examine what is in your
QButtonGroup::buttons()[0]in more detail --- go find out in code what type it really is.I think I'm seeing
clsQtRadioButton(0x60000176f140throughout for theMaleone, so the object instance does not seem to be changing. -
@JonB said in Layout problem:
I assume you would know if your code ever generates an object name like
Widget: radiobutton, id: rdoM.^^^^^ ?????
Now that you say you (seem to) have a object name being changed (which I did not expect), use signal
QObject::objectNameChangedto monitor when that happens.You could examine what is in your
QButtonGroup::buttons()[0]in more detail --- go find out in code what type it really is.I think I'm seeing
clsQtRadioButton(0x60000176f140throughout for theMaleone, so the object instance does not seem to be changing. -
@JonB said in Layout problem:
I assume you would know if your code ever generates an object name like
Widget: radiobutton, id: rdoM.^^^^^ ?????
Now that you say you (seem to) have a object name being changed (which I did not expect), use signal
QObject::objectNameChangedto monitor when that happens.You could examine what is in your
QButtonGroup::buttons()[0]in more detail --- go find out in code what type it really is.I think I'm seeing
clsQtRadioButton(0x60000176f140throughout for theMaleone, so the object instance does not seem to be changing.@JonB , fixed another bug which prevents renaming of object already assigned a name:
void clsXMLnode::setObjName(QObject& robjObject, const QString& crstrName) { if ( crstrName.isEmpty() != true ) { QString strCurrent(robjObject.objectName()); if ( strCurrent.isEmpty() != true ) { qdbg() << "Current name: " << strCurrent << ", Tried to change to: " << crstrName; } else { //Set the object name robjObject.setObjectName(crstrName); } } }Now the output is:
S000000000044E000000000659T14:37:04.373DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Male S000000000045E000000000659T14:37:04.373DL00000122F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Male S000000000046E000000000659T14:37:04.373DL00000125F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x60000002ac70, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000047E000000000659T14:37:04.373DL00000127F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=1 list:(clsQtRadioButton(0x600001723c00, name=Male)) S000000000048E000000000659T14:37:04.373DL00000128F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[0]: clsQtRadioButton(0x600001723c00, name=Male) S000000000049E000000000659T14:37:04.373DL00003730F../clsMainWnd.cpp[static void clsXMLnode::setObjName] Current name: Male S000000000050E000000000659T14:37:04.373DL00000051F../clsQtRadionButton.cpp[auto clsQtRadioButton::clsQtRadioButton] Female S000000000051E000000000659T14:37:04.373DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Female S000000000052E000000000659T14:37:04.373DL00000122F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Female S000000000053E000000000659T14:37:04.373DL00000125F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x60000002ac70, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000054E000000000659T14:37:04.373DL00000127F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=2 list:(clsQtRadioButton(0x600001723c00, name=Male), clsQtRadioButton(0x60000170f340, name=Female)) S000000000055E000000000660T14:37:04.373DL00000128F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[0]: clsQtRadioButton(0x600001723c00, name=Male) S000000000056E000000000660T14:37:04.373DL00000130F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[1]: clsQtRadioButton(0x60000170f340, name=Female) -
@JonB , fixed another bug which prevents renaming of object already assigned a name:
void clsXMLnode::setObjName(QObject& robjObject, const QString& crstrName) { if ( crstrName.isEmpty() != true ) { QString strCurrent(robjObject.objectName()); if ( strCurrent.isEmpty() != true ) { qdbg() << "Current name: " << strCurrent << ", Tried to change to: " << crstrName; } else { //Set the object name robjObject.setObjectName(crstrName); } } }Now the output is:
S000000000044E000000000659T14:37:04.373DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Male S000000000045E000000000659T14:37:04.373DL00000122F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Male S000000000046E000000000659T14:37:04.373DL00000125F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x60000002ac70, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000047E000000000659T14:37:04.373DL00000127F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=1 list:(clsQtRadioButton(0x600001723c00, name=Male)) S000000000048E000000000659T14:37:04.373DL00000128F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[0]: clsQtRadioButton(0x600001723c00, name=Male) S000000000049E000000000659T14:37:04.373DL00003730F../clsMainWnd.cpp[static void clsXMLnode::setObjName] Current name: Male S000000000050E000000000659T14:37:04.373DL00000051F../clsQtRadionButton.cpp[auto clsQtRadioButton::clsQtRadioButton] Female S000000000051E000000000659T14:37:04.373DL00000113F../clsQtLayout.cpp[void clsQtLayout::addButton] BEFORE: Female S000000000052E000000000659T14:37:04.373DL00000122F../clsQtLayout.cpp[void clsQtLayout::addButton] AFTER: Female S000000000053E000000000659T14:37:04.373DL00000125F../clsQtLayout.cpp[void clsQtLayout::addButton] CURRENT GROUP: QButtonGroup(0x60000002ac70, name = Layout: form, ID: rdoLO, QButtonGroup: enSEX) S000000000054E000000000659T14:37:04.373DL00000127F../clsQtLayout.cpp[void clsQtLayout::addButton] BUTTONS.length=2 list:(clsQtRadioButton(0x600001723c00, name=Male), clsQtRadioButton(0x60000170f340, name=Female)) S000000000055E000000000660T14:37:04.373DL00000128F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[0]: clsQtRadioButton(0x600001723c00, name=Male) S000000000056E000000000660T14:37:04.373DL00000130F../clsQtLayout.cpp[void clsQtLayout::addButton] Button[1]: clsQtRadioButton(0x60000170f340, name=Female)Finally, fixed!

When processing the widgets, if the type of widget is a layout then the actual widget connected to the parent layout is the scroll area not the layout itself.
I then had another issue which was to take the widgets that are children of any groups in the layout and adding these to the layouts.
Next issue which I'm now going to fix is the individual row height of the children so that have the correct height in the layout.
Final demo with row height fixed:

From XML:
<layout id="rdoLO" height="56" hspacing="0" type="form" vspacing="0" properties="QLayout { background-color:#ffaaaa;}"> <buttongroup id="enSEX"> <radiobutton id="rdoM" height="28" text="Male" default="true" position="0,0"/> <radiobutton id="rdoF" height="28" text="Female" position="1,0"/> </buttongroup> </layout> -
@SPlatten said in Layout problem:
clsQtLayout
Please reconsider the naming of your class. They make things harder to understand for no benefits. Your clsQtLayout is in fact a QWidget. The Qt layout classes are not QWidgets. This name is confusing at best.