【问题标题】:Prevent Firing Signals in Qt在 Qt 中防止触发信号
【发布时间】:2011-04-03 04:05:12
【问题描述】:

我们有一个QCheckBox 对象,当用户检查它或删除检查时,我们想要调用一个函数,因此我们将我们的函数连接到stateChanged ( int state ) 信号。另一方面,根据某些情况,我们也会在代码中改变QCheckBox对象的状态,这会导致不需要的信号。

在某些情况下有什么办法可以防止触发信号吗?

【问题讨论】:

    标签: c++ qt checkbox signals-slots


    【解决方案1】:

    您可以使用clicked 信号,因为它仅在用户实际单击复选框时发出,而不是在您使用setChecked 手动检查时发出。

    如果您只是不希望在某个特定时间发出信号,您可以像这样使用QObject::blockSignals

    bool oldState = checkBox->blockSignals(true);
    checkBox->setChecked(true);
    checkBox->blockSignals(oldState);
    

    这种方法的缺点是所有信号都会被阻塞。但我想在QCheckBox 的情况下这并不重要。

    【讨论】:

    • liaK 提出的先断开连接再连接的方案似乎比阻塞该特定任务的所有信号要好。
    • @Longfield:我认为使用clicked 信号最适合此特定任务。
    • 嗯,状态也可以由程序改变,不一定由用户通过 UI。无论如何,我们在这里谈论细节,我猜metdos找到了适合他的解决方案。
    【解决方案2】:

    您可以QObject::disconnect删除相应的信号槽连接,完成后可以QObject::connect再次...

    【讨论】:

      【解决方案3】:

      QObject派生类中,可以调用blockSignals(bool)来防止对象发出信号。比如:

      void customChangeState(bool checked)
      {
          blockSignals(true);
          ui->checkBox->setCheckState(Qt::Checked);
          // other work
          blockSignals(false);
      }
      

      上述方法会在没有点击、stateChanged 或任何其他信号发出的情况下更改检查状态。

      【讨论】:

      • 这不会阻止 ui->checkBox 发出信号,因此它不会按预期工作。您应该调用 ui->checkBox->blockSignals(true) 或使用 QSignalBlocker(从 Qt5.3 开始)
      【解决方案4】:

      您始终可以使用QObject::blockSignals() 阻止 QObjects 上的信号发射。请注意,要正确处理事情,您应该记住旧状态(从函数调用返回),并在完成后恢复它。

      在我的工作中,我们更喜欢 RAII 来处理这类事情。一个简单的类可能如下所示:

      class SignalBlocker
      {
      public:
          SignalBlocker( QObject *obj ) : m_obj( obj ), m_old( obj->blockSignals( true ) )
          {
          }
      
          ~SignalBlocker()
          {
              m_obj->blockSignals( m_old );
          }
      
      private:
          QObject *m_obj;
          bool m_old;
      };
      

      编辑:从 Qt 5.3 开始,请参阅 QSignalBlocker(h/t to HappyCactus in cmets)

      【讨论】:

      • 遵循这种方式有什么具体原因吗?只是问 B'cos 我们习惯了QObject::disconnect() 在这种情况下..
      • 这取决于有多少东西可能连接到对象的信号,您是否确定已断开适当的信号,以及重新连接它们的难易程度。我自己,我认为这通常更容易,但使用断开连接也可以。
      • @liaK:RAII 是一个很好的理由,每个(每个)C++ 程序员都应该知道,包括为什么和如何。基本上,在这种情况下,即使抛出异常并且错过了手动解锁,它也会释放块。就个人而言,我使用了类似的方法,我想知道为什么 Qt 的库中没有这个(或者我确实错过了它)。
      • 这是在 Qt5.3 QSignalBlocker 中引入的:doc.qt.io/qt-5/qsignalblocker.html
      【解决方案5】:

      当某些 UI 元素不应响应用户时,禁用它是适当的。这样用户就会知道这个元素不接受输入。

      【讨论】:

      • 你说的是真的,但与OP的问题无关。
      【解决方案6】:

      在学习 Qt 时,我遇到了这个问题,我想“原子地”更新一组相互连接的小部件。我喜欢@cjhuitt 的解决方案,但发现在proxy objects 的基础上加上一点语法糖会更好。这是我使用的方法...

      首先,我为拦截器代理对象定义了一个类模板。像 Caleb 的一样,这会阻止构建时的信号,然后在破坏时恢复其先前的状态。但是,它还重载了-> 运算符以返回指向被阻止对象的指针:

      template<class T> class Blocker {
          T *blocked;
          bool previous;
      public:
          Blocker(T *blocked)
              : blocked(blocked),
                previous(blocked->blockSignals(true)) {}
          ~Blocker() { blocked->blockSignals(previous); }
          T *operator->() { return blocked; }
      };
      

      接下来,我定义了一个小模板函数来构造并返回一个 Blocker:

      template<class T> inline Blocker<T> whileBlocking(T *blocked) {
          return Blocker<T>(blocked);
      }
      

      把这一切放在一起,我会这样使用它:

      whileBlocking(checkBox)->setChecked(true);
      

      whileBlocking(xyzzySpin)->setValue(50);
      

      这让我获得了 RAII 的所有好处,自动配对阻塞和恢复方法调用,但我不需要命名任何包装器或状态标志。它很好,很简单,而且非常万无一失。

      【讨论】:

      • 似乎过于复杂,因为所有需要阻塞的对象无论如何都将从 QObject 派生。我认为这里不需要模板。
      • @André,并不是所有的QObjects都有setChecked方法,所以模板确实是必须的。
      【解决方案7】:

      Qt5.3 引入了 QSignalBlocker 类,它以异常安全的方式完成所需的工作。

      if (something) {
         const QSignalBlocker blocker(someQObject);
         // no signals here
      }
      

      【讨论】:

        【解决方案8】:

        即使在 QT5 中,当有很多/几个要阻止的东西时,它也有点麻烦。这是一个简洁易用的多对象版本:

        class SignalBlocker
        {
        public:
          SignalBlocker(QObject *obj)
          {
            insert( QList<QObject*>()<<obj );
          }    
          SignalBlocker(QList<QObject*>  objects)
          {
            insert(objects);
          }    
          void insert(QList<QObject*>  objects)
          {
            for (auto obj : objects)
              m_objs.insert(obj, obj->signalsBlocked());
            blockAll();
          }    
          void blockAll() {
            for( auto m_obj : m_objs.keys() )
              m_obj->blockSignals(true);
          }    
          ~SignalBlocker()
          {
            for( auto m_obj : m_objs.keys() )
              m_obj->blockSignals( m_objs[m_obj] );
          }    
        private:
          QMap<QObject*,bool> m_objs;      
        };
        

        用法:

        void SomeType::myFunction()
        {
            SignalBlocker tmp( QList<QObject*>() 
            << m_paramWidget->radioButton_View0
            << m_paramWidget->radioButton_View1
            << m_paramWidget->radioButton_View2
            );
            // Do more work, ... 
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-08
          • 1970-01-01
          相关资源
          最近更新 更多