WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!

Pages:     | 1 | 2 || 4 | 5 |   ...   | 10 |

«Петр Дарахвелидзе Евгений Марков Санкт-Петербург «БХВ-Петербург» 2003 УДК 681.3.06 ББК 32.973.26-018.2 Д20 П. Г., Марков Е. П. ...»

-- [ Страница 3 ] --

Увеличивает размер памяти, отведенной под список Возвращает первый указатель function First: Pointer;

из списка Возвращает индекс указателя, function Pointer): Integer;

заданного параметром Вставляет новый элемент procedure Integer;

Item в позицию Index Item: Pointer);

function Last: Pointer;

Возвращает последний указатель в списке Перемещает элемент списка procedure на новое место Integer);

166 Часть II. Интерфейс и логика приложения Таблица 7.2 (окончание) Объявление Описание procedure Pack;

Удаляет из списка все пустые (Nil) указатели function Remove (Item: Pointer): Integer;

Удаляет из списка указатель Item type = function Сортирует элементы списка Item2: Pointer): Integer;

procedure TListSortCompare);

Пример использования списка указателей Рассмотрим использование списков указателей на примере приложения DemoList. При щелчке мышью на форме приложения отображается точка, которой присваивается порядковый номер. Одновременно координаты и номер точки записываются в соответствующие свойства создаваемого эк земпляра класса Указатель на этот объект передается в новый элемент списка pixList.

В результате после очистки формы всю последовательность точек можно восстановить, использовав указатели на объекты точек из списка.

Список точек можно отсортировать по координате X в порядке возрастания.

j Листинг 7.1. Модуль главной формы проекта DemoList unit Main;

interface uses Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;

type = ListBtn: TBitBtn;

ClearBtn: TBitBtn;

DelBtn: TBitBtn;

SortBtn: TBitBtn;

procedure TObject);

procedure TObject;

var Action: TCloseAction);

Глава 7. Списки и коллекции procedure TObject;

Button: TMouseButton;

Shift: X, Y: Integer);

procedure TObject);

procedure TObject);

procedure procedure TObject);

private PixList: TList;

Integer;

public { Public declarations } end;

TMyPixel FX: Integer;

FY: Integer;

FText: Integer;

constructor Y, Integer);

procedure SetPixel;

end;

var MainForm: TMainForm;

implementation *.DFM} const PixColor = var CurPixel: TMyPixel;

constructor Y, Num: Integer);

begin inherited Create;

FX := X;

FY := Y;

FText := Num;

SetPixel;

end;

procedure begin FY), FY)]);

+ 1, FY + 1, end;

Часть II. Интерфейс и логика приложения function Pointer): Integer;

var Pix2: TMyPixel;

begin Pixl := Iteml;

Pix2 := Item2;

Result := - Pix2.FX;

end;

procedure begin PixList := := 1;

{Счетчик := PixColor;

{Цвет точки} := := Color;

(Цвет фона текста равен цвету формы} end;

procedure TObject;

var Action: TCloseAction);

begin PixList.Free;

end;

procedure TObject;

Button: TMouseButton;

Shift: TShiftState;

X, Y: Integer);

begin Y, PixNum));

end;

procedure TObject);

var i: Integer;

begin with PixList do for i := 0 to Count — 1 do begin CurPixel := [i];

CurPixel.SetPixel;

end;

end;

procedure begin 0, Width, Height));

end;

Глава 7. Списки и procedure TObject);

begin 1;

end;

procedure TObject);

var i: Integer;

begin with PixList do for i := 0 to Count — 1 do := i + 1;

end;

end.

Класс TMyPixel обеспечивает хранение координат точки и ее порядковый номер в серии. Эти параметры передаются в конструктор класса. Метод обеспечивает отрисовку точки на канве формы (см. гл. 10).

Экземпляр класса создается для каждой новой точки при щелчке кнопкой мыши в методе-обработчике FormMouseDown. Здесь же указатель на новый объект сохраняется в создаваемом при помощи метода Add элементе списка PixList. Таким образом, программа "запоминает" расположение и порядок следования точек.

Метод-обработчик обеспечивает отображение точек. Для этого в цикле текущий указатель списка передается в переменную объектного ти па т. е. в этой переменной по очереди "побывают" все созданные объекты, указатели на которые хранятся в списке.

Это сделано для того, чтобы получить доступ к свойствам объектов (непо средственно через указатель этого сделать нельзя). Второй способ приведе ния типа рассмотрен в методе-обработчике Перед вторичным отображением точек необходимо очистить поверхность Эту ВЫПОЛНЯеТ ClearBtnClick.

Список точек можно отсортировать по координате X в порядке возрастания.

Для этого в методе-обработчике SortBtnCiick вызывается метод списка PixList. В параметре метода (переменная процедурного типа) передается функция PixCompare, которая обеспечивает инкапсулированный в методе Sort механизм перебора элементов списка алгоритмом принятия решения о старшинстве двух соседних элементов.

Если функция возвращает положительное число, то элемент больше элемента Если результат отрицательный, то itemi меньше, чем Если элементы равны, функция должна возвращать ноль.

Часть II. Интерфейс и логика приложения В нашем случае сравнивались координаты X двух точек. В результате такой сортировки по возрастанию объекты оказались расположены так, что пер вый элемент списка указывает на объект с минимальной координатой X, а последний — на объект с максимальной координатой X.

После сортировки осталось заново пронумеровать все точки. Это делает цикл в Обратите внимание на применен ный в этом случае способ приведения типа, обеспечивающий обращение к свойствам экземпляров класса TMyPixei.

Метод-обработчик DeiBtnciick обеспечивает полную очистку списка Коллекции Коллекция представляет собой разновидность списка указателей, оптимизи рованную для работы с объектами определенного вида. Сама коллекция ин капсулирована в классе Элемент коллекции должен быть эк земпляром класса, унаследованного от класса Это облегча ет программирование и позволяет обращаться к свойствам и методам объектов напрямую.

Коллекции объектов широко используются в компонентах VCL. Например, панели компонента (см. гл. 5) объединены в коллекцию. Класс объединяющий панели, является наследником класса А отдельная панель — экземпляром класса TCooiBar, происходящего от класса TCoiiectionitem.

Поэтому знание свойств и методов классов коллекции позволит успешно использовать их при работе со многими компонентами (TDBGrid, TListview, TStatusBar, TCooiBar И Т. Д.).

Для работы с коллекцией, независимо от инкапсулирующего ее компонен та, применяется специализированный Редактор коллекции (рис. 7.1), набор Editing 1 • 7.1. Редактор коллекции Глава 7. Списки и коллекции элементов управления которого может немного изменяться для разных ком понентов.

Список Редактора объединяет элементы коллекции. При выборе одной строки из списка свойства объекта коллекции становятся доступны в Ин спекторе объектов. В список можно добавлять новые элементы и удалять существующие, а также менять их взаимное положение.

Примеры использования коллекций представлены при описании соответст вующих компонентов.

Класс TCollection Класс TCollection является оболочкой коллекции, обеспечивая разработчи ка набором свойств и методов для управления ею (табл. 7.3).

Сама коллекция содержится в свойстве property Integer]: TCollectionltem;

Полное объявление свойства в классе выглядит следующим образом:

property Integer]: TCollectionltem read write Setltem;

Методы и обращаются к внутреннему TList;

Именно оно хранит коллекцию объектов во время выполнения. Отсюда следует, что коллекция представляет собой список указателей на экземпля ры класса TCollectionltem или его наследника. Класс TCollection обеспечи вает удобство использования элементов списка.

Таблица 7.3. Свойства и методы класса TCollection Объявление Описание Возвращает число элементов property Count: Integer;

коллекции type = class of Возвращает класс-наследник TCollectionltem, экземпляры которого собраны в коллекции property TcollectionltemClass;

property Integer]: Коллекция экземпляров класса TCollectionltem;

Добавляет к коллекции новый function Add: TCollectionltem;

экземпляр класса Копирует коллекцию из объекта procedure TPersistent);

override;

Source в данный объект Часть II. и логика приложения Таблица 7.3 (окончание) Объявление Описание procedure virtual;

Отменяет перерисовку коллекции.

Используется при внесении изменений в коллекцию procedure Clear;

Удаляет из коллекции все элементы procedure EndUpdate;

virtual;

Отменяет действие метода BeginUpdate function Integer): Возвращает объект коллекции TCoiiectionitem;

с номером function string;

override;

Возвращает имя класса коллекции во время выполнения, если кол лекция не имеет владельца. Иначе возвращает название свойства класса, владеющего коллекцией function Integer): Вставляет в коллекцию новый объект на место с номером Класс TCoiiectionitem Класс TCoiiectionitem инкапсулирует основные свойства и методы элемента коллекции (табл. 7.4). Свойства класса обеспечивают хранение информации о расположении элемента в коллекции.

Таблица 7.4. Свойства и методы класса TCoiiectionitem Объявление Описание property Col l ecti on: Tcol l ecti on;

Содержит экземпляр класса коллекции, которой принадлежит данный элемент property DisplayName: string;

Содержит имя элемента, которое пред ставляет его в Редакторе коллекции Содержит уникальный номер элемента property ID: Integer;

в коллекции, который не может изме няться property Index: Integer;

Содержит порядковый номер элемента в коллекции. Он соответствует положению элемента в списке и может изменяться Глава 7. Списки и коллекции Резюме Списки, объединяющие элементы различных типов, играют важную роль при создании программной логики приложения. В Delphi используются три основных вида списков.

Классы TStrings и TstringList обеспечивают применение списков строк.

• Класс инкапсулирует список указателей.

Классы и позволяют применять в компонен тах и программном коде коллекции группы однородных объектов.

В среде Delphi вы можете найти еще много полезных классов общего при менения. В модуле есть класс TBits, обеспечивающий побит ное чтение и запись информации. В модуле CONTNRS.PAS есть классы TStack И TQueue (стек И очередь), также ПОТОМКИ TList — TClassList, TComponentList и т. д. Они помогут вам решать типовые задачи быстро и без "изобретения велосипеда".

ГЛАВА Действия (Actions) и связанные с ними компоненты С давних пор повелось, что стандарты на пользовательский интерфейс Windows-приложений Microsoft задает "явочным порядком". Первая громкая история на эту тему связана с появлением в 1994 г. Excel 2.0 for Windows, когда разработчики из Редмонда впервые применили интерфейс со многими документами (впоследствии широко известный как MDI) и даже не подума ли задокументировать и опубликовать его. После справедливого возмуще ния широких кругов программистской общественности Microsoft исправил ся и теперь новые возможности интерфейса публикуются если не до выхода продукта, то, по крайней мере, ненамного позже. Вспомним, с Internet Explorer появилась панель CoolBar и кнопки, картинки, которые подсвечи вались при прохождении над ними курсора мыши. Большинство же нови нок связано с флагманским продуктом Microsoft — Office. Одна из них — весьма, кстати, полезная — это система настраиваемых пользователем меню и панелей инструментов.

В Delphi 7 разработчику предоставляется доступ к аналогичным возмож ностям. Правда, для работы с ними придется забыть "старый" интерфейс — компоненты TToolBar — и полностью переучиться. Теперь "про двинутый" СОСТОИТ ИЗ НОВЫХ TActionManager, TActionToolBar И примкнувшего К НИМ (страница Палитры компонентов Additional). Для читателя уже знакомого с действиями (Actions) названия этих компонентов покажутся знакомыми.

Действительно, это — гибриды прежнего TActionList и панелей инструмен тов, плюс новые свойства и методы, плюс большое число полезных стан дартных действий.

Данная глава посвящена рассмотрению принципов их использования. Сна чала поговорим о понятии действия (Action) и рассмотрим компонент TActionList, который является кроссплатформенным (работает как в Delphi 7, так и в Далее рассмотрим обширный набор стандартных действий.

Глава 8. Действия (Actions) и связанные с ними компоненты И в заключение читатель узнает о Windows-потомке TActionList под названием и о связанных с ним компонентах.

Действия. Компонент TActionList Пользовательский интерфейс современных приложений весьма многообра зен, и зачастую один и тот же результат можно получить разными способа ми — щелчком на кнопке на панели инструментов, выбором пункта меню, нажатием комбинации клавиш и т. п. Можно решить проблему "в лоб" и повторить один и тот же код два, три раза и т. д. Недостатки такого подхо да, очевидно, не обязательно комментировать. Можно воспользоваться Ин спектором объектов и назначить пункту меню тот же обработчик события, что и кнопке, благо событие имеет везде одинаковый синтаксис.

Этот способ неплох, но при большом количестве взаимных ссылок легко запутаться.

Наконец, современный способ — это воспользоваться компонентом TActionList и разом решить эти проблемы. Как следует из названия, это — централизованное хранилище, где воздействия со стороны пользователя связываются с реакциями на них.

Действием (Action) будем именовать операцию, которую пользователь хочет произвести, воздействуя на элементы интерфейса. Тот компонент, на кото рый он хочет воздействовать, называется целью действия (Action target).

Компонент, посредством которого действие инициировано (кнопка, пуню меню), — клиент действия (Action client). Таким образом, в иерархии клас сов Delphi действие TAction — это невизуальный компонент, который игра ет роль "черного ящика", получающего сигнал от одного или нескольких клиентов, выполняющих действия над одной (или разными) целями.

( Примечание Действия могут работать только будучи объединенными в список компонентов TActionList или Вне этих компонентов применение действий невозможно.

Спроектировав на бумаге пользовательский интерфейс, начните работу с помещения на форму компонента TActionList. Он находится в Палитре компонентов на первой странице Standard (вот видите какое ему уделяется внимание!). После этого следует запустить редактор списка действий двой ным щелчком мышью на компоненте или с помощью контекстного меню (рис. 8.1).

Теперь можно начинать добавление действий. Для программиста преду смотрен набор типовых, наиболее часто встречающихся действий, которые описаны в следующем разделе.

Часть II. Интерфейс и логика приложения Inspector ] Properties I vents j "False" | Caption Category (No (No Category) Checked False Edit ( True " (All Actions) :

„ Hint Imagelndex Name (None) 'Tag True All shown 8.1. Внешний вид редактора действий Рис. 8.2. Опубликованные свойства компонента TAct i onLi st объекта Помимо них можно вставить и обычное действие, которое получит имя Actioni. Итак, что же из себя представляет действие? Его опубликованные свойства показаны на рис. 8.2. Рассмотрим их по группам.

События, связанные с действиями Компонент TAction реагирует на три события: OnExecute, И OnHint.

Первое — и самое главное — должно быть как раз реакцией на данное дей ствие. Это событие возникает в момент нажатия кнопки, пункта меню — короче, при поступлении сигнала от клиента действия. Здесь — как прави ло—и пишется обработчик. Почему "как правило"? Потому что схема об работки сигнала 4-этапная:

1. Сначала вызывается обработчик события OnExecute списка действий TActionList:

property OnExecute:

TActionEvent = procedure (Action: TBasicAction;

var Handled: Boolean) of object;

Если обработчик этого события вами не предусмотрен, или в параметре Handled он вернул значение False, происходит генерация следующего со бытия — шаг 2.

2. Вызывается обработчик события onActionExecute глобального объекта Application (тип события тот же — TActionEvent). Если и оно не обрабо тало сигнал действия, переходим к следующему шагу.

Глава 8. Действия (Actions) и связанные с ними компоненты 3. Вызывается обработчик события onExecute самого действия (объекта ти па TAction или его потомка).

4. Если первые три шага не обработали ситуацию (вернули False), то, вероятно, это было связано с неправильной целью (Target) действия.

В качестве "последнего шанса" приложению посылается сообщение В этом случае происходит поиск другой цели для дан ного действия (об алгоритме поиска цели см. ниже).

Первые две возможности в этом списке используются относительно редко.

Тем не менее они полезны, если вам нужно глобально добавлять/уда лять/разрешать/запрещать действия.

Введение события является очень хорошей находкой, о нем напи шем подробно. И автор этих строк, и, возможно, вы потратили немало вре мени, чтобы в разрабатываемых программах элементы управления находи лись в актуальном состоянии. Если, скажем, вашей программой открыт первый файл, то нужно активировать ряд кнопок и пунктов меню (Save, Save as, Print и т. п.);

как только закрыт последний — отключить их. Если в буфере обмена есть что-то подходящее, необходимо активизировать пункт меню и кнопку Paste, если нет — отключить. В результате код, отслежи вающий это, у неопытных программистов "размазывается" по всему прило жению. А ведь можно поступить проще. Событие возника ет в моменты простоя приложения, т. е. тогда, когда оно не занято обработ кой сообщений (цикл содержится в методе объекта Application). Это гарантирует, что оно возникнет ДО ТОГО, как пользователь щелкнет мы шью и увидит выпадающие пункты меню;

поэтому можно успеть обновить их состояние. Пример использования события procedure TObject);

begin := end;

Примечание Перед вызовом события также происходит 4-этапная последователь ность действий, точно такая же, как при OnExecute.

Третье событие имеет такой тип:

THintEvent = procedure (var HintStr: string;

var CanShow: Boolean) of object;

Оно вызывается тогда, когда от элемента управления требуется показать подсказку, связанную с данным действием. В обработчике события можно указать, будет ли что-нибудь показываться (параметр CanShow) и, если да, то что именно (параметр Hintstr).

Часть II. Интерфейс и логика приложения Это были события, относящиеся к компоненту TAction. Сам компонент TActionList также имеет три события: OnExecute, И OnChange.

О первых двух мы уже сказали;

третье происходит в момент изменения списка (добавления или удаления действий).

Свойства, распространяемые на клиентов действия Если у нескольких кнопок или пунктов меню общий обработчик, разумно потребовать, чтобы у них были и другие общие свойства. Так оно и реали зовано в Delphi. В табл. 8.1 перечислены свойства, чье значение автомати чески распространяется на всех клиентов данного действия.

Таблица 8.1. Свойства компонента TAction, автоматически распространяемые на всех его клиентов Назначение property Caption: string;

Заголовок, связанный с действием property Hint: string;

Подсказка к действию property Enabled: Boolean;

Устанавливает, разрешено/запрещено ли действие property Checked: Boolean;

Устанавливает, отмечено ли действие property Integer;

Индекс группы, в которую входит дейст вие. Объекты TAction с одним значением этого свойства (причем большим нуля) имеют зависимое переключение. Если свойство Checked любого объекта из группы устанавливается в True, у осталь ных оно сбрасывается в False property boolean;

Установка в True автоматически меняет значение свойства Checked на противо положное после каждого выполнения действия property Integer;

Индекс картинки в общем наборе карти нок (набор указывается в свойствах роди тельского TActionList) property HelpType: THelpType;

Указывает на тип значения, связывающе го действие с разделом системы помощи property HelpContext: Если свойство HelpType установлено в htContext, это свойство содержит ID раздела системы помощи Глава 8. Действия (Actions) и связанные с ними компоненты Таблица (окончание) Назначение property string;

Если свойство HelpType установлено в то свойство содержит ключе вое слово (термин), по которому происхо дит открытие соответствующего раздела системы помощи Вы привыкли к программам с картинками в меню и на панелях инструмен тов? Действие также можно снабдить картинкой. Компонент TActionList связывается со списком картинок а действие TAction — с кон кретной картинкой через свойство Таким образом, все элементы управления, с действием, — кнопки и пункты меню — будут иметь одну и ту же картинку, как показано на рис. 8.3. Впрочем, это отно сится ко всем остальным свойствам из табл. 8.1.

,.

Undo Cut Paste ' 8.З. Меню и панель инструментов используют один список действий Прочие свойства Чтобы связать с действием комбинацию "горячих" клавиш, выберите одну из возможных комбинаций в редакторе свойства shortcut. Более того, в Delphi 7 существует возможность добавлять не одну, а множество комби наций "горячих" клавиш. Вторая и последующие должны быть заданы в свой стве secondaryshortcuts. Когда вызывается редактор этого свойства, пользо ватель видит обычный редактор набора строк. И вводить комбинации нуж но по принципу "как слышится, так и пишется": например +, ++<0> и т. п., по одной комбинации на каждой строке.

Для упорядочивания все действия разбиты на категории:

property Category: string;

Часть II. Интерфейс и логика приложения Это свойство содержит условное название категории, к которой относится действие, например, File, Edit, и т. п. Роль категории сводится к тому, чтобы объединить сходные действия при показе в ActionList или ActionManager. Названия категорий вы видите на панели меню на самом верхнем уровне.

Иногда программисту все-таки необходимо знать, какой конкретно кли ент — меню, кнопка — привел к выполнению действия. Узнать это можно, воспользовавшись значением свойства компонента TAction:

property Перед вызовом это свойство содержит указатель на клиента, ини циировавшего действие. После вызова значение свойства очищается.

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

Стандартные действия Те, кто собирается пропустить этот раздел, считая, что в нем описаны оче видные вещи, сильно ошибаются. Как раз в применении стандартных дей ствий разработчики Borland продвинулись очень сильно. Кто хочет в этом убедиться, может открыть пример WordPad, поставляемый с Delphi 7. Пол нофункциональный текстовый редактор, построенный полностью на стан дартных действиях, содержит всего две строчки кода.

Шаблоны и заготовки для типовых меню и кнопок появились еще в самой первой версии Delphi. Но в шестой версии действия действительно стали действиями. Это значит, что раньше заготовка содержала только подходя щий заголовок. Теперь они содержат в себе все субкомпоненты, весь про граммный код и делают всю необходимую работу сами.

Возьмем, например, действие Оно уже содержит внутри компо нент типа показывающий список открываемых файлов. Вместо ручного программирования процедуры задания имени файла теперь нужно написать обработчик события (если пользователь ввел в диалоге кнопку ОК) или (если отказался от открытия файла). Вот так выглядит весь программный код приложения WordPad:

procedure TObject);

begin Глава 8. Действия (Actions) и связанные с ними компоненты end;

procedure TObject);

begin end;

Standard Action Classes Available Action Classes:

(No Category) TEditCut TEditPaite • TEditllndo TEditDelete ]•• !•••• TRichEditBullets 8.4. Окно выбора стандартных действий С точки зрения программирования стандартное действие — это класс потомок TCustomAction. Классы действий описаны в трех модулях: более распространенные в stdActns, менее — в ExtActns, действия с наборами данных содержатся в DBActns. Наконец, два действия, работающие со спи сками, — TStaticListAction И TVirtualLitAction — В ОТДеЛЬНОМ Модуле ListActns.

Для выполнения ряда стандартных действий нужно определить понятие "цели" действия (Action Target). Под целью понимается компонент, в отно шении которого будет совершаться данное действие. Так, операции редак тирования могут выполняться, когда на форме активен текстовый элемент управления (TEdit, И Т. П.). У ДеЙСТВИЯ TBasicAction) есть три метода:

function TObject): Boolean;

virtual;

procedure virtual;

procedure TObject);

virtual;

182 Часть II. Интерфейс и логика приложения Метод проверяет, применимо ли действие к цели Target. Если да, то действие производится вызовом метода ExecuteTarget. Если нет, по иск подходящей цели продолжается.

Цель в Delphi 7 определяется по следующему правилу:

первым кандидатом является активный элемент управления на форме (СВОЙСТВО ActiveControl);

еСЛИ Нет ИЛИ ОН ЯВЛЯеТСЯ ПОДХОДЯЩИМ (меТОД HandiesTarget вернул значение False), целью становится текущая форма, получившая сигнал о действии;

если и она не подходит, происходит рекурсивный перебор всех компо нентов на форме в поисках первого подходящего.

В ряде случаев вы можете произвести действие над желаемым компонентом, вызвав метод ExecuteTarget и передав в него в качестве параметра этот ком понент.

( Примечание Стандартные действия редактирования, чьи имена начинаются с TEdit, и поис ка применимы только к потомкам компонента Стан дартные действия расширенного редактирования, имена которых начинаются с применимы только к потомкам TCustomRichEdit. Оконные стан дартные действия (упорядочивание, смена, закрытие дочерних окон;

имена на чинаются с применимы только к формам многодокументного интер фейса, чье свойство установлено в f (рис. 8.4).

Многие классы стандартных действий не требуют элемента управления — цели. Так устроены все действия, вызывающие стандартные диалоговые окна (выбор файла, цвета, шрифта, настройка принтера и т. п.). Чтобы отреаги ровать на такое действие, нужно написать обработчики следующих событий:

property BeforeExecute: TNotifyEvent;

property OnAccept: TNotifyEvent;

property OnCancel: TNotifyEvent;

Первое возникает до показа диалога, второе — после нажатия пользовате лем кнопки третье — после нажатия Cancel.

Примечание Поскольку диалоги входят в действия в качестве дочерних компонентов, вы мо жете реагировать и на все "дочерние" события, которые происходят в соответ ствующем диалоге OnCanClose, OnClose И Т. П.) Поместив на форму стандартные действия, вы заметите, что все они имеют предопределенное значение свойства Если так, то изображе Глава 8. Действия (Actions) и связанные с ними компоненты ние, на которое эти индексы указывают? Вы можете раздобыть его, открыв демонстрационный проект WordPad (папка Demos\ActionBands в поставке Delphi 7). Откройте редактор компонента и экспортируйте весь список в виде одного большого файла формата BMP.

Категория Edit В эту категорию входят компоненты, которые работают с редактируемыми элементами — потомками TCustomEdit. Это, к примеру, TEdit, TRichEdit, НОВЫЙ И Причем целью может являться не любой редактируемый элемент, а только тот, что имеет фокус ВВОДа. К категории ОТНОСЯТСЯ: TEditCopy, TEditPaste, TEditSelectAll, TEditDelete, Категория Search Действия поиска и замены тоже производятся только над потомками TCustomEdit. И это не прихоть разработчиков Borland: это сделано для ваше го же удобства. Раньше для поиска приходилось самому программировать события onFind и соответствующих диалогов, а сейчас требуемый код уже содержится внутри действий.

К Компонентам ЭТОЙ категории ОТНОСЯТСЯ: TSearchFind, TSearchFindNext, TSearchReplace.

Категория Help С помощью этих действий (табл. 8.2) вы можете вызвать справочную систе му вашего приложения.

Таблица 8.2. Стандартные действия категории Help Компонент Назначение THelpContents Показывает оглавление системы справки Показывает подсказку по использованию системы справки THelpContext Показывает справку по контексту активного элемента управ ления (причем он должен быть ненулевым) THelpTopicSearch Показывает окно поиска системы справки Категория File Эти действия скорее всего будут наиболее востребованы разработчиками.

И ОНИ ЯВЛЯЮТСЯ ДОВОЛЬНО ПРОСТЫМИ В ИСПОЛЬЗОВанИИ. TFileOpen, 184 Часть II. Интерфейс и логика приложения — это оболочки над соответствующими диало гами. О том, как работать с такими действиями, описано выше. Действие вообще не требует комментариев — оно просто завершает прило жение, закрывая его главную форму.

ОсобНЯКОМ СТОИТ ТОЛЬКО Категория Dialog Эта категория примыкает к предыдущей, в ней содержатся остальные пять ТИПОВЫХ ДеЙСТВИЙ-ДИаЛОГОВ: TPrintDlg, TColorSelect, TFontEdit (ИЗ МОДУЛЯ StdActns), TOpenPicture, TSavePicture (модуль ExtActns).

Категория Window Эти действия стоит включать в интерфейс, только если вы используете мно годокументный интерфейс (MDI). Названия компонентов говорят сами за себя:

Категория Tab Здесь всего два компонента — TNextTab и TPreviousTab. Если цель дейст вия — набор страниц они переключат его на следующую и предыдущую страницу соответственно.

Категория List В этой категории выделяются две группы действий. Первые пять из них (табл. 8.3) автоматизируют работу с выделенными элементами списков. Ос тавшиеся — TStaticListAction И TVirtualListAction — ОТДеЛЬ ного рассмотрения.

Таблица 8.3. Действия по работе с выделенными элементами спис Действие Назначение TListControlSelectAll Выделяет все элементы списка. Активно, только если у списка свойство MultiSelect установлено в значение True TListControlClearSelection Отменяет выделение элементов в списке TListControlDeleteSelection Удаляет выделенные элементы TListControlCopySelection Копирует выделенные элементы списка в список Destination Глава 8. Действия (Actions) и связанные с ними компоненты Таблица 8.3 (окончание) Действие Назначение Переносит выделенные элементы списка в спи сок Dest i nat i on Действия работают с компонентом TListBox, а в среде — еще и с (не путать с одноименным компонентом для Windows — он не годится для данной категории). Подходит также и В отличие от многих других действий члены этой категории могут явно связываться с нужным вам списком. Если задано значение свойства то все действия производятся над ним. Если нет, то выбирается активный список из числа имеющихся на форме.

ОсобнЯКОМ СТОЯТ ДеЙСТВИЯ — TStaticListAction И TVirtualListAction По замыслу разработчиков они являются централизованными хранилищами элементов для многих списков. Причем элементы списка могут храниться сразу с заданными картинками (т. е. свойствами и указателями на сопутствующие данные.

Дальнейшее просто — разработчик выбирает нужные компоненты TListBox, тсотЬоВох и т. п. и в их свойстве Action указывает на действие — хранили ще. Опубликовано свойство Action у компонента (впервые по явившегося в Delphi 6). У остальных потомков это свойство отно сится к группе видимости public, поэтому вы можете сделать присвоение при запуске приложения (в методе главной формы).

Если действие и компонент-список связаны, то должны происходить две вещи:

при изменении текущего элемента в любом из компонентов происходит синхронное изменение во всех остальных;

когда пользователь выбирает один из элементов списка, выполняется действие, связанное с этим списком, и вызывается метод-обработчик type = TCustomListAction;

Control: TControi) of object;

property TltemSelectedEvent;

Категория Internet Здесь всего три — типовых для пользователя Сети — действия.

Действие TBrowseURL открывает URL, заданный в одноименном свойстве.

ПОСКОЛЬКУ ЭТО ПРОИСХОДИТ ПОМОЩИ фуНКЦИИ Shel l Execut e, ДЛЯ смотра открывается браузер, зарегистрированный в системе по умолчанию.

186 Часть II. Интерфейс и логика приложения Действие запускает программу — почтового клиента для отправки письма (с помощью интерфейса MAPI). Текст письма вы можете задать в свойстве Text. Но! Ни получателя, ни тему, ни вложений задать нельзя — это придется делать вручную в почтовой программе. При желании пол ностью автоматизировать процесс отправки вам придется породить дочер ний ОТ TSendMaii, Где И ExecuteTarget.

Исходные тексты — в модуле ExtActns.

Наконец, самый сложный компонент Он позволяет загрузить содержимое с адреса URL и сохранить его на локальной машине под име нем Поскольку загрузка — процесс долгий, в то время, пока она происходит, периодически возникает событие property TDownloadProgressEvent;

= Progress, Cardinal;

StatusCode: StatusText:

var Cancel: Boolean) of object;

Параметры обработчика этого события следующие.

Progress и ProgressMax — текущее и максимальное значение показателя хода скачивания. Во-первых, не все HTTP-серверы правильно сообщают о размере ответа;

во-вторых, для некоторых типов файлов (например, HTML) эти параметры вычисляются не всегда верно (вы можете это ви деть в Internet Explorer);

в-третьих, из-за маршрутизации пакетов ожи дать ритмичного изменения параметра Progress не следует. Поэтому пользователю показывать соотношение Progress/ProgressMax.

Примечание Значение ProgressMax может быть равно нулю. В этом случае о ходе загрузки численно судить нельзя. Информацию несут другие параметры события.

CD StatusCode и StatusText — код, описывающий текущее состояние опера ции и соответствующий ему текст. Список возможных кодов содержит около 30 значений. Для тех, кто знает протокол HTTP и хочет разо браться в этом глубже, следует обратиться к описанию интерфейса в MSDN. Если же вам достаточно показать пользо вателю текст, то он содержится во втором параметре. По содержанию он представляет примерно то же, что вы видите при загрузке файлов с по мощью Internet Explorer.

cancel — этот параметр одинаков для всех долго продолжающихся опе раций. Установив его в значение True, вы можете прервать выполнение загрузки.

Глава 8. (Actions) и связанные с ними компоненты Категория Format Действия этой категории представляют собой расширенные операции ре дактирования для "продвинутого" редактора TRichEdit. Эти операции долж ны быть знакомы вам по программе WordPad из состава Windows. В крайнем случае откройте демонстрационный пример Delphi с тем же названием — там присутствуют действия настоящей категории и подавляющее боль шинство В СПИСКе Присутствуют TRichEditBold, TRichEditStrikeout (установка СТИЛЯ шрифта), абзацев), TRichEditAlignLeft, TRichEditAlignRight, (выравнивание текста).

Категория set Эти действия можно увидеть, например, в качестве кнопок на любом компоненте TDBNavigator: TDataSetFirst, TDataSetPrior, TDataSetNext, TDataSetLast, TDataSetDelete, TDataSetEdit, TDataSetPost, TDataSetRefresh. Читатель задаст вопрос: а как действие свя зывается с набором данных? Очень просто: через дополнительное (для дан ной категории) свойство DataSource. Если источник данных существует и связан с имеющимся набором данных (свойство DataSource.DataSet), то дей ствие выполняется над ним.

Категория Tools Здесь содержится один-единственный член: Будучи вызванным, это действие вызывает диалог настройки панелей действий, от носящихся к компоненту о котором, собственно, сейчас и пойдет речь.

Компонент TActionManager Если вы не думаете о переносе своего приложения в среду Linux, то имеют ся все основания воспользоваться потомком TActionList — компонентом TActionManager (далее в тексте — менеджер действий). Более современный и "продвинутый" он обеспечит вас многими дополнительными возможностя ми. Итак, обо всем по порядку.

Будучи применен сам по себе, компонент TActionManager ничем не отлича ется от предшественника. Отличия проявляются, если действия из этого компонента разместить на специальных панелях — TActionMainMenuBar (бу дем называть его панелью главного меню) и TActionTooiBar (далее — панель действий).

На первой странице редактора TActionManager (вызывается двойным щелч ком или командой Customize из контекстного меню;

показан на рис. 8.5) Часть II. Интерфейс и логика приложения как раз и содержится список всех панелей, связанных с данным менедже ром действий. Вы можете добавить новый или убрать компонент нажатием кнопок New и Delete соответственно. С компонен том так по понятным причинам поступить нельзя — ме ню полагается иметь одно.

Editing | T [Checkmark toggles visibility] Wit Apply caption options Jo all toolbars Drag to create Separators Close :

8.5. Первая страница редактора свойств компонента TActionManager Самый простой и рекомендованный Borland способ для связи действий с одной стороны и панелей меню и инструментов с другой — это перетас кивание (Drag-and-Drop). На второй странице редактора содержится список всех действий по категориям. И отдельное действие, и целую категорию можно брать и тащить мышью на нужную панель (рис. 8.6).

Когда вы перетаскиваете действие на панель, на нем появляется специаль ный компонент, похожий на пункт меню или кнопку. Его роль — служить клиентом данного действия. Поэтому, естественно, он сразу и автомати чески получает нужные Hint и прочие общие для всех клиентов свойства, о которых говорилось выше. Класс этого клиента — TActionciientitem;

будем называть их псевдокнопками или псевдоэлементами.

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

при перетаскивании всей категории на панель главного меню она появ ляется в виде пункта меню верхнего уровня и содержит при этом все свои дочерние действия;

при перетаскивании всей категории на панель действий создаются псев докнопки для всех дочерних действий в категории. Логично поступить по Глава 8. Действия (Actions) и связанные с ними компоненты принципу "одна категория — одна панель действий", это будет полезно для настройки интерфейса пользователем;

• если вы ошиблись при перетаскивании, не нажимайте кнопку Delete — при этом удалится не только псевдокнопка, но и само действие. Перетя ните ненужный псевдоэлемент за пределы действий, тогда он будет удален;

если вы уже перетянули категорию на панель главного меню, а потом решили добавить к ней действия, то вам придется убрать соответствую щий ей псевдоэлемент и перетянуть всю категорию заново. Либо вос пользоваться ручным способом, который описывается ниже.

I Editing Actions j (All Actions) fipen-,:

List (All Actions) the with a new add actions to drag and from Y, to Separators I;

;

i 8.6. Страница действий редактора свойств компонента TActionManager Изменение и настройка внешнего вида панелей Мы ПОДОШЛИ К СОВСеМ НОВОМУ СВОЙСТВУ — перь — как в Microsoft Office — возможно прятать редко используемые пункты меню. В самом деле, интерфейс программ подчас настолько сложен, что используют его на 100% минимальное количество пользователей. По этому элементы интерфейса, которые пользователь не задействовал в ка ком-то числе предыдущих запусков, автоматически прячутся.

Что и когда прятать, определяется свойством property Priority-Schedule: TStringList;

значение которого по умолчанию приведено в табл. 8.4. В левой колонке содержится общее количество запусков приложения, в течение которых 190 Часть II. Интерфейс и логика приложения пользователь применял данное действие;

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

Естественно, чем чаще пользователь обращался к действию, тем дольше оно удержится на виду. Впрочем, если у вас другие взгляды на интерфейс, вы можете изменить значение Таблица 8.4. Условия скрытия элементов панелей действий Количество запусков приложения Количество запусков приложения с обращением к действию после последнего обращения О, 1 2 3 4,5 9-13 14-24 Более Для подсчета величин, указанных в этой таблице, введены такие свойства:

у объекта TActionBars (дочерний объект есть СВОЙСТВО property Integer;

которое представляет собой глобальный счетчик запусков приложения;

у каждого объекта есть два свойства:

• property LastSession: Integer;

в этом свойстве хранится номер последнего запуска приложения, в течение которого использовался данный элемент (нумерация совпа дает С SessionCount);

• property Integer;

счетчик использования элемента.

Но для того, чтобы оперировать данными о количестве запусков, их надо где-то хранить. Организована система хранения следующим образом. У са мого менеджера действий есть свойство property Глава 8. Действия (Actions) и связанные с ними компоненты которое указывает на файл, содержащий все настройки панелей, связанных с данным менеджером. Он имеет формат двоичной формы и считывает ся/записывается при запуске и выходе из приложения. Впрочем, можно это И В ПОМОЩИ И SaveToFile.

Все эти величины меняются автоматически, и их описание приведено для понимания сути происходящего. Сбросить же счетчик статистики запусков можно следующим образом: на этапе разработки на странице Options редак тора свойств менеджера действий есть кнопка Reset Usage Count. На этапе выполнения точно такая кнопка есть в диалоге Помимо данных для подсчета запусков в этом файле хранится и вся прочая информация о настройках. Последний из не упоминавшихся нами компо нентов — диалог настройки TCustomizeDig. Он представляет собой точную копию редактора свойств но позволяет делать все операции с действиями в режиме выполнения. Вызвать его просто — вызовом метода А можно поступить еще проще — есть стандартное действие (категория Tools), которое и подразумевает вызов этого диалога.

Ручное редактирование коллекций панелей и действий Перетаскивание имеет много достоинств, однако оно не всегда удобно. По этому было бы странно, если бы не было предусмотрено другого способа.

Хоть он напрямую и не рекомендован в документации, но в ряде случаев более эффективен.

Рассмотрим работу с дочерними объектами менеджера действий, которые упакованы один в другой, как матрешки.

Итак, щелкнем на свойстве на форме и посмотрим на содер жимое Инспектора объектов. Внутри него мы обнаружим сразу две "мат решки" — свойство ActionBars содержит коллекцию ссылок на дочерние панели, свойство может содержать дополнительный спи сок действий, отданных "в управление" данному менеджеру — например, для централизованной установки общих свойств.

Щелкнем на свойстве ActionBars. Появится редактор панелей (рис. 8.7), а в Инспекторе объектов обратим внимание на следующее свойство объекта ActionBars:

property Customizable: Boolean;

Это свойство указывает, может ли коллекция редактироваться во время вы полнения.

В коллекции содержатся не сами панели, а их "заместители" — объекты типа которые на них указывают. Надпись на рисунке -> ActionTooIBar2" показывает, что первый элемент коллекции Часть II. Интерфейс и логика приложения связан с панелью Вы можете добавлять и удалять элементы этой коллекции, по мере необходимости связывая их через свойство ActionBar с уже существующей панелью.

Editing 2 -ActionBar 8.7. Редактор коллекции панелей компонента TActionManager Через Инспектор объектов вы можете изменять внешний вид объектов типа TActionBaritem и соответствующих им панелей.

property Color: TColor;

отвечает за фоновый цвет панели. Если вам изменения цвета недостаточно, в качестве фона выберите картинку property Background:

которая будет расположена на панели в соответствии со значением свойства property TBackgroundLayout;

= blStretch, blTile, blLeftBanner, blRightBanner);

Помимо внешнего вида можно разрешить/запретить перетаскивание пане лей и их дочерних элементов. Обратимся к свойству property TChangesAllowedSet;

= caMove, caDelete);

TChangesAllowedSet = set of TChangesAllowed;

Множество из трех возможных значений позволяет запретить те или иные нежелательные изменения для дочерних элементов панели. Если в него не включен режим caDelete, то элемент нельзя убирать (перетаскивать) с пане ли. Если нет режима caMove — нельзя передвигать внутри панели. Наконец, отсутствие режима caModify означает запрет на изменение визуальных свойств (заголовка и т. п.).

Внутри коллекции TActionBaritem спрятаны еще две "матрешки" — свойства items и Оба свойства представляют из себя коллекции объек Глава 8. Действия (Actions) и связанные с ними компоненты тов, указывающих на действия (класс коллекции TActionCiients, класс эле мента коллекции Первое свойство указывает непосред ственно на дочерние действия, второе — на действия, которые будут пока заны в качестве всплывающего меню при нажатии правой кнопки мыши.

У коллекции TActionCiients есть заслуживающие особого упоминания свой ства.

Свойство property TCaptionOptions;

TCaptionOptions (coNone, coSelective, задает показ/отсутствие заголовков дочерних действий. В случае установки в coNone они не показываются, — показываются все, coSelective — показываются в соответствии со значением дочернего объекта TActionCiientitem. Это свойство можно также установить на первой страни це редактора менеджера действий в одноименном выпадающем списке.

Свойство property Boolean;

указывает размер значков, соответствующих действиям. По умолчанию ус тановлено в значение True (маленькие значки). Визуально оно доступно че рез тот же редактор — третья страница, флажок Large Icons.

property Boolean;

разрешает скрытие редко используемых действий, описанное в предыдущем разделе. Если вы не хотите пользоваться механизмом скрытия, на третьей странице редактора менеджера действий и диалога есть фла жок Menu show recent items first. Сбросьте его, и свойства HideUnused у кли ентов действий установятся в значение False.

И, наконец, коллекцию можно сделать нередактируемой. Для этого у нее есть СВОЙСТВО Customizable.

Ну вот, мы уже добрались до самой маленькой матрешки — TActionCiientitem.

Этот объект связывается напрямую с одним действием через свойство Action. Правда в него можно спрятать еще меньшую матрешку — у него также есть свойства items и items. Эти свойства используются при организации многоуровневых меню и меню, выпадающих из кнопок (точ нее, псевдокнопок — напомним, объекты TActionCiientitem на панелях не являются ни кнопками, ни компонентами вообще).

7 Зак. 194 Часть II. Интерфейс и логика приложения Резюме Хорошо знакомые со времен Delphi 1 составляющие интерфейса — меню КНОПКИ (TButton, панели TPanel — постепенно уходят, уступая место компонентам с расширенной функцио нальностью. Центральным местом, где обрабатывается весь ввод пользова теля, ДеЙСТВИЙ — TActionList ИЛИ В этой главе мы подробно рассмотрели оба компонента;

читателю решать, на базе чего строить свой интерфейс.

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

ГЛАВА Файлы и устройства ввода/вывода Большинство приложений создаются для того, чтобы обрабатывать дан ные — это прописная истина. С развитием программных технологий мы имеем возможность получать и посылать все более крупные и сложные мас сивы данных;

однако до сих пор 90% из них хранятся в файлах.

Для использования файлов в приложении разработчику приходится решать множество задач. Главные из них — поиск необходимого файла и выполне ние с ним операций ввода/вывода.

Основные принципы и структура файловой системы мало изменились со времен MS-DOS. Файловые системы NTFS), появившаяся в Windows 2000 служба Active Directory не изменяют главного — понятия файла и способов обращения к нему.

Среда Delphi дает вам возможность выбрать один из четырех вариантов ра боты:

использование традиционного набора функций работы с файлами, унас ледованного от Turbo Pascal;

использование функций ввода/вывода из Windows API;

использование потоков (Tstream и его потомки);

использование отображаемых файлов.

В этой главе мы изучим все основные способы работы с файлами в прило жениях Delphi на конкретных примерах создания профаммного кода.

Использование файловых переменных.

Типы файлов Зачастую современный программный код Delphi для чтения данных из фай ла удивительно похож на аналогичный, написанный, к примеру, в Turbo 196 Часть II. Интерфейс и логика приложения Pascal Это возможно потому, что программисты Borland сохранили не изменным "старый добрый" набор файловых функций, работающих через файловые переменные.

При организации операций файлового ввода/вывода в приложении большое значение имеет, какого рода информация содержится в файле. Чаще всего это строки, но встречаются двоичные данные или структурированная ин формация, например массивы или записи.

Естественно, что сведения о типе хранящихся в файле данных важно изна чально задать. Для этого используются специальные файловые переменные, определяющие тип файла. Они делятся на нетипизированные и типизиро ванные.

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

В Delphi имеется возможность создавать нетипизированные файлы. Для их обозначения используется ключевое слово file:

var file;

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

Типизированные файлы обеспечивают ввод/вывод с учетом конкретного типа данных. Для их объявления используется ключевое слово file of, к кото рому добавляется конкретный тип данных. Например, для работы с файлом, содержащим набор байтов, файловая переменная объявляется так:

var ByteFile: file of byte;

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

type Country = record Name: String;

Capital: String;

Population:

Square: Longlnt;

end;

var CountryFile: file of Country;

Глава 9. Файлы и устройства ввода/вывода Для работы с текстовыми файлами используется специальная файловая пе ременная TextFile ИЛИ Text:

var F: TextFile;

Операции ввода/вывода Теперь рассмотрим две самые распространенные операции, выполняемые при работе с файлами. Это чтение и запись. Для их осуществления приме няются специальные функции файлового ввода/вывода.

Итак, для выполнения операции чтения или записи необходимо произвести следующие действия:

1. Объявить файловую переменную необходимого типа.

2. При помощи функции связать эту переменную с требуемым файлом.

3. Открыть файл ПОМОЩИ функций Append, Reset, Rewrite.

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

5. Закрыть файл при помощи функции Внимание!

По сравнению с Turbo Pascal изменились названия только двух функций:

Assign стала AssignFile, a Close превратилась В В качестве примера рассмотрим небольшой фрагмент исходного кода.

var F: TextFile;

S: string;

begin if then else Exit;

while Not EOF(F) do begin Readln(F, S);

;

end;

end;

198 Часть II. Интерфейс и логика приложения Если в диалоге открытия файла был выбран файл, то его имя свя зывается с файловой переменной при помощи процедуры В качестве имени файла рекомендуется всегда передавать полное имя файла (включая его маршрут). Как раз в таком виде возвращают результат выбора файла диалоги работы с файлами Затем при помощи процедуры Reset этот файл открывается для чтения и записи.

В цикле выполняется чтение из файла текстовых строк и запись их в ком понент Процедура осуществляет чтение текущей строки файла и переходит на следующую строку. Цикл выполняется, пока функция EOF не сообщит о достижении конца файла.

После завершения чтения файл закрывается.

Такой же исходный код можно использовать и для записи данных в файл.

Необходимо только заменить процедуру чтения на процедуру записи.

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

Открытие файла может осуществляться тремя процедурами — в зависимо сти от типа его дальнейшего использования.

Процедура procedure F: File [;

RecSize: Word ]);

открывает существующий файл для чтения и записи, текущая позиция уста навливается на первой строке файла.

Процедура procedure F: Text);

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

Процедура procedure F: File [;

RecSize: Word ]);

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

Переменная RecSize используется только при работе с нетипизированными файлами и определяет размер одной записи для операции передачи данных.

Если этот параметр опущен, то по умолчанию RecSize равно 128 байт.

Чтение данных из типизированных и текстовых файлов выполняют про цедуры Read И Глава 9. Файлы и устройства Процедура Read имеет различное объявление для текстовых и других типи зированных файлов:

procedure Read([var F: Text;

] [, для текстовых файлов;

procedure Read(F, [, V2,...,Vn]);

для других типизированных файлов.

При одном вызове процедуры можно читать данные в произвольное число переменных. Естественно, что тип переменных должен совпадать с типом файла. При чтении в очередную переменную читается ровно столько байтов из файла, сколько занимает тип данных. В следующую переменную читает ся столько же байтов, расположенных следом. После выполнения процеду ры текущая позиция устанавливается на первом непрочитанном байте. Ана логично работают несколько процедур Read для одной переменной, выпол ненных подряд.

Процедура procedure Readln([ var F: Text;

] [, V2,...,Vn ]);

считывает одну строку текстового файла и устанавливает текущую позицию на следующей строке. Если использовать процедуру без переменных то она просто передвигает текущую позицию на очередную строку файла.

Процедуры для записи в файл write и описаны аналогично:

procedure F: Text;

] [, P2,..., Pn] ) ;

procedure var F: Text;

] [, P2,...,Pn ]);

Параметры P2,..., Pn могут быть одним из целых или вещественных ти пов, одним из строковых типов или логическим типом. Но у них есть можность дополнительного форматирования при выводе. Каждый параметр записи может иметь форму:

[: [: DecPlaces ] ] Pn — выводимая переменная или выражение;

— минимальная ширина поля в символах, которая должна быть больше 0;

DecPlaces — содержит количество десятичных символов после запятой при отображении вещественных чисел с фиксированной точкой.

Обратите внимание, что для текстовых файлов в функциях Read и файловая переменная F может быть опущена. В этом случае чтение и запись осуществляются в стандартные файлы ввода/вывода. Когда программа ком пилируется как консольное приложение (флаг {$APPTYPE CONSOLE}), Delphi автоматически связывает входной и выходной файлы с окном консоли.

200 Часть II. Интерфейс и логика приложения Для контроля за текущей позицией в файле применяются две основные функции. Функция EOF(F) возвращает значение True, если достигнут конец файла. Функция EOLN(F) аналогично сигнализирует о достижении конца строки. Естественно, в качестве параметра в функции необходимо переда вать файловую переменную.

Процедура procedure F;

N: Longint);

обеспечивает смещение текущей позиции на N элементов. Размер одного элемента в байтах зависит от типа данных файла (от типизированной пере менной).

Рассмотрим теперь режим блочного ввода/вывода данных между файлом и областью адресного пространства (буфером). Этот режим отличается значи тельной скоростью передачи данных, причем скорость пропорциональна размеру одного передаваемого блока — чем больше блок, тем больше ско рость.

Для реализации этого режима необходимо использовать только нетипизиро ванные файловые переменные. Размер блока определяется в процедуре от крытия файла (Reset, Rewrite). Непосредственно для выполнения операций BlockRead И Процедура procedure F: File;

var Buf;

Count: Integer [;

var AmtTransferred: Integer]);

выполняет запись блока из файла в буфер. Параметр F ссылается на нети пизированную файловую переменную, связанную с нужным файлом.

Параметр Buf определяет любую переменную (число, строку, массив, струк туру), в которую читаются байты из файла. Параметр содержит число считываемых блоков. Наконец, необязательный параметр AmtTransferred возвращает число реально считанных блоков.

При использовании блочного чтения или записи размер блока необходимо выбирать таким образом, чтобы он был кратен размеру одного значения того типа, который хранится в файле. Например, если в файле хранятся значения типа Double (8 байт), то размер блока может быть равен 8, 16, 24, 32 и т. д. Фрагмент исходного кода блочного чтения при этом выглядит следующим образом:

var F: File;

DoubleArray: array [0..255] of Double;

Transfered: Integer;

Глава 9. Файлы и устройства begin if then else Exit;

64);

DoubleArray, 32, CloseFile(F);

блоков');

end;

Как видно из примера, размер блока установлен в процедуре Reset и кратен размеру элемента массива DoubleArray, в который считываются данные.

В переменной возвращается число считанных блоков. Если раз мер файла меньше заданного в процедуре числа блоков, ошибка не возникает, а в переменной Transfered передается число реально считан ных блоков.

Процедура procedure f: File;

var Count: Integer [;

var Integer]);

используется аналогично.

Оставшиеся функции ввода/вывода, работающие с файловыми переменны ми, подробно описаны в табл. 9.1.

Таблица Процедуры и функции для работы с файлом Объявление Описание function Функция позволяет изменить расширение FileName, Extension: string) : файла. При этом сам файл не переименовы string;

вается procedure ChDir(S: string);

Процедура изменяет текущий каталог на дру гой, путь к которому описан в строке s procedure F);

Вызов процедуры разрывает связь между файловой переменной и файлом на диске.

Имя этой процедуры изменено из-за кон фликта имен в Delphi (в Borland Pascal ис пользуется процедура Close) function DeleteFile (const Функция производит удаление файла FileName FileName: string) : Boolean;

диска и возвращает значение False, если файл удалить не удалось или файл не суще ствует Часть II. Интерфейс и логика приложения Таблица (продолжение) Объявление Описание function Функция возвращает расширение файла FileName: string): string;

function Извлекает имя и расширение файла, содер FileName: string): string;

жащегося в параметре FileName function Функция возвращает полный путь к файлу FileName: string): string;

procedure F);

Удаляет файл, связанный с файловой пере менной F function Данная процедура производит поиск в ката Name, DirList: string): логах Di rLi st файла Name. Если в процессе string;

выполнения FileSearch обнаруживается ис комое имя файла, то функция возвращает в строке типа Stri ng полный путь к найден ному файлу. Если файл не найден, то воз вращается пустая строка function Присваивает файлу с именем FileName атри FileName: string;

Attr:

буты Attr. Функция возвращает 0, если при Integer): Integer;

своение атрибутов прошло успешно. В про тивном случае возвращается код ошибки function F): Возвращает текущую позицию файла. Функ ция используется для нетекстовых файлов.

Перед вызовом FilePos файл должен быть открыт function F): FileSize возвращает размер файла в байтах или количество записей в файле, содержа щем записи. Перед вызовом данной функции файл должен быть открыт.

Для текстовых файлов функция FileSize не используется procedure F: Text), Процедура очищает буфер текстового файла, открытого для записи. F— файловая пере менная.

Когда текстовый файл открыт для записи с использованием функции Rewrite или Append, Flush очищает выходной буфер, свя занный с файлом. После выполнения данной процедуры все символы, которые направлены для записи в файл, будут гарантированно записаны в нем Глава 9. и Таблица (продолжение) Объявление Описание procedure GetDir(D: Byte;

Возвращает число, соответствующее диску, var S: string);

на котором содержится текущий каталог S.

D может принимать одно из следующих зна чений:

• 0 — по умолчанию (текущий);

.

.

• 3-С т. д.

Процедура не генерирует код ошибки. Если имя диска в D оказывается ошибочным, то в строке s возвращается значение Х:\, как если бы текущая папка была на этом ошибочно указанном диске function IOResult: Integer;

Функция возвращает статус последней про изведенной операции ввода/вывода, если контроль ошибок выключен procedure Процедура создает новый каталог, который описывается в строке s procedure F;

Процедура изменяет имя файла, связанного NewName: string);

с файловой переменной F. Переменная NewName является строкой типа stri ng или PChar (если включена поддержка расширен ного синтаксиса) Процедура удаляет пустой каталог, путь к procedure RmDir(S: string);

которому задается в строке s. Если указан ный каталог не существует или он не пустой, то возникает сообщение об ошибке вво да/вывода procedure F;

Перемещает текущую позицию курсора на N N: ;

позиций. Данная процедура используется только для открытых типизированных или не типизированных файлов для проведения чте ния/записи с нужной позиции файла.

Началу файла соответствует нулевой номер Для добавления новой информации в конец существующего файла необходимо устано вить указатель на символ, следующий за по следним. Для этого можно использовать вы ражение Часть //. и логика приложения Таблица (окончание) Объявление Описание function SeekEof[(var F: Возвращает значение True, если указатель Text)]: Boolean;

текущей позиции находится на символе кон ца файла.

SeekEof может быть использован только с открытым текстовым файлом function SeekEoln[(var F: Возвращает значение True, если указатель Text)]: Boolean;

текущей позиции находится на символе кон ца строки.

SeekEoln может быть использован только с открытым текстовым файлом procedure F: Связывает с текстовым файлом буфер вво Text;

var Buf [;

Size: да/вывода. F— файловая переменная тексто Integer]);

вого типа.

Каждая файловая переменная текстового типа имеет внутренний буфер емкостью 128 байт, в котором накапливаются данные при чтении и записи. Такой буфер пригоден для боль шинства операций. Однако при выполнении программ с интенсивным вводом/выводом буфер может переполниться, что приведет к записи операций ввода/вывода на диск и, как следствие, к существенному замедлению работы приложения.

SetTextBuf позволяет помещать в текстовый файл F информацию об операциях вво да/вывода вместо ее размещения в буфере.

size указывает размер буфера в байтах. Ес ли этот параметр опускается, то полагается размер, равный Новый буфер действует до тех пор, пока F не будет связана с новым файлом процедурой AssignFile procedure F);

Удаляет все позиции, следующие после те кущей позиции в файле. А текущая позиция становится концом файла.

С переменной F может быть связан файл лю бого типа за исключением текстового Ввод/вывод с использованием функций Windows API Для тех, кто переходит на Delphi не с прежних версий Turbo Pascal, а с С, других языков или начинает освоение с "нуля", более привычными будут стандартные функции работы с файлами Windows. Тем более, что возмож Глава 9. Файлы и устройства ввода/вывода ности ввода/вывода в них расширены. Каждый файл в этом наборе функ ций описывается не переменной, а дескриптором (Handle) — 32-разрядной величиной, которая идентифицирует файл в операционной системе.

В Win32 файл открывается при помощи функции, имеющей обманчивое название:

function dwShareMode: DWORD;

PSecurityAttributes;

dwCreationDistribution, DWORD;

THandle): THandle;

Хоть ее название и начинается с но она позволяет не только созда вать, но и открывать уже существующие файлы.

Такое огромное количество параметров оправдано, т. к. CreateFile исполь зуется для открытия файлов на диске, устройств, каналов, портов и вообще любых источников ввода/вывода. Назначение параметров описано в табл. 9.2.

Таблица 9.2. Параметры функции CreateFile Параметр Описание lpFileName: pChar Имя открываемого объекта. Может представ лять собой традиционную строку с путем и именем файла, UNC (для открытия объектов в сети, имя порта, драйвера или устройства) dwDesiredAccess,: DWORD Способ доступа к объекту. Может быть равен:

• — ДЛЯ • GENERIC_WRITE — ДЛЯ Их комбинация позволяет открыть файл для чтения и записи. Параметр 0 применяется, если нужно получить атрибуты файла без его фактического открытия DWORD Режим совместного использования файла:

• 0 — совместный доступ запрещен;

• FILE_SHARE_READ — ДЛЯ • — ДЛЯ Их комбинация — для полного совместного доступа Атрибуты защиты файла. В Windows 95/98 не PSecurityAttributes используются (должны быть равны nil).

В Windows NT/2000 этот параметр, равный ni l, дает объекту атрибуты по умолчанию Часть II. Интерфейс и логика приложения Таблица 9.2 (окончание) Параметр Описание DWORD;

Способ открытия файла:

• CRE A T E _ NE W — соз дает ся новый файл, если таковой уже существует, функция возвра щает ОШИбку • CRE AT E _ AL WAY S — соз дает ся новый файл, если таковой уже существует, он переза писывается;

• — открывает существую щий файл, если таковой не найден, функ ция возвращает ошибку;

• — от к рывает с у ще с т в у ющи й файл, если таковой не найден, он созда ется DWORD;

Набор атрибутов (скрытый, системный, сжа тый) и флагов для открытия объекта. Подроб ное описание см. в документации по Win THandle Файл-шаблон, атрибуты которого используют ся для открытия. В Windows 95/98 не исполь зуется и должен быть равен О Функция возвращает дескриптор открытого объекта ввода/вы вода. Если открытие невозможно из-за ошибок, возвращается код а расширенный код ошибки можно узнать, вызвав ФУНКЦИЮ GetLastError.

Закрывается файл в Win32 функцией (не a Правда, "легко" запомнить? Что поделать, так их назвали раз работчики Win32).

Приведем из большого разнообразия несколько приемов использования функции CreateFile. Часто программисты хотят иметь возможность органи зовать посекторный доступ к физическим устройствам хранения — напри мер к дискете. Сделать это не так уж сложно, но при этом методы для Windows 98 и Windows 2000 различаются. В Windows 2000 придется откры вать устройство ('\\.\A:'), а в Windows 98 — специальный драйвер доступа (обозначается '\\.\vwin32'). И то и другое делается функцией Листинг 9.1. Чтение сектора с дискеты при помощи функции CreateFile type pDIOCRegs = Глава 9. Файлы и устройства TDIOCRegs = packed record rFlags : DWORD;

end;

const = 1;

=4;

// SectorSize = 512;

function Track, Sector: Integer;

buffer : pointer;

Floppy:

var hDevice : THandle;

Regs : TDIOCRegs;

: string;

nb : Integer;

begin if О then begin (win95/98} hDevice := 0, nil, 0, 0);

if (hDevice = INVALID_HANDLE_VALUE) then begin Result := FALSE;

Exit;

end;

regs.rEDX := Head * $100 + in 'B']);

:= $201;

// код операции read sector regs.rEBX := // buffer regs.rECX := Track * $100 + Sector;

:= $0;

Result := @regs, nb, nil) and and $l)=0);

;

end {win95/98} else begin // Windows NT/ DevName :='\\.\A:';

if Floppy in ['b', then DevName[5] := Floppy;

hDevice := GENERIC_READ, FILE_SHARE_READ or nil, 0);

if (hDevice = then begin Result := FALSE;

208 Часть II. Интерфейс и логика приложения Exit;

end;

nil, FILE_BEGIN);

// нумерация с Result := SectorSize, nb, nil) and (nb=SectorSize);

end;

// Windows NT/ end;

Для чтения и записи данных в Win32 используются функции:

function THandle;

var Buffer;

nNumberOfBytesToRead:

DWORD;

var DWORD;

lpOverlapped: POverlapped): BOOL;

function THandle;

const Buffer;

DWORD;

var DWORD;

lpOverlapped: POverlapped):

BOOL;

Здесь все сходно с и — это дескриптор файла, — адрес, по которому будут читаться (писаться) данные;

третий па раметр означает требуемое число читаемых (записываемых) байтов, а чет вертый — фактически прочитанное (записанное). Последний параметр — lpOverlapped — обсудим чуть Функция используется и для доступа к портам ввода/вывода.

Часто программисты сталкиваются с задачей: как организовать обмен дан ными с различными нестандартными устройствами, подключенными к па раллельному или последовательному порту? В Turbo Pascal для DOS был очень хороший псевдомассив пишешь Port[X] := у;

и не знаешь проблем. В Win32 прямой доступ к портам запрещен и приходится откры вать их как файлы:

:= or GENERIC_WRITE, 0, NIL, 0);

if = then begin raise открытия порта: %d', [GetLastError]);

end;

Самое большое отличие от предыдущего примера — в скромном флаге FILE_FLAG_OVERLAPPED. О роли этих изменений — в следующем разделе.

Отложенный (асинхронный) ввод/вывод Эта принципиально новая возможность введена впервые в Win32 с появле нием реальной многозадачности. Вызывая функции чтения и записи дан ных, вы на самом деле передаете исходные данные одному из потоков Глава 9. Файлы и устройства ввода/вывода (threads) операционной системы, который и осуществляет фактические обя занности по работе с устройством. Время доступа всех периферийных уст ройств гораздо больше доступа к ОЗУ, и ваша программа, вызвавшая Read или будет дожидаться окончания операции ввода/вывода. Замедление работы программы налицо.

Выход был найден в использовании отложенного (overlapped) ввода/вывода.

До начата отложенного ввода/вывода инициализируется дескриптор объекта типа события (функция createEvent) и структура типа Вы вы зываете функцию или в которой последним параметром указываете на TOveriapped. Эта структура содержит дескриптор события Windows (event).

ОС начинает операцию (ее выполняет отдельный программный поток, скрытый от программиста) и немедленно возвращает управление;

вы можете не тратить время на ожидание. Признак того, что операция началась и про должается — получение кода возврата ERROR_IO_PENDING. Пусть вас не пугает слово "error" в названии — это совершенно нормально. Если операция про должается долго (а чтение и запись файлов на дискете, да и на диске, име нованных каналов можно отнести к "длинным" операциям), то программа может спокойно выполнять последующие операторы. Событие будет "взве дено" ОС тогда, когда ввод/вывод закончится.

Когда, по мнению программиста, ввод/вывод должен быть завершен, можно ЭТО, ИСПОЛЬЗОВав функцию function THandle;

DWORD):

DWORD;

Объект ожидания (параметр в этом случае — тот самый, который создан нами, указан в структуре TOveriapped и передан в качестве параметра в функцию ReadFile или WriteFiie. Можно указать любое время ожидания, в том числе бесконечное (параметр Timeout при этом равен константе INFINITE). Признаком нормального завершения служит получение кода воз врата j Листинг 9.2, Пример отложенной операции чтения function TMyClass.Read(var Buffer;

Count: Longint;

var succ : boolean;

nb : : Longint;

Overlap: TOveriapped;

begin := True, False, nil);

Result := succ := Buffer, Count, nb, 210 Часть II. Интерфейс и логика приложения II Здесь можно вставить любые операторы, которые // могут быть выполнены до окончания ввода/вывода // if not succ then begin LastError := GetLastError;

if LastError = then begin if then OverlapRd, TRUE);

end else raise failed, error %d',[LastError]));

end;

Result := nb;

CloseHandle(hEvent);

end;

Если вы задали конечный интервал в миллисекундах, а операция еще не закончена, WaitForSingleObject вернет код завершения Функ ция GetOverlappedResult возвращает в параметре число байтов, действи тельно прочитанных или записанных во время отложенной операции.

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

Контроль за ошибками ввода/вывода зависит от применяемых функций.

При использовании доступа через Win32 API все функции возвращают код ошибки Windows, который и нужно проанализировать.

При возникновении ошибок ввода/вывода в функциях, использующих файло вые переменные, генерируется исключительная ситуация класса Но так происходит только в том случае, если включен контроль ошибок ввода/вывода. Для этого используются соответствующие директивы компи лятора:

— контроль включен (установлен по умолчанию);

отключен.

Глава 9. Файлы и устройства Класс отличается тем, что у него есть поле ErrorCode. При воз никновении этой исключительной ситуации вы можете получить его значе ние и принять решение. Основные коды имеют такие значения:

О 2- файл не найден;

3 — неверное имя файла;

4 — слишком много открытых файлов;

• 5 — доступ запрещен;

100 — достигнут конец файла;

101 — диск переполнен;

106 — ошибка ввода.

При отключенном контроле в случае возникновения ошибки выполнение программы продолжается без остановки. Однако в этом случае устранение возможных последствий ошибки возлагается на разработчика. Для этого применяется функция function IOResult: Integer;

которая возвращает значение 0 при отсутствии ошибок.

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

Запись type = string;

TSearchRec = record Time: Integer;

{Время и дата создания} Size: Integer;

{Размер Attr: Integer;

{Параметры файла} Name: TFileName;

{Полное имя файла} Integer;

используется} FindHandle: THandle;

{Дескриптор файла} FindData: используется} end;

обеспечивает хранение характеристик файла после удачного поиска. Дата и время создания файла хранятся в формате MS-DOS, поэтому для получения Часть II. Интерфейс и логика приложения этих параметров в принятом в Delphi формате TDateTime необходимо ис пользовать следующую функцию:

function Integer): TDateTime;

Обратное преобразование выполняет функция function TDateTime): Integer;

Свойство Attr может содержать комбинацию следующих флагов-значений:

• — только для чтения;

faDirectory — каталог;

faHidden — скрытый;

faArchive — архивный;

faSysFile — faAnyFile — — метка тома;

Для определения параметров файла используется оператор AND:

if (SearchRec.Attr AND faReadOniy) > then только для чтения');

Непосредственно для поиска файлов используются функции FindFirst И FindNext.

Функция function Path: string;

Attr: Integer;

var F: TSearchRec):

Integer;

находит первый файл, заданный полным маршрутом Path и параметрами Attr (см. выше). Если заданный файл найден, функция возвращает 0, ина че — код ошибки Windows. Параметры найденного файла возвращаются В записи F типа function F: TSearchRec): Integer;

применяется для повторного поиска следующего файла, удовлетворяющего критерию поиска. При этом используются те параметры поиска, которые заданы последним вызовом функции FindFirst. В случае удачного поиска возвращается 0.

Для освобождения ресурсов, выделенных для выполнения поиска, применя ется функция:

procedure F: TSearchRec);

В качестве примера организации поиска файлов рассмотрим фрагмент ис ходного кода, в котором маршрут поиска файлов задается в однострочном текстовом редакторе DirEdit, а список найденных файлов передается в ком понент TListBox.

Глава 9. Файлы и устройства procedure begin faArchive + faHidden, SearchRec);

while = 0 do end;

Потоки — очень удачное средство для унификации ввода/вывода для раз личных носителей. Потоки представляют собой специальные объекты наследники абстрактного класса Tstream. Сам Tstream "умеет" открываться, читать, писать, изменять текущее положение и закрываться. Поскольку для разных носителей эти вещи происходят по-разному, конкретные аспекты реализованы в его потомках. Наиболее часто используются потоки для ра боты с файлами на диске и памятью.

Многие VCL унифицированные МеТОДЫ И saveTostream, которые обеспечивают обмен данными с потоками. От того, с каким физическим носителем работает поток, зависит место хранения данных.

Базовые классы TStream и В основе иерархии классов потоков лежит класс Tstream. Он обеспечивает выполнение основных операций потока безотносительно к реальному носи телю информации. Основными из них являются чтение и запись данных.

Класс Tstream порожден непосредственно от класса TObject.

Потоки также играют важную роль в чтении/записи компонентов из файлов ресурсов (DFM). Большая группа методов обеспечивает взаимодействие компонента и потока, чтение свойств компонента из ресурса и запись зна чений свойств в ресурс.

Таблица 9.3. Свойства и методы класса Объявление Описание property Position: Longint;

Определяет текущую позицию в потоке property Longint;

Определяет размер потока в байтах 214 Часть II. Интерфейс и логика приложения Таблица 9.3 (окончание) Объявление Описание function TStream;

Копирует из потока Source Count бай Count: Longint): Longint;

ты, начиная с текущей позиции. Воз вращает число скопированных байтов function Read(var Buffer;

Count:

Абстрактный класс, перекрываемый в Longint): Longint;

virtual;

наследниках. Считывает из потока abstract;

Count байты в буфер Buffer. Возвра щает число скопированных байтов procedure Buffer;

Считывает из потока байты в бу Count: Longint);

фер Возвращает число скопи рованных байтов function Longint;

Абстрактный класс, перекрываемый в Origin: Word): Longint;

virtual;

наследниках. Смещает текущую пози abstract;

цию в реальном носителе данных на Offset байтов в зависимости от усло вия Origin (см. ниже) function Buffer;

Абстрактный класс, перекрываемый в Count: Longint): Longint;

наследниках. Записывает в поток Count virtual;

abstract;

байты из буфера Buffer. Возвращает число скопированных байтов procedure Buffer;

Записывает в поток байты из бу Count: Longint);

фера Возвращает число скопи рованных байтов Передает данные из потока в компонент TComponent):

заполняя его свойства зна чениями function Считывает заголовок ресурса компо TComponent): TComponent;

нента и значения его свойств из потока.

procedure ReadResHeader;

Считывает заголовок ресурса компо нента из потока procedure Передает в поток значения свойств ком TComponent);

понента Instance procedure Записывает в поток заголовок ресурса string;

Instance:

компонента instance и значения его TComponent);

Итак, в основе операций считывания и записи данных в потоке лежат методы Read и Именно они вызываются для реального выполне ния операции внутри методов ReadBuffer И WriteBuffer, ReadComponent И Глава 9. Файлы и устройства Так как класс является абстрактным, методы Read и write также являются абстрактными. В классах-наследниках они перекры ваются, обеспечивая работу с конкретным физическим носителем данных.

Метод seek используется для изменения текущей позиции в потоке. "Точка отсчета" позиции зависит от значения параметра — смещение должно быть положительным и отсчитыва ется от начала потока;

— смещение относительно текущей позиции в потоке;

— смещение должно быть отрицательным и отсчитывается от конца потока.

Группа методов обеспечивает чтение и запись из потока ресурса компонен та. Они используются при создании компонента на основе данных о нем, сохраненных в формате файлов ресурсов. Для чтения ресурса используется метод ReadComponentRes, в котором последовательно вызываются:

метод ReadResHeader — для считывания заголовка ресурса компонента из потока;

метод — для считывания значений свойств компонента.

Для записи ресурса в поток применяется метод writeComponentRes.

Класс инкапсулирует поток, связанный с физическим носите лем данных через дескриптор.

Для создания потока используется конструктор constructor Integer);

в параметре которого передается дескриптор. Впоследствии доступ к деск риптору осуществляется через свойство:

property Handle: Integer;

Класс TFileStream Класс TFileStream позволяет создать поток для работы с файлами. При этом поток работает с файлом без учета типа хранящихся в нем данных (см.

выше).

Полное имя файла задается в параметре FileName при создании потока:

constructor FileName: string;

Mode: Word);

Параметр Mode определяет режим работы с файлом. Он составляется из фла гов режима открытия:

fmCreate — файл создается;

fmOpenRead — файл открывается для чтения;

216 Часть II. Интерфейс и логика приложения • — файл открывается для записи;

• — файл открывается для чтения и записи.

И флагов режима совместного использования:

— файл недоступен для открытия другими приложе ниями;

— другие приложения могут читать данные из файла;

— другие приложения могут писать данные в файл;

— другие приложения могут производить с файлом лю бые операции.

Для чтения и записи из потока используются методы Read и write, унасле дованные ОТ procedure TObject);

var Streaml, TFileStream;

IntBuf: of Integer;

begin if Not then Exit;

try Streaml := try Stream2 := fmOpenWrite);

soFromEnd);

finally Stream2.Free;

end;

finally end;

end;

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

При необходимости копирования одного файла в другой целиком использу ется метод copyFrom, унаследованный от класса Tstream:

procedure var Streaml, Stream2: TFileStream;

begin if Not then Exit;

Глава 9. Файлы и устройства try := fmOpenRead);

Stream2 fmOpenWrite);

finally Streaml.Free;

Stream2.Free;

end;

end;

Обратите внимание, что в данном случае для определения размера переда ваемого потока необходимо использовать свойство size, которое дает реальный объем данных, содержащихся в потоке. Функция (stream) в этом случае даст размер объекта потока, и не более того.

Класс TMemoryStream Класс TMemoryStream обеспечивает сохранение данных в адресном простран стве. При этом методы доступа к этим данным остаются теми же, что и при работе с файловыми потоками. Это позволяет использовать адресное про странство для хранения промежуточных результатов работы приложения, а также при помощи стандартных методов осуществлять обмен данными между памятью и другими физическими носителями.

property Memory: Pointer;

определяет область памяти, отведенную для хранения данных потока.

Изменение размера отведенной памяти осуществляется методом procedure Longint);

override;

Для очистки памяти потока используется метод procedure Clear;

Чтение/запись данных в память выполняется привычными методами Read И Write.

Также запись данных в память может осуществляться методами:

procedure FileName: string);

файла;

procedure ;

потока.

Дополнительно можно использовать методы записи данных в файл или поток:

procedure FileName: string);

procedure TStream);

218 Часть II. Интерфейс и логика приложения Класс Так как строковые константы и переменные широко применяются при раз работке приложений, то для удобства работы с ними создан специальный класс TStringStream. Он обеспечивает хранение строки и доступ к ней во время выполнения приложения.

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

Свойство только для чтения property DataString: string;

обеспечивает доступ к хранимой строке.

Методы function Read(var Buffer;

Count: override;

И function Buffer;

Count: Longint): Longint;

override;

реализуют обычный для потоков способ чтения и записи строки для произ вольной переменной Buffer.

Метод function Longint): string;

обеспечивает чтение байтов строки потока, начиная с текущей пози ции.

Метод procedure AString: string);

дописывает к строке строку AString, начиная с текущей позиции.

При работе с файлами и потоками используются дополнительные классы исключительных ситуаций.

Класс EFCreateError файла, a EFOpenError — при открытии файла.

При чтении/записи данных в поток могут возникнуть исключительные EReadError И EWriteError.

Оповещение об изменениях в файловой системе Многие программисты задавались вопросом: как получить сигнал от опера ционной системы о том, что в файловой системе произошли какие-то из менения? Такой вид оповещения позаимствован из ОС UNIX и теперь дос тупен программистам, работающим с Win32.

Глава 9. Файлы и устройства ввода/вывода Для организации мониторинга файловой системы нужно использовать три функции — FindFirstChangeNotification, FindNextChangeNotification И Первая из них возвращает дескриптор объекта файлового оповещения, который можно передать в функцию ожидания.

Объект активизируется тогда, когда в заданной папке произошли те или иные изменения (создание или уничтожение файла или папки, изменение прав доступа и т. Вторая функция — готовит объект к реакции на сле дующее изменение. Наконец, с помощью третьей функции следует закрыть, ставший ненужным, объект.

Так может выглядеть код метода Execute потока, созданного для монито ринга:

:

procedure var r: Cardinal;

fn : THandle;

begin fn := repeat r := if r = then if not then break;

until Terminated;

end;

На главной форме должны находиться компоненты, нужные для выбора обследуемой папки, а также компонент в который будут записы ваться имена файлов:

procedure TObject);

var dir : string;

begin if then begin := dir;

DirName := dir;

end;

end;

procedure var SearchRec: TSearchRec;

220 Часть II. Интерфейс и логика приложения begin ListBoxl.Clear;

faAnyFile, SearchRec);

repeat until <> 0;

end;

Приложение готово. Чтобы оно стало полнофункциональным, предусмотри те в нем механизм перезапуска потока при изменении обследуемой папки.

Использование отображаемых файлов Последний — самый нетрадиционный вид работы с файлами — это так на зываемые отображаемые файлы.

Вообще говоря, в 32-разрядной Windows под "памятью" подразумевается не только оперативная память (ОЗУ), но также и память, резервируемая опе рационной системой на жестком диске. Этот вид памяти называется вирту альной памятью. Код и данные отображаются на жесткий диск посредством страничной системы (paging system) подкачки. Страничная система исполь зует для отображения страничный файл (win386.swp в Windows 95/98 и в Windows NT). Необходимый фрагмент виртуальной памяти пе реносится из страничного файла в ОЗУ и, таким образом, становится доступным.

А что, если так же поступить и с любым другим файлом и сделать его частью адресного пространства? В Win32 это возможно. Для выделения фрагмента памяти должен быть создан специальный системный объект Win32, называемый отображаемым файлом. Этот объект "знает", как соотнести файл, находящийся на жестком диске, с памятью, адресуемой процессами.

Одно или более приложений могут открыть отображаемый файл и получить тем самым доступ к данным этого объекта. Таким образом, данные, поме щенные в страничный файл приложением, использующим отображаемый файл, могут быть доступны другим приложениям, если они открыли и ис пользуют тот же самый отображаемый файл.

Создание и использование объектов файлового отображения осуществляется посредством функций Windows API. Этих функций три:

Отображаемый файл создается операционной системой при вызове функ ции CreateFileMapping. Этот объект поддерживает соответствие между со Глава 9. Файлы и устройства файла и адресным пространством процесса, использующего этот файл. Функция имеет шесть параметров:

function THandle;

PSecurityAttributes;

flProtect, DWORD;

PChar): THandle;

Первый параметр имеет тип THandle. Он должен соответствовать дескрипто ру уже открытого при помощи функции файла. Если значение параметра hFile равно $FFFFFFFF, ЭТО приводит к связыванию объекта файлового отображения со страничным файлом операционной системы.

Второй параметр — указатель на запись типа При от сутствии требований к защите данных в Windows NT значение этого пара метра всегда равно nil. Третий параметр имеет тип ОН определяет атрибут защиты. Если при помощи отображаемого файла вы планируете совместное использование данных, третьему параметру следует присвоить значение Четвертый и пятый параметры также имеют тип DWORD. Когда выполняется функция CreateFiieMapping, значение типа DWORD четвертого параметра сдви гается влево на четыре байта и затем объединяется со значением пятого па раметра посредством операции and. Проще говоря, значения объединяются в одно 64-разрядное число, равное объему памяти, выделяемой объекту файлового отображения из страничного файла операционной системы. По скольку вы вряд ли попытаетесь осуществить выделение более чем 4 Гбайт данных, то значение четвертого параметра всегда должно быть равно нулю.

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

Шестой параметр имеет тип PChar и представляет собой имя объекта файло вого отображения.

Функция CreateFiieMapping возвращает значение типа THandle. В случае ус пешного завершения возвращаемое функцией значение представляет собой дескриптор созданного объекта файлового отображения. В случае возникно вения какой-либо ошибки возвращаемое значение будет равно 0.

Следующая задача — спроецировать данные файла в адресное пространство нашего процесса. Этой цели служит функция Функция File имеет пять параметров:

function THandle;

dwDesiredAccess:

DWORD;

dwFileOffsetHigh, DWORD):

Pointer;

Первый параметр имеет тип THandle. Его значением должен быть дескрип тор созданного объекта файлового отображения — тот, который возвращает 222 Часть II. Интерфейс и логика приложения функция createFileMapping. Второй параметр определяет режим доступа к файлу: FILE_MAP_READ ИЛИ Третий и четвертый параметры также имеют тип ЭТО — смещение отображаемого участка относительно начала файла в байтах. В нашем слу чае эти параметры должны быть установлены в нуль, поскольку значение, которое мы даем пятому (последнему) параметру функции также равно нулю.

Пятый (и последний) параметр функции MapViewOfFile, как и предыдущие параметры, имеет тип DWORD. ОН используется для определения (в байтах) количества данных объекта файлового отображения, которые надо отобра зить в процесс (сделать доступными для вас). Для достижения наших целей это значение должно быть установлено в нуль, что означает автоматическое отображение в процессе всех данных, выделенных перед этим функцией CreateFileMapping.

Значение, возвращаемое функцией MapViewOfFile, имеет тип "указатель".

Если функция отработала успешно, то она вернет начальный адрес данных объекта файлового отображения.

Следующий фрагмент кода демонстрирует вызов функции PChar;

begin hMappedFile := (FHandle, nil, 0', 0, ;

if (hMappedFile = 0) then else begin pSharedBuf := 0, 0, 0);

if (pSharedBuf = nil) then ShowMessage error');

end;

end;

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

Последние две функции, имеющие отношение к объекту файлового отобра жения, называются UnMapViewOfFile И CloseHandle. Функция делает то, что подразумевает ее название. Она прекращает отображение Глава 9. Файлы и устройства ввода/вывода в адресное пространство процесса того файла, который перед этим был ото бражен ПОМОЩИ ФуНКЦИЯ ет дескриптор объекта файлового отображения, возвращаемый функцией ФуНКЦИЯ Должна вызываться функцией CloseHandle.

Функции передает единственный параметр типа указатель:

procedure TObject);

begin end;

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

Резюме При разработке приложений очень часто приходится решать задачи обмена данными между приложениями или приложением и устройством вво да/вывода. При этом большая часть созданного кода обеспечивает работу приложения с файлами.

В этой главе рассмотрены методы, обеспечивающие взаимодействие про граммы с файловой системой и примеры их использования.

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

Затруднительно сказать, при изучении каких глав будет полезен материал этой главы. Скорее всего, он понадобится везде.

ГЛАВА Использование графики "Одно изображение стоит тысячи слов", — говорил древнекитайский импе ратор Сун;

его слова верны и в наши времена. 80% информации мозг чело века получает по зрительному каналу, и не удивительно, что программисты стараются придать внешнему виду своих программ максимум привлекатель ности.

Поэтому в Delphi с самого начала появились развитые средства для работы с графическими возможностями Windows. Этому набору объектов и посвя щена данная глава.

Графические инструменты Delphi Разработчики Delphi уделили большое внимание возможностям работы с деловой графикой: простота и удобство ее использования напрямую сказы ваются на простоте и удобстве созданных приложений. Вместо дебрей гра фического интерфейса Windows разработчик получил несколько инструмен тов, сколь понятных, столь же и В стандартном графическом интерфейсе Windows (GDI) основой для рисо вания служит дескриптор контекста устройства нос и связанные с ним шрифт, перо и кисть. В состав VCL входят объектно-ориентированные над стройки над последними, назначением которых является удобный доступ к свойствам инструментов и прозрачная для пользователя обработка всех их изменений.

Обязательным для любого объекта, связанного с графикой в Delphi, являет ся событие:

property OnChange: TNotifyEvent;

Его обработчик вызывается всякий раз, когда меняются какие-то характери стики объекта, влияющие на его внешний вид.

Глава 10. Использование графики Класс TFont Класс инкапсулирует шрифт Windows. В Delphi допускаются только гори зонтально расположенные шрифты. В конструкторе объекта по умолчанию принимается шрифт System, цвета и размером 10 пунктов.

Свойства класса приведены в табл. 10.1.

Таблица 10.1. Свойства класса TFont Описание property Handle: HFont;

Содержит дескриптор шрифта property TFontName;

Содержит имя (начертание) шрифта, на пример, property Style: TFontStyles;

Содержит стиль (особенности начертания) шрифта: соответственно жирный, курсив, TFontStyle = (fsBold, подчеркнутый и перечеркнутый fsUnderline, TFontStyles = set of TFontStyle;

property Color: TColor;

Определяет цвет шрифта TColor = + 1)..$2FFFFFF;

property Charset: TFontCharset Содержит номер набора символов шрифта.

П о у м о л ч а н и ю р а в н о TFontCharset = 0..255;

Для вывода символов кириллицы требуется RUSSIAN CHARSET property Pitch: TFontPitch;

Определяет способ установки ширины TFontPitch = (fpDefault, символов шрифта. Значение fpFixed со ответствует моноширинным шрифтам;

fpVariable, fpVariable — шрифтам с переменной ши риной символа. Установка fpDefault оз начает принятие того способа, который определен начертанием property Height: Integer;

Содержит значение высоты шрифта в пик селах property Integer;

Определяет число точек на дюйм. Перво начально равно числу точек на дюйм в кон тексте экрана. Программист не должен изменять это свойство, т. к. оно использу ется системой для приведения изображе ния на экране и на принтере к одному виду property Size: Integer;

Содержит размер шрифта в пунктах (как принято в Windows). Это свойство связано с Height соотношением:

:= 8 Зак. 226 Часть II. Интерфейс и логика приложения Установка этих свойств вручную, как правило, не нужна. Если вы хотите изменить шрифт для какого-то компонента, воспользуйтесь компонентом В нем можно и поменять свойства, и сразу увидеть получив шийся результат на тестовой надписи;

потом выбранный шрифт присваива ется свойству Font нужного компонента:

if then := Примечание Если вы хотите, не закрывая диалог, увидеть результат применения шрифта на вашем тексте, включите опцию fdApplyButton в свойстве Options объекта TFontDiaiog и напишите для него обработчик события OnApply. При этом в диалоговом окне появится кнопка Apply, по нажатии которой (событие OnApply) можно изменить параметры шрифта.

Класс ТРеп Этот класс инкапсулирует свойства пера GDI Windows. В конструкторе по умолчанию создается непрерывное черное перо шириной в один пиксел. Свойства класса приведены в табл. 10.2.

Таблица 10.2. Свойства класса ТРеп Свойство Описание property Handle: Содержит дескриптор пера property Color: TColor;

цвет пера property Mode: TPenMode;

Содержит идентификатор одной из = растровых операций, которые оп ределяют взаимодействие пера с поверхностью. Эти операции соот стандартным, опреде ленным В Windows property TPenStyle;

Определяет стиль линии, рисуемой TPenStyle = (psSolid, psDash, psDot, пером. Соответствующие стили psDashDot, psDashDotDot, psClear, также определены в Windows property Содержит значение толщины пера в пикселах К сожалению, пунктирные и штрихпунктирные линии (стили psDash, psDot, psDashDot, psDashDotDot) могут быть установлены только для линий единич Глава Использование графики ной толщины. Более толстые линии должны быть сплошными — такое ог раничение существует в Windows.

Примечание с Операция подходит для рисования перемещающихся линий или фи гур, например, при выделении мышью какой-либо области. Если вы два раза нарисуете одну и ту же фигуру таким пером, то после первого раза она появит ся, после второго — полностью сотрется.

Класс TBrush Этот класс инкапсулирует свойства кисти — инструмента для заливки облас тей. Когда создается экземпляр этого класса, первоначально используется белая сплошная кисть. Свойства класса приведены в табл. 10.3.

Таблица 10.3. Свойства класса TBrush Описание property Handle: Содержит дескриптор кисти property Color: TColor;

Определяет цвет кисти property Style: TBrushStyle;

Определяет стиль кисти (фактура закраски) = (bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross);

property Bitmap: Содержит битовую карту, определенную поль зователем для закраски поверхностей. Если это свойство определено, то свойства Color и недействительны Шрифт, перо и кисть не могут использоваться самостоятельно. Они явля ются составными частями специального класса, который и будет сейчас рассмотрен.

Класс TCanvas Этот класс — сердцевина графической подсистемы Delphi. Он объединяет в себе и "холст" (контекст конкретного устройства GDI), и "рабочие инст рументы" (перо, кисть, шрифт), и даже "подмастерьев" (набор функций по рисованию типовых геометрических Будем называть его канвой.

Часть II. Интерфейс и логика приложения Канва не является компонентом, но она присутствует в качестве свойства во многих других компонентах, которые должны уметь нарисовать себя и отобразить какую-либо информацию.

Читатели, знакомые с графикой Windows, узнают в TCanvas объектно ориентированную надстройку над контекстом устройства Windows (Device Context, DC). Дескриптор устройства, над которым "построена" канва, мо жет быть востребован для различных низкоуровневых операций. Он задает ся свойством:

property Handle:

Для рисования канва включает в себя шрифт, перо и кисть:

property Font: TFont;

property Pen: TPen;

property Brush: TBrush;

Кроме того, можно рисовать и поточечно, получив доступ к каждому пик селу. Значение свойства:

property Y: Integer]: TColor;

соответствует цвету точки с координатами X, Y.

Необходимость отрисовывать каждую точку возникает нередко. Однако, ес ли нужно модифицировать все или хотя бы многие точки изображения, свойство Pixels надо сразу отбросить — настолько оно неэффективно. Го раздо быстрее редактировать изображение при помощи свойства объекта TBitmap;

об этом рассказано ниже.

Канва содержит методы-надстройки над всеми основными функциями рисования GDI Windows и свойства, которые приведены в табл. 10. и 10.5. При их рассмотрении имейте в виду, что все геометрические фигуры рисуются текущим пером. Те из них, которые можно закрашивать, закра шиваются с помощью текущей кисти. Кисть и перо при этом имеют теку щий цвет.

Таблица 10.4. Методы класса TCanvas Метод Описание procedure Arc X2, Метод рисует сегмент эллипса. Эллипс определя Y2, ХЗ, Y3, Х4, ется описывающим прямоугольником (Х1, Y1)— ;

(Х2, его размеры должны лежать в диапа зоне от 2 до 32 767 точек.

Начальная точка сегмента лежит на пересече нии эллипса и луча, проведенного из его центра через точку (ХЗ, Y3). Конечная точка сегмента лежит на пересечении эллипса и луча, прове денного из его центра через точку (Х4, Y4). Сег мент рисуется против часовой стрелки Глава Использование графики Таблица 10.4 (продолжение) Метод Описание procedure X2, Рисует хорду и заливает отсекаемую ею часть Y2, ХЗ, Y3, Х4, Y4: эллипса. Эллипс, начальная и конечная точки Integer);

определяются, как в методе Arc procedure Yl, X2, Рисует и закрашивает эллипс, вписанный в Y2: Integer);

прямоугольник (Х1, Y1) — (Х2, Y2) procedure Y:

Проводит линию текущим пером из текущей Integer) ;

точки в (X, Y) procedure Перемещает текущее положение пера (свойст (X, Y: Integer) ;

во в точку (X, Y) procedure Производит специальное копирование. Прямо Dest: TRect;

Bitmap:

угольник Source из битовой карты Bitmap ко TBitmap;

const Source:

пируется в прямоугольник Dest на канве;

при TRect;

Color: TColor);

этом цвет Color заменяется на цвет текущей кисти С помощью этого метода можно нарисовать "прозрачную" картинку. Для этого нужно вы брать соответствующий фону цвет кисти и за тем заменить на него фоновый или наиболее часто встречающийся цвет битовой карты (см.

procedure Производит копирование прямоугольника Dest: TRect;

Canvas: Source из канвы Canvas в прямоугольник Dest TCanvas;

const Source:

в области самого объекта TRect);

procedure Производит заливку прямоугольника (текущей Rect: TRect);

procedure Осуществляет рисование контура прямоуголь ника цветом текущей кисти (без заполнения) procedure Y: Integer;

Осуществляет рисование графического объек Graphic: Tgraphic);

та Graphic (точнее, вызов метода его рисова ния) в области с верхним левым углом (X, Y) procedure Осуществляет рисование объекта Graphic в Rect: TRect;

Graphic: заданном прямоугольнике Rect. Если их раз TGraphic);

меры не совпадают, Graphic масштабируется procedure Производит отрисовку прямоугольной рамки точек (как на элементе, имеющем фокус вво да). Поскольку метод использует логическую операцию (исключающее ИЛИ), повторный вызов для того же прямоугольника приводит изображение к начальному виду Часть //. и логика приложения Таблица 10.4 (окончание) Метод Описание procedure Y: Производит заливку области текущей кистью.

Integer;

Color: TColor;

Процесс начинается с точки (X, Y). Если режим FillStyle: TfillStyle);

FillStyle равен fsSurface, то он продолжа ется до тех пор, пока есть соседние точки с TFillStyle = цветом Color. В режиме fsBorder закрашива fsBorder);

ние, наоборот, прекращается при выходе на границу с цветом Color procedure X2, Y2, Рисует сектор эллипса, описываемого прямо X3, Y3, X4, Y4: Integer);

угольником (Х1, Y1) — (Х2, Y2). Стороны секто ра лежат на лучах, проходящих из центра эл липса через точки (ХЗ, Y3) и (Х4, Y4) procedure Строит многоугольник, используя массив коор Points: array of TPoint);

динат точек Points. При этом последняя точка соединяется с первой и внутренняя область закрашивается procedure Строит ломаную линию, используя массив ко Points: array of TPoint);

ординат точек Points procedure Строит кривую Безье (кубический сплайн), ис Points: array of TPoint);

пользуя массив координат точек Points procedure Строит кривую Безье (кубический сплайн), ис Points: array of TPoint);

пользуя массив координат точек Points. Теку щая точка используется в качестве первой procedure Yl, Рисует прямоугольник с верхним левым углом X2, Y2: Integer);

в и нижним правым в (Х2, Y2) procedure Yl, Рисует прямоугольник с закругленными угла X2, Y2, X3, Y3: Integer);

ми. Координаты вершин — те же, что и в методе Rectangle. Закругления рисуются как сегмен ты эллипса с размерами осей по горизонтали и вертикали и Y function Задает высоту строки Text в пикселах Text: string): Integer;

function Задает ширину строки Text в пикселах Text: string): Integer;

procedure Y:

Производит вывод строки Text. Левый верхний Integer;

const Text:

угол помещается в точку канвы (X, Y) string);

procedure Производит вывод текста с отсечением. Как и TRect;

X, Y: Integer;

в TextOut, строка Text выводится с позиции const Text: string);

(X, Y);

при этом часть текста, лежащая вне пре делов прямоугольника Rect, отсекается и не будет видна Глава Использование графики Таблица 10.5. Свойствакласса Описание property ClipRect: TRect;

Определяет область отсечения канвы. То, что при рисовании попадает за пределы этого прямо угольника, не будет изображено. Свойство доступ но только для чтения — его значение переустанав ливается системой в контексте устройства, с кото рым связана канва property PenPos: Содержит текущую позицию пера канвы (изменя ется посредством метода Метод procedure Refresh;

сбрасывает текущие шрифт, перо и кисть, заменяя их на стандартные, за имствованные из установок Windows Предусмотрено два события для пользовательской реакции на изменение канвы:

property OnChange: TNotifyEvent;

property OnChanging: TNotifyEvent;

Эти события возникают при изменении свойств и вызове методов TCanvas, меняющих вид канвы (т. е. при любом рисовании. В методе MoveTo, напри мер, они не возникают). Отличие их в том, что событие OnChanging вызывает ся до начала изменений, а событие OnChange — после их завершения.

Идентификатор (код) растровой операции при копировании прямоугольных блоков содержится в свойстве property TCopyMode;

TCopyMode = Longint;

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

cmNotSrcErase, cmPatCopy, cmPatPaint, Все они стандартно определены в Windows, и подробное их описание можно найти в документации GDI. Значением свойства CopyMode по умолчанию является cmSrcCopy — копирование пикселов источника поверх существующих.

Из возможностей, появившихся в классе TCanvas, следует отметить под держку рисования кривых (полиномов) Безье. Эта возможность впервые Часть II. Интерфейс и логика приложения появилась в API Windows NT. Для построения одной кривой нужны мини мум четыре точки — начальная, конечная и две опорные. По ним будет по строена кривая второго порядка. Если задан массив точек, они используют ся для построения последовательных кривых, причем последняя точка од ной кривой является первой для следующей кривой.

Хорошей иллюстрацией использования объекта TCanvas может служить при мер GraphEx, поставляемый вместе с (папка \Demos\Doc\GraphEx).

Есть только одно "но" — он приводится в неизменном виде, начиная с вер сии Delphi 1.0. Поэтому сделаем часть работы за программистов Borland.

В нашем примере модернизированы Панели инструментов — они выполне ны на компонентах и добавлена поддержка файлов JPEG;

и, наконец, добавлена возможность рисования кривых Безье. Обнов ленный внешний вид главной формы примера GraphEx показан на рис. 10.1.

GraphEx v. File \ о Line Origin: (40, 44) 'Current: (194, 168) 10.1. Так теперь выглядит главная форма примера GraphEx Где же найти ту канву, на которой предстоит рисовать? Во-первых, ею ПОТОМКИ TGraphicControl И Т. е.

все визуальные компоненты из Палитры компонентов;

в том числе и фор ма. Во-вторых, канву имеет растровая картинка (класс TBitmap);

вы можете писать и рисовать не на пустом месте, а на готовом изображении (об этом см. ниже в разд. "Класс TBitmap" данной главы). Но иногда нужно рисовать и прямо на экране. В этом случае придется прибегнуть к использованию Глава Использование графики функций API. Функция GetDC возвращает контекст устройства заданного окна, если ей передается параметр 0 — то всего экрана:

ScreenCanvas := := // Рисование на ScreenCanvas ReleaseDC(0, Пример необходимости рисования на экране — программы сохранения экрана (Screen savers).

Когда и следует рисовать? Этот вопрос далеко не риторический, как может показаться с первого взгляда.

Помимо графических примитивов, таких как линии и фигуры, на канве мож но разместить готовые изображения. Для их описания создан класс Класс TGraphic Канва, перо, кисть и шрифт нужны, чтобы нарисовать свою картинку. Что бы загрузить готовую, необходимы объекты, "понимающие" графические форматы Windows.

Абстрактный класс TGraphic является родительским для трех видов изобра жений, общепринятых в графике Windows — значка (компонент ме тафайла (компонент И растровой КарТИНКИ (компонент Четвертым потомком TGraphic является — сжатая растровая кар тинка в формате JPEG.

Работая над приложением в Delphi, вы никогда не будете создавать объекты класса TGraphic, но переменной этого типа вы можете присваивать указа тель на любой из перечисленных классов-потомков.

Метод:

procedure TPersistent);

переопределяет одноименный метод предка, допуская полиморфное при сваивание графических объектов.

Загрузку и выгрузку графики в поток осуществляют методы:

procedure TStream);

procedure TStream);

а загрузку и выгрузку в файл — методы:

procedure Filename: string);

procedure Filename: string);

234 Часть II. Интерфейс и логика приложения Эти методы создают соответствующий файловый поток и затем вызывают Два метода осуществляют взаимодействие с буфером обмена Windows:

procedure Word;

AData: THandle;

APalette: HPALETTE);

procedure AFormat: Word;

var AData: THandle;

var APalette: HPALETTE);

Здесь AFormat — ИСПОЛЬЗуеМЫЙ формат;

AData И APalette — данные и палитра (если она требуется). Потомок должен иметь свой формат представления в буфере обмена и уметь обрабатывать данные, представлен ные в нем.

Загрузка больших графических файлов может продолжаться очень долго.

Чтобы скрасить пользователю ожидание, программист может обработать событие type TProgressStage = (psStarting, psEnding);

TProgressEvent procedure (Sender: TObject;

Stage: TProgressStage;

PercentDone: Byte;

Boolean;

const R: TRect;

const Msg: string) of object;

property OnProgress: TProgressEvent;

Оно вызывается графическими объектами во время длительных операций.

Параметр stage означает стадию процесса (начало/протекание/завершение), a PercentDone — процент сделанной работы. Сразу оговоримся, что не все из тех объектов, которые будут нами описаны, вызывают обработчик события OnProgress.

Свойство:

property Empty: Boolean;

устанавливается в значение True, если графический объект пуст (в него не загружены данные).

Высота и ширина графического объекта задаются свойствами:

property Height: Integer;

property Width: Integer;

Для каждого дочернего типа эти параметры вычисляются своим способом.

property Modified: Boolean;

показывает, модифицировался ли данный графический объект. Это свойст во устанавливается в значение True внутри обработчика события onChange.

Глава Использование графики Многие графические объекты при отрисовке должны быть прозрачными.

Одни из них прозрачны всегда (значок, метафайл), другие — в зависимости от значения свойства property Transparent: Boolean;

Класс TPicture Это класс-надстройка над точнее — над его потомками. Он имеет поле Graphic, которое может содержать объекты классов TBitmap, и Предназначение класса TPicture — управлять вызо вами соответствующих методов, скрывая при этом хлопоты с определением типа графического объекта и детали его реализации.

Кроме того, на уровне TPicture определены возможности регистрации и использования других — определенных пользователем — классов графиче ских объектов, порожденных от Доступ к графическому объекту осуществляется посредством свойства:

property Graphic: TGraphic;

Если графический объект имеет один из трех предопределенных типов, то к нему можно обратиться и как к одному из свойств:

property Bitmap: TBitmap;

property Icon:

property Metafile: TMetafile;

Обращаясь к этим функциям, нужно быть осторожным. Если в поле Graphic хранился объект одного класса, а затребован объект другого класса, то прежний объект уничтожается, а вместо него создается пустой объект тре буемого класса. Например:

//Создан и загружен объект класса := // прежний уничтожается Если же вы описали свой класс (допустим, то к его методам и свойствам следует обращаться так:

(Graphic as := MyValue;

Перечислим остальные методы и свойства.

procedure Filename: string);

Анализирует расширение имени файла и если оно известно (за регистрировано), то создается объект нужного класса и вызывается его метод LoadFromFile. В противном случае возникает исключительная си туация Стандартными расширениями являются ico, 236 Часть II. Интерфейс и логика приложения и bmp. Если подключить к приложению модуль то можно будет загрузить и файлы с расширениями jpg и jpeg.

procedure Filename: string);

Сохраняет графику в файле, вызывая соответствующий метод объекта procedure Word;

AData: THandle;

APalette: HPALETTE);

Во многом аналогичен методу Если формат AFormat най ден среди зарегистрированных, то AData и APalette передаются для загрузки методу соответствующего объекта. Изначально зарегистри рованных форматов три: битовое изображение метафайлы И procedure AFormat: Word;

var AData: THandle;

var APal ette: HPALETTE);

Сохраняет в буфере обмена, вызывая метод объекта procedure TPersistent);

Метод Assign переписан таким образом, чтобы присваиваемый объект мог принадлежать как классу TPicture, так и TGraphic или любого его по томка. Кроме того, он может быть равен nil — в этом случае поле Graphic очищается с удалением прежнего объекта.

class function Word): Boolean;

Метод класса возвращает значение True, если формат AFormat поддержи вается классом TPicture (зарегистрирован в системе). Напомним, что методы класса можно вызывать через ссылку на класс без создания экземпляра объекта.

class procedure AExtension, ADescription:

string;

TGraphicClass);

class procedure Word;

AGraphicClass:

TGraphicClass);

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

property Width: Integer;

property Height: Integer;

Ширина и высота картинки. Значения этого свойства всегда те же, что и У Объекта ИЗ СВОЙСТВа Все три разновидности графических объектов имеют свои системы кэширо вания. Это означает, что на один реально существующий в системе (и за Глава Использование графики нимающий долю ресурсов!) дескриптор могут одновременно ссылаться не сколько объектов. Реализуется такое связывание через метод Assign. Выра жение:

;

означает, что два этих объекта разделяют теперь один, фактически находя щийся в памяти, значок.

Более простым является кэширование для классов и кото рые умеют только отображать себя и не предназначены для редактирования (создатели Delphi считают, что дескриптор графического объекта дается программисту не для того, чтобы "ковыряться" в нем на уровне двоичных кодов). Гораздо сложнее устроен механизм кэширования для канала который имеет свою канву для рисования.

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

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

Кого-то может удивить отсутствие объявленных методов рисования, вроде метода Draw для классов Ticon, и TBitmap. Объяснение простое — в процессе рисования они играют пассивную роль;

рисуют не они — рисуют их. Все рисование должно осуществляться через вызовы методов Draw и stretchDraw канвы, содержащей графику, ибо канва соответствует тому кон тексту, в котором должна осуществляться операция.

Рассмотрим предопределенные графические классы.

Класс TMetafiie Инкапсулирует свойства метафайла Windows. С появлением Windows к стандартному метафайлу (формат WMF) добавился расширенный (формат EMF), обладающий расширенными возможностями. Соответственно в объ екте TMetafiie имеется property Enhanced: Boolean;

Внутреннее представление метафайла всегда новое (EMF), и устанавливать свойство Enhanced в значение False следует только для обеспечения совмес тимости со старыми программами.

В TMetafiie перекрываются методы Assign, В буфер об 238 Часть II. Интерфейс и логика приложения мена объект помещает свое содержимое в формате ПОМИМО общих, класс имеет следующие свойства:

дескриптор метафайла property Handle:

• свойство property Word. Число точек на дюйм в координатной системе метафайла. Связано с установленным режимом отображения;

свойства property Integer;

property Integer;

это настоящие высота и ширина метафайла в единицах, равных 0,01 мм.

Свойства Height и width задаются в пикселах;

в метафайл можно добавить свою палитру:

property Palette: HPalette;

вы можете увековечить себя, установив два свойства метафайла:

property Description: string;

property string;

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

Класс Этот класс инкапсулирует значок Windows.

пытайтесь изменить размеры значка — они по определению постоянны (и равны И И попытке присвоить новые значения возникает исключительная ситуация Значок нельзя также читать и писать в буфер об мена, т. к. в Windows нет соответствующего формата.

Свойство Transparent для значка всегда равно значению True. Изменить его нельзя — значки прозрачны также по определению.

В ЭТОМ классе перекрываются методы TGraphic: Assign, LoadFromStream и SaveToStream. Дополнительно также определены:

property Handle: — Дескриптор function ReleaseHandle: HICON;

— "отдает" дескриптор — ВОЗВра щает его значение, обнуляя ссылку на него в объекте.

Класс TBitmap Класс TBitmap является основой растровой графики в Delphi. В первых вер сиях среды этот класс соответствовал битовой карте, зависимой от устрой Глава 10. Использование графики ства (Device Dependent Bitmap, DDB). Этот формат хорош для деловой гра фики — отображения небольших картинок с малой глубиной цвета, напри мер, на кнопках. Формат DDB появился во времена первых версий Windows, когда еще не было графических ускорителей и кое-где еще пом нили о EGA. Поэтому и форматы хранения были привязаны к определен ным видеорежимам.

Со временем аппаратура совершенствовалась, росло и количество поддер живаемых видеорежимов. Появились режимы High Color (15—16 бит на точку) и True Color (24 бита на точку). Все это привело к тому, что картин ка стала храниться в аппаратно-независимом формате (Device Independent Bitmap, DIB), а проблемы ее быстрого отображения легли на аппаратуру и драйверы.

За формат битовой карты — DIB или DDB — отвечает свойство:

type = property HandleType: TBitmapHandleType;

По умолчанию устанавливается режим bmDIB. Впрочем, можно заставить приложение, написанное на Delphi, вернуться к старому типу. Для этого нужно установить глобальную переменную (модуль в значение True. Впрочем, необходимость этого сомнительна. Все новые видеокарты и драйверы к ним, а также графические интерфейсы (такие, как DirectX) оптимизированы для использования DIB.

Желаемую глубину цвета битовой карты можно узнать и переустановить, меняя значение свойства:

= (pfDevice, pflbit, pf4bit, pf8bit, pfl5bit, pfl6bit, pf24bit, property TPixelFormat;

Режим pfDevice соответствует битовой карте DDB. Глубина цвета в 1, 4 и бит на пиксел — традиционная и предусматривает наличие у изображения палитры. Другие режимы заботятся о хранении непосредственных яркостей точек в каждом из трех основных цветов — красном (R), зеленом (G) и си нем (В). Разрядность 15 бит соответствует распределению бит 5-5- (RGB555), 16 бит — RGB 565, 24 бит — RGB888. Режим 32 бит похож на 24-битный, но в нем дополнительно добавлен четвертый канал (альфа канал), содержащий дополнительную информацию о прозрачности каждой точки. Режим предназначен для реализации программистом собст венных графических конструкций. В стандартном классе TBitmap установка свойства PixelFormat в режим приведет к ошибке — поэтому ис пользовать его нужно только в написанных вами потомках TBitmap.

Битовая карта является одним из видов ресурсов. Естественно, что класс TBitmap поддерживает загрузку из ресурсов приложения:

240 Часть II. Интерфейс и логика приложения procedure Integer);

procedure THandle;

const string);

Здесь instance — это глобальная переменная модуля System, хранящая уни кальный идентификатор запущенной копии приложения (или динамиче ской библиотеки).

Канва битовой карты доступна через свойство:

property TCanvas;

С ее помощью можно рисовать на поверхности растрового изображения.

Обратите внимание, что никакие другие потомки TGraphic канвы не имеют.

Дескрипторы битовой карты и ее палитры доступны как свойства:

property Handle: HBITMAP;

property Palette: HPALETTE;

Имея дело с классом TBitmap, учитывайте, что принцип "один объект — один дескриптор" из-за наличия механизма кэширования неверен. Два метода:

function ReleaseHandle: HBITMAP;

function ReleasePalette: HPALETTE;

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

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

procedure Dormant — выгружает изображение в поток и уничтожает деск рипторы битовой карты и палитры;

procedure — "освобождающий" дескриптор битовой карты дальнейшего использования и внесения изменений. Это означает, что если на данный дескриптор есть ссылки, то он дублируется;

поток очищается.

Битовая карта может быть монохромной и цветной, что определено свой ством:

property Monochrome: Boolean;

Значение True соответствует монохромной битовой карте. При его измене нии происходит преобразование содержимого к требуемому виду.

За прозрачность битовой карты отвечают следующие свойства:

property TransparentColor: TColor;

type TTransparentMode = property TransparentMode: TTransparentMode;

Глава 10. Использование графики Если свойство установлено в режим tmAuto, то за прозрач ный (фоновый) принимается цвет верхнего левого пиксела. В противном случае этот цвет берется из свойства Битовая карта может использоваться в качестве маски для других битовых карт. В этом случае она превращается в двухцветную, где в белый цвет окра шиваются точки фона (см. свойство Transparentcoior), а в черный — все остальные. Для поддержки этого режима служат следующие методы и свойства:

procedure TColor);

property MaskHandle:

function ReleaseMaskHandle: HBitmap;

Наконец, последним счету будет рассмотрено очень важное свойство би товой карты Если формат ее хранения — DIB, то есть возможность получить доступ к данным самой битовой карты:

property Integer]: Pointer;

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

поэтому не удивляйтесь, если с ростом параметра значение свойства уменьшается.

Внутри строки данные упорядочены в соответствии с форматом Для формата pf8bit все просто — каждый байт в строке соот ветствует одному пикселу. Для форматов и пикселу соответ ствуют два байта (в этих 16 битах упакованы данные о трех каналах), — три байта (по байту на канал).

Примерно так может выглядеть обработчик события выводя щий на панель состояния информацию о яркости в данной точке (подразу мевается, что формат битовой карты — 8 или 24 бита):

procedure TObject;

Shift: TShiftState;

X, Y: Integer);

begin if not then Exit;

with Imagel. do case PixeiFormat of pf8bit: := %d y: %d b: %d',[x, y, ]);

pf24bit: SimpleText := FormatCx: %d y: %d R: %d,G: B:

[x,y, 3*x+l], 3*x+2]]);

end;

Часть II. Интерфейс и логика приложения Само значение свойства scanLine изменить нельзя (оно доступно только для чтения). Но можно изменить данные, на которые оно указывает. Вот так можно получить негатив 24-битной картинки:

Var line : pByteArray;

For i:=0 to — 1 do Begin Line := For j:=0 to 3 - 1 do := 255 End;

Pages:     | 1 | 2 || 4 | 5 |   ...   | 10 |



© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.