首页 文章

Qt信号插槽架构不需要的无限循环

提问于
浏览
4

我有qt信号槽系统的问题 .

首先,我创建了一个名为System in Singleton模式的类,因此我可以访问我想要的实例 . 系统有一个信号SelectionChanged .

我有一个列表小部件,我将它的itemSelectionChanged信号连接到我的自定义插槽,称为onSelectionChanged . 在onSelectionChanged插槽中,我发出System的SelectionChanged信号 . 还没有问题 .

在我的软件设计中,许多GUI小部件或自定义类可以使用一系列对象,而System的SelectionChanged信号可以由列表小部件以外的小部件发出 .

所以我在列表小部件中创建一个名为OnSystemSelectionChanged的插槽,然后将其连接到System的SelectionChanged信号 . OnSystemSelectionChangedSlot是这样的 .

void MyListWidget::OnSystemSelectionChanged(QObject *sender)
{
    if (sender == this) return;
    // Then I want to get a list of selected objects and set them as selection of this widget like this:
    this->SetSelection(System::Instance()->GetSelectedObjects());
}

但问题是当我开始设置列表小部件的所选项时,它将发出itemSelectionChanged信号并且将调用我的onSelectionChanged插槽 . 然后插槽将发出System的SelectionChanged信号,然后也将调用OnSystemSelectionChanged . 它将通过sender参数停止,但是没有方法可以立即设置列表小部件的所选项目 .

我怎么能解决这个问题呢 .

我希望我能很好地解释我的问题 . 提前致谢 .

编辑:更正拼写和语法错误 .

4 回答

  • 8

    在Qt中有几种方法可以解决这个问题 .

    成语

    • 对一个基础模型使用多个视图 . 这会自动处理对多个视图控件的更改传播,您无需执行任何额外操作 . 您可以使用QDataWidgetMapper将"plain old"窗口小部件链接到模型中的数据元素 . 我会说这应该是首选的做事方式 . 拥有所有UI的基础模型无论如何都是朝着良好软件设计方向迈出的一步 .

    • 在数据模型之间传播更改时,同时实现 DisplayRoleEditRole . 视图将使用其中一个角色(例如, EditRole )名义上修改模型,而您可以使用其他角色(例如, DisplayRole )以编程方式修改模型 . 您可以在自己的插槽中处理来自模型的 dataChanged 信号,正确处理角色,并在具有其他角色的其他模型上调用 setData . 这可以防止循环 .

    • 对于非 QAbstractItemView 的控件,实现两个信号:一个在任何更改时发出,另一个仅在基于键盘/鼠标输入的更改时发出 . 这是 QAbstractButton 公开的接口,例如: toggled(bool) 信号是前者, clicked() 是后者 . 然后,您只能连接到基于输入的信号 .

    您自己的代码必须将编程更改传播到所有相互关联的控件,因为从代码中更改一个控件不会修改其他控件 . 这应该不是问题,因为设计良好的代码应该从其余代码封装UI控件的实现细节 . 因此,对话框/窗口类将以不与显示特定属性的控件数相关联的方式公开其属性 .

    黑客让我们 - 希望 - 他们不会成为习语

  • 2

    我能想到两种可能的解决方案:

    • 添加一个可以忽略特定信号的标志:
    void MyListWidget::OnSystemSelectionChanged(QObject *sender)
    {
        if (sender == this || inhibitSelectionChanged)
            return;
    
        this->inhibitSelectionChanged = true;
        this->SetSelection(System::Instance()->GetSelectedObjects());
        this->inhibitSelectionChanged = false;
    }
    
    • disconnect信号的插槽,并在更改选择后重新连接:
    void MyListWidget::OnSystemSelectionChanged(QObject *sender)
    {
        if (sender == this)
            return;
    
        this->disconnect(SIGNAL(SelectionChanged()));
    
        this->SetSelection(System::Instance()->GetSelectedObjects());
    
        this->connect(
            this, SIGNAL(SelectionChanged()), 
            this, SLOT(OnSystemSelectionChanged(QObject*)));
    }
    
  • 2

    我在QObject :: blockSignals()方法中找到了我的解决方案 . 当我设置所选项目时,它将阻止从列表小部件发出信号 .

    感谢BartoszKP的所有答案和解决方案 . 这个解决方案看起来像是他第一个解决方案的官方方式 .

  • 1

    问题是:你试图偷工减料并创造了一个单身人士 . 不是单身人士的经典案例 .

    信号和插槽用于通知,每个对象通知感兴趣的对象它做了什么或反映其新状态 .

    我建议改变设计如下:

    • 没有单身信号 .

    • 每个对象都有自己的相关事件信号和插槽(例如选择更改) .

    • 应用程序或更高级别的对象(创建窗口小部件/对象)执行信号到插槽连接 . 如果这些小部件放在列表中,这很简单 .

相关问题