Difference between revisions of "LCL Tips/ru"

From Lazarus wiki
Jump to navigationJump to search
 
(14 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
{{LCL Tips}}
 
{{LCL Tips}}
  
= Создание графического интерфейса с помощью кода =
+
__TOC__
 +
== Создание графического интерфейса с помощью кода ==
  
 
При работе в среде Lazarus, графический пользовательский интерфейс (GUI) можно полностью создавать с помощью Pascal-кода. При этом доступны все возможности, которые обычно используются через интерфейс IDE. Далее рассмотрены примеры программы и модуля (codegui.lpr and mainform.pas) которые вы можете использовать в качестве шаблона. Важнее всего – не забывать устанавливать свойство Parent компонентов. Элементы управления для размещения на форме лучше всего создавать в конструкторе формы:
 
При работе в среде Lazarus, графический пользовательский интерфейс (GUI) можно полностью создавать с помощью Pascal-кода. При этом доступны все возможности, которые обычно используются через интерфейс IDE. Далее рассмотрены примеры программы и модуля (codegui.lpr and mainform.pas) которые вы можете использовать в качестве шаблона. Важнее всего – не забывать устанавливать свойство Parent компонентов. Элементы управления для размещения на форме лучше всего создавать в конструкторе формы:
Line 7: Line 8:
 
Файл основной программы:
 
Файл основной программы:
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
program codedgui;
 
program codedgui;
  
Line 27: Line 28:
 
Модуль, содержащий форму:
 
Модуль, содержащий форму:
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
unit mainform;
 
unit mainform;
  
Line 80: Line 81:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
= Создание элементов управления вручную без потери быстродействия =
+
== Создание элементов управления вручную без потери быстродействия ==
  
== Устанавливайте свойство Parent в последнюю очередь ==
+
=== Устанавливайте свойство Parent в последнюю очередь ===
  
 
'''Для пользователей, перешедших с Delphi''': В отличие от Delphi, LCL позволяет устанавливать практически все свойства в любой последовательности. Например, в Delphi невозможно установить позицию элемента управления, если для него не установлен родительский элемент (свойство Parent). LCL позволяет это сделать, и это можно использовать для повышения быстродействия.
 
'''Для пользователей, перешедших с Delphi''': В отличие от Delphi, LCL позволяет устанавливать практически все свойства в любой последовательности. Например, в Delphi невозможно установить позицию элемента управления, если для него не установлен родительский элемент (свойство Parent). LCL позволяет это сделать, и это можно использовать для повышения быстродействия.
 
+
<syntaxhighlight lang="pascal">
 
   with TButton.Create(Form1) do begin
 
   with TButton.Create(Form1) do begin
 
     // 1. создаём кнопку с размером по умолчанию
 
     // 1. создаём кнопку с размером по умолчанию
Line 100: Line 101:
 
     Parent:=Form1;
 
     Parent:=Form1;
 
   end;
 
   end;
 
+
</syntaxhighlight>
 
Когда для элемента управления задано свойство Parent, все изменения его свойств сразу же применяются. Когда свойство Parent не задано, многие свойства только изменяют хранимое значение, и как только Parent будет установлен, все изменения будут применены. Это особенно актуально для компонентов-"внуков":
 
Когда для элемента управления задано свойство Parent, все изменения его свойств сразу же применяются. Когда свойство Parent не задано, многие свойства только изменяют хранимое значение, и как только Parent будет установлен, все изменения будут применены. Это особенно актуально для компонентов-"внуков":
 
+
<syntaxhighlight lang="pascal">
 
   GroupBox1:=TGroupBox.Create(Self);
 
   GroupBox1:=TGroupBox.Create(Self);
 
   with GroupBox1 do begin
 
   with GroupBox1 do begin
Line 113: Line 114:
 
   end;
 
   end;
 
   Form1.Show;
 
   Form1.Show;
 
+
</syntaxhighlight>
 
Автоматическая установка размера начинается только когда для всех элементов установлены  родительские элементы и форма становится видимой.
 
Автоматическая установка размера начинается только когда для всех элементов установлены  родительские элементы и форма становится видимой.
  
== Избегайте раннего создания хэндла (Handle) ==
+
=== Избегайте раннего создания хэндла (Handle) ===
  
 
Как только для TWinControl создаётся хэндл, все изменения свойств элемента начинают менять его графическое представление (называемое виджетом). Даже если элемент невидим, если у него есть хэндл, изменения требуют много вычислительных ресурсов.
 
Как только для TWinControl создаётся хэндл, все изменения свойств элемента начинают менять его графическое представление (называемое виджетом). Даже если элемент невидим, если у него есть хэндл, изменения требуют много вычислительных ресурсов.
  
== Используйте SetBounds вместо Left, Top, Width, Height ==
+
=== Используйте SetBounds вместо Left, Top, Width, Height ===
  
 
Вместо
 
Вместо
 +
<syntaxhighlight lang="pascal">
 
   with Button1 do begin
 
   with Button1 do begin
 
     Left:=10;
 
     Left:=10;
Line 129: Line 131:
 
     Height:=25;
 
     Height:=25;
 
   end;
 
   end;
 +
</syntaxhighlight>
 
используйте
 
используйте
 +
<syntaxhighlight lang="pascal">
 
   with Button1 do begin
 
   with Button1 do begin
 
     SetBounds(10,10,100,25);
 
     SetBounds(10,10,100,25);
 
   end;
 
   end;
 
+
</syntaxhighlight>
 
Left, Top, Width, Height вызывают SetBounds, и каждое изменение размера или позиции приводит к повторному вычислению параметров соседних элементов, и возможно рекурсивному вычислению параметров родительских и/или дочерних элементов.
 
Left, Top, Width, Height вызывают SetBounds, и каждое изменение размера или позиции приводит к повторному вычислению параметров соседних элементов, и возможно рекурсивному вычислению параметров родительских и/или дочерних элементов.
  
= DisableAlign / EnableAlign =
+
== DisableAlign/EnableAlign ==
  
 
При позиционировании многих элементов управления полезно отключать автоматическое вычисление размера, выравнивания и привязок.
 
При позиционировании многих элементов управления полезно отключать автоматическое вычисление размера, выравнивания и привязок.
 
+
<syntaxhighlight lang="pascal">
 
   DisableAlign;
 
   DisableAlign;
 
   try
 
   try
Line 148: Line 152:
 
     EnableAlign;
 
     EnableAlign;
 
   end;
 
   end;
 
+
</syntaxhighlight>
'''Внимание''': Каждый вызов DisableAlign требует вызова EnableAlign. Например, если вы вызываете DisableAlign два раза, то должны вызвать EnableAlign тоже два раза.
+
{{Note| Каждый вызов DisableAlign требует вызова EnableAlign. Например, если вы вызываете DisableAlign два раза, то должны вызвать EnableAlign тоже два раза.}}
  
 
'''Для пользователей, перешедших с Delphi''': Данные вызовы работают рекурсивно. DisableAlign запрещает выравнивание всех дочерних элементов, элементов-"внуков" и т.д.
 
'''Для пользователей, перешедших с Delphi''': Данные вызовы работают рекурсивно. DisableAlign запрещает выравнивание всех дочерних элементов, элементов-"внуков" и т.д.
  
== Creating a non-rectangular window or control ==
+
== Создание не прямоугольных окон или элементов управления ==
  
One can easily create non-rectangular windows or controls in Lazarus. For this, one can simply call TWinControl.SetShape, with the visible region as a parameter. Note that this will work for both windows and controls, as both TCustomForm and TCustomControl descend from TWinControl. One can also call the LCLIntf routine SetWindowRgn, which is completely equivalent to calling the SetShape method.
+
В Lazarus можно легко создавать не прямоугольные окна или элементы управления. Для этого можно просто вызвать TWinControl.SetShape с видимой областью в качестве параметра. Обратите внимание, что это будет работать как для окон, так и для элементов управления, так как TCustomForm и TCustomControl происходят от TWinControl. Можно также вызвать подпрограмму LCLIntf SetWindowRgn, которая полностью эквивалентна вызову метода SetShape.
  
Using SetWindowRgn the code will be similar to this:
+
С использованием SetWindowRgn код будет похож на следующий:
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
uses LCLIntf, LCLType;
 
uses LCLIntf, LCLType;
  
Line 172: Line 176:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
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:
+
Эквивалентный код, который использует объект TRegion более высокого уровня, доступен в Lazarus 0.9.31+ (обратите внимание, что предыдущий способ работы все еще поддерживается и будет [поддерживаться] в будущем):
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
uses Graphics;
 
uses Graphics;
  
Line 191: Line 195:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The result of this operation in a window in Mac OS X using the Qt widgetset can be see here:
+
Результат этой операции в окне в macOS с использованием набора виджетов Qt можно увидеть здесь:
  
[[Image:non_rectangular_window.png]]
+
[[Image:non_rectangular_window.png|bottom]]
  
Note that SetShape can also accept a TBitmap to describe the transparent region.
+
Обратите внимание, что SetShape также может принять TBitmap для описания прозрачной области.
  
See also:
+
См.также:
  
 
* [[LCL_Internals#Shaped_Windows]]
 
* [[LCL_Internals#Shaped_Windows]]
  
Documentation entries:
+
Соответствующая документация:
  
 
* http://lazarus-ccr.sourceforge.net/docs/lcl/lclintf/setwindowrgn.html
 
* http://lazarus-ccr.sourceforge.net/docs/lcl/lclintf/setwindowrgn.html
 
* http://lazarus-ccr.sourceforge.net/docs/lcl/controls/twincontrol.setshape.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.
+
В Gtk2 регион может быть установлен только после того, как окно реализовано. Вызов SetWindowRgn в обработчике события OnShow не работает, единственный способ - вызвать его, например, из таймера, установленного с интервалом 1. Включите таймер в Form.OnShow и отключите его в обработчике OnTimer.
  
==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:
+
Имитировать ввод с помощью мыши и клавиатуры в LCL очень легко, просто используйте процедуры из модуля LCLMessageGlue, как это делают все интерфейсы widgetset. Этот модуль имеет процедуры, такие как:
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
unit LCLMessageGlue;
 
unit LCLMessageGlue;
  
Line 237: Line 241:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Showing the Virtual Keyboard in Smartphones/tablets==
+
==Показ виртуальной клавиатуры в смартфоне/планшете==
  
To show the virtual keyboard when a widget receives focus just add the csRequiresKeyboardInput ControlStyle:
+
Чтобы показать виртуальную клавиатуру, когда виджет получает фокус, в ControlStyle просто добавьте csRequiresKeyboardInput:
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
constructor TMyTextEditor.Create(AOwner : TComponent);
 
constructor TMyTextEditor.Create(AOwner : TComponent);
 
begin
 
begin
Line 250: Line 254:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
[[Image:Virtual_keyboard.png]]
+
[[Image:Virtual_keyboard.png|bottom]]
  
==Iterating through all child controls of a TWinControl==
+
==Перебор по всем дочерним элементам управления TWinControl==
  
This is very easy, just use a loop to iterate over TWinControl.ControlCount and TWinControl.Controls[Index]. The index is zero based.
+
Это очень легко, просто используйте цикл для перебора TWinControl.ControlCount и TWinControl.Controls[Index]. Индекс начинается с нуля.
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
procedure TWinControl.WriteLayoutDebugReport(const Prefix: string);
 
procedure TWinControl.WriteLayoutDebugReport(const Prefix: string);
 
var
 
var

Latest revision as of 05:45, 22 December 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;

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

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 можно увидеть здесь:

non rectangular window.png

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

См.также:

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

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

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

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

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

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:

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

Virtual keyboard.png

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

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

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;