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. QDoubleSpinBox - blockSignals() does not work
Forum Updated to NodeBB v4.3 + New Features

QDoubleSpinBox - blockSignals() does not work

Scheduled Pinned Locked Moved Solved General and Desktop
16 Posts 5 Posters 1.6k Views 2 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 Offline
    D Offline
    DL5EU
    wrote on last edited by
    #1

    Dear all,

    I have got a problem with the QDoubleSpinBox widget.

    In order not to emit the valueChanged() signal when I set the value, I have done this in my "set" function:

        ui->startFrequency->blockSignals(true);
        ui->startFrequency->setValue(m_startFreqHz / m_startFreqScaleFactor);
        ui->startFrequency->blockSignals(false);
    

    However, although I block the signals before I set the value, the valueChanged() signal is emitted. The connection to the slot is done like this:

    connect(ui->startFrequency, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &FrequencyRangeDialog::startFrequencyChanged);
    

    Strangely, when I do not execute

        ui->startFrequency->blockSignals(false);
    

    the valueChanged() signal is not emitted. This means to me that the lock is released before the signal is emitted, but why? What am I doing wrong?

    The Qt version is 5.15.2 (open source version) on Windows 10.

    Thank you for your help,

    Ralf

    jsulmJ 1 Reply Last reply
    0
    • D Offline
      D Offline
      DL5EU
      wrote on last edited by
      #5

      This works, but if it is a hack, it is perhaps not the best solution.

      What I would like to achieve in my program is to detect when a user has modified the content of the spin box and to distinguish this from a call to "setValue()". I thought I could use QObject's "setModified()" method whenever the value is changed and just block the signals before calling "setValue()" so that this does not trigger the emission of the "valueChanged()" signal.

      Is there a better way to do this? If yes, could you please let me know? I am no Qt expert...

      Thanks.

      J.HilkJ 1 Reply Last reply
      0
      • D DL5EU

        Dear all,

        I have got a problem with the QDoubleSpinBox widget.

        In order not to emit the valueChanged() signal when I set the value, I have done this in my "set" function:

            ui->startFrequency->blockSignals(true);
            ui->startFrequency->setValue(m_startFreqHz / m_startFreqScaleFactor);
            ui->startFrequency->blockSignals(false);
        

        However, although I block the signals before I set the value, the valueChanged() signal is emitted. The connection to the slot is done like this:

        connect(ui->startFrequency, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &FrequencyRangeDialog::startFrequencyChanged);
        

        Strangely, when I do not execute

            ui->startFrequency->blockSignals(false);
        

        the valueChanged() signal is not emitted. This means to me that the lock is released before the signal is emitted, but why? What am I doing wrong?

        The Qt version is 5.15.2 (open source version) on Windows 10.

        Thank you for your help,

        Ralf

        jsulmJ Offline
        jsulmJ Offline
        jsulm
        Lifetime Qt Champion
        wrote on last edited by
        #2

        @DL5EU said in QDoubleSpinBox - blockSignals() does not work:

        This means to me that the lock is released before the signal is emitted, but why?

        Because a signal is not emitted immediately, but next time the event loop is running, which is not before your code was executed (so, after ui->startFrequency->blockSignals(false);).

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

        1 Reply Last reply
        0
        • D Offline
          D Offline
          DL5EU
          wrote on last edited by
          #3

          Two comments:

          1. In this case, what is blockSignals() good for if it does not have the desired effect and how can I achieve what I need?
          2. Even if I add
          QApplication::processEvents();
          

          before unblocking the signals it does not work as expected.

          J.HilkJ 1 Reply Last reply
          0
          • D DL5EU

            Two comments:

            1. In this case, what is blockSignals() good for if it does not have the desired effect and how can I achieve what I need?
            2. Even if I add
            QApplication::processEvents();
            

            before unblocking the signals it does not work as expected.

            J.HilkJ Offline
            J.HilkJ Offline
            J.Hilk
            Moderators
            wrote on last edited by
            #4

            @DL5EU

            try

            ui->startFrequency->blockSignals(true);
                ui->startFrequency->setValue(m_startFreqHz / m_startFreqScaleFactor);
            QMetaObject::invokeMethod(this, [=]()->void{ui->startFrequency->blockSignals(false);}, Qt::QueuedConnection);
            

            it should work, but its a hacky workaround

            I would consider other paths


            Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


            Q: What's that?
            A: It's blue light.
            Q: What does it do?
            A: It turns blue.

            1 Reply Last reply
            1
            • D Offline
              D Offline
              DL5EU
              wrote on last edited by
              #5

              This works, but if it is a hack, it is perhaps not the best solution.

              What I would like to achieve in my program is to detect when a user has modified the content of the spin box and to distinguish this from a call to "setValue()". I thought I could use QObject's "setModified()" method whenever the value is changed and just block the signals before calling "setValue()" so that this does not trigger the emission of the "valueChanged()" signal.

              Is there a better way to do this? If yes, could you please let me know? I am no Qt expert...

              Thanks.

              J.HilkJ 1 Reply Last reply
              0
              • D DL5EU

                This works, but if it is a hack, it is perhaps not the best solution.

                What I would like to achieve in my program is to detect when a user has modified the content of the spin box and to distinguish this from a call to "setValue()". I thought I could use QObject's "setModified()" method whenever the value is changed and just block the signals before calling "setValue()" so that this does not trigger the emission of the "valueChanged()" signal.

                Is there a better way to do this? If yes, could you please let me know? I am no Qt expert...

                Thanks.

                J.HilkJ Offline
                J.HilkJ Offline
                J.Hilk
                Moderators
                wrote on last edited by J.Hilk
                #6

                @DL5EU I would subclass QDoubleSpinbox and make my own, better suited one:

                #include <QApplication>
                #include <QDebug>
                #include <QTimer>
                #include <QDoubleSpinBox>
                
                class MyDoubleSpinBox : public QDoubleSpinBox
                {
                    Q_OBJECT
                
                public:
                    MyDoubleSpinBox(QWidget*parent = nullptr) : QDoubleSpinBox(parent)
                    {
                        connect(this, QOverload<double>::of(&MyDoubleSpinBox::valueChanged), this, &MyDoubleSpinBox::onValueChanged);
                    }
                
                    //new setter
                    void setValueByCode(double value){
                        m_changedByCode = true;
                        setValue(value);
                    }
                
                signals:
                    void valueChangedByUser(double value);
                    void valueChangedByCode(double value);
                
                private slots:
                    void onValueChanged(double value){
                        if(m_changedByCode)
                            emit valueChangedByCode(value);
                        else
                            emit valueChangedByUser(value);
                        m_changedByCode = false;
                    }
                
                private:
                    bool m_changedByCode{false};
                
                };
                
                int main(int argc, char *argv[])
                {
                    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
                    QApplication app(argc, argv);
                
                    MyDoubleSpinBox mdsb;
                
                    //Test: automatically set value by code via timer
                    QTimer t;
                    QObject::connect(&t, &QTimer::timeout, [&]()->void{mdsb.setValueByCode(mdsb.value()+1);});
                    
                    //see, if it was changed by hand or by program
                    QObject::connect(&mdsb, &MyDoubleSpinBox::valueChangedByCode, [](double value)->void{qDebug() << "value by code" << value;});
                    QObject::connect(&mdsb, &MyDoubleSpinBox::valueChangedByUser, [](double value)->void{qDebug() << "value by user" << value;});
                
                    t.start(5000);
                    mdsb.show();
                    return app.exec();
                
                }
                
                #include "main.moc"
                

                Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                Q: What's that?
                A: It's blue light.
                Q: What does it do?
                A: It turns blue.

                D 1 Reply Last reply
                2
                • D Offline
                  D Offline
                  DL5EU
                  wrote on last edited by
                  #7

                  That is of course a solution but I thought I could avoid subclassing QDoubleSpinBox. The more you can use existing components the less code you have to maintain. However, I think I will try this way.

                  Thank you very much,

                  Ralf

                  1 Reply Last reply
                  1
                  • D Offline
                    D Offline
                    DL5EU
                    wrote on last edited by
                    #8

                    I have noticed that when I subclass QDoubleSpinBox as proposed, the first modification by the user is not recognised as such. This is probably due to the original problem, i.e. that the valueChanged() signal is not processed. In Qt Creator in debug mode I can see that the setter is entered but not the onValueChanged() method of my new object (in response to the call to setValueByCode()). That is the reason why m_changedByCode is still true when onValueChanged() is entered in repsponse to a modification done by the user.

                    This is what the code of my new double spin box looks like:

                    .h file:

                    class GDoubleSpinBox : public QDoubleSpinBox
                    {
                        Q_OBJECT
                    public:
                        explicit GDoubleSpinBox(QWidget *parent = nullptr);
                    public slots:
                        void setValueByCode(double value);
                    signals:
                        void valueChangedByCode(double value);
                        void valueChangedByUser(double value);
                    private:
                        bool m_changedByCode;
                    private slots:
                        void onValueChanged(double value);
                    };
                    
                    

                    .cpp file:

                    GDoubleSpinBox::GDoubleSpinBox(QWidget *parent)
                        : QDoubleSpinBox(parent)
                        , m_changedByCode(false)
                    {
                        connect(this, QOverload<double>::of(&GDoubleSpinBox::valueChanged), this, &GDoubleSpinBox::onValueChanged);
                    }
                    
                    void GDoubleSpinBox::setValueByCode(double value)
                    {
                        m_changedByCode = true;
                        setValue(value);
                    }
                    
                    void GDoubleSpinBox::onValueChanged(double value)
                    {
                        if (m_changedByCode) {
                            emit valueChangedByCode(value);
                        }
                        else {
                            emit valueChangedByUser(value);
                        }
                        m_changedByCode = false;
                    }
                    
                    

                    Any idea?

                    J.HilkJ JonBJ 2 Replies Last reply
                    0
                    • D DL5EU

                      I have noticed that when I subclass QDoubleSpinBox as proposed, the first modification by the user is not recognised as such. This is probably due to the original problem, i.e. that the valueChanged() signal is not processed. In Qt Creator in debug mode I can see that the setter is entered but not the onValueChanged() method of my new object (in response to the call to setValueByCode()). That is the reason why m_changedByCode is still true when onValueChanged() is entered in repsponse to a modification done by the user.

                      This is what the code of my new double spin box looks like:

                      .h file:

                      class GDoubleSpinBox : public QDoubleSpinBox
                      {
                          Q_OBJECT
                      public:
                          explicit GDoubleSpinBox(QWidget *parent = nullptr);
                      public slots:
                          void setValueByCode(double value);
                      signals:
                          void valueChangedByCode(double value);
                          void valueChangedByUser(double value);
                      private:
                          bool m_changedByCode;
                      private slots:
                          void onValueChanged(double value);
                      };
                      
                      

                      .cpp file:

                      GDoubleSpinBox::GDoubleSpinBox(QWidget *parent)
                          : QDoubleSpinBox(parent)
                          , m_changedByCode(false)
                      {
                          connect(this, QOverload<double>::of(&GDoubleSpinBox::valueChanged), this, &GDoubleSpinBox::onValueChanged);
                      }
                      
                      void GDoubleSpinBox::setValueByCode(double value)
                      {
                          m_changedByCode = true;
                          setValue(value);
                      }
                      
                      void GDoubleSpinBox::onValueChanged(double value)
                      {
                          if (m_changedByCode) {
                              emit valueChangedByCode(value);
                          }
                          else {
                              emit valueChangedByUser(value);
                          }
                          m_changedByCode = false;
                      }
                      
                      

                      Any idea?

                      J.HilkJ Offline
                      J.HilkJ Offline
                      J.Hilk
                      Moderators
                      wrote on last edited by
                      #9

                      @DL5EU I can't see any obvious problems,

                      But my example is a compile ready one (you just throw it into a main.cpp and hit compile) and when I test it there, the first edit by a use is registered as an edit by the user.

                      Have you tried your class in a standalone project to verify it works as expected? It my be an other part of your code, thats the issue here


                      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                      Q: What's that?
                      A: It's blue light.
                      Q: What does it do?
                      A: It turns blue.

                      1 Reply Last reply
                      0
                      • D DL5EU

                        I have noticed that when I subclass QDoubleSpinBox as proposed, the first modification by the user is not recognised as such. This is probably due to the original problem, i.e. that the valueChanged() signal is not processed. In Qt Creator in debug mode I can see that the setter is entered but not the onValueChanged() method of my new object (in response to the call to setValueByCode()). That is the reason why m_changedByCode is still true when onValueChanged() is entered in repsponse to a modification done by the user.

                        This is what the code of my new double spin box looks like:

                        .h file:

                        class GDoubleSpinBox : public QDoubleSpinBox
                        {
                            Q_OBJECT
                        public:
                            explicit GDoubleSpinBox(QWidget *parent = nullptr);
                        public slots:
                            void setValueByCode(double value);
                        signals:
                            void valueChangedByCode(double value);
                            void valueChangedByUser(double value);
                        private:
                            bool m_changedByCode;
                        private slots:
                            void onValueChanged(double value);
                        };
                        
                        

                        .cpp file:

                        GDoubleSpinBox::GDoubleSpinBox(QWidget *parent)
                            : QDoubleSpinBox(parent)
                            , m_changedByCode(false)
                        {
                            connect(this, QOverload<double>::of(&GDoubleSpinBox::valueChanged), this, &GDoubleSpinBox::onValueChanged);
                        }
                        
                        void GDoubleSpinBox::setValueByCode(double value)
                        {
                            m_changedByCode = true;
                            setValue(value);
                        }
                        
                        void GDoubleSpinBox::onValueChanged(double value)
                        {
                            if (m_changedByCode) {
                                emit valueChangedByCode(value);
                            }
                            else {
                                emit valueChangedByUser(value);
                            }
                            m_changedByCode = false;
                        }
                        
                        

                        Any idea?

                        JonBJ Offline
                        JonBJ Offline
                        JonB
                        wrote on last edited by JonB
                        #10

                        @DL5EU said in QDoubleSpinBox - blockSignals() does not work:

                        In Qt Creator in debug mode I can see that the setter is entered but not the onValueChanged() method of my new object

                        I cannot be sure whether this is your case but: Assuming you mean you placed a breakpoint and run under debugger, you will find that does not work right on a spin box. I discovered this (at great cost!) a while ago. It is because QSpinBox::onValueChanged() uses an internal timer to do its work, and when you slow things down by debugging it "breaks" how it works.

                        Here is the way I wrote to connect the QSpinBox::valueChanged() signal so that I can break in the debugger if necessary:

                        template <typename Context, typename Method>
                            QMetaObject::Connection connectSpinBoxValueChanged(QSpinBox *spin, Context slotObject, Method method)
                            {
                                // connect `spin->valueChanged(int i)` signal to slot
                                // see https://forum.qt.io/topic/113606/qspinbox-valuechanged-with-debugger-breakpoint-brain-damaged and https://bugreports.qt.io/browse/QTBUG-14259
                                // for why `Qt::QueuedConnection` is specified here
                                return QObject::connect(spin, QOverload<int>::of(&QSpinBox::valueChanged), slotObject, method, Qt::QueuedConnection);
                            }
                        

                        which is what I now use, whether debugging or not. You can read up on the whys & wherefores in my https://forum.qt.io/topic/113606/qspinbox-valuechanged-with-debugger-breakpoint-brain-damaged and https://bugreports.qt.io/browse/QTBUG-14259.

                        1 Reply Last reply
                        0
                        • D Offline
                          D Offline
                          DL5EU
                          wrote on last edited by
                          #11

                          No, I have not tried it stand alone yet but I will do so later this day.

                          1 Reply Last reply
                          0
                          • D Offline
                            D Offline
                            DL5EU
                            wrote on last edited by
                            #12

                            Thanks @JonB, I will try this too.

                            1 Reply Last reply
                            0
                            • jeremy_kJ Offline
                              jeremy_kJ Offline
                              jeremy_k
                              wrote on last edited by jeremy_k
                              #13

                              This works for me, with Qt 5.15.5 on macOS

                              #include <QtGlobal>
                              #include <QVBoxLayout>
                              #include <QWidget>
                              #include <QApplication>
                              #include <QDoubleSpinBox>
                              #include <QPushButton>
                              #include <QDebug>
                              
                              int main(int argc, char *argv[])
                              {
                                  QApplication a(argc, argv);
                              
                                  QDoubleSpinBox *box = new QDoubleSpinBox;
                                  QPushButton *button = new QPushButton(box->signalsBlocked() ? "true" : "false");
                                  QObject::connect(button, &QPushButton::clicked, [box, button]() {
                                      bool isBlocked = box->signalsBlocked();
                                      box->blockSignals(!isBlocked);
                                      button->setText(!isBlocked ? "true" : "false");
                                  });
                                  QObject::connect(box, qOverload<double>(&QDoubleSpinBox::valueChanged), [](double value) { qDebug() << "value" << value; });
                              
                                  QPushButton *button2 = new QPushButton("set to 1.0");
                                  QObject::connect(button2, &QPushButton::clicked, [box](){ box->setValue(1.0);});
                              
                                  QWidget container;
                                  QVBoxLayout layout(&container);
                                  layout.addWidget(box);
                                  layout.addWidget(button);
                                  layout.addWidget(button2);
                                  container.show();
                              
                                  return a.exec();
                              }
                              

                              When button reads "true", using the spinbox or clicking button2 does not result in a debug message. When button reads "false", the spinbox controls generate debug output, and if the value isn't already 1.0, clicking button2 also generates output.

                              I suspect something else is going on.

                              Asking a question about code? http://eel.is/iso-c++/testcase/

                              1 Reply Last reply
                              0
                              • J.HilkJ J.Hilk

                                @DL5EU I would subclass QDoubleSpinbox and make my own, better suited one:

                                #include <QApplication>
                                #include <QDebug>
                                #include <QTimer>
                                #include <QDoubleSpinBox>
                                
                                class MyDoubleSpinBox : public QDoubleSpinBox
                                {
                                    Q_OBJECT
                                
                                public:
                                    MyDoubleSpinBox(QWidget*parent = nullptr) : QDoubleSpinBox(parent)
                                    {
                                        connect(this, QOverload<double>::of(&MyDoubleSpinBox::valueChanged), this, &MyDoubleSpinBox::onValueChanged);
                                    }
                                
                                    //new setter
                                    void setValueByCode(double value){
                                        m_changedByCode = true;
                                        setValue(value);
                                    }
                                
                                signals:
                                    void valueChangedByUser(double value);
                                    void valueChangedByCode(double value);
                                
                                private slots:
                                    void onValueChanged(double value){
                                        if(m_changedByCode)
                                            emit valueChangedByCode(value);
                                        else
                                            emit valueChangedByUser(value);
                                        m_changedByCode = false;
                                    }
                                
                                private:
                                    bool m_changedByCode{false};
                                
                                };
                                
                                int main(int argc, char *argv[])
                                {
                                    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
                                    QApplication app(argc, argv);
                                
                                    MyDoubleSpinBox mdsb;
                                
                                    //Test: automatically set value by code via timer
                                    QTimer t;
                                    QObject::connect(&t, &QTimer::timeout, [&]()->void{mdsb.setValueByCode(mdsb.value()+1);});
                                    
                                    //see, if it was changed by hand or by program
                                    QObject::connect(&mdsb, &MyDoubleSpinBox::valueChangedByCode, [](double value)->void{qDebug() << "value by code" << value;});
                                    QObject::connect(&mdsb, &MyDoubleSpinBox::valueChangedByUser, [](double value)->void{qDebug() << "value by user" << value;});
                                
                                    t.start(5000);
                                    mdsb.show();
                                    return app.exec();
                                
                                }
                                
                                #include "main.moc"
                                
                                D Offline
                                D Offline
                                DL5EU
                                wrote on last edited by
                                #14

                                @J-Hilk: I have tried to compile your program in Qt Creator but main.moc is missing and without it it does not compile. As I wrote, I am no Qt expert and I am used to develop in Qt Creator where I don't have to create .moc files myself. I will set up a project with your code as soon as I have got the time (in a week).

                                @jeremy_k: your program works as described. Perhaps there is a problem elsewhere that I don't see yet. I will check this when I am back from holidays.

                                J.HilkJ D 2 Replies Last reply
                                0
                                • D DL5EU

                                  @J-Hilk: I have tried to compile your program in Qt Creator but main.moc is missing and without it it does not compile. As I wrote, I am no Qt expert and I am used to develop in Qt Creator where I don't have to create .moc files myself. I will set up a project with your code as soon as I have got the time (in a week).

                                  @jeremy_k: your program works as described. Perhaps there is a problem elsewhere that I don't see yet. I will check this when I am back from holidays.

                                  J.HilkJ Offline
                                  J.HilkJ Offline
                                  J.Hilk
                                  Moderators
                                  wrote on last edited by J.Hilk
                                  #15

                                  @DL5EU said in QDoubleSpinBox - blockSignals() does not work:

                                  Creator but main.moc is missing and without it it does not compile.

                                  it‘s auto generated by qmake, its missing on the first compile, if not explicitly set up in the pro file.

                                  simply hit compile a 2nd time, and it should work.


                                  Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                                  Q: What's that?
                                  A: It's blue light.
                                  Q: What does it do?
                                  A: It turns blue.

                                  1 Reply Last reply
                                  0
                                  • D DL5EU

                                    @J-Hilk: I have tried to compile your program in Qt Creator but main.moc is missing and without it it does not compile. As I wrote, I am no Qt expert and I am used to develop in Qt Creator where I don't have to create .moc files myself. I will set up a project with your code as soon as I have got the time (in a week).

                                    @jeremy_k: your program works as described. Perhaps there is a problem elsewhere that I don't see yet. I will check this when I am back from holidays.

                                    D Offline
                                    D Offline
                                    DL5EU
                                    wrote on last edited by
                                    #16

                                    @J-Hilk I had the time now to try out your example. It works indeed as expected. There must be something in my code that prevents the application from working as expected, perhaps a side effect of something that I have not found yet. I will further investigate.

                                    Ralf

                                    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