Quebrando ciclos de eventos em GUIs
-
02-07-2019 - |
Pergunta
Ao escrever GUIs, eu freqüentemente vêm sobre o seguinte problema: Suponha que você tenha um modelo e um controlador. O controlador tem um W
widget que é usado para mostrar uma X
propriedade do modelo.
Porque o modelo pode ser alterada a partir do lado de fora do controlador (pode haver outros controladores utilizando o mesmo modelo, desfazer operações, etc), as escutas controlador para alterações no modelo. O controlador também escuta eventos no W
widget e atualiza o X
propriedade em conformidade.
Agora, acontece o seguinte:
- o valor em
W
é alterado - um evento é gerado, o manipulador na controller é invocado
- controller define o novo valor para
X
na modelo - modelo emite eventos porque foi alterado
- controller recebe um evento de alteração do modelo
- controller obtém o valor
X
e define-o no widget - Goto 1.
Existem várias soluções possíveis para isso:
- Modificar o controlador para definir um sinalizador quando o modelo é atualizado, e não reagir a eventos do modelo se este sinalizador está definido.
- Desligue o controlador temporariamente (ou dizer o modelo não enviar quaisquer eventos durante algum tempo)
- Congelar nenhuma atualização do widget
No passado, eu costumava ir para a opção 1., porque é a coisa mais simples. Tem o inconveniente de desordenar suas aulas com bandeiras, mas os outros métodos têm suas desvantagens também.
Apenas para o registro, eu tive esse problema com vários kits de ferramentas GUI, incluindo GTK +, Qt e SWT, então eu acho que é bastante kit de ferramentas agnóstica.
Qualquer melhores práticas? Ou é a arquitetura que eu uso simplesmente errado?
@Shy: Essa é uma solução para alguns casos, mas você ainda obter uma rodada de eventos supérfluos se X
é alterada de fora do controlador (por exemplo, quando se utiliza o padrão de comando para undo / redo), porque, então, o valor tem mudado, W
é atualizado e dispara um evento. A fim de evitar uma outra atualização (inútil) para o modelo, o evento gerado pelo widget tem de ser ingerido.
Em outros casos, o modelo pode ser mais complexa e uma verificação simples sobre o que exatamente mudou pode não ser viável, por exemplo, uma exibição em árvore complexa.
Solução
Normalmente, você deve responder a eventos de entrada no widget e não a eventos de mudança. Isso impede que este tipo de circuito do que ocorrem.
- usuário muda a entrada no widget
- Widget emite evento de alteração (rolagem feito / Enter clicado licença / rato, etc.)
- responde controlador, traduz a mudança no modelo
- Modelo emite evento
- responde controlador, altera o valor em Widget evento de alteração
- Valor emitido, mas não ouviu pelo controlador
Outras dicas
A forma padrão QT de lidar com isso e também o sugerido em seu tutorial muito útil é para fazer a mudança para o valor no controlador somente se o novo valor é diferente do valor atual.
Esta é sinais maneira tem a semântica de valueChanged()
Bandeiras para indicar o trabalho de atualização. Você pode envolvê-los em métodos como BeginUpdate e EndUpdate.