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. need help on tristate toolbutton creation
Forum Updated to NodeBB v4.3 + New Features

need help on tristate toolbutton creation

Scheduled Pinned Locked Moved Solved General and Desktop
14 Posts 4 Posters 963 Views 3 Watching
  • 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.
  • D django.Reinhard

    Hi,

    thank you for your attention.

    May be, we don't understand each other.
    I cannot find any code for tristate-support. Not in QAction nor in QPushButton.
    Also QIcon has a boolean as state (QIcon::State).

    I did not use QIcon::addFile, but QIcon::addPixmap - which I suppose, does the same.

    QAction looks like this:

            if(d->checkable) {
                .
                .
                .
                setChecked(!d->checked);
    

    To me, that does not look like tristate-support out of the box.

    I haven't highlighted my main question. Maybe you missed it:
    I was looking for the code, that selects the pixmap from QIcon

    artwawA Offline
    artwawA Offline
    artwaw
    wrote on last edited by
    #5

    @django-Reinhard QIcon operates on states. You can addFile() for each state.
    QAction offers tri-state support out of the box via checkable property.
    QIcon documentation, first link from my previous post, offers snippet for the custom icon selection depending on state:

    void MyWidget::drawIcon(QPainter *painter, QPoint pos)
    {
        QPixmap pixmap = icon.pixmap(QSize(22, 22),
                                       isEnabled() ? QIcon::Normal
                                                   : QIcon::Disabled,
                                       isChecked() ? QIcon::On
                                                   : QIcon::Off);
        painter->drawPixmap(pos, pixmap);
    }
    

    For more information please re-read.

    Kind Regards,
    Artur

    1 Reply Last reply
    1
    • D Offline
      D Offline
      django.Reinhard
      wrote on last edited by
      #6

      Sorry Artur,

      but your postings made me feel like you are shitting on me.
      I could not find any tristate-support out of the box.

      The code sample is the only a user function, which does not get called. So you're up on your own :(

      Anyway - I was able to solve the problem, but the solution is absolutely ugly and I hate it. But as I don't know any better, I have to live with it.

      What turned me mad is the fact, that its not possible to add a subclass of QToolButton to a toolbar (or at least I was not able to solve it).
      QToolBar::addAction creates a QToolButton hidden in layout labyrinth and if you add a Subclass instance of QToolButton, toolbar creates a QActionWidget with the subclass attached, but the possibly attached action of the toolbutton subclass gets lost :(
      Same is true for any already established action connections.

      ... anyway - here's my solution:

      my icon subclass declaration:

      class MIcon : public QIcon
      {
      public:
        MIcon(const QString& stdFileName, const QString& selFileName);
        MIcon(const QString& normalFile, const QString& activeFile, const QString& selectedFile);
        static void setDisabledFileName(const QString& fileName);
      
      protected:
        static QString disabledFileName;
        };
      

      ... with this constructor:

      MIcon::MIcon(const QString& normalFile, const QString& activeFile, const QString& selectedFile)
       : QIcon(normalFile) {
        addPixmap(QPixmap(activeFile), QIcon::Mode::Active);
        addPixmap(QPixmap(selectedFile), QIcon::Mode::Selected);
        }
      

      ... the action definition (its an extended Action, so just ignore the conditions):

        power       = new DynaAction(MIcon(":/res/SK_PowerOff.png"
                                         , ":/res/SK_PowerOff_1.png"
                                         , ":/res/SK_PowerOn.png")
                                   , tr("Poweroff")
                                   , new TrueCondition()
                                   , new EqualCondition(vm.getModel("taskState"), EMC_TASK_STATE_ENUM::EMC_TASK_STATE_ON)
                                   , this);
        power->setCheckable(true);
      

      ... and finally the toolbar creation and handling of tristate:

        powerTB = new QToolBar(tr("Power"), this);
        powerTB->setObjectName("PowerTB");
        powerTB->setIconSize(s);
      
        powerTB->addAction(power);
      
        connect(power, &QAction::triggered, this, [=](bool){
          int state  = power->property("state").toInt() + 1;
          QToolButton* btPow = static_cast<QToolButton*>(powerTB->widgetForAction(power));
      
          if (state > 2) state = 0;
          power->setProperty("state", state);
          switch (state) {
            case 1:
                 btPow->setIcon(power->icon().pixmap(powerTB->iconSize()
                                                   , QIcon::Mode::Active));
                 break;
            case 2:
                 btPow->setIcon(power->icon().pixmap(powerTB->iconSize()
                                                   , QIcon::Mode::Selected));
                 break;
            default:
                 btPow->setIcon(power->icon().pixmap(powerTB->iconSize()
                                                   , QIcon::Mode::Normal));
                 break;
            }
          qDebug() << "power - state == " << state;
          });
      

      Why I hate this solution?
      Well, in my opinion a QAction::triggered callback should be used for functionality only. Not for styling or painting.
      So here two worlds are mixed, that should not be mixed ...

      My conclusion so far: there's absolutely no support for tristate buttons in Qt, but a workaround is possible.

      artwawA Pl45m4P 2 Replies Last reply
      0
      • M Offline
        M Offline
        mchinand
        wrote on last edited by
        #7

        @django-Reinhard said in need help on tristate toolbutton creation:

        My conclusion so far: there's absolutely no support for tristate buttons in Qt

        QCheckbox has tristates, so this is not true.

        D 1 Reply Last reply
        0
        • D django.Reinhard

          Sorry Artur,

          but your postings made me feel like you are shitting on me.
          I could not find any tristate-support out of the box.

          The code sample is the only a user function, which does not get called. So you're up on your own :(

          Anyway - I was able to solve the problem, but the solution is absolutely ugly and I hate it. But as I don't know any better, I have to live with it.

          What turned me mad is the fact, that its not possible to add a subclass of QToolButton to a toolbar (or at least I was not able to solve it).
          QToolBar::addAction creates a QToolButton hidden in layout labyrinth and if you add a Subclass instance of QToolButton, toolbar creates a QActionWidget with the subclass attached, but the possibly attached action of the toolbutton subclass gets lost :(
          Same is true for any already established action connections.

          ... anyway - here's my solution:

          my icon subclass declaration:

          class MIcon : public QIcon
          {
          public:
            MIcon(const QString& stdFileName, const QString& selFileName);
            MIcon(const QString& normalFile, const QString& activeFile, const QString& selectedFile);
            static void setDisabledFileName(const QString& fileName);
          
          protected:
            static QString disabledFileName;
            };
          

          ... with this constructor:

          MIcon::MIcon(const QString& normalFile, const QString& activeFile, const QString& selectedFile)
           : QIcon(normalFile) {
            addPixmap(QPixmap(activeFile), QIcon::Mode::Active);
            addPixmap(QPixmap(selectedFile), QIcon::Mode::Selected);
            }
          

          ... the action definition (its an extended Action, so just ignore the conditions):

            power       = new DynaAction(MIcon(":/res/SK_PowerOff.png"
                                             , ":/res/SK_PowerOff_1.png"
                                             , ":/res/SK_PowerOn.png")
                                       , tr("Poweroff")
                                       , new TrueCondition()
                                       , new EqualCondition(vm.getModel("taskState"), EMC_TASK_STATE_ENUM::EMC_TASK_STATE_ON)
                                       , this);
            power->setCheckable(true);
          

          ... and finally the toolbar creation and handling of tristate:

            powerTB = new QToolBar(tr("Power"), this);
            powerTB->setObjectName("PowerTB");
            powerTB->setIconSize(s);
          
            powerTB->addAction(power);
          
            connect(power, &QAction::triggered, this, [=](bool){
              int state  = power->property("state").toInt() + 1;
              QToolButton* btPow = static_cast<QToolButton*>(powerTB->widgetForAction(power));
          
              if (state > 2) state = 0;
              power->setProperty("state", state);
              switch (state) {
                case 1:
                     btPow->setIcon(power->icon().pixmap(powerTB->iconSize()
                                                       , QIcon::Mode::Active));
                     break;
                case 2:
                     btPow->setIcon(power->icon().pixmap(powerTB->iconSize()
                                                       , QIcon::Mode::Selected));
                     break;
                default:
                     btPow->setIcon(power->icon().pixmap(powerTB->iconSize()
                                                       , QIcon::Mode::Normal));
                     break;
                }
              qDebug() << "power - state == " << state;
              });
          

          Why I hate this solution?
          Well, in my opinion a QAction::triggered callback should be used for functionality only. Not for styling or painting.
          So here two worlds are mixed, that should not be mixed ...

          My conclusion so far: there's absolutely no support for tristate buttons in Qt, but a workaround is possible.

          artwawA Offline
          artwawA Offline
          artwaw
          wrote on last edited by
          #8

          @django-Reinhard I misunderstood. Please accept my apologies.
          I confused checkable (on/off state) with what you mean (on/off/partial). One of my weaker days it seems.

          For more information please re-read.

          Kind Regards,
          Artur

          1 Reply Last reply
          1
          • D django.Reinhard

            Sorry Artur,

            but your postings made me feel like you are shitting on me.
            I could not find any tristate-support out of the box.

            The code sample is the only a user function, which does not get called. So you're up on your own :(

            Anyway - I was able to solve the problem, but the solution is absolutely ugly and I hate it. But as I don't know any better, I have to live with it.

            What turned me mad is the fact, that its not possible to add a subclass of QToolButton to a toolbar (or at least I was not able to solve it).
            QToolBar::addAction creates a QToolButton hidden in layout labyrinth and if you add a Subclass instance of QToolButton, toolbar creates a QActionWidget with the subclass attached, but the possibly attached action of the toolbutton subclass gets lost :(
            Same is true for any already established action connections.

            ... anyway - here's my solution:

            my icon subclass declaration:

            class MIcon : public QIcon
            {
            public:
              MIcon(const QString& stdFileName, const QString& selFileName);
              MIcon(const QString& normalFile, const QString& activeFile, const QString& selectedFile);
              static void setDisabledFileName(const QString& fileName);
            
            protected:
              static QString disabledFileName;
              };
            

            ... with this constructor:

            MIcon::MIcon(const QString& normalFile, const QString& activeFile, const QString& selectedFile)
             : QIcon(normalFile) {
              addPixmap(QPixmap(activeFile), QIcon::Mode::Active);
              addPixmap(QPixmap(selectedFile), QIcon::Mode::Selected);
              }
            

            ... the action definition (its an extended Action, so just ignore the conditions):

              power       = new DynaAction(MIcon(":/res/SK_PowerOff.png"
                                               , ":/res/SK_PowerOff_1.png"
                                               , ":/res/SK_PowerOn.png")
                                         , tr("Poweroff")
                                         , new TrueCondition()
                                         , new EqualCondition(vm.getModel("taskState"), EMC_TASK_STATE_ENUM::EMC_TASK_STATE_ON)
                                         , this);
              power->setCheckable(true);
            

            ... and finally the toolbar creation and handling of tristate:

              powerTB = new QToolBar(tr("Power"), this);
              powerTB->setObjectName("PowerTB");
              powerTB->setIconSize(s);
            
              powerTB->addAction(power);
            
              connect(power, &QAction::triggered, this, [=](bool){
                int state  = power->property("state").toInt() + 1;
                QToolButton* btPow = static_cast<QToolButton*>(powerTB->widgetForAction(power));
            
                if (state > 2) state = 0;
                power->setProperty("state", state);
                switch (state) {
                  case 1:
                       btPow->setIcon(power->icon().pixmap(powerTB->iconSize()
                                                         , QIcon::Mode::Active));
                       break;
                  case 2:
                       btPow->setIcon(power->icon().pixmap(powerTB->iconSize()
                                                         , QIcon::Mode::Selected));
                       break;
                  default:
                       btPow->setIcon(power->icon().pixmap(powerTB->iconSize()
                                                         , QIcon::Mode::Normal));
                       break;
                  }
                qDebug() << "power - state == " << state;
                });
            

            Why I hate this solution?
            Well, in my opinion a QAction::triggered callback should be used for functionality only. Not for styling or painting.
            So here two worlds are mixed, that should not be mixed ...

            My conclusion so far: there's absolutely no support for tristate buttons in Qt, but a workaround is possible.

            Pl45m4P Offline
            Pl45m4P Offline
            Pl45m4
            wrote on last edited by
            #9

            @django-Reinhard said in need help on tristate toolbutton creation:

            What turned me mad is the fact, that its not possible to add a subclass of QToolButton to a toolbar (or at least I was not able to solve it).

            Why do you think so? Should be doable (maybe not the standard way)

            Have your tried reimplementing createWidget?

            • https://doc.qt.io/qt-5/qwidgetaction.html#createWidget

            and then use your custom toolButton as widget.
            Haven't done this, but this is how I would try it.


            If debugging is the process of removing software bugs, then programming must be the process of putting them in.

            ~E. W. Dijkstra

            D 1 Reply Last reply
            0
            • M mchinand

              @django-Reinhard said in need help on tristate toolbutton creation:

              My conclusion so far: there's absolutely no support for tristate buttons in Qt

              QCheckbox has tristates, so this is not true.

              D Offline
              D Offline
              django.Reinhard
              wrote on last edited by
              #10

              @mchinand said in need help on tristate toolbutton creation:

              QCheckbox has tristates, so this is not true.

              Ok, although QCheckbox is a descendant of QAbstractButton - for me a CheckBox is not a button.

              @Pl45m4 said in need help on tristate toolbutton creation:

              Why do you think so? Should be doable (maybe not the standard way)

              Thanks for the hint. I'll check it out.

              My standard way is reading docs to understand the interface of a class. It that does not work like expected, I'll try to find the sources and follow the function calls.

              This way I found that QToolBar::addWidget does not respect the action assotiated with the parameter widget.
              Following QToolBar::addAction I found out, that QToolButton is created by layout (QToolBarLayout::createItem).

              As I don't use QWidgetAction I found no trace of createWidget
              ... so for me, this path will only be visible after a professional hint like yours.
              So - thank you for the pointer.

              Pl45m4P 1 Reply Last reply
              0
              • Pl45m4P Pl45m4

                @django-Reinhard said in need help on tristate toolbutton creation:

                What turned me mad is the fact, that its not possible to add a subclass of QToolButton to a toolbar (or at least I was not able to solve it).

                Why do you think so? Should be doable (maybe not the standard way)

                Have your tried reimplementing createWidget?

                • https://doc.qt.io/qt-5/qwidgetaction.html#createWidget

                and then use your custom toolButton as widget.
                Haven't done this, but this is how I would try it.

                D Offline
                D Offline
                django.Reinhard
                wrote on last edited by
                #11

                @Pl45m4 said in need help on tristate toolbutton creation:

                Have your tried reimplementing createWidget?

                https://doc.qt.io/qt-5/qwidgetaction.html#createWidget

                and then use your custom toolButton as widget.

                I had a quick research at Qt. If that way is to be promising, then the function must be used or called within Qt.
                However, I could not find any evidence for this.

                There is this comment at QToolBar::actionEvent

                            // reparent the action to this toolbar if it has been created
                            // using the addAction(text) etc. convenience functions, to
                            // preserve Qt 4.1.x behavior. The widget is already
                            // reparented to us due to the createWidget call inside
                            // createItem()
                

                but QToolBar does not have a createItem function. So I guess, the createItem of toolbarlayout is meant. But that function does not use createWidget. Instead creation of QToolButton is hardcoded:

                    if (!widget) {
                        QToolButton *button = new QToolButton(tb);
                

                So if it really is possible to add a subclass of QToolButton to QToolBar, I need some help to find the way to do it.

                1 Reply Last reply
                0
                • D django.Reinhard

                  @mchinand said in need help on tristate toolbutton creation:

                  QCheckbox has tristates, so this is not true.

                  Ok, although QCheckbox is a descendant of QAbstractButton - for me a CheckBox is not a button.

                  @Pl45m4 said in need help on tristate toolbutton creation:

                  Why do you think so? Should be doable (maybe not the standard way)

                  Thanks for the hint. I'll check it out.

                  My standard way is reading docs to understand the interface of a class. It that does not work like expected, I'll try to find the sources and follow the function calls.

                  This way I found that QToolBar::addWidget does not respect the action assotiated with the parameter widget.
                  Following QToolBar::addAction I found out, that QToolButton is created by layout (QToolBarLayout::createItem).

                  As I don't use QWidgetAction I found no trace of createWidget
                  ... so for me, this path will only be visible after a professional hint like yours.
                  So - thank you for the pointer.

                  Pl45m4P Offline
                  Pl45m4P Offline
                  Pl45m4
                  wrote on last edited by Pl45m4
                  #12

                  @django-Reinhard said in need help on tristate toolbutton creation:

                  As I don't use QWidgetAction I found no trace of createWidget

                  You don't use it directly, but it's used internally when you add QActions to your ToolBar.
                  Might be interesting to read
                  (Not exactly what you are doing, but somehow related)

                  Since QToolBar is a container for QWidget which represents QActions in most cases (-> "widgetForAction"), a custom widget is drawn/created internally for every QAction you add to your standard QToolBar. This is why you can't just add a custom QWidget to a toolBar without breaking a lot of stuff (resize behavior, toolBar layout etc.).
                  This is also the case, if you don't have a QMainWindow with its "standard" QToolBar. Like it is the case in my linked topic above. As you've said before, every additional QToolBar is somewhere added to your layout, which you don't want.

                  You want your custom tristate ToolButton in your toolBar:

                  This function is called whenever the action is added to a container widget that supports custom widgets.

                  (from: https://doc.qt.io/qt-5/qwidgetaction.html#createWidget)

                  So maybe your understanding of how QToolButtons work in a QToolBar, is not quite correct.

                  • https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qwidgetaction.cpp.html

                  The problem is, that createWidget has to create the widget. So it's not possible to pass any existing widget to your custom QWidgetAction and add it to your QToolBar. See the linked topic, where @Alexey-Serebryakov tried to pass custom widgets to QWidgetAction. The issue was that QToolBar creates this so called Extension menu when you resize your main widget where the toolBar is located, and therefore createWidget is called again to populate this menu (re-draw/ -create all actionWidgets again).

                  So from my understanding, you could create custom widgetActions which will create your TristateButtons. Then you can add them to any standard QToolBar with addAction without losing any functionality.

                  A, B, C, D, E are regular QToolButtons created by Qt with addAction("A").
                  MyTristateToolButton is a custom widgetAction subclass which creates a custom QToolButton. There you can do anything you want.

                  TriStateToolBar.png

                  "Extension menu" -> widgets redrawn, still works.
                  TristateExtension.png


                  If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                  ~E. W. Dijkstra

                  D 1 Reply Last reply
                  0
                  • Pl45m4P Pl45m4

                    @django-Reinhard said in need help on tristate toolbutton creation:

                    As I don't use QWidgetAction I found no trace of createWidget

                    You don't use it directly, but it's used internally when you add QActions to your ToolBar.
                    Might be interesting to read
                    (Not exactly what you are doing, but somehow related)

                    Since QToolBar is a container for QWidget which represents QActions in most cases (-> "widgetForAction"), a custom widget is drawn/created internally for every QAction you add to your standard QToolBar. This is why you can't just add a custom QWidget to a toolBar without breaking a lot of stuff (resize behavior, toolBar layout etc.).
                    This is also the case, if you don't have a QMainWindow with its "standard" QToolBar. Like it is the case in my linked topic above. As you've said before, every additional QToolBar is somewhere added to your layout, which you don't want.

                    You want your custom tristate ToolButton in your toolBar:

                    This function is called whenever the action is added to a container widget that supports custom widgets.

                    (from: https://doc.qt.io/qt-5/qwidgetaction.html#createWidget)

                    So maybe your understanding of how QToolButtons work in a QToolBar, is not quite correct.

                    • https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qwidgetaction.cpp.html

                    The problem is, that createWidget has to create the widget. So it's not possible to pass any existing widget to your custom QWidgetAction and add it to your QToolBar. See the linked topic, where @Alexey-Serebryakov tried to pass custom widgets to QWidgetAction. The issue was that QToolBar creates this so called Extension menu when you resize your main widget where the toolBar is located, and therefore createWidget is called again to populate this menu (re-draw/ -create all actionWidgets again).

                    So from my understanding, you could create custom widgetActions which will create your TristateButtons. Then you can add them to any standard QToolBar with addAction without losing any functionality.

                    A, B, C, D, E are regular QToolButtons created by Qt with addAction("A").
                    MyTristateToolButton is a custom widgetAction subclass which creates a custom QToolButton. There you can do anything you want.

                    TriStateToolBar.png

                    "Extension menu" -> widgets redrawn, still works.
                    TristateExtension.png

                    D Offline
                    D Offline
                    django.Reinhard
                    wrote on last edited by
                    #13

                    Hi,

                    @Pl45m4 said in need help on tristate toolbutton creation:

                    (Not exactly what you are doing, but somehow related)

                    Thank you for the pointer!
                    Very interesting.
                    I like the post, where Alexey wrote, that he got mad.
                    Same happened to me, when I looked at the sources of QToolBar and QToolBarLayout :(

                    @Pl45m4 said in need help on tristate toolbutton creation:

                    As you've said before, every additional QToolBar is somewhere added to your layout, which you don't want.

                    No! That's a misunderstanding. Of cause I want toolbars with their layout.
                    FalconView03.jpg

                    You see 5 toolbars, all dockable and floating allowed.
                    The Notebook is my central widget, which is a widget stack. Displayed widget is decided by user action and application flow.
                    All other widgets are dockable widgets with dynamic form loading.

                    For the toolbar buttons I use empty Icon for disabled state. So a disabled action can not be identified.

                    What I don't like, on the other hand, is the abuse of responsibility and competence.
                    Look:
                    If I want some money from you, the most natural and commonly accepted way is, that I ask you for money (and you decide what to do).
                    Only in programming business many devs find it ok, to grab your moneybox and take your money out without asking.

                    So - for me - a layout should care for arrangement of child widgets of the widget it controls - in that context a layout may change size and position of a child-widget. But under no conditions a layout should be allowed to create widgets on its own, like QToolBarLayout is doing.
                    QToolBarLayout could use a factory method of QToolBar or QToolBar could create the toolbuttons on adding the action, what ever ...

                    Same rule applies to my code. I don't like, that icon of a toolbutton is changed by application code. Therefore I wanted to subclass QToolButton.
                    The most preferred way would be, to extend an Action to be able to handle all tristate logic. But for that to work, the QIcon needs tristate-support and whatever code is responsible for extracting pixmap by status from qicon should be extended for tristate support too.

                    Therefore: I don't know enuf to subclass a QToolButton to support tristate icons.

                    ... but I'll try QWidgetAction::createWidget anyway.

                    1 Reply Last reply
                    0
                    • D Offline
                      D Offline
                      django.Reinhard
                      wrote on last edited by
                      #14

                      Hi Pl45m4,

                      toolbar seems to be a crazy environment. I shouted the 'F'-word several times. Had lot of crashes, but finally I think I got it.
                      Not perfect, but a solution, I can live with ;)

                      Every posibility, I thought I should work with is blocked/prohibited. Either function not virtual, or no function used for access ...

                      QWidgetAction is the only posibility to get individual widgets into a toolbar. But that widget will be reduced to a stupid painter subject. The factory action is still used as an action (well - I thought it would be just a factory action, but it is not. Stil a complete QAction!).

                      After I got that point, it was straightforward to implement -
                      MultiStateButton - declaration:

                      class MultiStateToolButton : public QToolButton
                      {
                        Q_OBJECT
                      public:
                        MultiStateToolButton(ValueModel* vm, const QIcon& s0Icon, const QIcon& s1Icon, const QIcon& s2Icon, QWidget* parent = nullptr);
                      
                      protected slots:
                        void          stateChanged(const QVariant& state);
                      
                      private:
                        ValueModel*  model;
                        const QIcon& s0Icon;
                        const QIcon& s1Icon;
                        const QIcon& s2Icon;
                        };
                      

                      I implemented it for 3 States, but it could be adapted to any number of states.

                      ValueModel is used to hold the buttons state - as in my application the buttons state can be changed from outside of my application ...
                      model could be a Qt-property as well.

                      MultiStateButton - definition:

                      MultiStateToolButton::MultiStateToolButton(ValueModel* vm, const QIcon& s0Icon, const QIcon& s1Icon, const QIcon& s2Icon, QWidget* parent)
                       : QToolButton(parent)
                       , model(vm)
                       , s0Icon(s0Icon)
                       , s1Icon(s1Icon)
                       , s2Icon(s2Icon) {
                        setIcon(this->s0Icon);
                        connect(model, &ValueModel::valueChanged, this, &MultiStateToolButton::stateChanged);
                        }
                      
                      
                      void MultiStateToolButton::stateChanged(const QVariant& state) {
                        qDebug() << "MultiStateButton::stateChanged ... " << state;
                      
                        switch (state.toInt()) {
                          case 2:   setIcon(s1Icon); break;
                          case 3:   setIcon(s2Icon); break;
                          default:  setIcon(s0Icon); break;
                          }
                        }
                      

                      The hardest point for me to get rid of was, that the toolbutton works fine with const references to icons, but the QWidgetAction will crash with icons as references.
                      As I could not use one QIcon with multiple pixmaps, I decided to use one QIcon for each picture.

                      So the subclass of QWidgetAction is straight forward:

                      
                      class MultiStateAction : public QWidgetAction
                      {
                        Q_OBJECT
                      public:
                        MultiStateAction(ValueModel* vm, const QIcon& s0Icon, const QIcon& s1Icon, const QIcon& s2Icon, QWidget* parent = nullptr);
                      
                        QWidget* createWidget(QWidget* parent) override;
                      
                      private:
                        ValueModel* model;
                        QIcon       s0Icon;
                        QIcon       s1Icon;
                        QIcon       s2Icon;
                        };
                      

                      I started with const references in the widget action, as I thought, I only have to store the parameters for widget creation later on ...

                      MultiStateAction::MultiStateAction(ValueModel* vm, const QIcon& s0Icon, const QIcon& s1Icon, const QIcon& s2Icon, QWidget *parent)
                       : QWidgetAction(parent)
                       , model(vm)
                       , s0Icon(s0Icon)
                       , s1Icon(s1Icon)
                       , s2Icon(s2Icon) {
                        setIcon(s0Icon);
                        setText("MultiStateFactoryAction");
                        }
                      
                      
                      QWidget *MultiStateAction::createWidget(QWidget *parent) {
                        QToolButton* tb = new MultiStateToolButton(model, s0Icon, s1Icon, s2Icon, parent);
                      
                        tb->setDefaultAction(this);
                      
                        return tb;
                        }
                      

                      QToolButton::setDefaultAction() is the most important step. Without that, the button does not emit a triggered signal. With that function call, button and action emit a triggered signal.

                      Creation of toolbar (in mainwindow or similar) looks like:

                      Window::Window()
                       : model(new ValueModel("state", 0))
                       , tb(new QToolBar)
                       , a1(new QAction("A"))
                       , a2(new QAction("B"))
                       , a3(new QAction("C"))
                       , a4(new QAction("D"))
                       , msa(new MultiStateAction(model
                                                , QIcon(":/res/doc.png")
                                                , QIcon(":/res/hand.png")
                                                , QIcon(":/res/ok.png"))) {
                        setLayout(new QVBoxLayout);
                        setupActions();
                        connectActions();
                        }
                      
                      
                      void Window::setupActions() {
                        tb->setIconSize(QSize(100, 100));
                        tb->addAction(a1);
                        tb->addAction(a2);
                        tb->addAction(a3);
                        tb->addAction(msa);
                        tb->addAction(a4);
                      
                        layout()->addWidget(tb);
                        }
                      

                      First I had no call of setDefaultAction used and got no signal from the multistatebutton. So I tried several ways to get the button-press-event ...

                      As said - button state can be changed outside of my app, so the plain actions simulate external status change ...

                      void Window::connectActions() {
                        connect(a1, &QAction::triggered, this, [=]() {
                          qDebug() << "A";
                          model->setValue(0);
                          });
                        connect(a2, &QAction::triggered, this, [=]() {
                          qDebug() << "B";
                          model->setValue(1);
                          });
                        connect(a3, &QAction::triggered, this, [=]() {
                          qDebug() << "C";
                          model->setValue(2);
                          });
                        connect(a4, &QAction::triggered, this, [=]() {
                          qDebug() << "D";
                          model->setValue(3);
                          });
                      
                        connect(msa, &QAction::triggered, this, [=]() {
                          qDebug() << " - MultiStateAction triggered ... !";
                          });
                      
                        QToolButton* multiStateButton = static_cast<QToolButton*>(tb->widgetForAction(msa));
                      
                        if (multiStateButton) {
                           connect(multiStateButton, &QToolButton::triggered, this, [=]() {
                             qDebug() << "MultiStateToolButton clicked ...";
                             });
                           }
                        else {
                           throw QString("OUPS - toolbar has no MultiStateToolButton button!");
                           }
                        }
                      

                      @Pl45m4 thank you very much for your help!

                      1 Reply Last reply
                      1

                      • Login

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