Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to change QWizard default buttons in each QWizardPage
QtWS25 Last Chance

How to change QWizard default buttons in each QWizardPage

Scheduled Pinned Locked Moved Solved General and Desktop
qt4.8.5qwizardqwizardpagebutton custom s
16 Posts 3 Posters 8.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Offline
    R Offline
    roseicollis
    wrote on last edited by
    #1

    Hello again!! I'm making a program with Qt 4.8.5 on a Fedora system (unix). It's a QWizard structure with its QWizardPages.

    I needed (in past, before my boss told me to make some changes) to change the default QWizard buttons (back, next, finish...) and customize it. I found I could do it putting the next lines on the constructor of my QWizard class called BaseWizard (class BaseWizard : public QWizard)

    QList<QWizard::WizardButton> button_layout;
    button_layout <<QWizard::Stretch << QWizard::CustomButton1 << QWizard::BackButton << QWizard::NextButton << QWizard::FinishButton;
    this->setOptions(QWizard::NoDefaultButton);
    

    With that what I have from left to right is one custom buttons, the back button, the next button and the finish button, and when I want I can show or hide the custom buttons with SetVisible() / SetDisabled() / setEnabled() functions.

    That worked perfect for what I wanted until now... I have to make some changes to the program so I need to change those buttons depending on the page the user is. As I said before, I know I can change the visibility of CustomButton 1 for example BUT I can't do the same with back button so... my quesiton is: How can I decide which buttons I show in every QWizardPage (and their text) and which is the best way to do it?

    I've tried creating a function on my BaseWizard

    // Function to have only 2 custom buttons
    void BaseWizard::ChangeButtons()
    {
        QList<QWizard::WizardButton> button_layout;
        button_layout <<QWizard::Stretch << QWizard::CustomButton1 << QWizard::CustomButton2;
     }
    

    And then in the QWizardPage (lets call it WP) using it like:

    BaseWizard *bz;
    bz->ChangeButtons();
    

    But when I do that nothing changes.I can still see the NextButon for example. I have tried also using first a button_layout.clear(); to see if clenaing it before adding the buttons works, but not.

    I have also tried changing the text of CustomButton1. If I do it in the WP after calling ChangeButtons with wizard()->button(QWizard::CustomButton1)->setText("TEXT CHANGED"); Then the text changes but if I put it in the ChangeButton() function with this->button(QWizard::CustomButton1)->setText("BBBBB"); it does nothing (But its entering into the funcion). Ofc if I try to change the text of CustomButton2 in WP nothing happens because I can't still see that button... so any idea of what I am doing wrong or how could I get what I try will be very apreciated,

    Thank you so much.

    1 Reply Last reply
    0
    • R Offline
      R Offline
      roseicollis
      wrote on last edited by
      #2

      No one? :(( I'm still stuck there :(

      1 Reply Last reply
      0
      • jsulmJ Offline
        jsulmJ Offline
        jsulm
        Lifetime Qt Champion
        wrote on last edited by jsulm
        #3

        What do you want to achieve with this code:

        void BaseWizard::ChangeButtons()
        {
            QList<QWizard::WizardButton> button_layout;
            button_layout <<QWizard::Stretch << QWizard::CustomButton1 << QWizard::CustomButton2;
         }
        

        You create a local variable button_layout which is destroyed when ChangeButtons() finishes!
        Shouldn't you call setButtonLayout(button_layout):

        void BaseWizard::ChangeButtons()
        {
            QList<QWizard::WizardButton> button_layout;
            button_layout <<QWizard::Stretch << QWizard::CustomButton1 << QWizard::CustomButton2;
            setButtonLayout(button_layout)
         }
        

        https://forum.qt.io/topic/113070/qt-code-of-conduct

        R 1 Reply Last reply
        0
        • jsulmJ jsulm

          What do you want to achieve with this code:

          void BaseWizard::ChangeButtons()
          {
              QList<QWizard::WizardButton> button_layout;
              button_layout <<QWizard::Stretch << QWizard::CustomButton1 << QWizard::CustomButton2;
           }
          

          You create a local variable button_layout which is destroyed when ChangeButtons() finishes!
          Shouldn't you call setButtonLayout(button_layout):

          void BaseWizard::ChangeButtons()
          {
              QList<QWizard::WizardButton> button_layout;
              button_layout <<QWizard::Stretch << QWizard::CustomButton1 << QWizard::CustomButton2;
              setButtonLayout(button_layout)
           }
          
          R Offline
          R Offline
          roseicollis
          wrote on last edited by
          #4

          @jsulm O.o ermmmm... wow! yes, I used that function on the constructor the first time I create the buttons_layout but forgot it there in the function .... what a Stupid mistake... BUT now, putting it like you said it makes my program crashes.

              QList<QWizard::WizardButton> button_layout;
              button_layout.clear();
              qDebug() << " 1";
              button_layout <<QWizard::Stretch << QWizard::CustomButton1 << QWizard::CustomButton2 << QWizard::NextButton << QWizard::FinishButton;
              qDebug() << " 2 ";
              setButtonLayout(button_layout);
              qDebug() << " 3";
          

          If I run it with that code it prints 1 2 and then crashes in the setButtonLayout() function with a nice The program has unexpectedly finished.

          kshegunovK 1 Reply Last reply
          0
          • jsulmJ Offline
            jsulmJ Offline
            jsulm
            Lifetime Qt Champion
            wrote on last edited by
            #5

            Can you post the stack trace?

            https://forum.qt.io/topic/113070/qt-code-of-conduct

            R 1 Reply Last reply
            0
            • R roseicollis

              @jsulm O.o ermmmm... wow! yes, I used that function on the constructor the first time I create the buttons_layout but forgot it there in the function .... what a Stupid mistake... BUT now, putting it like you said it makes my program crashes.

                  QList<QWizard::WizardButton> button_layout;
                  button_layout.clear();
                  qDebug() << " 1";
                  button_layout <<QWizard::Stretch << QWizard::CustomButton1 << QWizard::CustomButton2 << QWizard::NextButton << QWizard::FinishButton;
                  qDebug() << " 2 ";
                  setButtonLayout(button_layout);
                  qDebug() << " 3";
              

              If I run it with that code it prints 1 2 and then crashes in the setButtonLayout() function with a nice The program has unexpectedly finished.

              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by
              #6

              @roseicollis

              BaseWizard *bz;
              bz->ChangeButtons();
              

              Is this exactly how you use your wizard? If that's the case bz is just a wild pointer and doesn't actually reference any object.

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              1
              • jsulmJ jsulm

                Can you post the stack trace?

                R Offline
                R Offline
                roseicollis
                wrote on last edited by
                #7

                @jsulm said:

                stack trace?

                How can I get it?

                @kshegunov Well I put the funcion on my BaseWizard and was trying to call it from a QWizardPage like that thinking it was correct... how should I do it instead?

                kshegunovK 1 Reply Last reply
                0
                • jsulmJ Offline
                  jsulmJ Offline
                  jsulm
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  You should get it if you execute your app from QtCreator and it crashes.

                  You did not initialize the pointer, so it is showing to nirvana:

                  BaseWizard *bz;
                  bz->ChangeButtons();
                  

                  https://forum.qt.io/topic/113070/qt-code-of-conduct

                  R 1 Reply Last reply
                  0
                  • R roseicollis

                    @jsulm said:

                    stack trace?

                    How can I get it?

                    @kshegunov Well I put the funcion on my BaseWizard and was trying to call it from a QWizardPage like that thinking it was correct... how should I do it instead?

                    kshegunovK Offline
                    kshegunovK Offline
                    kshegunov
                    Moderators
                    wrote on last edited by kshegunov
                    #9

                    @roseicollis
                    Well, you have to operate on an object. If the way shown is the exact excerpt of the code you'll always get a segmentation fault, because in ChangeButtons() you dereference this by calling setButtonLayout(). You can use instead:

                    BaseWizard * bz = dynamic_cast<BaseWizard *>(wizard());
                    

                    or any other way that actually provides you with the valid wizard object.

                    Kind regards.

                    Read and abide by the Qt Code of Conduct

                    1 Reply Last reply
                    0
                    • jsulmJ jsulm

                      You should get it if you execute your app from QtCreator and it crashes.

                      You did not initialize the pointer, so it is showing to nirvana:

                      BaseWizard *bz;
                      bz->ChangeButtons();
                      
                      R Offline
                      R Offline
                      roseicollis
                      wrote on last edited by
                      #10

                      @jsulm Well in the Compile output it says that exit normally (if that is what you mean, if not, can you point me where can I find or see the stack trace?

                      @kshegunov said:

                      BaseWizard * bz = dynamic_cast<BaseWizard *>(wizard());

                      Annd.... DING DING DING! We have a winner here! Yes, this absollutely solves my problem. Thank you so much! I love a little with all that about casts... why can't you just do something like BaseWizard *bz = new BaseWizard() ? why do you have to use a cast and moreover, dynamic_cast ?

                      kshegunovK 1 Reply Last reply
                      0
                      • R roseicollis

                        @jsulm Well in the Compile output it says that exit normally (if that is what you mean, if not, can you point me where can I find or see the stack trace?

                        @kshegunov said:

                        BaseWizard * bz = dynamic_cast<BaseWizard *>(wizard());

                        Annd.... DING DING DING! We have a winner here! Yes, this absollutely solves my problem. Thank you so much! I love a little with all that about casts... why can't you just do something like BaseWizard *bz = new BaseWizard() ? why do you have to use a cast and moreover, dynamic_cast ?

                        kshegunovK Offline
                        kshegunovK Offline
                        kshegunov
                        Moderators
                        wrote on last edited by kshegunov
                        #11

                        @roseicollis
                        Firstly you'll need the wizard your page belongs to, this is the one you're trying to modify, that's why I suggested using the wizard() method. But that method (naturally) gives you a pointer to the base class (QWizard). Because you're trying to typecast a pointer down in the hierarchy and not up (which is always safe) your best bet is to do that with dynamic_cast. This ensures that if the object you receive is not from that hierarchy branch, or you pass a NULL pointer as argument, or your object can't be cast downwards (meaning that it actually is a base class object), the cast will return NULL. This provides you with a safe way to cast object pointers. The most clean approach is to use qobject_cast which works pretty much the same way, but for that you need your class to be declared as a metatype to Qt, which I was not sure you had done.

                        Kind regards.

                        Read and abide by the Qt Code of Conduct

                        R 1 Reply Last reply
                        0
                        • kshegunovK kshegunov

                          @roseicollis
                          Firstly you'll need the wizard your page belongs to, this is the one you're trying to modify, that's why I suggested using the wizard() method. But that method (naturally) gives you a pointer to the base class (QWizard). Because you're trying to typecast a pointer down in the hierarchy and not up (which is always safe) your best bet is to do that with dynamic_cast. This ensures that if the object you receive is not from that hierarchy branch, or you pass a NULL pointer as argument, or your object can't be cast downwards (meaning that it actually is a base class object), the cast will return NULL. This provides you with a safe way to cast object pointers. The most clean approach is to use qobject_cast which works pretty much the same way, but for that you need your class to be declared as a metatype to Qt, which I was not sure you had done.

                          Kind regards.

                          R Offline
                          R Offline
                          roseicollis
                          wrote on last edited by roseicollis
                          #12

                          @kshegunov

                          but for that you need your class to be declared as a metatype to Qt, which I was not sure you had done.

                          Neither do I :PWhat do you mean with metatype and how do you declare it? I just use to code making it works and if it does not, then I search on the internet and try to learn what it does but all that you mentioned its far away from my knowlegde...but I want to learn it all so that's why I ask it :) Thank you so much!

                          One more thing: I have my BaseWizard class like that:

                            #include "WPn"// include of all the wizardpages I have   (in the .cpp)
                            class BaseWizard : public QWizard
                          

                          And the BasePAge like this:

                             #include "basewizard.h" //(in the .cpp)
                             class BasePage : public QWizardPage
                          

                          And every QWizardPage like this:

                            #include "basewizard.h" // in .cpp
                            #include "basepage.h"   // in .cpp
                            class WP1 : public BasePage
                          

                          So now I'm trying to call in basewizard's constructor a function from basepage, but still have the same problem and don't know how to do it really, I've tried with something like:

                                BasePage *bp = dynamic_cast<BasePage *>( new QWizardPage()); // really not sure how to writte it sorry)
                                bp->SetSubTitleStyle();
                          

                          I know that as basewizard is not including basepage it is not possible (and I can't include it because its BaseWizard the one included on BasePage. So...How could I do that?

                          I need that because when you use setStyleSheet() on QT, it keeps the last one, it doesn't merge them or something like that so I have a general css for labels set in BaseWizard and I want one label (common in all wp) to have another style. I've also tried putting the Wizard style on basepage instead of basewizard but then the program crashes.. I do it putting that in the constructor of basepage:

                          BaseWizard * bz = dynamic_cast<BaseWizard *>(wizard());
                          bz->setStyleSheet("QWizard{background-color: black);}"
                                            "QComboBox:focus, QLineEdit:focus{ background-color: #dddddd; }"
                                            "QPushButton{ font-weight: bold; }"
                                            "QPushButton:focus{background-color: #8888a8); color:orange;}"
                                            "QLabel{font-weight: bold;color:orange}");
                          
                              SetSubTitleStyle(); //function that chagnes the common label css
                          
                          kshegunovK 1 Reply Last reply
                          0
                          • R roseicollis

                            @kshegunov

                            but for that you need your class to be declared as a metatype to Qt, which I was not sure you had done.

                            Neither do I :PWhat do you mean with metatype and how do you declare it? I just use to code making it works and if it does not, then I search on the internet and try to learn what it does but all that you mentioned its far away from my knowlegde...but I want to learn it all so that's why I ask it :) Thank you so much!

                            One more thing: I have my BaseWizard class like that:

                              #include "WPn"// include of all the wizardpages I have   (in the .cpp)
                              class BaseWizard : public QWizard
                            

                            And the BasePAge like this:

                               #include "basewizard.h" //(in the .cpp)
                               class BasePage : public QWizardPage
                            

                            And every QWizardPage like this:

                              #include "basewizard.h" // in .cpp
                              #include "basepage.h"   // in .cpp
                              class WP1 : public BasePage
                            

                            So now I'm trying to call in basewizard's constructor a function from basepage, but still have the same problem and don't know how to do it really, I've tried with something like:

                                  BasePage *bp = dynamic_cast<BasePage *>( new QWizardPage()); // really not sure how to writte it sorry)
                                  bp->SetSubTitleStyle();
                            

                            I know that as basewizard is not including basepage it is not possible (and I can't include it because its BaseWizard the one included on BasePage. So...How could I do that?

                            I need that because when you use setStyleSheet() on QT, it keeps the last one, it doesn't merge them or something like that so I have a general css for labels set in BaseWizard and I want one label (common in all wp) to have another style. I've also tried putting the Wizard style on basepage instead of basewizard but then the program crashes.. I do it putting that in the constructor of basepage:

                            BaseWizard * bz = dynamic_cast<BaseWizard *>(wizard());
                            bz->setStyleSheet("QWizard{background-color: black);}"
                                              "QComboBox:focus, QLineEdit:focus{ background-color: #dddddd; }"
                                              "QPushButton{ font-weight: bold; }"
                                              "QPushButton:focus{background-color: #8888a8); color:orange;}"
                                              "QLabel{font-weight: bold;color:orange}");
                            
                                SetSubTitleStyle(); //function that chagnes the common label css
                            
                            kshegunovK Offline
                            kshegunovK Offline
                            kshegunov
                            Moderators
                            wrote on last edited by kshegunov
                            #13

                            @roseicollis said:

                            What do you mean with metatype and how do you declare it?

                            A meta-type is a class/type that is known to Qt's meta-object system. You're required to declare your class as a meta-type when you intent to use Qt's template functions that depend on it (these include QVariant's conversions and qobject_cast). You also need to register your class at runtime as a meta-type if it will be used as an argument for queued signal-slot connections, which is relevant to multithreaded applications.

                            On your other question:

                                BasePage *bp = dynamic_cast<BasePage *>( new QWizardPage()); // really not sure how to writte it sorry)
                                bp->SetSubTitleStyle();
                            

                            This is a bad idea, because of two things:

                            1. Your wizard is managing your pages, but should not be required to know anything about how the pages are built or styled. This is a responsibility of your page, not your wizard.
                            2. The code is simply wrong, from C++ point of view. You create an object of type QWizardPage, then you try to cast the pointer returned to a derived class pointer, which will always cause dynamic_cast to return NULL (simply because QWizardPage is not a BasePage). So at the end of the day, when you call your function, you'll just get a segmentation fault because bp is NULL.

                            Suppose you have the following hierarchy:

                            class A { };
                            class B : public A  {};
                            class C : public A  {};
                            class D : public B  {};
                            

                            This means B is A, C is A, D is B (but because B is A, D is A as well). What you're trying to tell the compiler with your code is: "I have an object of type A and want to treat it (the dynamic_cast) as an object of type B", but you see, this is not correct, because while B is A, A is not B, so the dynamic_cast will always return NULL (meaning it can't provide a meaningful conversion). Now suppose you have objects created like this:

                            A * a = new A;   // This is okay, our pointer references object of the same type
                            A * b = new B;   // This is okay as well, our pointer references an object of type B (but threats it as an object of type A). It's possible because B extends A.
                            B * bb = new A; // Error! The object is of type A, and cannot be meaningfully accessed like it was of type B. B does not extend A, it's the other way around!
                            

                            So...How could I do that?

                            You could set your style for the page in the page's constructor (not in the wizard's constructor).

                            I hope this helps.
                            Kind regards.

                            Read and abide by the Qt Code of Conduct

                            R 1 Reply Last reply
                            1
                            • kshegunovK kshegunov

                              @roseicollis said:

                              What do you mean with metatype and how do you declare it?

                              A meta-type is a class/type that is known to Qt's meta-object system. You're required to declare your class as a meta-type when you intent to use Qt's template functions that depend on it (these include QVariant's conversions and qobject_cast). You also need to register your class at runtime as a meta-type if it will be used as an argument for queued signal-slot connections, which is relevant to multithreaded applications.

                              On your other question:

                                  BasePage *bp = dynamic_cast<BasePage *>( new QWizardPage()); // really not sure how to writte it sorry)
                                  bp->SetSubTitleStyle();
                              

                              This is a bad idea, because of two things:

                              1. Your wizard is managing your pages, but should not be required to know anything about how the pages are built or styled. This is a responsibility of your page, not your wizard.
                              2. The code is simply wrong, from C++ point of view. You create an object of type QWizardPage, then you try to cast the pointer returned to a derived class pointer, which will always cause dynamic_cast to return NULL (simply because QWizardPage is not a BasePage). So at the end of the day, when you call your function, you'll just get a segmentation fault because bp is NULL.

                              Suppose you have the following hierarchy:

                              class A { };
                              class B : public A  {};
                              class C : public A  {};
                              class D : public B  {};
                              

                              This means B is A, C is A, D is B (but because B is A, D is A as well). What you're trying to tell the compiler with your code is: "I have an object of type A and want to treat it (the dynamic_cast) as an object of type B", but you see, this is not correct, because while B is A, A is not B, so the dynamic_cast will always return NULL (meaning it can't provide a meaningful conversion). Now suppose you have objects created like this:

                              A * a = new A;   // This is okay, our pointer references object of the same type
                              A * b = new B;   // This is okay as well, our pointer references an object of type B (but threats it as an object of type A). It's possible because B extends A.
                              B * bb = new A; // Error! The object is of type A, and cannot be meaningfully accessed like it was of type B. B does not extend A, it's the other way around!
                              

                              So...How could I do that?

                              You could set your style for the page in the page's constructor (not in the wizard's constructor).

                              I hope this helps.
                              Kind regards.

                              R Offline
                              R Offline
                              roseicollis
                              wrote on last edited by
                              #14

                              @kshegunov Ohhh I see it now, I understand it so much better. Thank you for the explanation and the examples, they helped me a lot as they are easy to understand for me :). I know I can do it in every WP constructor but I wanted to avoid it as it mean I will have this line in every of my 50 wizardpages so that's why I was trying to set it in another way but seeing it is totally 'ilegal' then I'll do it this way.

                              Really greatfull for your reply! :D

                              kshegunovK 1 Reply Last reply
                              0
                              • R roseicollis

                                @kshegunov Ohhh I see it now, I understand it so much better. Thank you for the explanation and the examples, they helped me a lot as they are easy to understand for me :). I know I can do it in every WP constructor but I wanted to avoid it as it mean I will have this line in every of my 50 wizardpages so that's why I was trying to set it in another way but seeing it is totally 'ilegal' then I'll do it this way.

                                Really greatfull for your reply! :D

                                kshegunovK Offline
                                kshegunovK Offline
                                kshegunov
                                Moderators
                                wrote on last edited by
                                #15

                                @roseicollis
                                You're welcome.

                                I know I can do it in every WP constructor but I wanted to avoid it as it mean I will have this line in every of my 50 wizardpages so that's why I was trying to set it in another way but seeing it is totally 'ilegal' then I'll do it this way.

                                Well this is exactly why you have a BasePage class, isn't it? You put all the common functionality inside your base class (this would be your BasePage constructor) and when a wizard page is supposed to do something different you derive from your BasePage a new type that will provide you with the new behavior. My point is that your Wizard should not know about how the page is built or styled, but all the pages are of type BasePage, and BasePage does and should handle all the common things related to the wizard pages, while the wizard itself just collects and displays the pages.

                                Kind regards.

                                Read and abide by the Qt Code of Conduct

                                R 1 Reply Last reply
                                0
                                • kshegunovK kshegunov

                                  @roseicollis
                                  You're welcome.

                                  I know I can do it in every WP constructor but I wanted to avoid it as it mean I will have this line in every of my 50 wizardpages so that's why I was trying to set it in another way but seeing it is totally 'ilegal' then I'll do it this way.

                                  Well this is exactly why you have a BasePage class, isn't it? You put all the common functionality inside your base class (this would be your BasePage constructor) and when a wizard page is supposed to do something different you derive from your BasePage a new type that will provide you with the new behavior. My point is that your Wizard should not know about how the page is built or styled, but all the pages are of type BasePage, and BasePage does and should handle all the common things related to the wizard pages, while the wizard itself just collects and displays the pages.

                                  Kind regards.

                                  R Offline
                                  R Offline
                                  roseicollis
                                  wrote on last edited by
                                  #16

                                  @kshegunov Yes yes I understand you and that was what I was trying to achieve but dunno why it didn't work fine. finally it works correctly without having to call it every time in every constructor of every wizardpage. The label that was not ok with the stylesheet had a QPalette with the colour and then some css with setStyleSheet() so seems that both are incompatibles and doing that the setStyleSheet() function was ignored. I've removed the QPalette and now it works perfect.

                                  Thanks for everything!

                                  1 Reply Last reply
                                  0

                                  • Login

                                  • Login or register to search.
                                  • First post
                                    Last post
                                  0
                                  • Categories
                                  • Recent
                                  • Tags
                                  • Popular
                                  • Users
                                  • Groups
                                  • Search
                                  • Get Qt Extensions
                                  • Unsolved