Практика использования


Программирование с использованием библиотеки Turbo Vision весьма специфично по сравнению с обычным программированием в среде Турбо Паскаля. Эта специфика прежде всего связана с широко используемым в Turbo Vision механизмом ООП: как правило, нельзя чисто механически перенести старую программу в новую объектно-ориентированную среду, обычно для этого требуется переосмысление всего проекта в целом. Другая особенность Turbo Vision - интенсивное использование динамической памяти: если Вы не имеете достаточного опыта работы с кучей, у Вас могут быть проблемы, связанные с динамически размещаемыми объектами. И, наконец, программы, управляемые событиями - это еще одна весьма специфичная особенность Turbo Vision, которая вначале может создавать определенные трудности в отладке.
В этой главе обсуждаются дополнительные средства, имеющиеся в Turbo Vision и увеличивающие эффективность использования этой библиотеки. Кроме того, приводятся практические рекомендации по разработке всей программы в целом и программированию отдельных ее частей.
 
Контроль за динамической памятью
Как правило, объекты в Turbo Vision размещаются в куче. Это отвечает специфике диалоговых программ: на этапе разработки программист обычно не может учесть все возможные действия пользователя программы. Чтобы не накладывать неестественные ограничения на те или иные ее возможности, не следует злоупотреблять статическими определениями объектов, так как в этом случае программа не сможет гибко учитывать специфические требования пользователя.
В правильно разработанной программе контроль за доступной динамической памятью должен осуществляться перед каждым обращением к New, в противном случае нехватка памяти может вызвать аварийный останов программы и все подготовленные пользователем данные и промежуточные результаты будут безвозвратно потеряны.
В Turbo Vision имеются средства, упрощающие этот контроль: глобальная функция LowMemory будет возвращать True, если размер свободного участка кучи стал слишком мал (по умолчанию меньше 4 Кбайт). Таким образом, вместо того, чтобы контролировать кучу перед каждым обращением к New, можно обратиться к функции LowMemory перед началом размещения динамического объекта или сразу после того, как объект размещен в куче. Если LowMemory возвращает True, дальнейшая работа с кучей возможна только после ее очистки. Резервный участок кучи длиной в 4 Кбайт называется пулом надежности. Предполагается, что его размеры достаточны для размещения любого объекта Turbo Vision, поэтому обычно контроль с помощью LowMemory осуществляется сразу после процедуры динамического размещения нового видимого элемента.
В следующем примере создается простое диалоговое окно:
Uses Memory,...;{Функция LowMemory определена в модуле Memory}
.....
R.Assign(20,3,60,10);
D := New(Dialog, Init(R, 'Диалоговое окно')); 
with D do 
begin
R.Assign(2,2,32,3);
Insert(New(PStaticText, Init(R, 'Сообщение-вопрос'))); 
R.Assign(5,5,14,7);
Insert(New(PButton, Init(R, '~Y~es (Да)', cmYes))); 
RAssign(16,5,25,7);
Insert(New(PButton, Init(R, ' ~N~o (Нет)', cmNO))) 
end;
if LowMemory then 
begin
Dispose(D,Done); {Нет памяти: удаляем распределение} 
OutOfMemory; {Сообщаем об этом} 
DoIt := False {Признак ошибки} 
end 
else
Dolt := DeskTop.ExecView(D)=cmYes;
Если Вы используете вызов LowMemory сразу после динамического размещения объекта, то в ходе самого размещения не должен произойти аварийный останов, связанный с нехваткой памяти. Таким образом, размер пула надежности должен быть достаточным для размещения всего объекта. Переменная LowMemSize задает размер пула надежности в параграфах (участках, длиной по 16 байт). По умолчанию она имеет значение 4096 div 16 = 256, т.е. размер пула надежности составляет 4 Кбайт.
На практике вместо прямого обращения к LowMemory чаще используется вызов метода TProgram.ValidView (P: Pointer): Pointer. Этот метод получает в качестве параметра обращения указатель Р на динамический объект и осуществляет следующие действия:

  • если Р = NIL, метод возвращает NIL;
  • если LowMemory = True, метод освобождает память, связанную с Р, вызывает метод TProgram.OutOfMemory и возвращает NIL;
  • если обращение к методу TView. Valid (cm Valid) дает False (см. ниже), объект Р удаляется из кучи и метод ValidView возвращает NIL;
  • в противном случае считается, что размещение осуществлено успешно, и метод возвращает значение указателя Р.

Метод TProgram.ValidView осуществляет стандартные действия по контролю надежности использования кучи. Обычно его используют перед тем, как поместить новый видимый элемент в группу, например:
DeskTop.Insert(ValidView(New(TMyWindow, Init(...))));
Заметим, что нехватка памяти вызывает обращение к виртуальному методу OutOfMemory, предназначенному для выдачи сообщения о ненормальной ситуации. По умолчанию этот метод ничего не делает и просто возвращает управление вызывающей программе. Вы должны перекрыть его, если хотите сообщить пользователю о возникшей проблеме.
В ряде случаев может оказаться полезной глобальная функция Function MemAlloc (Size: Word): Pointer, которая осуществляет те же действия, что и New или GetMem, но в отличие от них не распределяет пул надежности. Функция возвращает указатель на выделенную область кучи или NIL, если в куче нет свободного блока нужного размера. Аналогичные действия осуществляет функция MemAllocSeg, отличающаяся от MemAlloc только тем, что выделяет память, выровненную на границу параграфа (на границу сегмента).
 
Обработка ошибок инициации и модальных состояний
Каждый видимый элемент наследует виртуальный метод TView.Valid (Command: Word): Boolean. С помощью этого метода решаются две задачи: если параметр обращения Command = cmValid = О, метод должен проверить правильность инициации объекта и выдать True, если инициация прошла успешно; при обращении с параметром Command о cmValid метод возвращает True только тогда, когда модальное состояние диалогового элемента можно завершить командой Command. По умолчанию метод Valid возвращает True. Вы должны перекрыть этот метод, если хотите автоматизировать контроль за инициацией объекта и/или за завершением работы модального элемента.
Поскольку метод Valid автоматически вызывается из метода ValidView, в нем нет необходимости контролировать правильность использования кучи - это делает ValidView. С другой стороны, в методе Valid можно проверить другие условия правильного функционирования объекта. Например, если в объекте используется дисковый файл, можно проверить существование этого файла.
Типичный метод Valid имеет такой вид:
Function TMyView. Valid (Command: Word): Boolean; 
begin
Valid := True;
{Проверяем корректность инициации:} 
if Command = cmValid then 
if not Correctlylnit then 
begin
ReportErrorlnit; {Сообщяем о некорректной инициации} 
Valid := False 
end 
else
{Проверяем корректность завершения:} 
else if Command <> EnableCommand then
begin 
ReportErrorEnd {Сообщяем о некорректном выходе}
Valid := False 
end 
end;
В этом фрагменте предполагается, что результат проверки правильности создания элемента возвращается в логической переменной Correctlylnit, проверка корректности завершения работы модального элемента осуществляется сравнением команды завершения с ожидаемой командой EnableCommand, а сообщения об обнаруженных отклонениях от нормы выдаются процедурами ReportErrorInit и ReportErrorEnd. Заметим, что сообщения об ошибках инициации, не связанных с динамическим распределением объекта в куче, реализуются в методе Valid, в то время как сообщения об ошибках кучи - в методе ValidView.
Если видимый элемент - модальный, метод Valid перекрывается также для того, чтобы сообщить вызывающей программе о том, будет ли корректным в данном контексте завершение модального состояния командой Command или нет. Таким способом можно, например, перехватить выход из окна редактора в случае, если в нем остался несохраненный в файле текст. Разумеется, в подобной ситуации программа может не только выдать сообщение пользователю, но и предпринять необходимые действия для корректного завершения работы модального элемента. Метод Valid автоматически вызывается методом Execute модального элемента перед завершением работы.
Перекрытие методов Valid особенно полезно на этапе создания сложных программ: автоматический вызов проверки состояния используемого видимого элемента и выдача исчерпывающей диагностики могут дать неоценимую помощь программисту. Если Вы перекрываете методы Valid, Вы можете программировать, не заботясь о многочисленных проверках - методы сделают это за Вас.
 
Отладка программ
Если Вы пытались отлаживать какую-либо программу в Turbo Vision, Вы наверняка убедились, что трассировка (пошаговое прослеживание логики работы) таких программ весьма неэффективна. Вызвано это двумя обстоятельствами. Во-первых, значительная часть библиотеки Turbo Vision скрыта от Вас: библиотека поставляется в TPU-файлах, прослеживание работы которых невозможно. Во-вторых, в Turbo Vision используется принцип отделения логики создания видимых элементов от логики обработки связанных с ними событий: как только видимый элемент активизируется вызовом Execute, начинает работать его метод HandleEvent, который может породить целую цепочку непрослеживаемых трассировкой действий программы.
Ключом к решению проблемы отладки программ в Turbo Vision является расстановка точек контроля в наследуемых методах HandleEvent. Если программа не хочет открывать диалоговое окно или не реагирует на нажимаемую кнопку, следует прежде всего убедиться в том, что Ваши действия действительно порождают нужное событие.
Может случиться, что установленная контрольная точка не будет реагировать вообще или, наоборот, будет активизироваться слишком часто. Если точка не активизируется, это означает, что Ваш обработчик событий просто «не видит» событие. В этом случае необходимо убедиться в том, что поле EventMask видимого объекта содержит маску, позволяющую ему реагировать на событие нужного вида. Другой причиной «исчезновения» события может быть его перехват (и обработка) другим видимым элементом. Это может быть вызвано различными обстоятельствами. Например, Вы могли ошибочно связать две разные команды с одной константой или используете команду, которую использует также другой видимый элемент. Кроме того, обычно в наследуемых методах HandleEvent вызывается обработчик событий объекта-родителя, который может «украсть» событие у Вашего обработчика. В таких ситуациях бывает достаточно сделать вызов родительского метода после того, как событие будет обработано Вами.
Если контрольная точка активизируется слишком часто, значит Вы установили ее неправильно. Например, если Вы установили эту точку внутри метода TGroup.Execute, точка будет непрерывно активизироваться, т.к. значительная часть времени работы программы тратится на ожидание события. Если Вам все-таки требуется установить контрольную точку именно в этом месте, сделайте ее условной, чтобы она не реагировала на пустые или ненужные события.
Иногда запущенная программа «зависает», т.е. перестает реагировать на любые действия пользователя. Такие ошибки отлаживать труднее всего. Если программа «зависла», попытайтесь прежде всего локализовать то место, в котором это происходит. Для этого обычно используется расстановка контрольных точек в подозрительных местах программы. Следует помнить, что в Turbo Vision «зависания» связаны в основном с тремя видами ошибок:

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

Uses Objects,Views; 
var
G1, G2: PGroup;
R: TRect; 
begin
R.Assign(10,5,70,20) ;
Gl := New(PGroup, Init(R));
R.Grow(-10, -3) ;
G2 := New(PGroup, Init(R));
G1.Insert(G2);
Dispose(G1, Done);
Dispose(G2, Done) {Здесь программа "зависнет"!} 
end.
Заметим, что перестановка операторов Dispose местами приводит к корректному варианту, т.к. метод G1.Done умеет контролировать освобождение своего подэлемента G2 и не освобождает его вторично. Во всех случаях оператор Dispose (G2, Done) излишен: освобождение группы вызывает автоматическое освобождение всех ее подэ-лементов.
Поскольку динамическая память используется в Turbo Vision очень интенсивно, полезно предусмотреть в отладочном варианте программы визуализацию ее размера. Для этого можно использовать такой объект THeapView:
Unit HeapView;
Interface
Uses Dialogs,Objects;
type
PHeapView = THeapView;
THeapView = object(TStaticText) 
Constructor Init(var R: TRect); 
Procedure Update;
end;
Implementation 
Constructor THeapView.Init; 
var
S: String; 
begin
Str(MemAvail,S);
Inherited lnit(R,#3+S) 
end;
Procedure THeapView.Update; 
var
S: String; 
begin
Str(MemAvail,S);
DisposeStr(Text);
Text := NewStr(#3+S);
Draw 
end; 
end.
Например, в следующей программе показан способ включения контрольного окна, создаваемого в этом объекте, в верхний правый угол экрана:
Uses Objects,Views,App, HeapView; 
var
H: PHeapView;{Окно для MemAvail}
W: PWindow; 
G: PGroup; 
R: TRect; 
P: TApplication;{Стандартная программа} 
begin 
P.Init;
R.Assign(70,0,80,1);{Верхний правый угол}
New(H, Init(R));{Создаем окно контроля} 
P.Insert(H);{Помещаем его на экран}
ReadLn; {Пауза - показываем начальный размер кучи}
R.Assign(10,5,70,20);
W := New(PWindow,Init(R,'',0)); {Создаем окно}
R.Assign(5,3,55,12);
G := New(PGroup, Init(R));
W.Insert(G); {Вставляем в окно группу}
DeskTop.Insert(W); {Выводим на экран}
Н.Update; {Обновляем окно контроля}
ReadLn; {Пауза - размер кучи перед освобождением}
Dispose(W, Done); {Освобождаем окно и группу}
НА.Update; {Обновляем окно контроля} 
ReadLn; {Пауза - размер после освобождения} 
Р.Done 
end.
Для получения текущего значения общего размера кучи используется вызов метода THeapView.Update в нужных местах программы. Вы можете автоматизировать обновление окна контроля, если включите вызов Update в перекрываемый метод TProgramIdle. В следующем варианте показан способ отображения MemAvail в фоновом режиме. Кроме того, в программе иллюстрируется возможное использование функции MessageBox.
{$Х+} {Используется расширенный синтаксис вызова функции MessageBox} 
Uses Objects,Views,App,HeapView,MsgBox; 
type
MyApp = object (TApplication) 
Procedure Idle; Virtual;
end; 
var
H: PHeapView; 
Procedure MyApp.Idle; 
begin
H^.Update 
end; 
var
W: PWindow;
G: PGroup;
R: TRect;
P: MyApp; 
begin
P.Init;
R.Assign(70,0,80,1);
New(H,Init(R));
P.Insert(H);
MessageBox(#3'Размер кучи до размещения',NIL,0);
R.Assign(10,5,70,20) ;
W := New(PWindow, Init(R,'',0));
R.Assign(5,3,55,12) ;
G := New(PGroup, Init(R));
WA.lnsert(G);
DeskTop.Insert(W);
MessageBox(#3'Размер кучи после размещения', NIL,0);
Dispose(W, Done);
MessageBox(#3'Размер кучи после освобождения', NIL,0);
Р.Done 
end.
Константа #3 вставляется в начало строки сообщения в том случае, когда требуется центрировать эту строку (расположить ее симметрично относительно границ окна сообщения).
Использование оверлея
Модули Turbo Vision разработаны с учетом возможного использования их в оверлейных программах. Все они могут быть оверлейными за исключением модуля Drivers, который содержит процедуры обработки прерываний и другой системный интерфейс низкого уровня.
При разработке оверлейных программ старайтесь спроектировать логические последовательности вызовов тех или иных модулей так, чтобы по возможности уменьшить свопинг (динамический обмен оверлеев). Поскольку программы Turbo Vision рассчитаны на интенсивное использование диалога с пользователем, размещайте (если это возможно) все процедуры, связанные с некоторой точкой диалога, в том же модуле, в котором создается и исполняется соответствующий модальный элемент. Например, используемые в программе диалоговые окна, как правило, порождаются от TDialog, а диалоговые элементы этих окон - от TInputLine и TListViewer. Если Вы поместите все три порожденных типа в один модуль, Ваша программа будет исполняться быстрее, так как взаимосвязанные вызовы объектов не будут приводить к свопингу оверлеев.
Заметим, что размеры всех основных оверлейных модулей - Арр, Objects, Views, Menus приблизительно одинаковы и составляют около 50 Кбайт. С учетом Ваших собственных объектов, обеспечивающих интенсивное взаимодействие с пользователем и порожденных от TWindow или TDialog, типичный размер оверлейного буфера составит не менее 64 КБайт. Если Вы хотите минимизировать потери времени на свопинг и в то же время создать достаточно компактную программу, Вам придется поэкспериментировать с размером оверлейного буфера и/или испытательной зоны. Вы можете также возложить на пользователя Вашей программы ответственность за выбор размера оверлейного буфера, предусмотрев соответствующий параметр в строке вызова программы.
В следующем примере показан возможный способ инициации оверлея.
{$F+,0+,S-}
{$М 8192,65536,655360}
Uses Overlay, Drivers, Memory, Objects, Views, Menus, 
Dialogs,istList, StdDlg, App; 
{Объявляем оверлейные модули:} 
{$0 App } 
{$O Dialogs } 
{$0 HistList } 
{$0 Memory } 
{$0 Menus } 
{$0 Objects } 
{$0 StdDlg } 
{$O Views } 
const
OvrBufDisk=96*1024;{Размер буфера без EMS-памяти}
OvrBufEMS=72*1024;{Размер буфера при использовании EMS-памяти}
type
ТМуАрр = object (TApplication) 
Constructor Init; Destructor Done; Virtual;
.....
end; {TMyApp}
Procedure InitOverlays; 
var
FileName: String [79] ; 
begin
FileName := ParamStr(0); 
Ovrlnit(FileName) ; 
if OvrResult <> 0 then 
begin
PrintStr('Невозможно открыть оверлейный файл ', FileName); 
Halt; 
end;
OvrinitEMS; 
if OvrResult = 0 then OvrSetBuf(OvrBufEMS) 
else 
begin
OvrSetBuf(OvrBufDisk) ; 
OvrSetRetry(OvrBufDisk div 2); 
end
end; {InitOverlays} 
Constructor TMyApp.Init; 
begin

InitOverlays; 
TApplication.Init;
.....
.....
end; {TMyApp.Init} 
Destructor TMyApp.Done; 
begin
.....
.....
TApplication.Done; 
end; {TMyApp.Done} 
var
MyApp: TMyApp; 
begin
MyApp.Init;
MyApp.Run;
MyApp. Done; 
end.
В этой программе используется механизм размещения оверлеев в исполняемом EXE-файле. Для этого после компиляции программы используйте команду ДОС
copy/b MyProg.exe+MyProg.ovr MyProg.exe
Чтобы определить маршрут поиска EXE-файла, в процедуре InitOverlays проверяется параметр вызова с номером 0. Заметим, что в этот параметр автоматически помещается маршрут доступа к загруженной программе только в том случае, когда Ваш ПК работает под управлением MS-DOS версии 3.0 и выше.
Обратите внимание: размер оверлейного буфера можно сделать меньшим, если программа обнаружит доступную EMS-память, ведь в этом случае потери времени на свопинг будут минимальными. Разумеется, инициация оверлея осуществляется до обращения к TApplication.Init, т.к. модуль Арр, в котором находится этот метод, сделан в программе оверлейным.
 
Порядок вызова наследуемого метода
Большая часть объектов Turbo Vision спроектирована в расчете на их дальнейшее перекрытие в прикладных программах. Типичным примером такого рода объектов является TView, метод Draw которого создает на экране пустой прямоугольник и, следовательно, не может отображать никакой полезной информации. Поскольку все видимые элементы порождены от TView, Вам необходимо перекрыть метод Draw в собственном объекте-потомке. Более того, поскольку TView.Draw не делает никакой полезной работы, его не нужно вызывать в перекрытом методе. Однако полностью перекрываемые методы, подобные TView.Draw, скорее исключение из общего правила. Обычно в перекрытом методе вызывается соответствующий метод, наследуемый от родителя, т.к. в нем реализуются некоторые необходимые для потомка действия. В такого рода ситуациях важна последовательность вызова наследуемого метода: вызывать ли его до реализации специфичных действий или после? Ниже приводятся практические рекомендации на этот счет.
 
Конструктор
Вызывайте наследуемый Метод до реализации дополнительных действий:
Procedure MyObject.Init(.....); 
begin
{Вызов наследуемого конструктора Init} 
{Реализация дополнительных действий} 
end;
Такая последовательность необходима по той простой причине, что вызов наследуемого конструктора приводит к обнулению всех дополнительных полей объекта MyObject. Если, например, Вы используете следующий фрагмент программы:
type
MyObject = object (TWindow) 
Value: Word; 
Ok : Boolean; 
Constructor Init(var Bounds: TRect; ATitle: TTitleStr;
AValue: Word; AOk: Boolean); 
end;
Constructor MyObject.Init; 
begin
Inherited Init(Bounds, ATitle, wnNoNumber); 
Value := 16; 
Ok := True; 
end;
то дополнительные поля Value и Ok получат нужные значения 16 и True. Однако, если обращение TWindow.Init (Bounds, ATitle, wnNoNumber); поставить после оператора Ok := True, в них будут помещены значения 0 и False. Из этого правила существует одно исключение, связанное с загрузкой коллекции из потока конструктором Load. Дело в том, что в наследуемом методе TCollection.Load реализуется следующий цикл:
Constructor TCollection.Load (var S: TStream); 
begin
.....
for I := 0 to Count - 1 do
AtPut(I, GetItem(S)); 
end;
Если элементами коллекции являются произвольные наборы двоичных данных (не объекты), Вам потребуется перед чтением очередного элемента сначала получить из потока его длину. Следующий пример иллюстрирует сказанное.
type
PDataCollection = ATDataCollection; 
TDataCollection = object (TStringCollection)
ItemSize: Word;
Constructor Load(var S: TStream);
Function GetItem(var S: TStream): Pointer; Virtual;
.....
end;
Constructor TDataCollection.Load(var S: TStream); 
begin
S.Read(ItemSize, SizeOf(ItemSize));
Inherited Load(S); 
end;
Function TDataCollection.GetItem(var S: TStream): Pointer; 
var
Item: Pointer; 
begin
GetMem(Item, ItemSize);
S.Read(Item, ItemSize);
GetItem := Item; 
end;
В этом примере конструктор Load сначала загружает из потока поле ItemSize, содержащее длину читаемого элемента. Затем вызывается конструктор TCollection.Load, в котором осуществляется вызов GetItem. Новый GetItem использует поле ItemSize, чтобы определить размер читаемых данных, и резервирует нужный буфер в динамической памяти. Разумеется, запись полиморфных коллекций в поток должна происходить в том же порядке, т.е. сначала записывается длина очередного элемента, а уже потом - его данные.
 
Деструктор
Вызывайте наследуемый метод после реализации дополнительных действий:
Procedure MyObject.Done; 
begin
{Реализация дополнительных действий} 
{Вызов наследуемого деструктора Done} 
end;
Работа деструктора проходит в обратном порядке по отношению к конструктору. Вначале Вы должны освободить всю дополнительно распределенную динамическую память, а уже затем вызвать наследуемый деструктор, чтобы уничтожить весь объект.
 
Другие методы
Порядок вызова наследуемого метода зависит от конкретного алгоритма. В большинстве случаев наследуемый метод вызывается первым, но могут использоваться и другие последовательности. Особое значение имеет вызов наследуемого обработчика событий HandleEvent. В самом общем виде структура нового обработчика будет такой:
Procedure MyObject.HandleEvent(var Event: TEvent); 
begin
{Изменение наследуемых свойств} 
{Вызов наследуемого обработчика} 
{Добавление новых свойств} 
end;
Таким образом, вначале Вы должны запрограммировать те действия, которые изменяют стандартное поведение перекрытого обработчика, затем вызвать его и, наконец, осуществить новую обработку событий. Разумеется, любая из этих трех частей может отсутствовать. Например, стандартный обработчик TDialog.HandleEvent лишь расширяет свойства наследуемого метода TWindow.HandleEvent, добавляя в него обработку событий от клавиатуры и событий-команд:
Procedure TDialog.HandleEvent(var Event: TEvent); 
begin
Inherited HandleEvent(Event);
case Event.what of
evKeyDown:
.....
evCommand:
.....
end ; 
end;
Этот обработчик перехватывает все события от клавиатуры и мыши, в том числе и нажатие на клавишу Tab. Если Вы хотите обработать событие от клавиши Tab особым способом, Вы должны перехватить это событие до вызова стандартного обработчика. Например:
Procedure TNoTabsDialog.HandleEvent(var Event: TEvent);
begin
if (Event.What = evKeyDown) then 
if (Event.KeyCode = kbTab) or
(Event.KeyCode = kbShiftTab) then 
ClearEvent(Event); Inherited HandleEvent(Event); 
end;
 
Примеры программных реализаций
В этом разделе приводятся примеры программных реализаций некоторых типичных задач, решаемых с помощью Turbo Vision. Эти примеры не имеют сколько-нибудь серьезного прикладного назначения, они просто иллюстрируют возможные приемы программирования. Поскольку большинство видимых объектов используется в тесной взаимосвязи, примеры иллюстрируют программирование сразу нескольких объектов.
 
Строка статуса
В следующей программе создается строка статуса, содержание которой зависит от установленного контекста подсказки (определяется значением поля TProgram.HelpCtx). В зависимости от действий пользователя эта строка будет содержать текст
Esc Выход F1 Сменить контекст на 1 
Однажды в студеную, зимнюю пору
либо
ESC Выход F2 Сменить контекст на 0 
Я из лесу вышел. Был сильный мороз...
Переключение строки осуществляется клавишами F1 и F2, для выхода из программы используется клавиша Esc.
Uses Objects,App,Menus,Drivers,Views; 
type
PMyStatusLine = TMyStatusLine; 
TMyStatusLine = object (TStatusLine)
Function Hint(Cntx: Word): String; Virtual; 
end; 
MyApp = object (TApplication)
StatLine: PMyStatusLine; 
Constructor Init;
Procedure InitStatusLine; Virtual; 
Procedure HandleEvent(var Event: Tevent); Virtual; 
end; 
const
cmCntxl =200; 
cmCntx2 = 201;
{-----------------}
Constructor MyApp.Init ; 
begin
Inherited Init;
Insert (StatLine) {Использовать нестандартную строку статуса} 
end {MyApp .Init} ;
Procedure MyApp. Ini tstatusLine ;
{Инициация нестандартного поля MyApp. StatLine}
var
R: Trect; 
begin
GetExtent (R) ;
R.A.Y := pred(R.B.Y) ;
StatLine := New(PMyStatusLine, Init(R,
NewStatusDef (0, 0, {Первый вариант строки}
NewStatusKey ( ' ~Esc~ Выход1 , kbEsc, cmQuit,
NewStatusKey (' ~F1~ Сменить контекст на 1', kbF1 , cmCntxl , NIL) ) ,
NewStatusDef (1, 1, {Второй вариант строки}
NewStatusKey (' ~Esc~ Выход ', kbEsc, cmQuit,
NewStatusKey (' ~F2~ Сменить контекст на 0 ' , kbF2 , cmCntx2 , NIL)) ,
NIL) ) ) ) ; end {MyApp. Ini tstatusLine} ;
{--------------------}
Procedure MyApp. HandleEvent;
{Переключение контекста и обновление строки статуса} 
begin
Inherited HandleEvent (Event) ; 
case Event . Command of 
cmCntxl: HelpCtx := 1; 
cmCntx2: HelpCtx := 0; 
else
ClearEvent (Event) ; 
end;
if Event. What <> evNothing then 
begin
StatLine. Update; 
ClearEvent (Event) 
end 
end {MyApp . HandleEvent } ;
{---------------------}
Function TMyStatusLine. Hint (Cntx: Word):String;
{Переключение поля подсказки}
const
Prompt: array [0..1] of String =(
'Однажды в студеную, зимнюю пору',
'Я из лесу вышел. Был сильный мороз...');
begin
Hint : = Prompt [Cntx] 
end {TMyStatusLine.Hint} ;
{---------------------}
var
P : MyApp ; 
begin
P.Init;
P . Run ;
P . Done 
end .

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика