В. Время, вперёд!

Приступим к рассмотрению последнего примера. Это приложение будет взаимодействовать с человеком не через командную строку, а через обычные экранные кнопки. Нажатие на эти кнопки можно осуществлять как при помощи мышки, так и при помощи клавиатуры сочетание клавиши "Alt" и выделенной буквы кнопки или клавиши "пробел" и "ввод", если кнопка уже выбрана). Данное приложение можно выполнять и без консольной оболочки, т.к. оно заканчивает свою работу не сразу после запуска, а только по команде пользователя.

Наше приложение является секундомером. Вот так оно будет выглядеть в работе:

Вид секундомера

Рассмотрим строение данных этого приложения. На экране определяется рабочее поле, а на поле выделяются наделы, в которых можно вводить и выводить данные. Положение верхнего левого угла поля отсчитывается относительно верхнего левого угла экрана, а положение наделов - от верхнего левого угла поля. В нашем примере используются следующие виды наделов: подложка поля, текстовая записка, экранная кнопка и разделительная черта. Подложка заполняет область поля одним цветом, рисует двойную рамку и выводит посередине сверху свой заголовок; записка показывает на экране текст (показания секундомера); кнопки передают в приложение действия от человека, а разделительные черты служат для улучшения внешнего вида.

Объявляем название нашего отдела приложения и название отделов, которые он использует:

ОТДЕЛ Секундомер+;

ИСПОЛЬЗУЕТ
  ОС       ИЗ "...\Отделы\Обмен\",
  Окно     ИЗ "...\Отделы\Обмен\",
  Ввод     ИЗ "...\Отделы\Обмен\",
  Поле     ИЗ "...\Отделы\Поле\",
  Подложка ИЗ "...\Отделы\Поле\",
  Записка  ИЗ "...\Отделы\Поле\",
  Кнопка   ИЗ "...\Отделы\Поле\",
  Черта    ИЗ "...\Отделы\Поле\";

Объявляем постоянные, соответствующие номерам действий, которые вырабатываются при нажатии экранных кнопок:

ПОСТ
  дПуск =1;
  дСтоп =2;
  дСброс=3;
  дВыход=4;

Заводим: логическую переменную, которая будет принимать значение ВКЛ, если секундомер в данный момент ведёт измерения -

ПЕР
  идётИзмерение:КЛЮЧ;

целую переменную, в которой будет храниться измеренный промежуток времени -

  измеренноеВремя:ЦЕЛ;

переменную для хранения момента времени, в который произошло последнее обновление показаний секундомера -

  моментОбновления:ЦЕЛ;

и переменную для вывода показаний -

  зВремя:Записка.Доступ;

Далее объявляем задачу, которая будет принимать и обрабатывать действия пользователя в экранном "поле". Причем "посылка" от пользователя на входе задачи содержит или "действие" от экранной кнопки, или "код" клавиши клавиатуры, или данные о перемещении мышки:

ЗАДАЧА Приём(поле:Поле.Доступ; посылка+:Ввод.Посылка);
УКАЗ

Разные виды переменной "посылка" имеют разные наборы данных, поэтому для разных видов этой переменной мы будем выполнять разную последовательность указаний. Для переменной "посылка" вида "Кнопка.Посылка":

  ДЛЯ посылка ВИДА
  | Кнопка.Посылка:

известно, что набор данных содержит переменную "кнопка". По значению этой переменной мы определим, какую кнопку нажал пользователь в данный момент, и выполним соответствующее указание. По нажатию на кнопку "Пуск" включаем измерение времени, по кнопке "Стоп" отключаем измерение, по кнопке "Сброс" обнуляем измеренное время, а по кнопке "Выход" сообщаем главной задаче о желании пользователя выйти из приложения:

      ВЫБРАТЬ посылка.кнопка.действие ИЗ
      | дПуск:  идётИзмерение:=ВКЛ
      | дСтоп:  идётИзмерение:=ОТКЛ
      | дСброс: измеренноеВремя:=0
      | дВыход: поле.состояние:=Поле.сДалее
      КОН

Учитывая, что в наборе данных вида "Ввод.ОтКлавиш" есть переменные "код", "упрКнопки", мы сможем выполнить следующее указание: при нажатии на клавишу "Esc" или клавишу "Alt" совместно с "X" сообщим через состояние поля, что требуется выход из приложения.

  | Ввод.ОтКлавиш:
      ЕСЛИ (Ввод.лАльт В посылка.упрКнопки) И (посылка.код = 58H) (* Alt+'X' *)
       ИЛИ (посылка.код = 1BH) ТО                                 (* Esc     *)
        поле.состояние:=Поле.сДалее
      КОН

Так как нам не нужно, чтобы приложение аварийно завершало свою работу при получении посылки вида, отличного от "Кнопка.Посылка" и "Ввод.ОтКлавиш", то отработаем это условие как пустую последовательность указаний:

  ИНАЧЕ
  КОН

Заканчиваем эту задачу и начинаем главную задачу. Определяем в ней переменные для поля, наделов и часов:

КОН Получить;

ЗАДАЧА Главная;
ПЕР
  поле:Поле.Доступ;                            (* рабочее поле экрана   *)
  подложка:Подложка.Доступ;                    (* подложка поля         *)
  кнСтоп,кнПуск,кнСброс,кнВыход:Кнопка.Доступ; (* управляющие кнопки    *)
  черта1,черта2:Черта.Доступ;                  (* разделительные черты  *)
  часы:ЦЕЛ;

В задаче "Главная" объявляем подзадачу "СоздатьПоле". В начале этой задачи создаём переменную "поле". Верхний левый угол поля будет расположен в верхнем левом углу экрана (0,0). Высота поля будет 8 строк, а ширина 17 колонок. Поле распологается на чистом экране (ПУСТО), и его не надо помещать в середину (ОТКЛ). Ввод от пользователя будет направлен в задачу "Приём". Номер справки для нашего поля определять не будем (0).

  ЗАДАЧА СоздатьПоле;
  УКАЗ
    поле:=Поле.Создать(0,0,8,17,ПУСТО,ОТКЛ,Приём,0);

Далее создаём наделы. См. задание размеров и положения наделов и полей.

    подложка:=Подложка.Создать(0,0,0,0,"");
    зВремя  :=Записка.Создать(1,3,1,11,"00:00:00.00");
    черта1:=Черта.Создать(2,0,1,0);
    кнПуск :=Кнопка.Создать(3,2,"[~Пуск]",дПуск,ВКЛ);
    кнСтоп :=Кнопка.Создать(3,9,"[~Стоп]",дСтоп,ВКЛ);
    кнСброс:=Кнопка.Создать(4,4,"[ С~брос ]",дСброс,ВКЛ);
    черта2:=Черта.Создать(5,0,1,0);
    кнВыход:=Кнопка.Создать(6,4,"[ ~Выход ]",дВыход,ВКЛ);

Знак "~" в надписи, передаваемой наделу, предваряет букву, по которой человек сможет перейти к данному наделу, нажимая сочетание из клавиш "Alt" и заданной буквы. В данном примере все наши экранные кнопки - выбираемые (ВКЛ).

В конце подзадачи добавляем наделы в порядке получения ввода. Этот порядок определяет очерёдность обхода наделов при нажатии пользователем клавиши "Tab". В начале работы приложения первому наделу, который является выбираемым (в нашем случае это кнопка Пуск), будет предоставлена возможность получать ввод от человека.

    Поле.ДобавитьНадел(поле,подложка);
    Поле.ДобавитьНадел(поле,зВремя);
    Поле.ДобавитьНадел(поле,черта1);
    Поле.ДобавитьНадел(поле,кнПуск);
    Поле.ДобавитьНадел(поле,кнСтоп);
    Поле.ДобавитьНадел(поле,кнСброс);
    Поле.ДобавитьНадел(поле,черта2);
    Поле.ДобавитьНадел(поле,кнВыход)
  КОН СоздатьПоле;

Далее начинается последовательность указаний задачи "Главная". Настраиваем окно вывода, вызываем подзадачу для создания поля, подгоняем размер окна приложения под размер нашего поля, рисуем поле со всеми наделами и создаём часы №1 с временем срабатывания в 100 мс:

УКАЗ
  Окно.Графическое("Секундомер","Lucida Console",30,ОбновитьПоказания);
(*
  Окно.Текстовое(ОбновитьПоказания);
*)
  СоздатьПоле;
  Поле.ОкноНаВсёПоле(поле);
  Поле.Рисовать(поле);
  часы:=Окно.СоздатьЧасы(1,100);
  Окно.Показать;

И, наконец, повтор указаний, который будет выполняться всё основное время работы приложения (пока состояние поля будет рабочим):

  ПОКА поле.состояние = Поле.сРабота ВЫП

Вызываем задачу поля - "Проверить", в которой проверяется и обрабатывается ввод от пользователя:

    Поле.Проверить(поле)

Заканчиваем повтор указаний, прекращаем вызывать по часам задачу "ОбновитьПоказания", завершаем объявление задачи:

  КОН;
  Окно.УдалитьЧасы(часы)
КОН Главная;

Следующая задача выводит через записку "зВремя" обновлённые показания времени в привычном для человека виде:

  ЗАДАЧА ОбновитьПоказания(номерЧасов:ЦЕЛ);
  ПЕР
    сотые,секунды,минуты,часы:ЦЕЛ;
    сейчас:ЦЕЛ;
    прошлоВремени:ЦЕЛ;
  УКАЗ
    сейчас:=ОС.Время();
    прошлоВремени:=сейчас-моментОбновления;
    моментОбновления:=сейчас;
    ЕСЛИ идётИзмерение ТО
      УВЕЛИЧИТЬ(измеренноеВремя,прошлоВремени)
    КОН;
    сотые:=измеренноеВремя ДЕЛИТЬ 10;
    секунды:=сотые ДЕЛИТЬ 100;
    минуты:=секунды ДЕЛИТЬ 60;
    часы:=минуты ДЕЛИТЬ 60;
    Записка.ИзЧЦел(зВремя,"%.2d:%.2d:%.2d.%.2d",
      часы,минуты ОСТАТОК 60,секунды ОСТАТОК 60,сотые ОСТАТОК 100)
  КОН ОбновитьПоказания;

Выставляем начальные значения рабочих переменных, указываем на выполнение задачи Главная при запуске приложения и завершаем объявление отдела:

УКАЗ
  измеренноеВремя:=0;
  идётИзмерение:=ОТКЛ;
  Главная
КОН Секундомер.

Полный текст данного приложения расположен в папке \Глагол\Приложения\Иное\.

Отметим, что в примерах игр (папка \Глагол\Приложения\Игры\) взаимодействие приложения с человеком организовано точно таким же образом, как и в разобранном выше примере.


    Сделано в России