Qt C ++: изменение значения соединения в двух спинбоксах

У меня есть форма с двумя спинбоксами, которые должны быть связаны из-за соотношения сторон для ширины и высоты. Когда я нажимаю на первый спинбокс и увеличиваю / уменьшаю значение, другой, второй спинбокс должен изменить свое значение в соответствии с первым спинбоксом. Я уже установил отношения отношений, но есть проблема, потому что я соединяю обе спин-боксы на 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);
}
}

0

Решение

Я думаю, что лучшее решение здесь было бы блокировать сигналы из спинбоксов, до изменения их значений в слотах:

Я обычно использую вспомогательный класс следующим образом:

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
1

Другие решения

Решение @ lol4t0 теперь в Qt, начиная с версии 5.3 с QSignalBlocker.
Значение QSignalBlocker может использоваться точно так же, как SignalsBlocker от @ lol4t0. Он блокирует сигналы при создании и восстанавливает предыдущий статус блока, когда он уничтожается.

Пример из @ 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

0

По вопросам рекламы [email protected]