Sign in Join
Wiki copytemporary

LCL Tips/ru

Deutsch · English · Français · Русский · 中文(中国)

Создание графического интерфейса с помощью кода

При работе в среде Lazarus, графический пользовательский интерфейс (GUI) можно полностью создавать с помощью Pascal-кода. При этом доступны все возможности, которые обычно используются через интерфейс IDE. Далее рассмотрены примеры программы и модуля (codegui.lpr and mainform.pas) которые вы можете использовать в качестве шаблона. Важнее всего – не забывать устанавливать свойство Parent компонентов. Элементы управления для размещения на форме лучше всего создавать в конструкторе формы:

Файл основной программы:

pascal
program codedgui;

{$MODE DELPHI}{$H+}

uses
  Interfaces, Forms, StdCtrls,
  MainForm;

var
  MyForm: TMyForm;
begin
  Application.Initialize;
  Application.CreateForm(TMyForm, MyForm);
  Application.Run;
end.

Модуль, содержащий форму:

pascal
unit mainform;

{$MODE DELPHI}{$H+}

interface

uses Forms, StdCtrls;

type
  TMyForm = class(TForm)
  public
    MyButton: TButton;
    procedure ButtonClick(ASender: TObject);
    constructor Create(AOwner: TComponent); override;
  end;

implementation

procedure TMyForm.ButtonClick(ASender:TObject);
begin
  Close;
end;

constructor TMyForm.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Position := poScreenCenter;
  Height := 400;
  Width := 400;

  VertScrollBar.Visible := False;
  HorzScrollBar.Visible := False;

  MyButton := TButton.Create(Self);
  with MyButton do
  begin
    Height := 30;
    Left := 100;
    Top := 100;
    Width := 100;
    Caption := 'Закрыть';
    OnClick := ButtonClick;
    Parent := Self;
  end;

  // Сюда можно добавить код создания других компонентов и установки их свойств
end;

end.

Создание элементов управления вручную без потери быстродействия

Устанавливайте свойство Parent в последнюю очередь

Для пользователей, перешедших с Delphi: В отличие от Delphi, LCL позволяет устанавливать практически все свойства в любой последовательности. Например, в Delphi невозможно установить позицию элемента управления, если для него не установлен родительский элемент (свойство Parent). LCL позволяет это сделать, и это можно использовать для повышения быстродействия.

pascal
  with TButton.Create(Form1) do begin
    // 1. создаём кнопку с размером по умолчанию
    // 2. изменяем положение. Пока ничего не меняется, потому что Parent=nil
    SetBounds(10,10,Width,Height);
    // 3. изменяем размер в зависимости от темы. 
    // Пока что размер не меняется, потому что Parent=nil
    AutoSize:=true;
    // 4. далее размер должен поменяться из-за включенного свойства AutoSize
    // Но опять это изменение откладывается, потому что Parent=nil
    Caption:='ОК';
    // 5. устанавливаем свойство Parent. Вот теперь все изменения применяются, 
    // но за один раз, как одно действие.
    Parent:=Form1;
  end;

Когда для элемента управления задано свойство Parent, все изменения его свойств сразу же применяются. Когда свойство Parent не задано, многие свойства только изменяют хранимое значение, и как только Parent будет установлен, все изменения будут применены. Это особенно актуально для компонентов-"внуков":

pascal
  GroupBox1:=TGroupBox.Create(Self);
  with GroupBox1 do begin
    with TButton1.Create(Self) do begin
      AutoSize:=true;
      Caption:='Нажми меня';
      Parent:=GroupBox1;
    end;
    Parent:=Form1;
  end;
  Form1.Show;

Автоматическая установка размера начинается только когда для всех элементов установлены родительские элементы и форма становится видимой.

Избегайте раннего создания хэндла (Handle)

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

Используйте SetBounds вместо Left, Top, Width, Height

Вместо

pascal
  with Button1 do begin
    Left:=10;
    Top:=10;
    Width:=100;
    Height:=25;
  end;

используйте

pascal
  with Button1 do begin
    SetBounds(10,10,100,25);
  end;

Left, Top, Width, Height вызывают SetBounds, и каждое изменение размера или позиции приводит к повторному вычислению параметров соседних элементов, и возможно рекурсивному вычислению параметров родительских и/или дочерних элементов.

DisableAlign/EnableAlign

При позиционировании многих элементов управления полезно отключать автоматическое вычисление размера, выравнивания и привязок.

pascal
  DisableAlign;
  try
    ListBox1.Width:=ClientWidth div 3;
    ListBox2.Width:=ClientWidth div 3;
    ListBox3.Width:=ClientWidth div 3;
  finally
    EnableAlign;
  end;

Note: Каждый вызов DisableAlign требует вызова EnableAlign. Например, если вы вызываете DisableAlign два раза, то должны вызвать EnableAlign тоже два раза.

Для пользователей, перешедших с Delphi: Данные вызовы работают рекурсивно. DisableAlign запрещает выравнивание всех дочерних элементов, элементов-"внуков" и т.д.

Создание не прямоугольных окон или элементов управления

В Lazarus можно легко создавать не прямоугольные окна или элементы управления. Для этого можно просто вызвать TWinControl.SetShape с видимой областью в качестве параметра. Обратите внимание, что это будет работать как для окон, так и для элементов управления, так как TCustomForm и TCustomControl происходят от TWinControl. Можно также вызвать подпрограмму LCLIntf SetWindowRgn, которая полностью эквивалентна вызову метода SetShape.

С использованием SetWindowRgn код будет похож на следующий:

pascal
uses LCLIntf, LCLType;

procedure TForm1.FormCreate(Sender: TObject);
var
  MyRegion: HRGN;
begin
  MyRegion := CreateRectRgn(0, 0, 100, 100);
  SetWindowRgn(Handle, MyRegion, True);
  DeleteObject(MyRegion);
end;

Эквивалентный код, который использует объект TRegion более высокого уровня, доступен в Lazarus 0.9.31+ (обратите внимание, что предыдущий способ работы все еще поддерживается и будет [поддерживаться] в будущем):

pascal
uses Graphics;

procedure TForm1.FormCreate(Sender: TObject);
var
  MyRegion: TRegion;
begin
  MyRegion := TRegion.Create;
  try
    MyRegion.AddRectangle(0, 0, 100, 100);
    Self.SetShape(MyRegion);
  finally
    MyRegion.Free;
  end;
end;

Результат этой операции в окне в macOS с использованием набора виджетов Qt можно увидеть здесь:

Обратите внимание, что SetShape также может принять TBitmap для описания прозрачной области.

См.также:

Соответствующая документация:

Ограничения:

В Gtk2 регион может быть установлен только после того, как окно реализовано. Вызов SetWindowRgn в обработчике события OnShow не работает, единственный способ - вызвать его, например, из таймера, установленного с интервалом 1. Включите таймер в Form.OnShow и отключите его в обработчике OnTimer.

Имитация событий мыши и ввода с клавиатуры

Имитировать ввод с помощью мыши и клавиатуры в LCL очень легко, просто используйте процедуры из модуля LCLMessageGlue, как это делают все интерфейсы widgetset. Этот модуль имеет процедуры, такие как:

pascal
unit LCLMessageGlue;

function LCLSendMouseMoveMsg(const Target: TControl; XPos, YPos: smallint;
  ShiftState: TShiftState = []): PtrInt;
function LCLSendMouseDownMsg(const Target: TControl; XPos, YPos: smallint;
  Button: TMouseButton; ShiftState: TShiftState = []): PtrInt;
function LCLSendMouseUpMsg(const Target: TControl; XPos, YPos: smallint;
  Button: TMouseButton; ShiftState: TShiftState = []): PtrInt;
function LCLSendMouseWheelMsg(const Target: TControl; XPos, YPos, WheelDelta: smallint;
  ShiftState: TShiftState = []): PtrInt;
function LCLSendCaptureChangedMsg(const Target: TControl): PtrInt;
function LCLSendSelectionChangedMsg(const Target: TControl): PtrInt;
function LCLSendClickedMsg(const Target: TControl): PtrInt;
function LCLSendMouseEnterMsg(const Target: TControl): PtrInt;
function LCLSendMouseLeaveMsg(const Target: TControl): PtrInt;
...
function LCLSendKeyDownEvent(const Target: TControl; var CharCode: word;
  KeyData: PtrInt; BeforeEvent, IsSysKey: boolean): PtrInt;
function LCLSendKeyUpEvent(const Target: TControl; var CharCode: word;
  KeyData: PtrInt; BeforeEvent, IsSysKey: boolean): PtrInt;

Показ виртуальной клавиатуры в смартфоне/планшете

Чтобы показать виртуальную клавиатуру, когда виджет получает фокус, в ControlStyle просто добавьте csRequiresKeyboardInput:

pascal
constructor TMyTextEditor.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle + [csRequiresKeyboardInput];
...
end;

Перебор по всем дочерним элементам управления TWinControl

Это очень легко, просто используйте цикл для перебора TWinControl.ControlCount и TWinControl.Controls[Index]. Индекс начинается с нуля.

pascal
procedure TWinControl.WriteLayoutDebugReport(const Prefix: string);
var
  i: Integer;
begin
  inherited WriteLayoutDebugReport(Prefix);
  for i:=0 to ControlCount-1 do
    Controls[i].WriteLayoutDebugReport(Prefix+'  ');
end;

From the Free Pascal wiki, page "LCL Tips/ru", as it stood on 2019-12-22 after a change by Trev, out of the wiki's dump of 2024-05-26. 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.

Revision 2, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.