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