Difference between revisions of "LCL Tips/ru"

From Lazarus wiki
Jump to navigationJump to search
m (categories)
 
(15 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>
 +
{{Note| Каждый вызов DisableAlign требует вызова EnableAlign. Например, если вы вызываете DisableAlign два раза, то должны вызвать EnableAlign тоже два раза.}}
 +
 +
'''Для пользователей, перешедших с Delphi''': Данные вызовы работают рекурсивно. DisableAlign запрещает выравнивание всех дочерних элементов, элементов-"внуков" и т.д.
 +
 +
== Создание не прямоугольных окон или элементов управления ==
 +
 +
В Lazarus можно легко создавать не прямоугольные окна или элементы управления. Для этого можно просто вызвать TWinControl.SetShape с видимой областью в качестве параметра. Обратите внимание, что это будет работать как для окон, так и для элементов управления, так как TCustomForm и TCustomControl происходят от TWinControl. Можно также вызвать подпрограмму LCLIntf SetWindowRgn, которая полностью эквивалентна вызову метода SetShape.
 +
 +
С использованием SetWindowRgn код будет похож на следующий:
 +
 +
<syntaxhighlight lang="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;
 +
</syntaxhighlight>
  
'''Внимание''': Каждый вызов DisableAlign требует вызова EnableAlign. Например, если вы вызываете DisableAlign два раза, то должны вызвать EnableAlign тоже два раза.
+
Эквивалентный код, который использует объект TRegion более высокого уровня, доступен в Lazarus 0.9.31+ (обратите внимание, что предыдущий способ работы все еще поддерживается и будет [поддерживаться] в будущем):
  
'''Для пользователей, перешедших с Delphi''': Данные вызовы работают рекурсивно. DisableAlign запрещает выравнивание всех дочерних элементов, элементов-"внуков" и т.д.
+
<syntaxhighlight lang="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;
 +
</syntaxhighlight>
 +
 
 +
Результат этой операции в окне в macOS с использованием набора виджетов Qt можно увидеть здесь:
 +
 
 +
[[Image:non_rectangular_window.png|bottom]]
 +
 
 +
Обратите внимание, что SetShape также может принять TBitmap для описания прозрачной области.
 +
 
 +
См.также:
 +
 
 +
* [[LCL_Internals#Shaped_Windows]]
 +
 
 +
Соответствующая документация:
 +
 
 +
* http://lazarus-ccr.sourceforge.net/docs/lcl/lclintf/setwindowrgn.html
 +
* http://lazarus-ccr.sourceforge.net/docs/lcl/controls/twincontrol.setshape.html
 +
 
 +
Ограничения:
 +
 
 +
В Gtk2 регион может быть установлен только после того, как окно реализовано. Вызов SetWindowRgn в обработчике события OnShow не работает, единственный способ - вызвать его, например, из таймера, установленного с интервалом 1. Включите таймер в Form.OnShow и отключите его в обработчике OnTimer.
 +
 
 +
==Имитация событий мыши и ввода с клавиатуры==
 +
 
 +
Имитировать ввод с помощью мыши и клавиатуры в LCL очень легко, просто используйте процедуры из модуля LCLMessageGlue, как это делают все интерфейсы widgetset. Этот модуль имеет процедуры, такие как:
 +
 
 +
<syntaxhighlight lang="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;
 +
</syntaxhighlight>
 +
 
 +
==Показ виртуальной клавиатуры в смартфоне/планшете==
 +
 
 +
Чтобы показать виртуальную клавиатуру, когда виджет получает фокус, в ControlStyle просто добавьте csRequiresKeyboardInput:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
constructor TMyTextEditor.Create(AOwner : TComponent);
 +
begin
 +
  inherited Create(AOwner);
 +
  ControlStyle := ControlStyle + [csRequiresKeyboardInput];
 +
...
 +
end;
 +
</syntaxhighlight>
 +
 
 +
[[Image:Virtual_keyboard.png|bottom]]
 +
 
 +
==Перебор по всем дочерним элементам управления TWinControl==
 +
 
 +
Это очень легко, просто используйте цикл для перебора TWinControl.ControlCount и TWinControl.Controls[Index]. Индекс начинается с нуля.
  
 +
<syntaxhighlight lang="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;
 +
</syntaxhighlight>
  
 
{{AutoCategory}}
 
{{AutoCategory}}
 
[[Category:Russian (unfinished translation)]]
 
[[Category:Russian (unfinished translation)]]
 
[[Category:LCL/ru]]
 
[[Category:LCL/ru]]

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;