Nickolay.info. Обучение. Учебник по Паскалю. Глава 24

24. Модуль crt и создание консольных интерфейсов

 

Модуль crt содержит процедуры и функции, предназначенные для работы с экраном консоли в текстовом режиме. Как и ряд других стандартных модулей, crt встроен в компилятор и содержится в файле turbo.tpl.

Экран в текстовом режиме разбивается на отдельные строки, а каждая строка -- на позиции, причем в каждую позицию может быть помещен только 1 символ из набора ASCII.

Для полного описания экранной позиции кроме символа следует задать еще и атрибут, содержащий информацию о цвете символа и фона на экране. Символ и атрибут занимают в памяти по 1 байту. Структура байта-атрибута показана на рис. 24.1.

 

Рис. 24.1. Структура байта-атрибута консоли

 

Старший бит 7 управляет мерцанием символа (символ на экране мерцает, если он установлен в 1), биты 4-6 содержат цвет фона (кодируется двоичными числами от 0 до 7 включительно), а биты 0-3 -- цвет  символа  (от 0 до 15). Разумеется, программисту обычно не приходится заполнять байт атрибута по битам, для этого есть стандартные коды цветов. Основные цвета кодируются цифрами от 0 до 15, причем цвет текста может быть любым, а цвет фона -- только из первых 8 цветов. Все цвета описаны в табл. 24.1.

 

 

Табл. 24.1. Коды и наименования стандартных цветов

Код

Наименование

Цвет

0

BLACK

черный

1

BLUE

синий

2

GREEN

зеленый

3

CYAN

циановый

4

RED

красный

5

MAGENTA

фиолетовый

6

BROWN

коричневый

7

LIGHTGRAY

светло-серый

8

DARKGRAY

темно-серый

9

LIGHTBLUE

голубой

10

LIGHTGREEN

светло-зеленый

11

LIGHTCYAN

светло-циановый

12

LIGHTRED

светло-красный

13

LIGHTMAGENTA

светло-фиолетовый

14

YELLOW

желтый

15

WHITE

белый

 

Можно обращаться к цвету как по цифровому коду, так и по англоязычному имени.

Широко используемые текстовые режимы имеют в окне консоли 25 строк по 80 столбцов (позиций) в строке. Нумерация строк и позиций начинается с 1 и считается слева направо и сверху вниз. Весь экран в текстовом режиме может быть описан  парой координат (1, 1), (80, 25). Обратите внимание на порядок записи -- столбец, затем строка.

Ниже рассмотрены основные процедуры и функции модуля. Везде для краткости введены следующие обозначения:

x,x1,x2 -- координаты столбцов экрана;

y,y1,y2 -- координаты строк экрана;

c -- значение цвета.

Особенность модуля crt состоит в том, что он позволяет работать не только со всем экраном, но и с выделенным на нем прямоугольным окном. При этом весь ввод, вывод и прокрутка текста происходят в пределах окна. По умолчанию размеры окна совпадают с размерами экрана, но можно явно установить их обращением к стандартной процедуре Window (x1,y1,x2,y2);, где (x1, y1) и (x2, y2) -- соответственно, левый верхний и правый нижний угол окна.

Цвет фона окна c задает процедура textbackground ( c );, а цвет символов -- textcolor ( c );.

Процедура без параметров clrscr; очищает текущее окно цветом фона.

Для установки текстового курсора в позицию окна с координатами (x, y) определена процедура gotoxy (x,y);.

Программно определить текущее положение курсора позволяют 2 стандартные функции Wherex:char; и Wherey:char;, возвращающие, соответственно, текущие x- и y-координату курсора.

Процедура ClrEol; удаляет все символы от позиции курсора до конца  строки включительно, заполняя этот участок цветом фона.

Процедура Delline; полностью удаляет строку, в которой находится курсор, а Insline; вставляет  пустую строку на экране в месте расположения курсора и заполняет ее цветом фона. Обе процедуры обеспечивают прокрутку содержимого окна.

Процедура Sound (F:word); включает встроенный динамик с частотой F герц, обеспечивая выдачу звукового сигнала.

Процедура Delay (T:word); задает задержку выполнения программы, равную T миллисекунд (1000 мс = 1 сек.). Эта процедура используется для организации задержек выполнения программы, а также всегда вызывается после sound, чтобы определить время звучания динамика.

Процедура без параметров NoSound; выключает динамик. Обязательно используется после пары Sound и Delay.

Наконец, в модуле crt определены 2 стандартных функции для работы с кодами нажатых клавиш. Функция readkey:char; возвращает код символа, прочитанный из буфера клавиатуры. Функция keyPressed:boolean; возвращает значение true, если была нажата клавиша на клавиатуре (за исключением вспомогательных клавиш Alt, Shift, Ctrl и т. д.). Использование последней функции позволяет организовать циклы, выполняющиеся до нажатия какой-либо клавиши.

При запуске программы из оболочки Паскаля монитор находится обычно в текстовом режиме и устанавливать его не нужно. Тем не менее, существует стандартная процедура textMode (Mode:integer), устанавливающая текстовый режим с номером Mode.

Стандартный цветной текстовый режим 25*80 позиций имеет номер 3, цветной текстовый режим 25*40 позиций -- номер 1.

Модуль crt содержит также системные переменные,  которые можно изменять в соответствии с указанным для них типом.

Переменная CheckBreak:boolean; управляет реакций программы на прерывание  по сочетанию клавиш Ctrl+Break. По умолчанию переменная имеет значение true (реакция включена).

Если переменная DirectVideo:boolean; имеет значение true, процедуры вывода на экран пишут данные непосредственно в видеопамять, не используя операционную систему. Это ускоряет вывод, но может использоваться только на полностью IBM-совместимых ЭВМ.

Переменная textAttr:integer; содержит текущий атрибут текста,  сформированный  по  описанным  выше правилам.

Приведем пример программы, определяющей коды нажатых клавиш. Конструкция repeat ... until в этой программе является образцом обработки ввода с клавиатуры. Проблема состоит в том, что функция readkey возвращает однобайтовый код клавиши, а ряд клавиш и сочетаний клавиш имеют двухбайтовые коды. С этим связан второй вызов функции readkey в программе.

uses crt;

var ch : char; {Символ, который вводим}

begin

 clrscr;   {Очистили экран}

 writeln ('Программа выводит коды клавиш;',

          ' Esc - выход.');

 repeat

  writeln('Нажмите клавишу:');

  ch := readkey;   {Ждем ввода символа}

  if ch = #0 then {Если нажата спец.

         клавиша, то функция вернула 0,}

  begin

   ch := readkey; {и нужно прочитать код

                   символа дополнительно}

   writeln('Нажата специальная клавиша ',

           'с кодом ', ord(ch));

  end

  else {Иначе если нажата обычная клавиша -

        сразу видим ее код}

   writeln('Нажата клавиша с ASCII-кодом',

           ' ',ord(ch));

 until ch=#27;   {Значение 27 -

                  это код клавиши Escape}

 writeln ('До свидания.');

end.

Как правило, в реальных программах широко используются небуквенные клавиши, такие как Enter, F1, Esc и т. д. Узнать их коды можно из таблиц ASCII-символов. Например, код клавиши Escape равен #27. Для записи клавиатурного кода на Паскале перед его значением ставится символ #, как сделано в этом примере. Более подробно об обработке нажатий клавиш рассказано в Приложении 5. Листинги 5-8 из Приложения 4 также иллюстрирует основные аспекты обработки нажатий клавиш.

В качестве развернутого примера использования функций модуля crt напишем программу, которая заполняет экран случайными цветными окнами, а также является примером проигрывания несложной "музыки" через встроенный динамик компьютера. Для рисования рамок в этой программе используются символы псевдографики, которые есть только в кодировке DOS (см. Приложение 1).

Program crt_example;

uses crt;  

const minLen=10; {минимальная длина окна}

  pause=500; {задержка при выводе звука}

  blink=128; {установка бита мерцания}

var x1,y1,x2,y2 :integer;

    {координаты окна}

  background, {цвет фона окна}

  color, {цвет текста}

  freq, {частота звука}

  setblink :integer; {есть/нет мерцание}

 

procedure doubleFrame (x1,y1,x2,y2:integer;

 Header: string);

{Процедура рисует двойной рамкой окно

 с заголовком и подготавливает его

 внутреннюю часть для ввода текста}

{ x1,y1,x2,y2 - координаты окна}

{ header  - заголовок окна}

var i,j: integer;

begin

 Window (1,1,80,25);

 {Рисуем верхнюю строку рамки }

 gotoxy (x1,y1); write ('╔');

 for i:=x1+1 to x2-1 do write('═');

 write ('╗');

 {Перебираем строки внутри окна}

 for i:=y1+1 to y2-1 do begin

  gotoxy (x1,i); write('║');

  for j:=x1+1 to x2-1 do write (' ');

   {Внутренность окна - пробелы}

  write('║'); {Правая граница}

 end;

 {Аналогично рисуем нижнюю строку}

 gotoxy (x1,y2);  write('╚');  

 for i:=x1+1 to x2-1 do write('═');

 write('╝');

 gotoxy (x1+(x2-x1+1-Length(Header))

  div 2,y1);

  {Ставим курсор в середину верхней строки}

 write (Header); {Выводим заголовок}

 Window (x1+1,y1+1,x2-1,y2-1);    

  {Устанавливаем текущее окно внутри рамки}

 gotoxy (1,1);{Ставим курсор в левый

               верхний угол нового окна}

end;

 

begin

 textbackground (BLACK);  

 Window (1,1,80,25);

  {окно вывода - весь экран}

 clrscr;

 {Инициализируем генератор случайных чисел}

 randomize;

  DirectVideo:=true;

 while not keyPressed do begin

  {Пока не нажата клавиша,

   выполняется цикл}

  x1:= 1 + random(80-minLen);

  x2:=x1 + minLen + random (80-x1-minLen);

  y1:= 1 + random(25);

  y2:= 1 + y1 + random (25-y1);

  {Выбрали случайные координаты окна }

  background:=random(8);

  color:=random(16);

  {Выбрали цвет фона и текста}

  setblink:=random(2);

  {Выбрали установку мерцания ДА или НЕТ}

  textbackground (background);

  textcolor(color+blink*setblink);

   {Цвет текста с учетом мерцания}

  doubleFrame (x1,y1,x2,y2,' Hello! ');

   {Рисуем окно с помощью процедуры}

  background := (textAttr and 112) shr 4;

   { Из байта цвета, содержащегося в

     переменной textAttr, выделяем цвет

     фона. Операция shr xx сдвигает

     байт вправо на xx бит, а 112 в

     двоичной системе это 01110000

     (включены биты, отвечающие за фон) }

  case background of  

   { В зависимости от цвета фона выбираем

     частоту звучания динамика }

  0: freq:=262;  {Частота ноты До}

  1: freq:=294;  {    -"-  Ре}

  2: freq:=330;  {    -"-  Ми}

  3: freq:=349;  {    -"-  Фа}

  4: freq:=392;  {    -"-  Соль}

  5: freq:=440;  {    -"-  Ля}

  6: freq:=494;  {    -"-  Си}

  7: freq:=524;  {    -"-  До}

  end;

  sound (freq);  {Включаем динамик}

  Delay (pause);

   {Ждем, пока не истечет задержка}

  Nosound;       {Выключаем динамик!}

 end; {Конец основного цикла}

 {Восстанавливаем атрибуты текста и окно}

 textbackground (BLACK);

 textcolor (LIGHTGRAY);

 Window (1,1,80,25);

 clrscr;

end.

Использование этой программы на современном быстром процессоре может и не дать вам насладиться "космической музыкой" -- проблема в реализации функции Delay, учитывающей не реально прошедшее время в миллисекундах, а "условное" время, связанное с тактовой частотой процессора. Для исправления ситуации следует написать и применять собственную реализацию Delay, привязанную к функции GetTime модуля dos, позволяющей получить "абсолютное" системное время в часах, минутах, секундах и сотых долях секунды. Ниже приводится одна из возможных версий такой функции с комментариями основных действий и тестом:

uses crt,dos;

function getlongintTime:longint;

{Вернет системное время как longint}

 var Hour,minute,second,sec100: word;

 var k,r:longint;

begin

 GetTime (Hour, minute, second, sec100);

 {Прямое вычисление по формуле

  Hour*360000+minute*6000+second*100+sec100

  не сработает из-за неявного

  преобразования word в longint:}

 k:=Hour; r:=k*360000;

 k:=minute; Inc (r,k*6000);

 k:=second; Inc(r,k*100);

 Inc(r,sec100); getlongintTime:=r;

end;

 

procedure MyDelay (ms:word);

 {Корректно работает с задержками

  до 65 сек.!}

var endTime,curTime : longint;

    cor:boolean; {признак коррекции времени

             с учетом перехода через сутки}

begin

 cor:=false;

 endTime:=getlongintTime + ms div 10;

 if endTime>8639994 then cor:=true;

 {Учитываем возможный переход через сутки;

  23*360000+59*6000+59*100+99=8639999 и

  отняли 5 мс с учетом частоты срабатывания

  системного таймера BIOS}

 repeat

  curTime:=getlongintTime;

  if cor=true then begin

   if curTime<360000 then

    Inc (curTime,8639994);

  end;

 until curTime>endTime;

end;

 

var Hour,minute,second,sec100: word;

begin

 clrscr;

 {setTime (23,59,58,99);}

  {если раскомментарить - может изменить

   системное время!}

 repeat

  gotoxy (1,1);

  GetTime (Hour, minute, second, sec100);

  write (Hour:2, ':', minute:2, ':',

         second:2, ':',sec100:2, ' ');

  MyDelay (500);

until keypressed;

end.

В Приложении 4 приведены также листинги программ для вывода кодов часто используемых клавиш, движения по экрану "прицела" с помощью клавиш со стрелками, а также программа создания несложного двухуровневого меню пользователя (листинги 5-7).

Рейтинг@Mail.ru
вверх гостевая; E-mail