Нарушение циклов событий в графических интерфейсах
-
02-07-2019 - |
Вопрос
При написании графических интерфейсов я часто сталкивался со следующей проблемой:Предположим, у вас есть модель и контроллер.В контроллере есть виджет W
который используется для отображения свойства X
модели.
Поскольку модель может быть изменена извне контроллера (могут быть другие контроллеры, использующие ту же модель, операции отмены и т.д.), контроллер прослушивает изменения в модели.Контроллер также прослушивает события в виджете W
и обновляет свойство X
соответственно.
Теперь происходит следующее:
- значение в
W
изменен - генерируется событие, обработчик в контроллер вызывается
- тот самый контроллер устанавливает новое значение для
X
в Модель - тот самый Модель генерирует события, потому что они были изменены
- тот самый контроллер получает событие изменения от Модель
- тот самый контроллер получает значение
X
и устанавливает его в виджете - переход 1.
Для этого есть несколько возможных решений:
- Измените контроллер, чтобы он устанавливал флаг при обновлении модели и не реагировал ни на какие события из модели, если этот флаг установлен.
- Временно отключите контроллер (или попросите модель не отправлять никаких событий в течение некоторого времени).
- Заморозьте все обновления из виджета
В прошлом я обычно выбирал вариант 1., потому что это самая простая вещь.У него есть недостаток в том, что он загромождает ваши классы флагами, но у других методов тоже есть свои недостатки.
Просто для справки, у меня была эта проблема с несколькими наборами инструментов GUI, включая GTK +, Qt и SWT, поэтому я думаю, что это не зависит от инструментария.
Есть какие-нибудь рекомендации?Или архитектура, которую я использую, просто неправильная?
@Застенчивый:Это решение для некоторых случаев, но вы все равно получите цикл лишних событий, если X
изменяется извне контроллера (например, при использовании шаблона команды для отмены / повтора), потому что тогда значение изменилось, W
обновляется и запускает событие.Чтобы предотвратить очередное (бесполезное) обновление модели, событие, сгенерированное виджетом, должно быть проглочено.
В других случаях модель может быть более сложной, и простая проверка того, что именно изменилось, может оказаться невозможной, напримерсложный древовидный вид.
Решение
Обычно вы должны реагировать на входные события в виджете, а не изменять события.Это предотвращает возникновение цикла такого типа.
- Пользователь изменяет вводимые данные в виджете
- Виджет выдает событие изменения (прокрутка завершена / нажат ввод / оставлена кнопка мыши и т.д.)
- Контроллер реагирует, преобразуя изменения в модели
- Модель генерирует событие
- Контроллер реагирует, изменяя значение в виджете
- Событие изменения значения, отправленное, но не прослушанное контроллером
Другие советы
Стандартный способ QT справиться с этим, а также тот, который предложен в их очень полезном руководстве, заключается в том, чтобы вносить изменения в значение в контроллере только в том случае, если новое значение отличается от текущего.
Таким образом, сигналы имеют семантику valueChanged()
Флаги, указывающие на работу по обновлению.Вы можете обернуть их в такие методы, как BeginUpdate и EndUpdate.