Sign in Join
Wiki copytemporary

Revision 2 of 4, by WikiArchive on 11-4-2026. The page as it is now.

How To Write Lazarus Component/ru

Это руководство по созданию компонентов.

Contents

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

package menu.png

Package Maker

Шаг 2: Создание модуля

Вы можете создать новый модуль или использовать существующий файл. Оба варианта описаны ниже.

Создание нового модуля

package new component.png

pascal
unit CustomControl1;
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
      Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls;
    
    type
      TCustomControl1 = class(TComboBox)
      private
        { Private declarations }
      protected
        { Protected declarations }
      public
        { Public declarations }
      published
        { Published declarations }
      end;
    
    procedure Register;
    
    implementation
    
    procedure Register;
    begin
      RegisterComponents('Standard', [TCustomControl1]);
    end;
    
    end.

package install.png

package rebuild.png

package installed.png

https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png Примечание: Если вы не видите свой новый компонент в палитре компонентов, скорее всего, вы используете не пересобранную версию Lazarus. Вы можете установить, где Lazarus пересобирается, используя [пункт меню] Tools -> Options -> Files -> Lazarus directory.

Вместо прямого вызова Lazarus вы также можете использовать startlazarus, который запускает недавно созданный Lazarus, например исполняемый файл Lazarus в каталоге ~ / .lazarus, если у вас нет доступа на запись в каталог, в который был установлен Lazarus.


Прим.перев. 23:36, 1 December 2018 (CET): В корневом каталоге Lazarus'а после пересборки обычно содержится три исполняемых файла: lazarus (текущий запускаемый файл), startlazarus (лаучер для lazarus) и lazarus.old (старая версия исполняемого файла, существующая после последней удачной пересборки среды). В примечании, вероятнее всего, речь идет о них.


Добавление существующего модуля

Если у вас уже есть модуль, вы можете добавить его в пакет:

package existing unit.png

Конечный результат должен выглядеть следующим образом:

Package Maker

Компонент создан и готов к использованию:

Component Created

Шаг 3: Создание значков для пакета

Вы должны создать PNG-файлы размером 24x24 пикселя в виде значков. Если для Lazarus 1.8+ вы хотите, чтобы значки палитры масштабировались при более высоких разрешениях экрана, вы также должны предоставлять значки 36x36 и 48x48 пикселей. Имена более крупных значков требуют добавления [суффиксов] "_150" и "_200" к имени файла, соответственно [увеличению разрешения] ("_150" для 150%, "_200" для 200%).

FPC способен использовать стандартные файлы ресурсов .rc или скомпилированные ресурсы .res, [начиная] с версии fpc 2.6. См. FPC ресурсы. [О файлах] .lrs: см.ниже.

Использование редактора изображений Lazarus

Вы можете использовать Lazarus Image Editor для создания изображений в .lrs формате.


Прим.перев. 17:13, 3 December 2018 (CET): Поскольку старшие версии Лазаруса (насколько я помню, после v.1.6.х точно) позволяют задавать иконку изображения в png-формате, то удобнее всего для их создания использовать бесплатный векторный редактор InkScape, "родным" для которого является формат SVG. Среди его преимуществ:


Использование lazres

lazres обычно находится в каталоге инструментов Lazarus.

Вам может понадобиться скомпилировать lazres при первом использовании. Просто откройте lazres.lpi в среде IDE и нажмите run > build в меню.

Создание lrs-файла

Для создания lrs-файла запустите:

pascal
~/lazarus/tools/lazres samplepackage.lrs TMyCom.png

или, если доступны изображения с высоким разрешением

pascal
~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyCom_150.pgn TMyCom_200.png

где

Вы можете добавить несколько изображений в файл lrs, добавив имя файла изображения в конец [команды]. Например:

pascal
~/lazarus/tools/lazres samplepackage.lrs TMyCom.png TMyOtherCom.png ...

Пример

Ниже приведен образец полученного файла samplepackage.lrs.

pascal
LazarusResources.Add('TMyCom','PNG',[
      #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#24#0#0#0#24#8#2#0#0#0'o'#21#170#175
      +#0#0#0#4'gAMA'#0#0#177#143#11#252'a'#5#0#0#0'|IDAT8O'#237#212#209#10#192' '#8
      +#5'P'#247#231#251's'#215#138#133#164#166'\'#220#195'`'#209'c'#157'L'#173#131
      +#153#169'd4'#168'dP'#137'r_'#235'5'#136'@Zmk'#16'd9'#144#176#232#164'1'#247
      +'I'#8#160'IL'#206'C'#179#144#12#199#140'.'#134#244#141'~'#168#247#209'S~;'#29
      +'V+'#196#201'^'#10#15#150'?'#255#18#227#206'NZ>42'#181#159#226#144#15'@'#201
      +#148#168'e'#224'7f<@4'#130'u_YD'#23#213#131#134'Q]'#158#188#135#0#0#0#0'IEND'
      +#174'B`'#130
    ]);

Обязательно включите свой файл ресурсов в исходник нового компонента, добавив нижеследующее в модуль вашего компонента и включив "LResources" в раздел uses

pascal
initialization
      {$I samplepackage.lrs}

Создание res-файла

lazres также может создать скомпилированный файл ресурсов, просто укажите файл с расширением .res, например:

pascal
~/lazarus/tools/lazres samplepackage.res TMyCom.png

или, для иконок с высоким разрешением:

pascal
~/lazarus/tools/lazres samplepackage.res TMyCom.png TMyCom_150.png TMyCom_200.png

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

pascal
{$R samplepackage.res}

Использование glazres

GLazRes это графическая версия lazres, которая может собирать файлы в файл ресурсов Lazarus (.lrs). Его можно найти в каталоге инструментов установки Lazarus.

Перекомпиляция пакетов

Вам нужно пересобирать пакет каждый раз, когда вы вносите изменения в файл mycom.pas. Чтобы пересобрать пакет, откройте файл samplepackage.lpk в диспетчере пакетов и нажмите кнопку Install.

Удаление пакетов

pascal
[![Installed Components](/wiki-images/0/0e/How_to_write_lazarus_component_installed_components.png)](</File:How_to_write_lazarus_component_installed_components.png> "Installed Components")

Если что-то пойдет не так с пакетом (например, каталог пакета удаляется без деинсталляции [пакета в первую очередь]), Lazarus может не разрешить вам удалять пакеты. Чтобы устранить проблему, в меню IDE выберите Tools > Build Lazarus. Lazarus пересоберет все пакеты и перезапустит их. Теперь вы можете удалить проблемные пакеты.

Усовершенствование mycom.pas

pascal
unit mycom;
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
      Classes, SysUtils, StdCtrls, Forms, Dialogs,
      LCLType,LCLIntf,lresources,LCLProc;
    
    type
    
      TSampleEvent = procedure(MyText: String) of Object;
    
      TMyCom = class (TCustomComboBox)
      private
        FMyText: String;
        FOnChange2: TNotifyEvent;
        FOnSample: TSampleEvent;
      public
        constructor Create(TheOwner: TComponent); override;
        procedure CreateWnd; override;
        procedure Change; override;
      protected
        function GetMyText2: String;
        procedure SetMyText2(MyText: String);
      published
        property MyText: String read FMyText write FMyText;
        property MyText2: String read GetMyText2 write SetMyText2;
        property OnChange2: TNotifyEvent read FOnChange2 write FOnChange2;
        property OnSample: TSampleEvent read FOnSample write FOnSample;
        
        // properties from TComboBox
        property Align;
        property Anchors;
        property ArrowKeysTraverseList;
        property AutoComplete;
        property AutoCompleteText;
        property AutoDropDown;
        property AutoSelect;
        property AutoSize;
        property BidiMode;
        property BorderSpacing;
        property CharCase;
        property Color;
        property Ctl3D;
        property Constraints;
        property DragCursor;
        property DragMode;
        property DropDownCount;
        property Enabled;
        property Font;
        property ItemHeight;
        property ItemIndex;
        property Items;
        property ItemWidth;
        property MaxLength;
        property OnChange;
        property OnChangeBounds;
        property OnClick;
        property OnCloseUp;
        property OnContextPopup;
        property OnDblClick;
        property OnDragDrop;
        property OnDragOver;
        property OnDrawItem;
        property OnEndDrag;
        property OnDropDown;
        property OnEditingDone;
        property OnEnter;
        property OnExit;
        property OnGetItems;
        property OnKeyDown;
        property OnKeyPress;
        property OnKeyUp;
        property OnMeasureItem;
        property OnMouseDown;
        property OnMouseMove;
        property OnMouseUp;
        property OnStartDrag;
        property OnSelect;
        property OnUTF8KeyPress;
        property ParentBidiMode;
        property ParentColor;
        property ParentCtl3D;
        property ParentFont;
        property ParentShowHint;
        property PopupMenu;
        property ReadOnly;
        property ShowHint;
        property Sorted;
        property Style;
        property TabOrder;
        property TabStop;
        property Text;
        property Visible;    
      end;
    
    procedure Register;
    
    implementation
    
    procedure Register;
    begin
      RegisterComponents('Sample',[TMyCom]);
    end;
    
    constructor TMyCom.Create(TheOwner: TComponent);
    begin
      inherited Create(TheOwner);
      Self.Style := csDropDownList;
    end;
    
    procedure TMyCom.CreateWnd;
    begin
      inherited CreateWnd;
      Items.Assign(Screen.Fonts);
    end;
    
    procedure TMyCom.Change;
    begin
      inherited;
      if Assigned(FOnChange2) then FOnChange2(Self);
      if Assigned(FOnSample) then FOnSample(FMyText);
    end;
    
    function TMyCom.GetMyText2: String;
    begin
      Result:=FMyText;
    end;
    
    procedure TMyCom.SetMyText2(MyText: String);
    begin
      FMyText:=MyText;
    end;
    
    initialization
      {$I samplepackage.lrs}
    
    end.

Также вы заметите, что в Инспекторе объектов IDE существуют некоторые необъявленные и, возможно, нежелательные элементы. Чтобы удалить те из них, которые вам не нужны, вы можете переобъявить их в разделе Published как простые переменные. Например:

pascal
Published
    ... 
        property Height: Integer;
        property Width:  Integer;
    ...

Использование встроенных (визуальных) компонентов

Можно использовать стандартные компоненты, встроенные в ваши собственные компоненты (посмотрите для примера на TLabeledEdit или TButtonPanel).

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

В итоге вы получите что-то вроде этого (показаны только основные части):

pascal
TEnhancedPanel = class(TCustomControl)
    private
      { Новые аттрибуты для встроенного label }
      FEmbeddedLabel: TLabel;
      
    public
      { Конструктор должен быть переопределен, чтобы label могла быть создана}
      constructor Create(AOwner: TComponent); override;
      
    published
      { Сделаем label видимым в IDE }
      property EmbeddedLabel: TLabel read FEmbeddedLabel;
    end;
    
    implementation
    
    constructor TEnhancedPanel.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
    
      // Устанавливаем дефолтовые width и height
      with GetControlClassDefaultSize do
        SetInitialBounds(0, 0, CX, CY);
    
      // Добавляем встроенный label
      FEmbeddedLabel := TLabel.Create(Self); // Добавляем встроенный label
      FEmbeddedLabel.Parent := self;         // Показываем label на panel
      FEmbeddedLabel.SetSubComponent(true);  // Говорим IDE сохранять измененные свойства
      FLabel.Name := 'EmbeddedLabel';        
      FLabel.Caption := 'Howdy World!';
    
      // Убеждаемся, что встроенная label не может быть выделена/удалена средствами IDE
      FLabel.ControlStyle := FLabel.ControlStyle - [csNoDesignSelectable];
      
      // Задаем другие свойства, если необходимо
      //...
      
    end;

Использование пользовательской процедуры рисования

Вы всегда можете переопределить компонент внутри вашей программы. Например, это реализует пользовательскую процедуру Paint для TLabel:

pascal
type
      TMyLabel = class(TLabel)
        protected
          procedure Paint; override;
      end;
    {...}
    implementation
    {...}
    procedure TMyLabel.Paint;
    begin
      // ваш код для реализации Paint, например
      Canvas.TextOut(0,0,Caption);
    end;

Теперь вы можете создать MyLabel внутри вашей программы во время выполнения с этой переопределенной процедурой Paint вместо стандартной.

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

pascal
procedure TMyLabel.Paint;
    begin
    
      inherited Paint;   /////////////////////
    
      // ваш код для реализации Paint, например
      Canvas.TextOut(0,0,Caption);
    end;

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

Интеграция компонента с IDE

Редакторы свойств

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

Каждый редактор свойств - это класс, который должен происходить из TPropertyEditor или одного из его потомков и реализовывать методы этого базового класса. Они должны быть зарегистрированы в процедуре 'Register', используя функцию RegisterPropertyEditor из модуля PropEdits. Это стандарт для имен редакторов свойств с именем свойства, за которым следует [слово] 'Property', например [имя] TFieldProperty для редактора свойств свойства TField.

pascal
TPropertyEditor = class
      public
        function  AllEqual: Boolean; Virtual;
        function  AutoFill: Boolean; Virtual;
        procedure Edit; Virtual;     // Активируется двойным щелчком по значению свойства
        procedure ShowValue; Virtual; // Активируется щелчком по элементу управления значения свойства
        function  GetAttributes: TPropertyAttributes; Virtual;
        function  GetEditLimit: Integer; Virtual;
        function  GetName: ShortString; Virtual;
        procedure GetProperties(Proc: TGetPropEditProc); Virtual;
        function  GetHint(HintType: TPropEditHint; x, y: integer): String; Virtual;
        function  GetDefaultValue: AnsiString; Virtual;
        procedure GetValues(Proc: TGetStrProc); Virtual;
        procedure SetValue(const NewValue: AnsiString); Virtual;
        procedure UpdateSubProperties; Virtual;
        function  SubPropertiesNeedsUpdate: Boolean; Virtual;
        function  IsDefaultValue: Boolean; Virtual;
        function  IsNotDefaultValue: Boolean; Virtual;
        // ... укорочено
      end;

Хорошим примером для редактора свойств является редактор свойств TFont.

Один из наиболее распространенных случаев для редакторов свойств - это свойства, которые являются классами. Поскольку классы имеют много полей и могут иметь различные форматы, Lazarus не может иметь [универсальное] поле редактирования в инспекторе объектов, способное их редактировать, [как это сделано,] например, для строк и числовых типов.

Для классов соглашение состоит в том, чтобы в поле значений постоянно отображалось имя класса в круглых скобках, например "(TFont)", а кнопка "..." показывала диалог для редактирования этого класса. Это поведение, за исключением диалога, реализуется стандартным редактором свойств для классов, называемых TClassPropertyEditor, который может быть унаследован при создании редакторов свойств для классов:

pascal
TClassPropertyEditor = class(TPropertyEditor)
    public
      constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); Override;
      function GetAttributes: TPropertyAttributes; Override;
      procedure GetProperties(Proc: TGetPropEditProc); Override;
      function GetValue: AnsiString; Override;
      property SubPropsTypeFilter: TTypeKinds Read FSubPropsTypeFilter
                                             Write SetSubPropsTypeFilter
                                           Default tkAny;
    end;

Возвращаясь к примеру TFont, наследование от TClassPropertyEditor уже предлагает часть желаемого поведения, а затем классу TFontPropertyEditor необходимо реализовать отображение диалога в методе Edit и установить атрибуты для редактора:

pascal
TFontPropertyEditor = class(TClassPropertyEditor)
      public
        procedure Edit; Override;
        function  GetAttributes: TPropertyAttributes; Override;
      end;
    
    procedure TFontPropertyEditor.Edit;
    var 
      FontDialog: TFontDialog;
    begin
      FontDialog := TFontDialog.Create(NIL);
      try
        FontDialog.Font    := TFont(GetObjectValue(TFont));
        FontDialog.Options := FontDialog.Options + [fdShowHelp, fdForceFontExist];
        if FontDialog.Execute then SetPtrValue(FontDialog.Font);
      finally
        FontDialog.Free;
      end;
    end;
    
    function TFontPropertyEditor.GetAttributes: TPropertyAttributes;
    begin
      Result := [paMultiSelect, paSubProperties, paDialog, paReadOnly];
    end;

Редакторы компонентов

Редакторы компонентов управляют поведением компонентов по щелчку правой кнопкой мыши и двойному щелчку в дизайнере форм.

Каждый редактор компонентов - это класс, который должен происходить от TComponentEditor или одного из его потомков и реализовывать методы из этого базового класса. Он должен быть зарегистрирован в процедуре 'Register', использущей функцию RegisterComponentEditor из модуля ComponentEditors. Это стандарт для именования редакторов компонентов с именем компонента, за которым следует [слово] 'Editor', например TStringGridComponentEditor для редактора свойств компонента TStringGrid. Хотя редакторы пользовательских компонентов должны быть основаны на TComponentEditor, большинство его методов на самом деле [берут начало] от предка, поэтому необходимо также знать TBaseComponentEditor:

pascal
TBaseComponentEditor = class
      protected
      public
        constructor Create(AComponent: TComponent;
                           ADesigner: TComponentEditorDesigner); Virtual;
        procedure Edit; Virtual; Abstract;
        procedure ExecuteVerb(Index: Integer); Virtual; Abstract;
        function  GetVerb(Index: Integer): String; Virtual; Abstract;
        function  GetVerbCount: Integer; Virtual; Abstract;
        procedure PrepareItem(Index: Integer; const AnItem: TMenuItem); Virtual; Abstract;
        procedure Copy; Virtual; Abstract;
        function  IsInInlined: Boolean; Virtual; Abstract;
        function  GetComponent: TComponent; Virtual; Abstract;
        function  GetDesigner: TComponentEditorDesigner; Virtual; Abstract;
        function  GetHook(out Hook: TPropertyEditorHook): Boolean; Virtual; Abstract;
        procedure Modified; Virtual; Abstract;
      end;

Наиболее важным методом редактора компонентов является [метод] Edit, который вызывается по двойному щелчку на компоненте. Когда вызывается контекстное меню для компонента, запрашивается метод GetVerbCount и GetVerb для создания меню. Если выбран один из глаголов (что означает пункты меню в этом случае), вызывается ExecuteVerb. Существует редактор компонентов по умолчанию (TDefaultEditor), который реализует Edit для поиска свойств компонента для наиболее подходящего для редактирования. Обычно он выбирает событие, которое будет редактироваться, путем добавления скелета кода в редактор кода и установки курсора в это место.

Другими важными методами из TBaseComponentEditor являются:

Простым и интересным примером является редактор компонентов TCheckListBox, который создает диалоговое окно для редактирования. Более удобный, чем реализация всех методов из TBaseComponentEditor, [он] наследуется от TComponentEditor, и это то, что делает TCheckListBoxEditor. Этот базовый класс добавляет пустые реализации для большинства методов и некоторые умолчательные для других. Для [метода] Edit он вызывает ExecuteVerb(0), поэтому, если первый пункт [popup menu] будет сопоставлен действию двойного щелчка мыши, что является условным обозначением для редактора, то нет необходимости в [ручной] реализации [кода метода] Edit. Это основное действие для двойного щелчка и первого элемента меню часто является диалогом, и для TCheckListBox это также реализуется [так]:

pascal
TCheckListBoxComponentEditor = class(TComponentEditor)
      protected
        procedure DoShowEditor;
      public
        procedure ExecuteVerb(Index: Integer); override;
        function  GetVerb(Index: Integer): String; override;
        function  GetVerbCount: Integer; override;
      end;
    
    procedure TCheckGroupComponentEditor.DoShowEditor;
    var
      Dlg: TCheckGroupEditorDlg;
    begin
      Dlg := TCheckGroupEditorDlg.Create(NIL);
      try
        // .. укорочено
        Dlg.ShowModal;
        // .. укорочено
      finally
        Dlg.Free;
      end;
    end;
    
    procedure TCheckGroupComponentEditor.ExecuteVerb(Index: Integer);
    begin
      case Index of
        0: DoShowEditor;
      end;
    end;
    
    function TCheckGroupComponentEditor.GetVerb(Index: Integer): String;
    begin
      Result := 'CheckBox Editor...';
    end;
    
    function TCheckGroupComponentEditor.GetVerbCount: Integer;
    begin
      Result := 1;
    end;

Отладка компонента в Design-time

Чтобы отловить ошибки времени разработки во (вновь созданном) dbgcomponent :

См. также

Все вопросы вы можете оставлять также на этой странице


From the Free Pascal wiki, page "How To Write Lazarus Component/ru", as the Internet Archive kept it on 2024-01-21. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.