Описание пользователя
Глава 3. Использование стандартных модулей автокомпиляции
§3.6. Принципы создания автокомпилируемых моделей блоков
§3.6.10. Пометки на блоках
Рассматривается вывод дополнительных изображений поверх блоков, которые могут привлекать внимание пользователя к ошибкам или к неправильно настроенным параметрам.
Независимо от того, каким именно способом задается внешний вид блока в подсистеме (прямоугольником с текстом, векторной картинкой или программно), его модель имеет возможность нарисовать что-либо дополнительное поверх этого изображения. Обычно эта возможность используется для привлечения внимания пользователя в тех случаях, когда блок не может правильно работать – например, из-за недопустимых значений на входе или из-за отсутствия необходимой ему динамической переменной. Если внешний вид блока рисуется программно, в таких дополнительных пометках обычно нет необходимости, поскольку их можно вывести в основной реакции рисования блока. Именно так мы поступали в примерах из §3.6.5: при отсутствии значений на входах индикатор уровня и стрелочный индикатор рисовались красным цветом. Если же внешний вид блока задается векторной картинкой или прямоугольником с текстом, возможности сообщения пользователю об ошибках сильно ограничены: векторная картинка не может отражать состояние блока в режиме редактирования, а внешний вид прямоугольника с текстом вообще не может быть изменен простыми средствами ни в одном из режимов RDS.
Рис. 448. Дополнительное рисование
в списке событий
Для рисования дополнительных изображений поверх блока используется специальная реакция его модели: «», которая на вкладке «» находится в разделе «» вместе с основной реакцией рисования (рис. 448). В отличие от последней, реакция на дополнительное рисование вызывается у всех блоков, независимо от их параметров, поэтому как-либо включать ее не нужно: достаточно ввести реакцию в модель. В остальном эти две реакции очень похожи: в реакции на дополнительное рисование тоже можно использовать как графические функции RDS, так и стандартные функции Windows API, и в нее передается указатель на структуру RDS_DRAWDATA, уже знакомую нам по §3.6.5.
В качестве примера дополнительного рисования изменим модель одного из двух блоков, рассмотренных в §3.6.3.2. Эти блоки обменивались данными через вещественную динамическую переменную с именем «AmbientTemperature»: блок-передатчик создавал переменную и записывал в нее значение своего входа, а блок-приемник (их могло быть несколько) считывал это значение и передавал его на свой выход. При отсутствии блока-передатчика в схеме блок-приемник никак не сообщал пользователю о невозможности работы – он просто ничего не передавал на выход. Сделаем так, чтобы, обнаружив отсутствие переменной «AmbientTemperature», блок-приемник рисовал поверх себя желтую иконку с красным восклицательным знаком (так поступают многие стандартные блоки).
По умолчанию в параметрах всех моделей включен запрет работы при отсутствии необходимых динамических переменных (см. рис. 392), и мы не сможем ничего нарисовать поверх блока: если переменная «AmbientTemperature» не будет существовать, не будет вызвана ни одна из реакций нашей модели, в том числе и реакция дополнительного рисования. Нужно выключить этот запрет и, поскольку теперь мы уже не можем быть уверены в существовании динамической переменной на момент вызова модели, включить в уже имеющуюся реакцию блока необходимые проверки.
Чтобы разрешить модели работать независимо от наличия или отсутствия заданных на вкладке редактора «» динамических переменных, необходимо вызвать окно параметров модели пунктом главного меню окна редактора «» и выключить флажок «» (рис. 449).
Рис. 449. Выключение запрета работы модели
без динамических переменных
Теперь наша модель будет запускаться и при отсутствии «AmbientTemperature», поэтому в реакцию на изменение динамической переменной необходимо вставить проверку ее существования. Если этого не сделать, при удалении этой переменной (в RDS удаление динамической переменной тоже считается ее изменением) модель попытается обратиться к ней, что вызовет ошибку. Текст на вкладке «» нужно исправить следующим образом (добавленные строки выделены цветом):
if(!AmbientTemperature.Exists()) // Нет переменной
return; // Завершаем реакцию
t=AmbientTemperature;
Ready=1; // Выходные связи должны сработать
Теперь в самом начале реакции мы вызываем у объекта AmbientTemperature, работающего с одноименной динамической переменной, функцию-член Exists, которая возвращает «истину», только если переменная существует. Если эта функция вернет «ложь», мы немедленно прерываем выполнение реакции оператором return – теперь оператор присвоения выходу t значения AmbientTemperature выполнится, только если динамическая переменная будет существовать.
Добавим в нашу модель реакцию на дополнительное рисование: на вкладке «» левой панели окна редактора раскроем раздел «» и дважды щелкнем на подразделе «» – в правой части окна появится новая пустая вкладка «». В ней необходимо ввести следующий текст:
if(!AmbientTemperature.Exists()) // Нет переменной { int w,h; if(rdsXGGetStdIconSize(RDS_STDICON_YELCIRCEXCLAM,&w,&h)) rdsXGDrawStdIcon(DrawData->Left+(DrawData->Width-w)/2, DrawData->Top+(DrawData->Height-h)/2, RDS_STDICON_YELCIRCEXCLAM); }
Как и в реакции на изменение переменной, здесь мы сначала проверяем существование переменной AmbientTemperature при помощи функции-члена Exists: если переменная существует, ничего рисовать не нужно. В противном случае мы будем рисовать иконку с красным восклицательным знаком.
Иконка, которую мы собираемся рисовать, входит в состав RDS, поэтому проще всего обратиться к ней при помощи встроенных графических функций. Красному восклицательному знаку в желтом круге соответствует константа RDS_STDICON_YELCIRCEXCLAM. Чтобы нарисовать эту иконку по центру изображения блока, нужно знать ее размеры – их мы получаем при помощи функции rdsXGGetStdIconSize, которая определяет ширину и высоту иконки, идентификатор которой передан в первом параметре, и помещает их в целые переменные, указатели на которые переданы во втором и третьем – в данном случае, это w и h. Зная размеры иконки и координаты прямоугольника блока, которые можно взять из структуры RDS_DRAWDATA, указатель на которую передается в функцию реакции в параметре DrawData, можно вывести эту иконку в центр блока вызовом функции rdsXGDrawStdIcon. Первые два параметра функции – вычисляемые нами горизонтальная и вертикальная координаты верхнего левого угла выводимой иконки, третий – идентификатор стандартной иконки RDS_STDICON_YELCIRCEXCLAM.
Рис. 450. Иконка поверх блока
Теперь, независимо от того, какой внешний вид мы дадим блоку и в каком режиме будет находится RDS, при отсутствии в схеме динамической переменной «AmbientTemperature» поверх нашего блока будет рисоваться восклицательный знак (рис. 450). В этом можно убедиться, удалив из схемы блок, создающий эту переменную – переменная удалится вместе с блоком, и на нашем блоке появится предупреждающая пометка.
Подобную индикацию ошибок поверх блоков имеет смысл сочетать с всплывающими подсказками, чтобы пользователь, наведя курсор на блок, смог прочесть, какая именно ошибка произошла. Здесь мы не будем добавлять подсказку к блоку, чтобы не усложнять пример.
Следует учитывать, что при включенном избирательном обновлении окон подсистем (см. §2.10.2 руководства программиста) блоки, внешний вид которых задан как прямоугольник с текстом, автоматически не перерисовываются, и событие дополнительного рисования у них тоже не возникает. В рассмотренном примере это не важно, поскольку динамическая переменная «AmbientTemperature», на отсутствие которой блок указывает рисованием иконки, может появиться или исчезнуть только в режиме редактирования из-за вставки или удаления создающего ее блока, а в режиме редактирования избирательное обновление не используется, и все блоки всегда перерисовываются. Когда RDS перейдет в режим расчета, где используется избирательное обновление, иконка уже будет нарисована и ее состояние не изменится.
Если же дополнительные пометки на блоке могут появляться и исчезать в режиме расчета, для нормальной работы избирательного обновления модель блока должна сообщать RDS об изменении этих пометок вызовом функции rdsForceBlockRedraw.