如果我调用Qt小部件的setValue()或类似函数,它的valueChanged()槽保证何时执行



假设我有多个输入小部件来设置同一个参数。例如,有一个QSlider和一个QSpinBox需要显示相同的值。在其中一个的valueChanged()时隙中,我调用另一个的setValue()。显然,这将导致他们之间无休止的相互呼叫。

当这个输入小部件控制一些外部资源或设备时,也会出现类似的问题。如果用户更改了值,则会将新值发送到外部设备。但是,如果外部设备更改了值(或者从设置文件中读取(,那么我必须更新小部件,它将依次发送值,然后更新小部件等等

第三种情况是,当我将值保存到文件或数据库中时,但我必须在开始时将小部件初始化为某个值,可能是在我从数据库中获得所有值之前。但是,通过在程序开始时初始化小部件,它们将把伪值写入数据库,覆盖真实值。

这些问题的明显解决方案是只使用一个bool,允许或禁止valueChanged()函数的副作用。

例如,如果我想更改滑块的值,我使用

editing = true;
slider.setValue(value);
editing = false;

而我在valueChanged()函数的开头有if (editing) return;

假设我没有手动设置信号和插槽,但它们是由QtCreator完成的,那么插槽是否有稍后被调用的危险,例如在editing标志再次设置为false之后?我试过了,它有效,但我不确定它有多大的保证

如果使用直接连接(同一线程中对象的默认连接(,则在发出信号后,即setValue方法返回之前,会立即调用slot。

如果使用Qt::QueuedConnection,则当控制返回到接收器线程的事件循环时,会调用插槽。

请参阅Qt::ConnectionType

解决这个问题的方法是使用另一个QObject作为您的数据模型。您的数据将集中在您的模型中,并通过模型获取/设置。这样,您的小部件就不需要相互了解,并且可以在代码中的不同位置创建,只要它们可以访问您的模型。

你的模型将有一个方法setValue和一个信号valueChanged,所以它看起来像这样:

class Model : public QObject {
Q_OBJECT
public:
void setValue(const QVariant& value) {
if (_value != value) {
_value = value;
emit valueChanged(_value);
}
}
const QVariant& getValue() const {
return _value;
}
public signals:
void valueChanged(QVariant& value);
private:
QVariant _value;
}

然后,您的小部件可以将Model的相同实例作为依赖项,并监听其valueChanged信号并更新自己。小部件还将听取用户输入,当用户更改值时,它们将更改模型中的值。这样,其他小部件就会收到有关更改的通知。

您的小部件将如下所示:

class MySlider : public QSlider {
Q_OBJECT
public:
explicit MySlider(QSharedPointer<Model> model, QWidget *parent=nullptr) 
: QSlider(parent), _model(model) {
connect(this, &QSlider::valueChanged, this, [this](int value){
_model->setValue(value);
});
connect(_model.data(), &Model::valueChanged, this, &MySlider::onValueChanged);
//this is to update the widget with the latest value upon creation
onValueChanged(_model->getValue());
}
private slots:
void onValueChanged(const QVariant& value) {
if (value.toInt() != value()) {
//this is calling QSlider::setValue
setValue(value.toInt());
}
}
}

在创建所有小部件之前,您可以使用默认值创建模型,因此假设它位于main:中

int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
//note that your model doesn't have a parent, it's a shared pointer
auto model = QSharedPointer<Model>::create();
auto mySlider1 = new MySlider(model, &w);
auto mySlider2 = new MySlider(model, &w);
return a.exec();
}

附言:你也可以查看QDataWidgetMapper,看看它是否能完成你想要的任务。

相关内容

最新更新