0

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

2 回答 2

1

我认为,这里最好的解决方案是在更改插槽中的值之前阻止来自 spinbox 的信号:

我通常使用这样的辅助类:

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
于 2013-06-08T09:48:07.267 回答
0

@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

于 2017-12-21T11:06:21.920 回答