LCL Tips/ru: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | ## Создание графического интерфейса с помощью кода | |
| 2 | 2 | ||
| 3 | 3 | При работе в среде Lazarus, графический пользовательский интерфейс (GUI) можно полностью создавать с помощью Pascal-кода. При этом доступны все возможности, которые обычно используются через интерфейс IDE. Далее рассмотрены примеры программы и модуля (codegui.lpr and mainform.pas) которые вы можете использовать в качестве шаблона. Важнее всего – не забывать устанавливать свойство Parent компонентов. Элементы управления для размещения на форме лучше всего создавать в конструкторе формы: | |
| 4 | 4 | ||
| 5 | 5 | Файл основной программы: | |
| 6 | 6 | ||
| 7 | 7 | ``` pascal | |
| 8 | 8 | program codedgui; | |
| 9 | 9 | ||
| 10 | 10 | {$MODE DELPHI}{$H+} | |
| 11 | 11 | ||
| 12 | 12 | uses | |
| 13 | 13 | Interfaces, Forms, StdCtrls, | |
| 14 | 14 | MainForm; | |
| 15 | 15 | ||
| 16 | 16 | var | |
| 17 | 17 | MyForm: TMyForm; | |
| 18 | 18 | begin | |
| 19 | 19 | Application.Initialize; | |
| 20 | 20 | Application.CreateForm(TMyForm, MyForm); | |
| 21 | 21 | Application.Run; | |
| 22 | 22 | end. | |
| 23 | 23 | ``` | |
| 24 | 24 | ||
| 25 | 25 | Модуль, содержащий форму: | |
| 26 | 26 | ||
| 27 | 27 | ``` pascal | |
| 28 | 28 | unit mainform; | |
| 29 | 29 | ||
| 30 | 30 | {$MODE DELPHI}{$H+} | |
| 31 | 31 | ||
| 32 | 32 | interface | |
| 33 | 33 | ||
| 34 | 34 | uses Forms, StdCtrls; | |
| 35 | 35 | ||
| 36 | 36 | type | |
| 37 | 37 | TMyForm = class(TForm) | |
| 38 | 38 | public | |
| 39 | 39 | MyButton: TButton; | |
| 40 | 40 | procedure ButtonClick(ASender: TObject); | |
| 41 | 41 | constructor Create(AOwner: TComponent); override; | |
| 42 | 42 | end; | |
| 43 | 43 | ||
| 44 | 44 | implementation | |
| 45 | 45 | ||
| 46 | 46 | procedure TMyForm.ButtonClick(ASender:TObject); | |
| 47 | 47 | begin | |
| 48 | 48 | Close; | |
| 49 | 49 | end; | |
| 50 | 50 | ||
| 51 | 51 | constructor TMyForm.Create(AOwner: TComponent); | |
| 52 | 52 | begin | |
| 53 | 53 | inherited Create(AOwner); | |
| 54 | 54 | ||
| 55 | 55 | Position := poScreenCenter; | |
| 56 | 56 | Height := 400; | |
| 57 | 57 | Width := 400; | |
| 58 | 58 | ||
| 59 | 59 | VertScrollBar.Visible := False; | |
| 60 | 60 | HorzScrollBar.Visible := False; | |
| 61 | 61 | ||
| 62 | 62 | MyButton := TButton.Create(Self); | |
| 63 | 63 | with MyButton do | |
| 64 | 64 | begin | |
| 65 | 65 | Height := 30; | |
| 66 | 66 | Left := 100; | |
| 67 | 67 | Top := 100; | |
| 68 | 68 | Width := 100; | |
| 69 | 69 | Caption := 'Закрыть'; | |
| 70 | 70 | OnClick := ButtonClick; | |
| 71 | 71 | Parent := Self; | |
| 72 | 72 | end; | |
| 73 | 73 | ||
| 74 | 74 | // Сюда можно добавить код создания других компонентов и установки их свойств | |
| 75 | 75 | end; | |
| 76 | 76 | ||
| 77 | 77 | end. | |
| 78 | 78 | ``` | |
| 79 | 79 | ||
| 80 | 80 | ## Создание элементов управления вручную без потери быстродействия | |
| 81 | 81 | ||
| 82 | 82 | ### Устанавливайте свойство Parent в последнюю очередь | |
| 83 | 83 | ||
| 84 | 84 | **Для пользователей, перешедших с Delphi**: В отличие от Delphi, LCL позволяет устанавливать практически все свойства в любой последовательности. Например, в Delphi невозможно установить позицию элемента управления, если для него не установлен родительский элемент (свойство Parent). LCL позволяет это сделать, и это можно использовать для повышения быстродействия. | |
| 85 | 85 | ||
| 86 | 86 | ``` pascal | |
| 87 | 87 | with TButton.Create(Form1) do begin | |
| 88 | 88 | // 1. создаём кнопку с размером по умолчанию | |
| 89 | 89 | // 2. изменяем положение. Пока ничего не меняется, потому что Parent=nil | |
| 90 | 90 | SetBounds(10,10,Width,Height); | |
| 91 | 91 | // 3. изменяем размер в зависимости от темы. | |
| 92 | 92 | // Пока что размер не меняется, потому что Parent=nil | |
| 93 | 93 | AutoSize:=true; | |
| 94 | 94 | // 4. далее размер должен поменяться из-за включенного свойства AutoSize | |
| 95 | 95 | // Но опять это изменение откладывается, потому что Parent=nil | |
| 96 | 96 | Caption:='ОК'; | |
| 97 | 97 | // 5. устанавливаем свойство Parent. Вот теперь все изменения применяются, | |
| 98 | 98 | // но за один раз, как одно действие. | |
| 99 | 99 | Parent:=Form1; | |
| 100 | 100 | end; | |
| 101 | 101 | ``` | |
| 102 | 102 | ||
| 103 | 103 | Когда для элемента управления задано свойство Parent, все изменения его свойств сразу же применяются. Когда свойство Parent не задано, многие свойства только изменяют хранимое значение, и как только Parent будет установлен, все изменения будут применены. Это особенно актуально для компонентов-"внуков": | |
| 104 | 104 | ||
| 105 | 105 | ``` pascal | |
| 106 | 106 | GroupBox1:=TGroupBox.Create(Self); | |
| 107 | 107 | with GroupBox1 do begin | |
| 108 | 108 | with TButton1.Create(Self) do begin | |
| 109 | 109 | AutoSize:=true; | |
| 110 | 110 | Caption:='Нажми меня'; | |
| 111 | 111 | Parent:=GroupBox1; | |
| 112 | 112 | end; | |
| 113 | 113 | Parent:=Form1; | |
| 114 | 114 | end; | |
| 115 | 115 | Form1.Show; | |
| 116 | 116 | ``` | |
| 117 | 117 | ||
| 118 | 118 | Автоматическая установка размера начинается только когда для всех элементов установлены родительские элементы и форма становится видимой. | |
| 119 | 119 | ||
| 120 | 120 | ### Избегайте раннего создания хэндла (Handle) | |
| 121 | 121 | ||
| 122 | 122 | Как только для TWinControl создаётся хэндл, все изменения свойств элемента начинают менять его графическое представление (называемое виджетом). Даже если элемент невидим, если у него есть хэндл, изменения требуют много вычислительных ресурсов. | |
| 123 | 123 | ||
| 124 | 124 | ### Используйте SetBounds вместо Left, Top, Width, Height | |
| 125 | 125 | ||
| 126 | 126 | Вместо | |
| 127 | 127 | ||
| 128 | 128 | ``` pascal | |
| 129 | 129 | with Button1 do begin | |
| 130 | 130 | Left:=10; | |
| 131 | 131 | Top:=10; | |
| 132 | 132 | Width:=100; | |
| 133 | 133 | Height:=25; | |
| 134 | 134 | end; | |
| 135 | 135 | ``` | |
| 136 | 136 | ||
| 137 | 137 | используйте | |
| 138 | 138 | ||
| 139 | 139 | ``` pascal | |
| 140 | 140 | with Button1 do begin | |
| 141 | 141 | SetBounds(10,10,100,25); | |
| 142 | 142 | end; | |
| 143 | 143 | ``` | |
| 144 | 144 | ||
| 145 | 145 | Left, Top, Width, Height вызывают SetBounds, и каждое изменение размера или позиции приводит к повторному вычислению параметров соседних элементов, и возможно рекурсивному вычислению параметров родительских и/или дочерних элементов. | |
| 146 | 146 | ||
| 147 | 147 | ## DisableAlign/EnableAlign | |
| 148 | 148 | ||
| 149 | 149 | При позиционировании многих элементов управления полезно отключать автоматическое вычисление размера, выравнивания и привязок. | |
| 150 | 150 | ||
| 151 | 151 | ``` pascal | |
| 152 | 152 | DisableAlign; | |
| 153 | 153 | try | |
| 154 | 154 | ListBox1.Width:=ClientWidth div 3; | |
| 155 | 155 | ListBox2.Width:=ClientWidth div 3; | |
| 156 | 156 | ListBox3.Width:=ClientWidth div 3; | |
| 157 | 157 | finally | |
| 158 | 158 | EnableAlign; | |
| 159 | 159 | end; | |
| 160 | 160 | ``` | |
| 161 | 161 | ||
| 162 | 162 | > **Note:** Каждый вызов DisableAlign требует вызова EnableAlign. Например, если вы вызываете DisableAlign два раза, то должны вызвать EnableAlign тоже два раза. | |
| 163 | 163 | ||
| 164 | 164 | **Для пользователей, перешедших с Delphi**: Данные вызовы работают рекурсивно. DisableAlign запрещает выравнивание всех дочерних элементов, элементов-"внуков" и т.д. | |
| 165 | 165 | ||
| 166 | 166 | ## Создание не прямоугольных окон или элементов управления | |
| 167 | 167 | ||
| 168 | 168 | В Lazarus можно легко создавать не прямоугольные окна или элементы управления. Для этого можно просто вызвать TWinControl.SetShape с видимой областью в качестве параметра. Обратите внимание, что это будет работать как для окон, так и для элементов управления, так как TCustomForm и TCustomControl происходят от TWinControl. Можно также вызвать подпрограмму LCLIntf SetWindowRgn, которая полностью эквивалентна вызову метода SetShape. | |
| 169 | 169 | ||
| 170 | 170 | С использованием SetWindowRgn код будет похож на следующий: | |
| 171 | 171 | ||
| 172 | 172 | ``` pascal | |
| 173 | 173 | uses LCLIntf, LCLType; | |
| 174 | 174 | ||
| 175 | 175 | procedure TForm1.FormCreate(Sender: TObject); | |
| 176 | 176 | var | |
| 177 | 177 | MyRegion: HRGN; | |
| 178 | 178 | begin | |
| 179 | 179 | MyRegion := CreateRectRgn(0, 0, 100, 100); | |
| 180 | 180 | SetWindowRgn(Handle, MyRegion, True); | |
| 181 | 181 | DeleteObject(MyRegion); | |
| 182 | 182 | end; | |
| 183 | 183 | ``` | |
| 184 | 184 | ||
| 185 | 185 | Эквивалентный код, который использует объект TRegion более высокого уровня, доступен в Lazarus 0.9.31+ (обратите внимание, что предыдущий способ работы все еще поддерживается и будет \[поддерживаться\] в будущем): | |
| 186 | 186 | ||
| 187 | 187 | ``` pascal | |
| 188 | 188 | uses Graphics; | |
| 189 | 189 | ||
| 190 | 190 | procedure TForm1.FormCreate(Sender: TObject); | |
| 191 | 191 | var | |
| 192 | 192 | MyRegion: TRegion; | |
| 193 | 193 | begin | |
| 194 | 194 | MyRegion := TRegion.Create; | |
| 195 | 195 | try | |
| 196 | 196 | MyRegion.AddRectangle(0, 0, 100, 100); | |
| 197 | 197 | Self.SetShape(MyRegion); | |
| 198 | 198 | finally | |
| 199 | 199 | MyRegion.Free; | |
| 200 | 200 | end; | |
| 201 | 201 | end; | |
| 202 | 202 | ``` | |
| 203 | 203 | ||
| 204 | 204 | Результат этой операции в окне в macOS с использованием набора виджетов Qt можно увидеть здесь: | |
| 205 | 205 | ||
| 206 | - | ||
| 207 | - | ||
| 208 | - | ||
| 209 | - | ||
| 206 | + |  | |
| 210 | 207 | ||
| 211 | 208 | Обратите внимание, что SetShape также может принять TBitmap для описания прозрачной области. | |
| 212 | 209 | ||
| 213 | 210 | См.также: | |
| 214 | 211 | ||
| 215 | 212 | - [[LCL Internals|LCL_Internals#Shaped_Windows]] | |
| 216 | 213 | ||
| 217 | 214 | Соответствующая документация: | |
| 218 | 215 | ||
| 219 | 216 | - <http://lazarus-ccr.sourceforge.net/docs/lcl/lclintf/setwindowrgn.html> | |
| 220 | 217 | - <http://lazarus-ccr.sourceforge.net/docs/lcl/controls/twincontrol.setshape.html> | |
| 221 | 218 | ||
| 222 | 219 | Ограничения: | |
| 223 | 220 | ||
| 224 | 221 | В Gtk2 регион может быть установлен только после того, как окно реализовано. Вызов SetWindowRgn в обработчике события OnShow не работает, единственный способ - вызвать его, например, из таймера, установленного с интервалом 1. Включите таймер в Form.OnShow и отключите его в обработчике OnTimer. | |
| 225 | 222 | ||
| 226 | 223 | ## Имитация событий мыши и ввода с клавиатуры | |
| 227 | 224 | ||
| 228 | 225 | Имитировать ввод с помощью мыши и клавиатуры в LCL очень легко, просто используйте процедуры из модуля LCLMessageGlue, как это делают все интерфейсы widgetset. Этот модуль имеет процедуры, такие как: | |
| 229 | 226 | ||
| 230 | 227 | ``` pascal | |
| 231 | 228 | unit LCLMessageGlue; | |
| 232 | 229 | ||
| 233 | 230 | function LCLSendMouseMoveMsg(const Target: TControl; XPos, YPos: smallint; | |
| 234 | 231 | ShiftState: TShiftState = []): PtrInt; | |
| 235 | 232 | function LCLSendMouseDownMsg(const Target: TControl; XPos, YPos: smallint; | |
| 236 | 233 | Button: TMouseButton; ShiftState: TShiftState = []): PtrInt; | |
| 237 | 234 | function LCLSendMouseUpMsg(const Target: TControl; XPos, YPos: smallint; | |
| 238 | 235 | Button: TMouseButton; ShiftState: TShiftState = []): PtrInt; | |
| 239 | 236 | function LCLSendMouseWheelMsg(const Target: TControl; XPos, YPos, WheelDelta: smallint; | |
| 240 | 237 | ShiftState: TShiftState = []): PtrInt; | |
| 241 | 238 | function LCLSendCaptureChangedMsg(const Target: TControl): PtrInt; | |
| 242 | 239 | function LCLSendSelectionChangedMsg(const Target: TControl): PtrInt; | |
| 243 | 240 | function LCLSendClickedMsg(const Target: TControl): PtrInt; | |
| 244 | 241 | function LCLSendMouseEnterMsg(const Target: TControl): PtrInt; | |
| 245 | 242 | function LCLSendMouseLeaveMsg(const Target: TControl): PtrInt; | |
| 246 | 243 | ... | |
| 247 | 244 | function LCLSendKeyDownEvent(const Target: TControl; var CharCode: word; | |
| 248 | 245 | KeyData: PtrInt; BeforeEvent, IsSysKey: boolean): PtrInt; | |
| 249 | 246 | function LCLSendKeyUpEvent(const Target: TControl; var CharCode: word; | |
| 250 | 247 | KeyData: PtrInt; BeforeEvent, IsSysKey: boolean): PtrInt; | |
| 251 | 248 | ``` | |
| 252 | 249 | ||
| 253 | 250 | ## Показ виртуальной клавиатуры в смартфоне/планшете | |
| 254 | 251 | ||
| 255 | 252 | Чтобы показать виртуальную клавиатуру, когда виджет получает фокус, в ControlStyle просто добавьте csRequiresKeyboardInput: | |
| 256 | 253 | ||
| 257 | 254 | ``` pascal | |
| 258 | 255 | constructor TMyTextEditor.Create(AOwner : TComponent); | |
| 259 | 256 | begin | |
| 260 | 257 | inherited Create(AOwner); | |
| 261 | 258 | ControlStyle := ControlStyle + [csRequiresKeyboardInput]; | |
| 262 | 259 | ... | |
| 263 | 260 | end; | |
| 264 | 261 | ``` | |
| 265 | 262 | ||
| 266 | - | ||
| 267 | - | ||
| 268 | - | ||
| 269 | - | ||
| 263 | + |  | |
| 270 | 264 | ||
| 271 | 265 | ## Перебор по всем дочерним элементам управления TWinControl | |
| 272 | 266 | ||
| 273 | 267 | Это очень легко, просто используйте цикл для перебора TWinControl.ControlCount и TWinControl.Controls\[Index\]. Индекс начинается с нуля. | |
| 274 | 268 | ||
| 275 | 269 | ``` pascal | |
| 276 | 270 | procedure TWinControl.WriteLayoutDebugReport(const Prefix: string); | |
| 277 | 271 | var | |
| 278 | 272 | i: Integer; | |
| 279 | 273 | begin | |
| 280 | 274 | inherited WriteLayoutDebugReport(Prefix); | |
| 281 | 275 | for i:=0 to ControlCount-1 do | |
| 282 | 276 | Controls[i].WriteLayoutDebugReport(Prefix+' '); | |
| 283 | 277 | end; | |
| 284 | 278 | ``` | |
| 285 | 279 | ||
| 286 | 280 | --- | |
| 287 | 281 | *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](/wiki-contact).* | |
| 288 | 282 | Category: LCL/ru | |
| 289 | 283 | Category: Russian | |
| 290 | 284 | Category: Russian (unfinished translation) |
