Is changing a QWizard into a QStackedWidget the best option?



  • Hi,

    I've made a program with Qt Creator 4.8.5 that is a configuration program (a Wizard) with the QWizard structure (plus like 30 QWizardPages). It works fine now but I had a lot of problems because I needed a different behaviour for my program than the QWizard allows me in some cases so some people told me to use the class QStackedWidget.
    Now I'm trying to figure if QStackedWidget class is what I really need (in case I had to do the program again) or maybe use another different class, or just use again the QWizard class.... so hope you can help me figuring this out.

    The idea of my program is that it has a lot of pages with a lot of Widgets on them (QLabels, Qcombobox, QPushButtons... ) depending on the page the user is. Moreover, as you know QWizard has its own buttons (NextButton, BackButton, FinishButton, CustomButtonX) to let you move between pages which is really nice BUT with that I had some problems as I need them in some pages but I don't need them or at least not all of them in some other pages. I add some examples of three different pages: Page1 Page2 Page3

    The worst problems I had with QWizard:

    • Can't change easily the QWizard buttons (I managed doing it but iwth a lot of problems and making code not really easy to mantain)
    • Can't jump from one page to another as I want as in some cases you get the "Page x: Already met" error (very important)

    So... do you think that I shoudl really change it to a QStackedWidget? If yes I have a doubt here... as the documentation says: "The QStackedWidget class provides a stack of widgets where only one widget is visible at a time." So that means that my Widget in this case will be my pages and so I need a class derived from QWidget like for example QWizardPage so... should I use QWizardPage as my pages class or is there any other class that fits best here ? (As I don't need all the logic QWizardPage has as ::nextId(), ::validatePage(), ::cleanup() functions that are related to the base QWizard)

    Thank you so much for your help and hope I explain my problem correctly.


  • Qt Champions 2016

    Hi
    The Stackedwidget is super for such design.
    The widget it talks about is a page.
    so u get all the pages u want and on each page,
    u can have all the widgets u like.

    Normally the page is a plain
    qwidget but u can use own page if u need but
    often there is no need.

    ps. if you use UI files, you can visually manage all the pages. very handy.



  • Hi @mrjj thank you for your reply. I'm really happy to know that QStackedWidget is the right way I have to take :D

    Now what I have is a QWizard that creates the QWizardpages with its behaviour:

    //QWizard .cpp constructor:
    WP1 *page1 = new WP1;
    setPage(Page1Mode, page1);
    
    // .h declaration
    enum {Page1Mode, Page2Mode, ...}
    

    So I imagine that now I need to have something like class MyStacked : public QStackedWidget and in its constructor something like:

    Page1Class *Page1 = new Page1Class();
    addWidget(Page1);
    

    am I right? So.. as this is my first contact with Qt I don't really know so much about it and its classes... you said: "Normally the page is a plain qwidget" ... So you mean that my pages will be something like class Page1: public QWidget ?? and there I can add my layouts and Qlabels and other QWidget as i had in my QWizardPages?

    Another thing? What about the tipical Next/Back buttons of the wizard configuration programs? As I said I don't need them all in every page so.. should I create them in every page of could I put them outside the QStackedWidget and make them general and just hide/ change their behaviour depending of the page shown?

    and a last question: With the wizard I could use the registerfield / set field functions to pass information between the QWizardPages so... how should I do it now ? Which will be the best method to avoid making global variables or a signal for every one? I'm talking that Page3 will need 5 different variables values from Page2 and other 6 differents from Page1 for example.

    Thank you!!!


  • Qt Champions 2016

    @roseicollis
    class MyStacked : public QStackedWidget
    That you only do if u need a subclass.
    else just use the base one.

    http://doc.qt.io/qt-5/qstackedwidget.html
    To insert u use
    http://doc.qt.io/qt-5/qstackedwidget.html#addWidget

    class Page1: public QWidget
    That you only need if u want a special page.
    Else u just
    new a QWidget and use for page. And add layout and stuff to it.
    You can use any widget as page.

    • Another thing? What about the tipical Next/Back buttons of the wizard configuration
      I would just put them outside and mainwindow controls the logic of next/prev and hide buttons.

    A more fancy version would be a custom composite control where it contains the logic inside but for 1 app. letting main handle it should be fine.

    -and a last question:
    I would have a Data class. and each page will save info to this class.
    So if page 5 read the data it will get what ever page 1 updated etc.
    No need to let the pages directly access each other.



  • @mrjj said:

    just use the base one.

    Then the base class of my app should be a QMainWindow where I create a QStackedWidget and add my QWidget pages or should I use another Base Class?

    class Page1: public QWidget
    That you only need if u want a special page. Else u just new a QWidget and use for page. And add layout and stuff to it.

    Yes but if I do it that means that if I need 30 pages I will have at least 30 layouts created with more than 100 different qwidgets all together which will be very ugly to see in code and difficult to understand to anyone that cames after me. So I think it will be better making a class for every page and create there all the QWidgets needed for that page... what do you think?


  • Qt Champions 2016

    @roseicollis
    Well if your application is of normal type then
    using QMainwindow as center seems good choice.
    You just place a QStacked in its UI and start adding pages.

    • 30 pages
      making a class for every page and create there all the QWidgets needed for that page... what do you think?

    Well I would add it all in UI file and there will be no need to see any of the code.

    But since I dont know how much processing each page does, its hard to say
    if your own sub class as page is better as u can then hook up signals etc internally.

    so depends on how much going on in each page.



  • @mrjj I understand... the thing is that every widget I use (layouts, labels, pushbuttons, combobox...) I create them by code like QLabel lab1 = new QLabel(); because it's faster for me and I prefer using its functions for position and height/width than just drag and drop on the ui file (which normally I have all in blank or I just don't have them). I'll think about if I should put it in the ui or in the code...

    One thing: I've created a new project to test that. The base clase of the project is QMainWindow so in its constructor I've write:

        QStackedWidget *st = new QStackedWidget;
        Page1 *page1 = new Page1;
        st->addWidget(page1);
    
        QVBoxLayout *layout = new QVBoxLayout;
           layout->addWidget(st);
           setLayout(layout);
    

    And then I've created a Page1 like this:

    //.h file:
    class Page1 : public QWidget
    {
        Q_OBJECT
    public:
        explicit Page1(QWidget *parent = 0);
        ~Page1();
    private:
        Ui::Page1 *ui;
        QPushButton *_b1;
        QPushButton *_b2;
    };
    
    //.cpp file, constructor:
    Page1::Page1(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Page1)
    {
        ui->setupUi(this);
       _b1 = new QPushButton;
       _b2 = new QPushButton;
    
       _b1->setText(QString::fromUtf8("AAAA"));
       _b1->setMinimumSize(320,100);
       _b1->setMaximumSize(320,100);
    
       _b2->setText(QString::fromUtf8("BBBB"));
       _b2->setMinimumSize(320,100);
       _b2->setMaximumSize(320,100);
    
       _b1->setVisible(true);
       _b2->setVisible(true);
    }
    

    And when I run the program what I expect to see is a windows with 2 QPushButtons with texts AAAA and BBBB but instead of this what I get is 3 windows opened at the same time: One for MainWindow, One with the size of _b1 and just the this button and another with _b2 size and just this button on it....

    What am I missing here?


  • Qt Champions 2016

    @roseicollis said:
    Hi
    Code is also fine. But normally one use use layout so they can scale to mainwindow size and inserting into the UI directly works great. But letting the page new the widgets is also 100% valid and its mostly a matter of taste. (IMHO)

    _b1 = new QPushButton;

    There is a rule here u dont know. If you dont give a widget a parent it will automatically set window flag on it self and become a window :)
    So _b1 = new QPushButton(this); will make them be inside the page.



  • @mrjj said:

    normally one use use layout so they can scale to mainwindow size and inserting into the UI directly works great.

    Ok so you mean I should create the layout in the UI file? And it auto scale to mainwindow or should I do something else?

    If you dont give a widget a parent it will automatically set window flag on it self and become a window :)

    Mmmm interesting... I understand it but if I put (this) for both buttons now its right that I don't have their 2 new windows but I don't see them also in the MainWindow window so thinking about what you said before, I tried adding (this) to QStackedWidget *st = new QStackedWidget; and now I see something in the MainWindow which is _b2 .... any idea why I don't see _b1? And another thing:
    I had to add a st->setMinimumSize(500,500);for my stacket widget... how could i tell him to take the parent's size, in this case MainWindow?

    I'm understanding a lot of the Qt behaviour, thank you so much for your explanations @mrjj


  • Qt Champions 2016

    @roseicollis

    • Ok so you mean I should create the layout in the UI file? And it auto scale to mainwindow or should I do something else?
      No i just state the reason why using UI files often works as well as directly code and you
      dont have to scroll around in all the "creation" code if handled by the UI.
      --
      I think ur b2 button is on top of b1
      try b1->move(100,100)
      --
    • setMinimumSize(500,500);for my stacket widget... how could i tell him to take the parent's size, in this case MainWindow?

    That what we use layouts for normally. It will scale all children to parent.
    So in mainwin central u add widget , give it layout. then add Stacked to layout.
    Now stacked follow main. Now u need to use layout in the pages so pages follow stacked that follow main. Sounds complicated but its not.
    http://doc.qt.io/qt-5/examples-layouts.html

    Will a sample help?



  • @mrjj said:

    I think ur b2 button is on top of b1
    try b1->move(100,100)

    Totally right!

    So in mainwin central u add widget , give it layout. then add Stacked to layout.
    Now stacked follow main.

    Isn't the same than what I already have? The difference I see is that you are adding first a widget to the mainwindow but I don't understand why...
    My mainWindow code actually looks like:

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        
        QStackedWidget *st = new QStackedWidget(this);
        st->setMinimumSize(800,800);
    
        Page1 *page1 = new Page1;
        st->addWidget(page1);
    
        layout = new QVBoxLayout; //defined in .h file
        layout->addWidget(st);
        setLayout(layout);
    }
    

    But then when I try to do layout()->addWidget(_b1); on Page1 and try to run the program it crashes at this point....

    I've tried doing what you said, I think you mean this:

        QWidget* centralWidget = new QWidget();
        QVBoxLayout *layout = new QVBoxLayout;
        layout->addWidget(st);
        setCentralWidget(centralWidget);
        centralWidget->setLayout(layout);
        setFixedSize(600,600);
    

    But again the program crashes in Page1 constructor when I put layout()->addWidget(_b1); or parent-> layout()->addWidget(_b1);


  • Qt Champions 2016

    Hi
    Main windows is a bit special since it has "central widget".
    which there to allow to define areas around it
    for Dock windows. That is why we insert widget first.
    For all types of other widget we would insert directly.

    QWidget* centralWidget = new QWidget();
    QVBoxLayout *layout = new QVBoxLayout;
    centralWidget->setLayout(layout);
    layout->addWidget(st);
    setCentralWidget(centralWidget);

    seems fine.
    For centralWidget , u can then use the Stacked.


    My mainWindow code actually looks like:

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {

    setLayout(layout);
    // that sets directly on mainwin. not sure it does what we think.

    You can just single step and see the values to find out why u crash.
    Sorry, i cant spot it. Running it helps.
    I think u need a setCentral also.

    That is the reason no 2 to why i like
    UI file so much. no crashing as u can only insert legally when visually :)
    And no code to try to spot bugs in.

    I think u need
    new widget
    widget as central
    new layout
    layout to widget
    inset stacked to that layout
    and then the page stuff



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