Мегаобучалка Главная | О нас | Обратная связь


Проект. Основные принципы



2015-11-12 447 Обсуждений (0)
Проект. Основные принципы 0.00 из 5.00 0 оценок




Один из возможных проектов - сконвертировать текстовый симулятор в «нечто» с удобным GUI интерфейсом (этот симулятор используется для изучения архитектуры компьютеров и языка ассемблер). Для такого проекта было бы неплохо иметь индикатор для отображения нулевых или единичных значений, и, хотя удобно было бы воспользоваться стандартным компонентом, ни один из них не имеет таких свойств.

Первое, с чего следует начать при написании компонента - это решить, что именно он должен делать. Компонент, который создается, должен быть одного цвета, когда он включен, и другого, когда выключен. Он также должен иметь свойство On/Off. OK, это звучит очень просто (первое правило - пытаться сохранить проект простым!).

Следующий шаг - решить, на каком компоненте будет основан создаваемый. Здесь стоит взглянуть на иерархию VCL. Ясно, что компонент будет разновидностью TGraphicControl, и уж если надо сохранить компонент простым, почему бы не сделать его простой окружностью вместо сложного битмапа. TShape на первый взгляд, кажется идеальным. Стоит познакомиться с ним поближе (это можно сделать с помощью хелпа или просто, поместив его на форму и внимательно взглянув на его свойства и методы). У него есть не нужные свойства и методовы, но, по крайней мере, в него встроена вся необходимая функциональность.

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

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

Как создать компонент

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

Шаг 1: Создание тестового приложения:

· Создание нового приложения (File | New Application).

· Добавление на форму ближе к низу компонент TButton.

· Выбор File | Save All и сохранение формы как LEDForm.cpp и проекта как LEDProj.

Шаг 2: Создание компонента:

· Выбор Component | New, или, в C++Builder v1 File | New и выбор Component в диалоге New File Items.

· Установка Ancestor type в TShape, имя класса TLED, Palette Page в "Samples". Щелчек "OK" без нажатия "Install" если используется версия 3 или старше (см. рис. 2.1.).

· Сохранение нового файла компонента. Если используется C++Builder v1 сохранение его под именем LED.cpp (несмотря на то, что по умолчанию unit1.cpp).

 

Рис. 2.1. Создание компонента

Теперь, когда предварительные действия завершены, можно посмотреть, что же сгенерировал C++ Builder. Файл LED.cpp будет выглядеть примерно так (возможно он будет слегка отличаться - в разных версиях по-разному):

 

//-----------------------------------------------------------------

 

#include <vcl.h>

#pragma hdrstop

 

#include "LED.h"

#pragma package(smart_init)

//-----------------------------------------------------------------

//-----------------------------------------------------------------

static inline void ValidCtrCheck(TLED *)

{

new TLED(NULL);

}

//-----------------------------------------------------------------

//-----------------------------------------------------------------

__fastcall TLED::TLED(TComponent* Owner)

: TShape(Owner)

{

}

//-----------------------------------------------------------------

//-----------------------------------------------------------------

namespace Led

{

void __fastcall PACKAGE Register()

{

TComponentClass classes[1] = {__classid(TLED)};

RegisterComponents("Samples", classes, 0);

}

}

//-----------------------------------------------------------------

//-----------------------------------------------------------------

 

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

Следующая функция - конструктор. Он вызывается, когда компонент помещается на форму, все его характеристики, в том числе и видимые в Object Inspector, получают значения по умолчанию именно здесь.

Третья функция регистрирует компонент. Пространства имен позволяют библиотекам (в данном случае компонентам) использовать одни и те же имена без конфликтов (на самом деле тут все немного сложнее). TComponentClass содержит массив имен компонентов, которые надо зарегистрировать (в одной этой функции можно зарегистрировать столько компонентов, сколько необходимо). Подробнее эта функция (т.е. если надо написать свою, вместо той, что подставил Builder) описана в разделе "Writing The Register Function" Component Writers Guide.

Теперь надо взглянуть на .h файл, созданный Билдером. Если этого еще не сделано, следует зайти в редактор и выбрать соответствующую закладку. На экране появится содержимое LED.h (если используется C++Builder v1, там не будет макроса PACKAGE):

 

//-----------------------------------------------------------------

#ifndef LEDH

#define LEDH

//-----------------------------------------------------------------

#include <SysUtils.hpp>

#include <Controls.hpp>

#include <Classes.hpp>

#include <Forms.hpp>

#include <ExtCtrls.hpp>

//-----------------------------------------------------------------

class PACKAGE TLED : public TShape

{

private:

protected:

public:

__fastcall TLED(TComponent* Owner);

__published:

};

//-----------------------------------------------------------------

#endif

 

Не очень много, только множество включений файлов, которые пришлось бы делать вручную. Теперь надо кое-что написать самим в дополнение к скелету, соданному Билдером. Далее будет нужна булева переменная для хранения статуса и специальные переменные для хранения цветов обоих состояний индикатора. Тип bool подойдет для статуса, а для хранения цвета имеется специальный класс (вернее, перечисляемый тип) TColor. Хорошее правило ООП - делать переменные внутри класса скрытыми, чтобы другие программисты имели к ним доступ только через интерфейс класса - это гарантия, что данные не будут испорчены. Поэтому все переменные будут в секции private. Класс будет выглядеть примерно так (пишется colour, как это принято в Великобритании и Канаде):

 

private:

bool FOnOff;

TColor FOnColour;

TColor FOffColour;

 

Префикс F в именах переменных - стандартное соглашение именования членов компонент (позволяет использовать то же имя, только без F, для соответствующего свойства (published property)). Теперь, раз есть переменные, нужны аксессоры (accessors) - функции, осуществляющие доступ, по-английски "access", к данным класса извне, и мутаторы (mutators) - функции, меняющие значения данных класса, по английски - "mutate", (здесь еще добавлены ключевые слова __published и __property). Странным может показаться, что эти функции помещены в ту же секцию, что и данные, и это будет объяснено позже:

 

private:

bool FOnOff;

TColor FOnColour;

TColor FOffColour;

void __fastcall SetOnOff(const bool Value);

void __fastcall SetOnColour(const TColor OnColour);

void __fastcall SetOffColour(const TColor OffColour);

 

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

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

Странный факт, что мутаторы объявлены как private. Здесь используется расширение фирмы Борланд в C++ ключевым словом __published. Все свойтва объекта, которыми планируется манипулировать визуально, должны быть объявлены как __published, а аксессоры и мутаторы подключаются к ним с помощью следующего синтаксиса: Если добавить три свойства в секции __published, и класс будет выглядеть примерно так:

 

__published:

__property bool LEDOn = {read = FOnOff, write = SetOnOff};

__property TColor OnColour = {read = FOnColour, write = SetOnColour};

__property TColor OffColour = {read = FOffColour, write = SetOffColour};

 

Все объявления свойств начинаются с ключевого слова __property, затем следуют тип переменной (свойства), и объявления членов доступа к свойству. Интересно, что read указывает на переменную, а write на функцию. И read, и write могут указывать и на функцию, и на переменную, но обычно делают так, чтобы write указывал на функцию, в которой есть некоторые проверки правильности значений и т.д. Имя свойства может не иметь ничего общего с именем переменной, которую оно модифицирует (например, переменная FOnOff модифицируется свойством LEDOn). Хотя рекомендуется использовать стандартные соглашения имен - имя свойства должно быть таким же, как и имя связанной с ним переменной, за исключением префикса F в начале. Это все, что нам нужно изменить в файле заголовка класса.

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

Значения по умолчанию

У свойтва может быть значение default, то есть -

 

__property bool LEDOn = {read=FOnOff, write=SetOnOff, default=false};

 

Это значение НЕ то, что видно по умолчанию в object inspector (их вы должны задать в конструкторе компонента, мы займемся этим позже). Тут заложен другой смысл. Каждый раз при добавлении компонента на форму, Билдер сохраняет информацию о нем в файле .DFM. Так как все свойства компонента определяют, как он будет выглядеть, где будет на форме и так далее, они просто сохраняются в двойчном .DFM-файле на диске. Значение по умолчанию, которое вы присваиваете компоненту, будет определять, будет ли свойство сохраняться в .DFM-файле или нет. Если значение свойства на форме совпадает со значением по умолчанию, оно НЕ будет сохраняться, а если оно другое, то сохранится.

Свойства могут быть объявлены с ключевым словом nodefault:

 

__property bool LEDOn = {read=FOnOff, write=SetOnOff, nodefault};

 

Это означает, что они ВСЕГДА будут сохраняться. Также имеется атрибут stored. Вы можете переопределить сохраняющие действия Билдера по умолчанию с помощью него. Если вы установите stored в true, свойтво всегда будет сохраняться, если в false - никогда не будет сохраняться. Кроме того, вы можете определить его с помощью метода, возвращающего логическое значение (в нижеприведенном примере функция check возвращает булево значение).

 

__property bool LEDOn = {read=FOnOff, write=SetOnOff, stored=true};

__property bool LEDOn = {read=FOnOff, write=SetOnOff, stored=false};

__property bool LEDOn = {read=FOnOff, write=SetOnOff, stored=Check};

 

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

 

__property TColor OnColour = {read=FOnColour, write=SetOnOffColour, index=1};

__property TColor OffColour = {read=FOffColour, write=SetOnOffColour, index=2};

 

Реализация функции SetOnOffColour должна выглядеть примерно так:

 

void __fastcall TLED::SetOnOffColour(int Index,TColor Colour)

{

switch(Index)

{

case 1 : FOnColour = Colour;

break;

case 2 : FOffColour = Colour;

break;

}

Brush->Color = (FOnOff)?FOnColour:FOffColour;

}

 

Вот как должен выглядеть файл LED.h:

 

//---------------------------------------------------------------------------

#ifndef LEDH

#define LEDH

//---------------------------------------------------------------------------

#include <SysUtils.hpp>

#include <Controls.hpp>

#include <Classes.hpp>

#include <Forms.hpp>

#include <ExtCtrls.hpp>

//---------------------------------------------------------------------------

class PACKAGE TLED : public TShape

{

private:

bool FOnOff;

TColor FOnColour;

TColor FOffColour;

void __fastcall SetOnOff(const bool Value);

void __fastcall SetOnColour(const TColor OnColour);

void __fastcall SetOffColour(const TColor OffColour);

 

protected:

public:

__fastcall TLED(TComponent* Owner);

__published:

__property bool LEDOn = {read = FOnOff, write = SetOnOff};

__property TColor OnColour = {read = FOnColour, write = SetOnColour};

__property TColor OffColour = {read = FOffColour, write = SetOffColour};

 

};

//---------------------------------------------------------------------------

#endif

 

CPP - файл

Самая трудная часть позади. Легкая часть - написание .cpp файла. Сначала будет написан код мутаторов:

 

//---------------------------------------------------------------------------

void __fastcall TLED::SetOnOff(const bool Value)

{

FOnOff = Value; // Set the state of FOnOff

Brush->Color = (FOnOff)?FOnColour:FOffColour;

}

//---------------------------------------------------------------------------

void __fastcall TLED::SetOnColour(const TColor OnColour)

{

FOnColour = OnColour;

Brush->Color = (FOnOff)?FOnColour:FOffColour;

}

//---------------------------------------------------------------------------

void __fastcall TLED::SetOffColour(const TColor OffColour)

{

FOffColour = OffColour;

Brush->Color = (FOnOff)?FOnColour:FOffColour;

}

//---------------------------------------------------------------------------

 

Теперь надо написать конструктор. В нем будут устанавливаться значения по умолчанию, которые будут видны в object inspector:

 

__fastcall TLED::TLED(TComponent* Owner)

: TShape(Owner)

{

Width = 15; // Установим ширину нашего LED в 15.

Height = 15; // Установим высоту нашего LED в 15.

FOnColour = clLime; // Установим OnColour лимонно-зеленым.

FOffColour = clRed; // Установим OffColour красным.

FOnOff = false; // Сосотояние по умолчанию - выключено

Shape = stEllipse; // Форма по умолчанию - эллипс

Pen->Color = clBlack; // Цвет пера - черный

Pen->Width = 1; // Толщина пера 1

Brush->Color = FOffColour; // Цвет кисти одинаков с цветом по умолчанию

}

 

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

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

 

__fastcall TLED::TLED(TComponent* Owner)

: TShape(Owner),

FOnColour(clLime), // установить OnColour в лимонно-зеленый

FOffColour(clRed) // установить OffColour красным

{

Width = 15; // Установим ширину нашего LED в 15.

Height = 15; // Установим высоту нашего LED в 15.

Shape = stEllipse; // Форма по умолчанию - эллипс

Pen->Color = clBlack; // Цвет пера - черный

Pen->Width = 2; // Толщина пера 2

Brush->Color = FOffColour; // Цвет кисти одинаков с цветом по умолчанию

}

Можно и так:

 

__fastcall TLED::TLED(TComponent* Owner)

: TShape(Owner), FOnColour(clLime), FOffColour(clRed), Width(15),

Height(15), Shape(stEllipse), Pen->Width(2),Brush->Color(FOffColour)

{}

 

Важно понимать, что fOnOff инициализировать не обязательно, так как тип bool имеет встроенное значение по умолчанию(false), в отличие от большинства простых типов C++, которые автоматически не инициализируются. Тем не менее, считается хорошим стилем инициализировать значения всех свойств.

Теперь, когда написан этот небольшой компонент, можно убедиться, что все работает. Сначала сохраняется проект (File | Save All). Затем добавляется модуль LED, который только что создан, к тестовому проекту. Чтобы это сделать, выберается (Project | Add to Project) и находится led.cpp (по умолчанию сохраненный в папку lib в v3 и v4). Потом вводится #include "LED.h" вверху LEDForm.h:

 

#include <vcl\Classes.hpp>

#include <vcl\Controls.hpp>

#include <vcl\StdCtrls.hpp>

#include <vcl\Forms.hpp>

#include "LED.h"

 

Теперь добавляется в секцию private файла LEDForm.h:

 

private: // User declarations

TLED* LED1;

 

После этого выполняется возврат на форму LEDForm, выберается событие OnCreate в object inspector и вводится следующий код в тело функции:

 

void __fastcall TForm1::FormCreate(TObject *Sender)

{

LED1 = new TLED(this);

LED1->Parent = this;

LED1->Left = (Width/2)-(LED1->Width/2);

LED1->Top = (Height/2)-(LED1->Height/2);

}

 

Этот код динамически создает новый компонент TLED и помещает его в центр экрана (если динамически создается компонент, надо задать все значения вручную). Очень важно также запомнить, что в этом случае необходимо задать свойство Parent компонента! Теперь выберается событие OnClick кнопки:

 

void __fastcall TForm1::Button1Click(TObject *Sender)

{

LED1->LEDOn = !LED1->LEDOn;

}

Теперь самое время нажать волшебную кнопку Run. Программа должна выглядеть так (рис. 2.2.):

 

Рис. 2.2. Кнопка Run “LED Change”

Щелкая кнопку, можно менять цвет индикатора с красного на зеленый с черным и обратно. Компонент польностью готов, и единственное, что осталось - инсталлировать его в палитру. В версии 3 и выше предварительно надо еще и создать пакетную библиотеку (пакедж).

Прежде чем добавить компонент в палитру, нужно разработать для него иконку (стандартная иконка слишком неинформативна) с помощью утилитки Image Editor (Tools | Image Editor) (если нет желания делать иконку для компонента, можно пропустить этот материал).

C++Builder v3 or later

Необходимо выбрать (File | New | Resource), щелкнуть правой клавишей на ветке дерева "Contents" и выбрать (New | Bitmap). В появившемся диалоге надо выбрать высоту и ширину 24 и VGA(16 colours), затем "OK". После этого следует переименовать иконку в LED (кликнув правой клавишей), и после этого дважды кликнуть на ней. Теперь можно нарисовать иконку. После этого надо выбрать (File | Save), сохранить файл ресурса в ту же папку, что и файл LED.cpp (папка lib по умолчанию), под именем LED.res (см. рис. 2.3.).

C++Builder v1

Необходимо выбрать (File | New | Bitmap), в диалоге выбрать высоту и ширину в 24, VGA (16 colours), щелкнуть "OK". Далее выбрать (Resource | Rename), назвать битмап TLED. Дважды кликнув, создать рисунок. Далее, (File | Save As) и сохранить файл ресурса в ту же папку, что и файл LED.cpp под именем LED.res.

 

Рис. 2.3. Создание иконки

Теперь можно инсталлировать компонент.

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

Инсталляция в C++Builder v1

Процедура инсталляции разработанного компонента ничем не отличается от процедуры инсталляции любого другого. Сначала последний вызывает (File | Save All), затем выбирается (Component | Install) из главного меню C++Builder. Появится диалог Install Components, где надо нажать кнопку Add, и затем (из диалога Add Module) нажать кнопку Browse. После выбирается LED.cpp в диалоге Add Component, и наконец нажимается кнопка "OK" в диалоге Install Components. C++Builder скомпилирует и подлинкует компонент, а затем добавит его в палитру.

Инсталляция в C++Builder v3 или старше

В C++Builder v3 Borland впервые ввела пакетные библиотеки (пакеджи). Компоненты теперь сначала компилируются в пакедж, который потом инсталлируется. Этим обеспечивается разделение компонентного кода между приложениями (через BPL, которые представляют собой особый вид DLL).

Сначала сохраняется все (File | Save All), затем закрываются все файлы (File | Close All). Теперь выбирается (Component | Install Component), далее выбирается "Into new package" и подключается файл LED.cpp. Необходимо назначить имя для пакеджа (оно должно отличаться от имени компонента) - например, LEDPack, и потом можно, если потребуется, ввести краткое описание пакеджа. Далее надо щелкнуть "OK". C++Builder спросит подтверждение того, что инсталлируется компонент, после этого скомпилирует и установит компонент.



2015-11-12 447 Обсуждений (0)
Проект. Основные принципы 0.00 из 5.00 0 оценок









Обсуждение в статье: Проект. Основные принципы

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (447)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.009 сек.)