打破GUI中的事件周期

时间:2020-03-06 14:38:10  来源:igfitidea点击:

在编写GUI时,我经常遇到以下问题:假设我们有一个模型和一个控制器。控制器有一个小部件" W",用于显示模型的属性" X"。

因为模型可能是从控制器外部更改的(可能有其他控制器使用相同的模型,撤消操作等),所以控制器会侦听模型上的更改。控制器还监听小部件" W"上的事件,并相应地更新属性" X"。

现在,发生以下情况:

  • W中的值已更改
  • 生成事件,调用控制器中的处理程序
  • 控制器在模型中为X设置新值
  • 该模型发出事件,因为它已被更改
  • 控制器从模型接收更改事件
  • 控制器获取" X"的值并将其设置在小部件中
  • 转到1.

有几种可能的解决方案:

  • 修改控制器以在模型更新时设置标志,并且如果设置了此标志,则不对来自模型的任何事件做出反应。
  • 暂时断开控制器的连接(或者告诉模型一段时间不发送任何事件)
  • 冻结小部件中的所有更新

过去,我通常选择选项1,因为这是最简单的事情。它的缺点是用标志将类弄乱了,但是其他方法也有缺点。

仅作记录,我在包括GTK +,Qt和SWT在内的多个GUI工具包中都遇到了这个问题,因此我认为这与工具包无关。

有最佳做法吗?还是我使用的架构完全错误?

@Shy:在某些情况下,这是一种解决方案,但是如果从控制器外部更改" X"(例如,在使用命令模式进行撤消/重做时),我们仍然会收到一堆多余的事件,因为这时值已更改," W"会更新并触发一个事件。为了防止对模型进行另一次(无用的)更新,必须吞下小部件生成的事件。
在其他情况下,该模型可能会更复杂,并且仅检查准确更改的内容可能不可行,例如,复杂的树状视图。

解决方案

处理此问题的标准QT方法以及在其非常有用的教程中建议的方法是,仅当新值与当前值不同时才对控制器中的值进行更改。
这是信号具有valueChanged()语义的方式

看到这个教程

指示正在更新的标志。我们可以将它们包装在BeginUpdate和EndUpdate之类的方法中。

通常,我们应该响应小部件中的输入事件,而不要更改事件。这样可以防止发生这种类型的循环。

  • 用户更改小部件中的输入
  • 小部件发出更改事件(滚动完成/输入点击次数/鼠标离开等)
  • 控制器响应,转换为模型变化
  • 模型发出事件
  • 控制器响应,更改小部件中的值
  • 发出值更改事件,但控制器未监听