[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" << 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 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 hereInternal error: pc 0x0 in read in psymtab, but not in symtab.)
@ -
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);
}
}
}@ -
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.
-
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));
}
}
@