[SOLVED] QWizard: Workaround for registerField function



  • I have made forms to be added as widgets to a QWizard page. Now I would like register the widgets on the form so I can be sure that "Next" is not pressed without filling something on each field on the page. The registerfield procedure is however protected. I have trying to find a work around but without succes. Any suggestions?

    @
    QWizardPage *MainWindow::createPage(QString title, QWidget *pWidget)
    {
    QWizardPage *page = new QWizardPage;
    QVBoxLayout *layout = new QVBoxLayout;
    QList <QWidget *> childWidgets = pWidget->findChildren <QWidget *>();
    int i;

    page->setTitle(title);
    layout->addWidget(pWidget);
    page->setLayout(layout);
    for (i = 0; i < childWidgets.count(); i++)
    {
        if (!childWidgets.at(i)->inherits("QLabel"))
        {
            if (childWidgets.at(i)->objectName() != "")
            {
                // page->registerField(childWidgets.at(i)->objectName(), NULL, NULL, NULL);
            }
        }
    }
    qDebug() << "Page ready" <&lt; title;
    return page;
    

    }
    @

    @
    pWidget = new firstWidget_UI_Form;
    MyWizard.addPage(createPage("Fancy title", pWidget));
    pWidget = new secondWidget_UI_Form;
    MyWizard.addPage(createPage("Fancy title 2", pWidget));
    @



  • I'd just make a custom QWizardPage subclass where you do have access to that method. You can even just give it a special method that does the iteration over the label-type widgets inside it, so you still don't need to expose the registerField function.



  • I tried something like this

    @class ModifiedWizardPage : QWizardPage
    {
    public:
    explicit ModifiedWizardPage(QWidget *parent = 0);
    void addField(QString fieldName);
    void addTitle(QString title);
    void addLayout(QLayout *pLayout);
    QWizardPage *getBase() { return static_cast<QWizardPage *>(this); }
    };

    pWidget = new firstWidget_UI_Form;
    ModifiedWizardPage *modPage = createPage("Fancy title 2", pWidget);
    QWizardPage *wizPage = ModifiedWizardPage->getBase();
    // CRASHED After this statement
    MyWizard.addPage(wizPage);
    @



  • The whole getBase thing is not needed. Simply publicly inherit ModifiedWizardPage from QWizardPage, and you can use your ModifiedWizardPage instance like you can any QWizardPage instance. Your approach also works if you prefer that, but in that case, don't use the class name on line 13, but the instance. So, replace ModifiedWizardPage with modPage and you should be fine. What you have now should not even compile, so I wonder why/how you get a crash.



  • It still eludes me a bit. This is the full code now and it crashes at the last statement.

    @
    class ModifiedWizardPage : public QWizardPage
    {
    public:
    explicit ModifiedWizardPage(QWidget *parent = 0);
    };

    ModifiedWizardPage::ModifiedWizardPage(QWidget *parent)
    {
    QList <QWidget *> childWidgets = parent->findChildren <QWidget *>();
    int i;

    setParent(parent);
    setTitle(parent->windowTitle());
    setObjectName(parent->windowTitle());
    for (i = 0; i < childWidgets.count(); i++)
    {
        if (!childWidgets.at(i)->inherits("QLabel"))
        {
            if (childWidgets.at(i)->objectName() != "")
            {
                qDebug() << childWidgets.at(i)->objectName();
                registerField(childWidgets.at(i)->objectName(), NULL, NULL, NULL);
            }
        }
    }
    

    }

    QWidget *pWidget;
    ModifiedWizardPage *modPage;

    pWidget = new firstWidget_UI_Form;
    modpage = new ModifiedWizardPage(pWidget);
    MyWizard.addPage(modpage); // crashes here

    Internal error: pc 0x0 in read in psymtab, but not in symtab.)
    @



  • Just a thought can it be that I get an error because I am registering fields that are not part of the QWizard yet?



  • You're doing weird things with parents and children. To get things straight: your wizard page is supposed to contain pWidget right?

    If so, then pWidget can't be the parent of your wizard page. It should become the child.

    How about something like this
    @
    class ModifiedWizardPage : public QWizardPage
    {
    public:
    explicit ModifiedWizardPage(QWidget* content, QWidget *parent = 0);
    };

    ModifiedWizardPage::ModifiedWizardPage(QWidget* content, QWidget parent):
    QWizardPage(parent)
    {
    QVerticalLayout
    layout = new QVerticalLayout(this);
    layout->addWidget(content);

    QList <QWidget *> childWidgets = parent->findChildren <QWidget *>();
    int i;
    
    setTitle(content->windowTitle());
    setObjectName(content->windowTitle()); //you probably don't want to do this. windowTitle is translable, while object names should not be
    for (i = 0; i < childWidgets.count(); i++)
    {
        if (!childWidgets.at(i)->inherits("QLabel"))
        {
            if (childWidgets.at(i)->objectName() != "") // not a good plan. Use another property instead.
            {
                qDebug() << childWidgets.at(i)->objectName();
                registerField(childWidgets.at(i)->objectName(), NULL, NULL, NULL); 
            }
        }
    }
    

    }

    QWidget *pWidget;
    ModifiedWizardPage *modPage;

    myWizard = new MyWizard();
    pWidget = new firstWidget_UI_Form;
    modpage = new ModifiedWizardPage(pWidget, myWizard);
    myWizard->addPage(modpage);
    myWizard->exec();
    @



  • Thanks for straightening out my code. It helps, but I still have a crash. When I comment this (#if 0) it works. Otherwise not. And it was this part that I needed to add. :(

    @
    for (i = 0; i < childWidgets.count(); i++)
    {
    if (childWidgets.at(i)->inherits("QLineEdit"))
    {
    if (childWidgets.at(i)->objectName() != "")
    {
    qDebug() << childWidgets.at(i)->objectName();
    registerField(childWidgets.at(i)->objectName(), NULL, NULL, NULL);
    }
    }
    }@



  • Come on... Did you even try a debugger? And it should be obvious that claiming "a crash" without including a backtrace is... eh... less than informative.



  • Like before (see end of 3rd post)

    Internal error: pc 0x0 in read in psymtab, but not in symtab.)
    signal name: sigsegv (segmentation fault)

    I review it bit more.



  • The problem is here (for example QLineEdit)

    @registerField(childWidgets.at(i)->objectName(), NULL, NULL, NULL);@

    I was relying on defaults, but it seems that I have to be explicit

    @registerField(childWidgets.at(i)->objectName(), childWidgets.at(i), "text", "textChanged");@

    That also means I have to distinguish between widgets. Which is not impossible, but somewhat annoying.

    @
    QAbstractButton bool checked toggled()
    QAbstractSlider int value valueChanged()
    QComboBox int currentIndex currentIndexChanged()
    QDateTimeEdit QDateTime dateTime dateTimeChanged()
    QLineEdit QString text textChanged()
    QListWidget int currentRow currentRowChanged()
    QSpinBox int value valueChanged()
    @

    The next button is still enabled even though the fields are reqistered.



  • Automatic should work just fine, I think. At least, as long as you handle only widget types that make sense and have a user property. Input type widgets generally have that, non-input type widgets don't. It is not hard to check though:

    @
    bool hasUserProperty(QObect* object) const
    {
    if (!object)
    return false;

    QMetaObject* mo = object->metaObject();
    return mo->userProperty().isValid();
    

    }
    @

    That would result in code like:
    @
    QWidget* childWidget = childWidgets.at(i);
    if (hasUserProperty(childWidget)) {
    registerField(childWidget->objectName(), childWidget);
    } else {
    // you'll need to handle these widgets in a different way
    }
    @



  • Just saw my mistake. The NULL in the second argument made it crash.

    @registerField(childWidgets.at(i)->objectName(), NULL, NULL, NULL);@

    should be

    @registerField(childWidgets.at(i)->objectName(), childWidgets.at(i), NULL, NULL);@

    I have combined that with your code. Filtering out widgets that not on the ui-form and buttons.

    @ModifiedWizardPage::ModifiedWizardPage(QWidget* content, QWidget parent):
    QWizardPage(parent)
    {
    QVBoxLayout
    layout = new QVBoxLayout(this);
    layout->addWidget(content);

    QList <QWidget *> childWidgets = content->findChildren <QWidget *>();
    int i;
    setTitle(content->windowTitle());
    
    for (i = 0; i < childWidgets.count(); i++)
    {       
        if (childWidgets.at(i)->objectName() != "")
        { // exclude runtime widgets - ui only
            if (childWidgets.at(i)->metaObject()->userProperty().isValid() && !childWidgets.at(i)->inherits("QAbstractButton"))
                registerField(childWidgets.at(i)->objectName(), childWidgets.at(i), NULL, NULL);
        }
    
    }
    

    }@

    The fields are registred now, but how is validation done?



  • Ok. Found the answer to my last question in the documentation pages.

    http://qt-project.org/doc/qt-4.8/qwizardpage.html#registerField

    To make it mandatory add a '*'.

    Full code here and my problem is solved. Thanks to Andre for helping out.

    @class ModifiedWizardPage : public QWizardPage
    {
    public:
    explicit ModifiedWizardPage(QWidget* content, QWidget *parent = 0);
    };

    ModifiedWizardPage::ModifiedWizardPage(QWidget* content, QWidget parent):
    QWizardPage(parent)
    {
    QVBoxLayout
    layout = new QVBoxLayout(this);
    layout->addWidget(content);

    QList <QWidget *> childWidgets = content->findChildren <QWidget *>();
    int i;
    setTitle(content->windowTitle());
    
    for (i = 0; i < childWidgets.count(); i++)
    {       
        if (childWidgets.at(i)->objectName() != "")
        {
            if (childWidgets.at(i)->metaObject()->userProperty().isValid() && !childWidgets.at(i)->inherits("QAbstractButton"))
                registerField(childWidgets.at(i)->objectName() + "*", childWidgets.at(i), NULL, NULL);
        }
    }
    

    }

    QWizard *myWizard;
    QWidget *pWidget;
    QWizardPage *modWizardPage;

    myWizard = new QWizard(this);
    myWizard->setWizardStyle(QWizard::ModernStyle);

    pWidget = new uiForm1;
    pWidget->setWindowTitle("My fancy title 1");
    modWizardPage = new ModifiedWizardPage(pWidget, csWizard);
    myWizard->addPage(modWizardPage);
    ..........
    pWidget = new uiForm2;--
    pWidget->setWindowTitle("My fancy title 2");
    modWizardPage = new ModifiedWizardPage(pWidget, csWizard);
    myWizard->addPage(modWizardPage);

    @



  • What I don't get is that you're changing the type of widgets you're interested in with every iteration of your code... You started out with QLabel, then it became QLineEdit, and now you're on to using QAbstractButton.

    Note that the NULL, NULL arguments are not needed, and rather C-ish. The default value for these parameters is 0 already (C++ style before C++/11), and the C++/11 version would be nullptr.

    final suggestion:
    @
    myWizard->addPage(new ModifiedWizardPage(
    new uiForm1,
    tr("My fancy title 1"),
    myWizard));
    myWizard->addPage(new ModifiedWizardPage(
    new uiForm2,
    tr("My fancy title 2"),
    myWizard));
    @

    that is: make the title simply an argument for the ModifiedWizardPage constructor as you're not setting it in the ui file anyway. You can also get rid of the intermediary variables.



  • When I started this I wanted to exclude QLabel from the widgets to be registered because they are not writable by the user. Then, just for debugging, I used only included QLineEdit objects. After that you came with

    metaObject()->userProperty().isValid()

    I put debugs and noticed that QLabel was false, but not the buttons so I modified that line. The buttons also dont need to be registered. They are just there to launch a dialog to fill the QLineEdit's.

    PS: I took your "final suggestion" and cleaned up the code a bit. I also included the tr() although it is not really necessary. The tool I am making is not for large scale deployment.



  • Ok. Just checking. Are you sure want to ignore all QAbstractButtons? Including QCheckBox and QRadioButton?



  • I noticed that too. :)

    QCheckbox and QRadiobutton are not part of this wizard, but in general I would like to have them. I assume it would that some tweak to include them again.



  • Not very elegant but it could be something like this

    @
    if (childWidgets.at(i)->metaObject()->userProperty().isValid())
    {
    if (!childWidgets.at(i)->inherits("QAbstractButton"))
    registerField(childWidgets.at(i)->objectName() + "", childWidgets.at(i));
    else
    {
    QString aMetaObject = (QString) childWidgets.at(i)->metaObject()->className();
    if (aMetaObject.compare("QRadioButton", Qt::CaseSensitive) == 0)
    registerField(childWidgets.at(i)->objectName() + "
    ", childWidgets.at(i));
    else if (aMetaObject.compare("QCheckBox", Qt::CaseSensitive) == 0)
    registerField(childWidgets.at(i)->objectName() + "*", childWidgets.at(i));
    }
    }
    @



  • No, indeed, that's not very elegant at all.



  • Sorry I have spend many years on C (some on Borland C++). Just getting back to higher level languages like Qt again. There is much I don't fully know. Just have a sense of what the questions are.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.