PDA

View Full Version : QWizard: Temporarily disable the Back button



ChrisW67
3rd June 2011, 02:40
Hi all,

I have a simple QWizard (code below). When the user moves to page 2 of the wizard a longish, asynchronous network request is made. During that request I'd like to disable the Back (and potentially other) buttons on the wizard. When the request is complete the buttons will be enabled and going Back is allowed. Using setEnabled() on the back button is not working, presumably the QWizard code is re-enabling it. I have had to resort to disabling the entire wizard.

I know that I could set page 1 as a commit page, but that would prohibit the back button entirely on page 2. I only need a temporary block on the back button.

Have I missed the really obvious way to do this?

Cheers,
Chris



#include <QtGui>
#include <QDebug>

class Page1: public QWizardPage {
Q_OBJECT

QLabel *label;
public:
Page1(QWidget *p = 0): QWizardPage(p) {
label = new QLabel(tr("This is an example page 1"), this);
label->setWordWrap(true);

QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(label);
setLayout(layout);
}
};

class Page2: public QWizardPage {
Q_OBJECT

QLabel *label;
public:
Page2(QWidget *p = 0): QWizardPage(p) {
label = new QLabel(tr("This is an example page 2"), this);
label->setWordWrap(true);

QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(label);
setLayout(layout);
}
void initializePage() {
// This does not disable the button for the long process
wizard()->button(QWizard::BackButton)->setEnabled(false);
// so I have to resort to this
// wizard()->setEnabled(false);

label->setText("Starting");
// simulate a longish process
QTimer::singleShot(5000, this, SLOT(expired()));
}
public slots:
void expired() {
label->setText("Finished");
wizard()->button(QWizard::BackButton)->setEnabled(true);
// wizard()->setEnabled(true);
}
};

class Wizard : public QWizard {
Q_OBJECT
public:
Wizard(QWidget *p = 0) : QWizard(p) {
addPage(new Page1);
addPage(new Page2);
}

};


int main(int argc, char *argv[])
{
QApplication app(argc, argv);


Wizard wiz;
wiz.show();
return app.exec();
}
#include "main.moc"

Lykurg
3rd June 2011, 07:50
Wow, that's crazy. QWizard seems to set the buttons at last. Even the signal currentIdChanged() is useless. The only workaround is to create a slot which disables the button and call that slot inside initializePage() with an 1ms single shot timer. That's not nice, but the only solution I can figure out right now.

Santosh Reddy
3rd June 2011, 07:53
QWizardPage::initializePage() is virtual function is called by QWizard::initializePage() to prepare the page just before it is shown, once the page is prepared the control goes back to QWizard, then QWizard sets the Back and Next button status based on the page options set in QWizardPage::initializePage(), so it may not be good idea to set buttons in there.

You can set the buttons sometime later, something like this


class Page2: public QWizardPage {
Q_OBJECT

QLabel *label;
public:
Page2(QWidget *p = 0): QWizardPage(p) {
label = new QLabel(tr("This is an example page 2"), this);
label->setWordWrap(true);

QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(label);
setLayout(layout);
}
void initializePage() {
label->setText("Starting");
// set trigger for longish process
QTimer::singleShot(0, this, SLOT(start()));
}
public slots:
void start() {
wizard()->button(QWizard::BackButton)->setEnabled(false);
// simulate a longish process
QTimer::singleShot(5000, this, SLOT(expired()));
}

void expired() {
label->setText("Finished");
wizard()->button(QWizard::BackButton)->setEnabled(true);
}
};

ChrisW67
3rd June 2011, 08:04
I guess the zero-duration single shot timer is an option. A bit clunky IMHO.

Edit: You also need to be careful not to emit completeChanged() or QWizard will reset your buttons (all of them, not just the Next/Finished button)

DIMEDROLL
31st January 2012, 01:00
Hello Guyz,

I've looked at Qt's sources and found out that it's possible to hide Back button by creating cusom button layout and ommiting Back button in the list:

QList<QWizard::WizardButton> button_layout;
button_layout << QWizard::HelpButton << QWizard::Stretch <<
QWizard::NextButton << QWizard::CustomButton1 <<
QWizard::CancelButton;
this->setButtonLayout(button_layout);

I hope this will save some time to somebody.

Regards

Added after 7 minutes:

AFAIU to avoid using QTimer it is needed to modify QWizard source code. The easies way will be to add a virtual function

virtual void buttonsUpdated();
and call it from the end of QWizard's:

void QWizardPrivate::_q_updateButtonStates()
Then reimplement this buttonsUpdated() in your QWizard sublass and disable Back button there.

wysota
31st January 2012, 01:27
Adding virtual functions to public classes is no fun as it breaks binary compatibility.

DIMEDROLL
31st January 2012, 01:41
wysota, thanks for mentioning about this.
The binary compatibility issue can be fixed using a signal instead of virtual function:
documentation (http://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++#Using_signals _instead_of_virtual_functions)