LCL Tips/ru
│
Deutsch (de) │
English (en) │
français (fr) │
русский (ru) │
中文(中国大陆) (zh_CN) │
Создание графического интерфейса с помощью кода
При работе в среде Lazarus, графический пользовательский интерфейс (GUI) можно полностью создавать с помощью Pascal-кода. При этом доступны все возможности, которые обычно используются через интерфейс IDE. Далее рассмотрены примеры программы и модуля (codegui.lpr and mainform.pas) которые вы можете использовать в качестве шаблона. Важнее всего – не забывать устанавливать свойство Parent компонентов. Элементы управления для размещения на форме лучше всего создавать в конструкторе формы:
Файл основной программы:
program codedgui;
{$MODE DELPHI}{$H+}
uses
Interfaces, Forms, StdCtrls,
MainForm;
var
MyForm: TMyForm;
begin
Application.Initialize;
Application.CreateForm(TMyForm, MyForm);
Application.Run;
end.
Модуль, содержащий форму:
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 позволяет это сделать, и это можно использовать для повышения быстродействия.
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 будет установлен, все изменения будут применены. Это особенно актуально для компонентов-"внуков":
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
Вместо
with Button1 do begin
Left:=10;
Top:=10;
Width:=100;
Height:=25;
end;
используйте
with Button1 do begin
SetBounds(10,10,100,25);
end;
Left, Top, Width, Height вызывают SetBounds, и каждое изменение размера или позиции приводит к повторному вычислению параметров соседних элементов, и возможно рекурсивному вычислению параметров родительских и/или дочерних элементов.
DisableAlign/EnableAlign
При позиционировании многих элементов управления полезно отключать автоматическое вычисление размера, выравнивания и привязок.
DisableAlign; try ListBox1.Width:=ClientWidth div 3; ListBox2.Width:=ClientWidth div 3; ListBox3.Width:=ClientWidth div 3; finally EnableAlign; end;
Для пользователей, перешедших с Delphi: Данные вызовы работают рекурсивно. DisableAlign запрещает выравнивание всех дочерних элементов, элементов-"внуков" и т.д.
Создание не прямоугольных окна или элемента управления
В Lazarus можно легко создавать не прямоугольные окна или элементы управления. Для этого можно просто вызвать TWinControl.SetShape с видимой областью в качестве параметра. Обратите внимание, что это будет работать как для окон, так и для элементов управления, так как TCustomForm и TCustomControl происходят от TWinControl. Можно также вызвать подпрограмму LCLIntf SetWindowRgn, которая полностью эквивалентна вызову метода SetShape.
С использованием SetWindowRgn код будет похож на следующий:
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;
An equivalent code, which uses a higher level TRegion object available in Lazarus 0.9.31+ (note that the previous way of doing things is still supported and will be in the future too), is:
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;
The result of this operation in a window in Mac OS X using the Qt widgetset can be see here:
Note that SetShape can also accept a TBitmap to describe the transparent region.
See also:
Documentation entries:
- http://lazarus-ccr.sourceforge.net/docs/lcl/lclintf/setwindowrgn.html
- http://lazarus-ccr.sourceforge.net/docs/lcl/controls/twincontrol.setshape.html
Limitations:
In Gtk2 a region can only be set after a window is realized. Calling SetWindowRgn in the OnShow event handler doesn't work, the only way seams to be to call it from a timer set with interval 1, for example. Enable the timer in Form.OnShow and disable it in it's OnTimer handler.
Simulating Mouse and Keyboard input
It is very easy to simulate mouse and keyboard input in the LCL, just use the routines from the unit LCLMessageGlue like all widgetset interfaces do. This unit has routines like:
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;
Showing the Virtual Keyboard in Smartphones/tablets
To show the virtual keyboard when a widget receives focus just add the csRequiresKeyboardInput ControlStyle:
constructor TMyTextEditor.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle + [csRequiresKeyboardInput];
...
end;
Iterating through all child controls of a TWinControl
This is very easy, just use a loop to iterate over TWinControl.ControlCount and TWinControl.Controls[Index]. The index is zero based.
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;