【问题标题】:Qt C++: Connecting value changing in two spinboxesQt C++:连接两个旋转框中的值变化
【发布时间】:2013-06-08 09:34:28
【问题描述】:

我有一个带有两个旋转框的表单,由于宽度和高度的纵横比,它们必须连接起来。当我单击第一个旋转框并增加/减少值时,另一个,第二个旋转框应将其值更改为与第一个旋转框成比例。我已经完成了比率连接,但是有问题,因为我在 SLOT valueChanged(int) 上连接了两个旋转框,并且这个方法阻塞了整个程序,因为无限循环。这意味着当我增加第一个旋转框的值时,首先为这个旋转框更改值,然后为再次调用第一个旋转框的第二个更改值。

我想解决这个问题,所以当我点击其中一个旋转框时,以正确的方式更改这两个值而不会出现无限循环。

所以有代码:

void MainWindow::on_sbHeight_valueChanged(int arg1)
{
    if (arg1 != 0) {
    if (ui->radioRatio1->isChecked()) {
        ui->sbWidth->setValue((arg1/8)*2);
    } else if (ui->radioRatio2->isChecked()) {
        ui->sbWidth->setValue((arg1/14)*3);
    }
    } else {
    ui->sbWidth->setValue(arg1);
    }
}

void MainWindow::on_sbWidth_valueChanged(int arg1)
{
    if (arg1 != 0) {
    if (ui->radioRatio1->isChecked()) {
        ui->sbHeight->setValue((arg1/2)*8);
    } else if (ui->radioRatio2->isChecked()) {
        ui->sbHeight->setValue((arg1/3)*14);
    }
    } else {
    ui->sbHeight->setValue(arg1);
    }
}

【问题讨论】:

    标签: c++ qt qt-creator signals-slots qspinbox


    【解决方案1】:

    我认为,这里最好的解决方案是从 spinboxes 到 block signals,然后再更改它们在插槽中的值:

    我通常这样使用辅助类:

    class SignalsBlocker
    {
    public:
        SignalsBlocker(QObject* ptr):
        _ptr(ptr)
        {
            _b = ptr->blockSignals(true);
        }
        ~SignalsBlocker()
        {
            _ptr->blockSignals(_b);
        }
    
    private:
        QObject* _ptr;
        bool _b;
    };
    

    所以你可以写

    void MainWindow::on_sbHeight_valueChanged(int arg1)
    {
        SignalsBlocker block(ui->sbWidth);
        if (arg1 != 0) {
        if (ui->radioRatio1->isChecked()) {
            ui->sbWidth->setValue((arg1/8)*2);
        //.....
    }
    
    void MainWindow::on_sbWidth_valueChanged(int arg1)
    {
        SignalsBlocker block(ui->sbHeight);
        if (arg1 != 0) {
        if (ui->radioRatio1->isChecked()) {
            ui->sbHeight->setValue((arg1/2)*8);
        //....
    }
    

    可以提供的直接解决方案是

    void foo(QObject* object)
    {
        object->blockSignals(true); 
        // some stuff
        object->blockSignals(false);
    }
    

    但是,这个解决方案是不正确的:想象一下下面的情况

    QObject* obj;
    obj->blockSignals(true);
    foo(obj);
    //some other stuff
    obj->blockSignals(false);
    

    人们可能希望信号在some otherstuff 之后被解除阻塞,但实际上它们会在foo 函数中被解除阻塞,这不是预期的行为。这就是为什么你应该保存块状态然后恢复它。

    但同样,RAII 辅助类是降低代码复杂性的最方便的解决方案。


    另请注意,您使用整数进行计算,例如

    (arg1/8)*2
    

    根本不准确。

    例如,让arg1 = 6。那么arg1/8 就是0(arg1/8)*2 就是0

    仅仅改变计算顺序就可以提高准确性:

     (arg1 * 2) / 8
    
     arg1 = 6
     arg1 * 2 = 12
     (arg1 * 2 / 8) = 1
    

    【讨论】:

    • ui->sbHeight->blockSignals() 是否与您的解决方案相同,或者它的某些部分阻塞信号不同?我已经尝试过提到的方法,但是如果我将它提供给两个 valueChanged(int) 方法,那么只有第一个 spinbox 才能正常工作。如果我在第一个 spinbox 中注释掉 blockSignals(),那么另一个开始工作,但第一个不能正常工作。
    • 您应该在使用后恢复信号阻塞状态(在函数结束时解除阻塞信号)。最方便且仍然正确的方法是使用辅助类。
    • 好的。我是 Qt 的新手,请您解释一下,为什么我不应该在方法结束时使用 blockSignals(false) 以及为什么这是一个不好的做法?谢谢!
    【解决方案2】:

    @lol4t0 解决方案现在在 Qt 中,从 5.3 版开始,带有 QSignalBlocker。 这意味着 QSignalBlocker 可以像 @lol4t0 中的 SignalsBlocker 一样使用。它在创建时阻塞信号,并在它被破坏时恢复之前的阻塞状态。

    来自@lol4t0 的示例,但带有 Qt 类:

    void MainWindow::on_sbHeight_valueChanged(int arg1)
    {
        QSignalBlocker block(ui->sbWidth);
        if (arg1 != 0) {
        if (ui->radioRatio1->isChecked()) {
            ui->sbWidth->setValue((arg1/8)*2);
        //.....
    }
    
    void MainWindow::on_sbWidth_valueChanged(int arg1)
    {
        QSignalBlocker block(ui->sbHeight);
        if (arg1 != 0) {
        if (ui->radioRatio1->isChecked()) {
            ui->sbHeight->setValue((arg1/2)*8);
        //....
    }
    

    http://doc.qt.io/qt-5/qsignalblocker.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-14
      • 2018-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多