Построение меню в программах

Авторы: © Romtek и volvo877.

Вступление

Часто приходится сталкиваться с программами, в которых студенты пытались построить нечто похожее на меню, и это было нечто… (слов не подобрать :-) ). Построение меню занимало большую часть программы и алгоритм терялся в недрах гигантских программ.

Программа состоит из набора алгоритмов, данных и интерфейса. Но ошибаются те, которые считают, что интерфейс главнее. Это большое заблужение! Потому, что основную задачу выполняют именно алгоритмы, а не украшательства. Интерфейс же совсем необязательная часть.

Более того, неприемлемо смешивание кода алгоритмов и интерфейса: в этом случае очень затрудняется поиск ошибок и нарушается строение программы.

Построение меню

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

uses crt;
Type
   MenuType = (Vertical, Horizontal);
 
const
   width = 9; { Максимальная длина элементов (букв) меню }
 
   Items1 = 5; { Количество элементов меню 1 }
   optText1: array [0..Items1-1] of string =
   ('Сложение',
    'Вычитание',
    'Деление',
    'Умножение',
    'Выход');
 
   Items2 = 3; { Количество элементов меню 2 }
   optText2: array [0..Items2-1] of string = (
    'item 1',
    'item 2',
    'item 3'
    );
 
   optNormal = LightGray; { цвет невыделенных элементов меню }
   optSelected = Yellow;  { цвет выделенных элементов меню }
 
var
   X, Y,
   selected,  { Индекс элемента, который будет подсвечиваться в начале
                программы }
   row: integer;
   _style: menuType;  { Указывает на тип меню: вертикальный (Vertical)
                        или горизонтальный (Horizontal) }
 
 
{ Эта процедура используется процедурой MenuOption для построения меню }
procedure MakeMenu (optText: array of string; MaxItems: integer);
var
   i, _X: byte;
begin
     Y := row;
     _X := X;
     for i := 0 to MaxItems-1 do
     begin
          GoToXY (_X, Y);
          if i = selected then
             TextColor (optSelected)
          else
             TextColor (optNormal);
          write (optText[i]);
 
          If _style = Horizontal Then
            inc (_X, width + 1)
          Else
            inc (Y, 2);
     end;
end;
 
{ выбираем нужный элемент меню этой функцией }
function MenuOption (optText: array of string; MaxItems: integer): byte;
var
   ch: char;
begin
     selected := 0;
 
     If _style = Vertical then begin
       X := (80 - width) div 2;
       row := (25 - MaxItems) div 2;
     End
     Else
     Begin
       X := (80 - MaxItems * width) div 2;
       row := 2; { строчка, в которой будет находиться горизонтальное меню }
       GotoXY(1, row); ClrEol; { ... а для горизонтального - только строку
                                 где будет меню. }
     End;
 
     repeat
           MakeMenu (optText, MaxItems);
 
           ch := readkey;
           if ch = #0 then
              ch := readkey;
 
           case ch of
           #80, #77: {Down/Right}
           begin
                inc (Selected);
                if Selected = MaxItems then
                   Selected := 0;
                MakeMenu (optText, MaxItems);
           end;
 
           #72, #75: {Up/Left}
           begin
                dec (Selected);
                if Selected < 0 then
                   Selected := MaxItems-1;
                MakeMenu (optText, MaxItems);
           end;
           end;
     until ch = #13; {Enter}
 
     MenuOption := Selected + 1;
 
     TextColor (optNormal);
     If _style = Vertical Then
        clrscr;
end;
 
procedure Add;
begin
end;
 
procedure Subtract;
begin
end;
 
procedure Divide;
begin
end;
 
procedure Multiply;
begin
end;
 
var
  Option: byte; { номер выбранного пункта }
 
begin
     clrscr;
 
     _style := Vertical; { вертикальное меню }
     Option := MenuOption (optText1, Items1);
 
     case option of
     1: Add;       { сложить числа }
     2: Subtract;  { вычесть числа }
     3: Divide;    { поделить числа }
     4: Multiply;  { умножить числа }
     5: exit;      { Выйти из программы }
     end;
 
     { вывести (если нужно) номер выбранного пункта }
     writeln ('Номер пункта: ', option);
     readln;
 
     _style := Horizontal; { горизонтальное меню }
     Option := MenuOption (optText2, Items2);
 
end.

Бывает, что есть необходимость добавить пояснительный текст рядом с меню. В подавляющем количестве студенты пишут тогда следующий код:

begin
     GotoXY (1,2);
     writeln ('Для управления пользуйтесь клавишами стрелок:');
     GotoXY (1,4);
     writeln ('1 - сделать то-то...');
     writeln ('2 - сделать то-то...');
     writeln ('3 - сделать то-то...');
     writeln ('4- сделать то-то...');
     writeln ('5 - сделать то-то...');
end;

Особенно неудобно в программах с большим текстом кода. В нём можно просто утонуть. Это дико неудобно возиться со множеством writeln, которые только отвлекают от сути, в то время как можно загрузить текст из текстового файла, заранее отредактированного в любом текстовом редакторе такой текст: Для управления пользуйтесь клавишами стрелок:

1 - сделать то-то...
2 - сделать то-то...
3 - сделать то-то...
4 - сделать то-то...
5 - сделать то-то...

Не правда ли, удобнее?

Теперь рассмотрим процедуру, которая загрузит текст из отдельного текстового файла.

{ Эта процедура используется процедурой TextWindow для загрузки текста из отдельного текстового файла. }

procedure LoadText (fname: string);
var F: text;
    str: string;
begin
     Assign (F, fname);
     {$I-}
     Reset (F);
     {$I+}
     if IOresult = 0 then
     while Not EOF (F) do
     begin
          readln (F, str);
          writeln (str);
     end;
     Close (F);
end;

Эта процедура выведет текст в заданном регионе (окне) из текстового файла fn.

{ Выводит текст в заданном регионе из текстового файла fn }
procedure TextWindow (x1, y1, x2, y2: integer; fn: string);
const
     TextColor = Cyan; { цвет фона региона }
begin
     Window (x1, y1, x2, y2);
     TextBackground (TextColor);
     ClrScr;
 
     LoadText (fn);
 
     Window (1, 1, 80, 25);
     TextBackground (Black);
end;

Таким образом, задав нужные вам параметры, вы увидите текст рядом с меню. Например,

     TextWindow (2, 2, 78, 7, 'strelki.txt');
     _style := Vertical; { вертикальное меню }
     Option := MenuOption (optText1, Items1);

Здесь текст выводится в окне с границами 2, 2, 78, 7 из файла 'strelki.txt'.

Вариант с использованием модуля

uses crt, menu_u;
 
const
   width1 = 9; { Максимальная длина элементов (букв) меню }
   Items1 = 5; { Максимальное количество элементов меню 1 }
   optText1: array [0..Items1-1] of string =
   ('Сложение',
    'Вычитание',
    'Деление',
    'Умножение',
    'Выход');
 
   width2 = 6; { Максимальная длина элементов (букв) меню }
   Items2 = 3; { Максимальное количество элементов меню 2 }
   optText2: array [0..Items2-1] of string = (
    'item 1',
    'item 2',
    'item 3'
    );
 
 
procedure Add;
begin
end;
 
procedure Subtract;
begin
end;
 
procedure Divide;
begin
end;
 
procedure Multiply;
begin
end;
 
var
  Option: byte; { номер выбранного пункта }
 
begin
     clrscr;
     TextWindow (2, 2, 78, 7, 'menu.txt');
     _style := Vertical; { вертикальное меню }
     Option := MenuOption (optText1, Items1, width1);
 
     case option of
     1: Add;       { сложить числа }
     2: Subtract;  { вычесть числа }
     3: Divide;    { поделить числа }
     4: Multiply;  { умножить числа }
     5: exit;      { Выйти из программы }
     end;
 
     { вывести (если нужно) номер выбранного пункта }
     writeln ('Номер пункта: ', option);
     readln;
 
     _style := Horizontal; { горизонтальное меню }
     Option := MenuOption (optText2, Items2, width2);
end.

Описание программы

За построение меню отвечает процедура MakeMenu - её вызывает функция MenuOption, которая возвращает результат в виде индекса выбранного пункта.

Стиль меню определяется значением _style (Vertical/Horizontal) - то, как меню будет располагаться на экране.

     _style := Vertical; { вертикальное меню }

Цвет нормальных элементов определяется константой optNormal, а цвет выделенных - optSelected.

Остаётся всего-лишь определить названия элементов меню в optText, константу Items (количество элементов меню), константу width (максимальная длина букв самой длинной строки меню) и меню готово!

Для вывода пояснительного текста рядом с меню можно воспользоваться процедурой TextWindow. Пять параметров означают координаты окна и название файла, из которого загрузится описание для меню.

Примечание:

Процедура TextWindow должна находиться перед построением меню. Иначе текста не будет видно!

Снимок экрана

Меню программы

Скачать

menudemo.zip

  1. Menudemo.pas - программа построения меню без использования модуля.
  2. Menu_t.pas - программа построения меню с модулем Menu_u.

Menu_u.pas - модуль с основными функциями и процедурами для построения меню.

Вариант 2 более предпочтителен, т.к. за счёт использования модуля гораздо легче ориентироваться в программе и отслеживать ошибки.

 
pascal/building_menu.txt · Последнее изменение: d.m.Y H:i — romtek
 
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki