Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var TempPanel:TPanel;//Объявляю переменную для панели



//Получить время
DecodeCTime(Time, H, M, S);
//Получить предыдущее время.
DecodeCTime(PrevTime, Hp, Mp, Sp);

//Сгенерировать событие OnSecond
if Assigned(FOnSecond) then FOnSecond(Self);
//Сгенерировать событие OnMinute
if Assigned(FOnMinute) AND (Mp < M) then FOnMinute(Self);
//Сгенерировать событие OnHour
if Assigned(FOnHour) AND (Hp < H) then FOnHour(Self);


//Сохранить текущее время в PrevTime
PrevTime:= Time;

if (NOT ShowSecondArrow) AND (Sp <= S) then exit;

//Прорисовать часы. DrawArrows; end;

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

У нас уже объявлено три события в разделе private компонента:

FOnSecond, FOnMinute, FOnHour: TNotifyEvent;

Все они появятся на закладке Events окна объектного инспектора, когда ты поставишь компонент на форму. Чтобы приложения могло поймать эти события, мы объявили переменные типа TNotifyEvent (это тип означает события).

Но всё это пока что переменные, а как же Delphi узнает, что это события, которые надо поместить на закладку Events объектного инспектора? Для этого, в разделе published мы должны описать само событие OnSecond и другие:

property OnSecond: TNotifyEvent read FOnSecond write FOnSecond; property OnMinute: TNotifyEvent read FOnMinute write FOnMinute; property OnHour: TNotifyEvent read FOnHour write FOnHour;

Вначале каждой строки стоит ключевое слово property, которое говорит о том, что мы объявляем свойство. После этого идёт имя свойства и после двоеточия стоит тип. Вот как раз по типу, Delphi и узнает, что объявленное свойство на самом деле событие.

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

Для генерации события мы пишем следующий код:

FOnSecond(Self) для события OnSecond.

FOnMinute(Self) для события OnMinute.

FOnHour(Self) для события OnHour.

При генерации события в качестве переменной передаётся Self. Эта переменная всегда указывает на объект, в котором мы сейчас находимся, в данном случае компонент TGraphicClock. Вспомни любой обработчик события. В любом из них есть как минимум один параметр – переменная Sender, которая указывает на объект, который сгенерировал событие. Теперь посмотри на код, которым мы генерируем событие. Как видишь, мы при генерации указываем объект Self, который генерирует событие и пользователь получит его в переменной Sender обработчика события.

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

if Assigned(FOnSecond) then FOnSecond(Self);

Теперь наш компонент сможет генерировать события.

С событиями вроде всё ясно (если нет, то посмотри на исходник на диске и попробуй разобраться, глядя на общую картину). Теперь переходим к свойствам. В разделе published мы можем создавать свойства, которые будут отображаться в объектном инспекторе при выделении наших часиков. Все свойства, которые есть у предка нужно просто описать

published property Align; property Enabled; property ParentShowHint; property ShowHint; property Visible;

Все эти свойства мы получаем от предков TGraphicControl, TControl и так далее. Слово property говорит о том, что мы описываем свойство. Для них не нужны процедуры или функции, потому что эти свойства уже есть у предка. Нам надо только описать их и всё. Я описал только маленькую часть из доступных у TGraphicControl функций. Ты можешь добавить любые из доступных. Чтобы узнать, какие функции можно добавлять, открой помощь (меню Help->Delphi Help) и найди там объект TGraphicControl. Щёлкни по нему дважды и в появившейся справке выбери пункт Properties (вверху окна). Появится окно с перечнем всех свойств. Ты можешь добавить любое из них. Например, чтобы добавить свойство Action нужно написать в разделе published:

published property Action;

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

property BGBitmap:TBitmap read FBGBitmap write SetBGBitmap;

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

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

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

Итак, для записи свойства BGBitmap я написал следующую процедуру:

procedure TGraphicClock.SetBGBitmap(Value: TBitmap);

begin FBGBitmap.Assign(Value); invalidate;

end;

В первой строке я просто копирую в переменную FBGBitmap переданную в качестве параметра картинку. Во второй строке я заставляю наш компонент прорисоваться.

Теперь ты можешь изменять фон простой операцией GraphicClock1.BGBitmap:=bitmap.

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

property ClockStyle:TClockStyle read FClockStyle write SetStyleStyle default scAnalog;

Мы объявляем свойство ClockStyle типа TClockStyle. Тип TClockStyle мы должны описать в самом начале, до описания нашего объекта TGraphicClock, в разделе type:

type TClockStyle = (scAnalog, scDigital);

TGraphicClock = class(TGraphicControl) private Ticker: TTimer;

Строка TClockStyle = (scAnalog, scDigital) -объявляет список переменных, которые и будут выпадать по выбору свойства.

Всё остальное происходит так же, за исключением нового слова default, которое устанавливает значение по умолчанию для данного свойство - scAnalog.

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

На компакт диске, в директории \Примеры\Глава 19\Component ты можеш ь увидеть приме р это й программы.

19.4 Создание иконки компонента.

сли ты установил созданный нами компонент в Delphi, то заметил, что он имеет абсолютно некрасивую картинку. Эта иконка выбирается по умолчанию для всех компонентов, если у них нет своей. У нас пока нет своей иконки и её

сейчас предстоит создать.

Для создания иконки будем пользоваться программой Image Editor, которая входит в поставку Delphi. Её главное окно ты можешь увидеть на рисунке 19.4.1. Здесь нужно выбрать из меню File пункт Newi и затем Component Resource File (.dcr) (файл ресурсов для компонентов). Программа создаст новое окно, содержащее дерево, в котором пока только один элемент Contents.

Для создания нового элемента, нужно щёлкнуть правой кнопкой мыши в созданном окне проекта ресурса и в появившемся меню выбрать пункт New->Bitmap. Перед тобой откроется окно свойств создаваемой картинки (рисунок 19.4.2).

Ширина и высота (параметры Width и Height) картинки должны быть равны 24. Режим оставляем VGA, потому что для иконки 16 цветов достаточно. Жми «ОК». Теперь у тебя в дереве элементов ресурсов появился раздел Bitmap и в нём наша картинка Bitmap1 (рисунок 19.4.3). Щёлкни по элементу Bitmap1 и в появившемся меню выбери пункт Rename. Переименуй нашу картинку, дав ей имя нашего компонента TGraphicClock. Картинка обязательно должна иметь то же имя, что и компонент, которому она предназначена, потому что в одном файле исходника может быть несколько компонентов и по имени картинки Delphi будет определять, к какому компоненту она относиться.

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

Теперь открывай в Delphi наш пакет othercomponents.dpk. Удали из него файл исходника часов и откомпилируй пакет. Это заставит Delphi удалить из оболочки наш компонент. После этого снова добавь файл исходника и откомпилируй пакет ещё раз. Теперь компонент установился обратно, но уже с нашей иконкой.

На компак т диске, в директории \Примеры\Глава 19\IconForComponent ты можешь увидет ь приме р мое й иконки.

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var TempPanel:TPanel;//Объявляю переменную для панели

begin // Создаю панель. В скобках у Create указан будущий владелец TempPanel:=TPanel.Create(Form1);

TempPanel.Left:=X; // Устанавливаю левую и правую координату TempPanel.Top:=Y; // в X и Y позицию, где нажата кнопка мыши

TempPanel.Width:=20; // Устанавливаю ширину TempPanel.Height:=20; // Устанавливаю высоту

// Далее устанавливаю обработчик нажатия на эту панель TempPanel.OnMouseDown:=PanelMouseDown;

// Добавляю панель в контейнер CompList (CompList.Add) // и сохраняю результат в TempPanel.Tag TempPanel.Tag:=CompList.Add(TempPanel);

Form1.InsertControl(TempPanel); // Вставляю панель на форму end;

Для начала вспомним, что это за свойство Tag у компонента TPanel. Это просто целое значение, которое ты можешь использовать по своему усмотрению. Именно этим свойством мы и будем часто пользоваться во время программирования нашего примера.

Теперь разберём написанный код. В разделе var я объявил одну переменную TempPanel типа TPanel. Это временная переменная, в которой будет инициализироваться новая панель. В первой же строчке кода обработчика я инициализирую эту переменную, как панель. В качестве параметра методу Create я должен передавать имя объекта, который будет являться родителем создаваемого компонента. Я передаю нашу главную форму, потому что компонент будет размещаться именно на нём.

Следующим этапом, я устанавливаю левую и правую позицию панели в координаты, где мы щёлкнули мышкой (X и Y, которые нам переданы в обработчике, указывают на точку, в которой была нажата кнопка мышки). Далее, я устанавливаю ширину и высоту панели. Я решил занести туда значение 20 (просто так захотелось).

Теперь об обработчике события TempPanel.OnMouseDown. Я туда засунул имя функции PanelMouseDown. Но такой функции нет среди стандартных функций и среди моего проекта. Поэтому мы должны её создать сами. Как это сделать эффективно? Вот тебе мой совет:

1. 1. Мы создаём обработчик для TPanel, поэтому временно поставь один экземпляр панели на форму в произвольное место.

2. 2. Создай для него обработчик на OnMouseDown и переименуй его в PanelMouseDown.

3. 3. Напиши нужный текст (я его покажу ниже) и можно удалять временно созданный на форме экземпляр TPanel.

Таким образом, ты можешь быть уверен, что ошибок не будет, потому что Delphi сама пропишет функцию PanelMouseDown где надо и укажет все необходимые параметры.

Если захочешь объявлять эту функцию вручную, то напиши в разделе private:

procedure PanelMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);

Объявлять можно и до private, там где объявляет Delphi обработчики событий. А ниже опиши саму функцию

procedure TForm1.PanelMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

Begin

end;

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

Всё, панель готова и её надо сохранить в нашем контейнере CompList. Для этого нужно выполнить метод Add нашего контейнера, в качестве параметра передать ему нашу панель:

CompList.Add(TempPanel)

Этот метод добавит панель в контейнер и вернёт нам индекс компонента в контейнере. Этот индекс я сохраняю в свойстве Tag нашей панели TempPanel. Это свойство абсолютно не влияет на сам компонент, а мне этот индекс пригодится.

Теперь давай посмотрим на функцию PanelMouseDown, которая должна быть такой:

procedure TForm1.PanelMouseDown(Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

begin Label1.Caption:=IntToStr(TPanel(CompList.Items[TPanel(Sender).Tag]).Left); Label2.Caption:=IntToStr(TPanel(Sender).Left);

end;

Здесь две строки. Обе строки выполняют одно и тоже, но по-разному. Обе строки записывают в свой ТLabel левую позицию панели, по которой ты щёлкнул.

Первая строка, чтобы получить левую позицию панели использует CompList, а вторая работает с панелью напрямую. Рассмотрим сначала вторую строку. В ней основным является выражение TPanel(Sender).Left. Sender -передаётся нам процедурой обработчиком PanelMouseDown. В нём записан указатель на объект, который сгенерировал событие OnMouseDown. В нашем случае это будет указатель на панель, по которой ты щёлкнул. Так как мы точно уверены, что это панель, то мы так и показываем TPanel(Sender). Этим мы приводим Sender к TPanel и теперь ты можешь использовать все свойства и методы панели, для примера нам достаточно свойства Left. Если бы мы знали точное имя панели, то этого писать не пришлось бы. Но это невозможно, потому что все создаваемые в Runtime панели (а их можно создать любое количество) у нас используют один обработчик нажатия мышкой, и мы не знаем, по какой именно панели был произведён щелчок. Получив значение левой позиции, мы переводим целое значение левой позиции в строку с помощью IntToStr.

Первоя строка очень похожа на вторую, только внутри TPanel() мы используем не Sender, а CompList.Items[TPanel(Sender).Tag], т.е. значение из конейнера. Чтобы получить первое значение из контейнера, нужно написать CompList.Items[0], для второго CompList.Items[1], для третьего CompList.Items[2] и т.д. Но по какой именно панели произведён щелчок? Чтобы это узнать я пишу TPanel(Sender).Tag, то есть получаю свойство Tag (там хранятся индекс панели) панели сгенерировавшей событие. Далее, всё происходит так же.

Запусти пример и пощёлкай по форме. По каждому щелчку будут создаваться панели. Потом попробуй пощёлкать по самим панелям. На двух TLabel будут появляться значения левой позиции панель, по которым ты щёлкал.

Теперь я хочу тебе показать ещё несколько интересных свойств и методов, которые есть у контейнера TList:

Count – в этом свойстве храниться количество элементов в контейнере.

Items – здесь хранятся ссылки на элементы контейнера. Для доступа к ссылкам

нужно написать Items[Индекс элемента]. Clear – очистить список. Delete – удалить элемент из списка. В качестве единственного параметра нужно

указать индекс удаляемого элемента. Exchange – поменять в контейнере местами два элемента. Здесь два параметры – индексы меняемых местами компонентов. First – получить указатель на первый элемент списка. Это то же самое, что и записать Items[0].

IndexOf – получить индекс указанного в качестве параметра объекта. Допустим, что ты знаешь объект (TPanel) и хочешь узнать, под каким индексом он расположен в контейнере. В этом случае ты можешь написать следующий код: CompList.IndexOf(Panel1). Если такой панели не найдено в списке, то тебе будет возвращено значение –1, иначе правильный индекс указанной панели.

Insert – вставить новый элемент. У этого метода два параметра – индекс, под которым надо вставить элемент и сам элемент. Last – получить последний элемент списка. Это то же самое, что использовать свойство Items[Count - 1]. Move – переместить элемент в новое место. У метода два параметра – индекс элемента, который надо переместить и индекс, который должен получить элемент.

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

Remove – удалить элемент. В качестве параметра нужно указывать элемент, который надо удалить, например CompList.Remove(Panel1).

Давай добавим в наш пример возможность удаления панели с формы и из контейнера. Это будет происходить по нажатию правой кнопки мышки по компоненту. Добавь с конец процедуры PanelMouseDown следующий код:

if Button=mbRight then

begin
index:=TPanel(Sender).Tag;
TPanel(CompList.Items[index]).Free;
CompList.Delete(index);

for i:=index to CompList.Count -1 do TPanel(CompList.Items[i]).Tag:=TPanel(CompList.Items[i]).Tag-1; end;

В разделе var этой процедуры нужно объявить две переменные index и i. Обе они будут числами целого типа.

Теперь разберём код. Сначала я проверяю, если нажата правая кнопка мыши, то нужно удалить компонент, по которому щёлкнули. Для этого, я сначала сохраняю индекс компонента TPanel(Sender).Tag в переменной index. Это необходимо, потому что после уничтожения компонента TPanel(Sender) не будет существовать, и я не смогу получить доступ к его свойству Tag.

Следующим этапом происходит удаление. Сначала я уничтожаю сам компонент - TPanel(CompList.Items[index]).Free, а после это уничтожаю ссылку на него в контейнере - CompList.Delete(index). Вроде бы всё удалили, но программа после этого будет работать неправильно. Допустим, что у нас в контейнере было 5 элементов, и мы удалили 3-й. По идее, в контейнере должны остаться элементы с индексами 1, 2, 4, 5, а 3-й должен отсутствовать. Но реально индексы перестроятся, и мы увидим индексы 1, 2, 3, 4. Если мы попытаемся оставить всё так, как есть, то программа будет нестабильна. Если ты щёлкнешь, по последней созданной панели, то программа обратиться к элементу в контейнере под номером 5, потому что в свойстве Tag панели находиться цифра 5. Но такого элемента не существует и произойдёт ошибка. Поэтому нам надо подкорректировать свойства Tag у всех панелей начиная с удаляемой. Для этого я запускаю цикл от индекса удаляемой панели до последнего элемента списка и уменьшаю их свойство Tag:

for i:=index to CompList.Count -1 do TPanel(CompList.Items[i]).Tag:=TPanel(CompList.Items[i]).Tag-1;

Вот теперь у меня в контейнере будут элементы с индексами от 1 до 4 и у всех панелей на форме в свойстве Tag будут правильные значения.

На компакт диске, в директории \Примеры\Глава 25\Runtime ты можеш ь увидеть приме р это й программы.

25.3. Тест на прочность.

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

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

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

Итак, нам придётся написать де программы:

1. 1. Редактор тестов. В ней будут создаваться тесты, заполняться вопросы, на которые надо будет отвечать и варианты правильных ответов.

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

Итак, начнём мы с редактора, потому что сначала нужно научиться создавать тесты, а потом уже будем их отображать и работать с ними. Для начала я создал главную форму, как показано на рисунке 25.3.1. Здесь у меня главное окно, в котором есть главное меню программы, панель кнопок быстрого вызова команд ToolBar и строка состояния, которая будет отображать подсказки. Попробуй создать что-то подобное.

На форме у меня созданы следующие кнопки (и соответствующий пункты меню):

1. 1. Создать;

2. 2. Открыть;

3. 3. Сохранить;

4. 4. Печать;

5. 5. Настройки программы;

6. 6. Помощь;

7. 7. О программе;

8. 8. Выход.

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

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





Дата публикования: 2014-11-04; Прочитано: 895 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.021 с)...