打破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之类的方法中。
通常,我们应该响应小部件中的输入事件,而不要更改事件。这样可以防止发生这种类型的循环。
- 用户更改小部件中的输入
- 小部件发出更改事件(滚动完成/输入点击次数/鼠标离开等)
- 控制器响应,转换为模型变化
- 模型发出事件
- 控制器响应,更改小部件中的值
- 发出值更改事件,但控制器未监听