Difference between revisions of "LCL Tips/ru"

From Lazarus wiki
Jump to navigationJump to search
Line 8: Line 8:
 
Файл основной программы:
 
Файл основной программы:
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
program codedgui;
 
program codedgui;
  
Line 28: Line 28:
 
Модуль, содержащий форму:
 
Модуль, содержащий форму:
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
unit mainform;
 
unit mainform;
  

Revision as of 12:10, 17 June 2019

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;
Light bulb  Примечание: Каждый вызов DisableAlign требует вызова EnableAlign. Например, если вы вызываете DisableAlign два раза, то должны вызвать EnableAlign тоже два раза.

Для пользователей, перешедших с 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:

non rectangular window.png

Note that SetShape can also accept a TBitmap to describe the transparent region.

See also:

Documentation entries:

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;

Virtual keyboard.png

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;