Qt C ++:在两个spinbox中连接值的变化

时间:2013-06-08 09:34:29

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

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

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

所以有代码:

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);
    }
}

2 个答案:

答案 0 :(得分:1)

我认为,在更改插槽中的值之前,此处的最佳解决方案是来自spinbox的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/80,而(arg1/8)*2会产生0

只需更改计算顺序即可提高准确度:

 (arg1 * 2) / 8

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

答案 1 :(得分:0)

@ lol4t0解决方案现在使用QSignalBlocker从版本5.3开始在Qt中。 含义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