PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » Вопросы по PureBasic » Взаимодействие основного кода с Form


Взаимодействие основного кода с Form

Сообщений 1 страница 18 из 18

1

Начал изучать pb 6.0, но сразу уткнулся в проблему логического взаимодействия main кода с кодом из form designer (FD). Нужной справочной информации мало. Помогите пройти барьер.

Иначе говоря, как выглядит код программы, например... которая меняет текст кнопки по нажатию этой самой кнопки. Окно-кнопка-нажал-сменил текст. Код окна автогенерируется в FD.

Или я не правильно понимаю роль FD?

Отредактировано Akella (16.11.2022 19:00:10)

0

2

В файле main.pb нужно подключить командой XIncludeFile файл с формой допустим это Form.pbf.

Form.pbf

Код:
;
; This code is automatically generated by the FormDesigner.
; Manual modification is possible to adjust existing commands, but anything else will be dropped when the code is compiled.
; Event procedures needs to be put in another source file.
;

Enumeration FormWindow
  #Window_0
EndEnumeration

Enumeration FormGadget
  #Button_0
EndEnumeration

Procedure OpenWindow_0(x = 0, y = 0, width = 264, height = 108)
  OpenWindow(#Window_0, x, y, width, height, "", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
  ButtonGadget(#Button_0, 84, 36, 84, 28, "0")
EndProcedure

Main.pb

Код:
XIncludeFile "Form.pbf"

OpenWindow_0()

Repeat
  Event = WaitWindowEvent()
  
  Select EventWindow()
    Case #Window_0

      Select Event
        Case #PB_Event_Gadget
          Select EventGadget()
            Case #Button_0
              SetGadgetText(#Button_0, Str(Val(GetGadgetText(#Button_0)) + 1))
          EndSelect
        Case #PB_Event_CloseWindow
          Break
      EndSelect
      
  EndSelect
  
ForEver

http://pure-basic.narod.ru/forum_files/FormExample.zip

0

3

Akella
Попробуйте IceDesign там код сразу с событиями.

0

4

Попробуйте IceDesign
вылечил от жадности последнюю версию:
https://disk.yandex.ru/d/W1andgocHMj64w

0

5

AZJIO
bizdon
Спасибо, попробую IceDesign.

Пётр
Спасибо. Разобрался. Только вместо Select в главном коде логичнее использовать сгенерированную процедуру в FD.

В итоге получилось так:
gui.pbf

Код:
;
; This code is automatically generated by the FormDesigner.
; Manual modification is possible to adjust existing commands, but anything else will be dropped when the code is compiled.
; Event procedures needs to be put in another source file.
;

Global Window_0

Global Button_0

Declare Click(EventType)

Procedure OpenWindow_0(x = 0, y = 0, width = 600, height = 70)
  Window_0 = OpenWindow(#PB_Any, x, y, width, height, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  Button_0 = ButtonGadget(#PB_Any, 10, 10, 580, 50, "")
EndProcedure

Procedure Window_0_Events(event)
  Select event
    Case #PB_Event_CloseWindow
      ProcedureReturn #False

    Case #PB_Event_Menu
      Select EventMenu()
      EndSelect

    Case #PB_Event_Gadget
      Select EventGadget()
        Case Button_0
          Click(EventType())          
      EndSelect
  EndSelect
  ProcedureReturn #True
EndProcedure

main.pb

Код:
IncludeFile "gui.pbf"

Procedure Click(event_type)
  Static i
  i = i + 1
  SetGadgetText(Button_0, Str(i))  
EndProcedure

OpenWindow_0()
Repeat
  EventID = WaitWindowEvent()
  Window_0_Events(EventID)
Until EventID = #PB_Event_CloseWindow

Отредактировано Akella (18.11.2022 14:50:52)

0

6

Akella

Есть привычка программирования и я не понимаю зачем окно и цикл событий разделять в пространство раздельных процедур. Ведь все переменные нужно делать глобальные. Допустим там функция будет загружена в стек для выполнения, тогда Repeat надо тоже делать внутри функции, чтобы каждый раз не загружать в стек, а один раз загрузил и она там крутится. В общем изначально неоптимальная ситуация. Используешь #PB_Any, хотя можно нумерованное перечисление констант сделать. Если как событие всегда использовать функцию (типа Click()), то проще перейти на способ BindEvent(). В IceDesign для меня более адекватная схема событий, я бы сказал идеальная. То есть там цикл заканчивается ForEver, а события получаются равнозначные впротивовес "Until EventID" где событие отправляются в функцию, а одно проверяется внешне, а по сути они равнозначные, одного поля ягодки и должны быть на одном уровне Case.

0

7

AZJIO

зачем окно и цикл событий разделять в пространство раздельных процедур. Ведь все переменные нужно делать глобальные.

Те глобальные переменные, что ссылаются на гаджеты, генерятся автоматически. Присвоенные им идентификаторы не особо волнуют. Честно говоря пока не понял, зачем нужно вручную (энумирацией) идентифицировать объекты. Может выглядит, как двойная работа, но по мне такая мелочь внимания не требует.

проще перейти на способ BindEvent()

Видел, пока не разобрался.

там цикл заканчивается ForEver, а события получаются равнозначные впротивовес "Until EventID" где событие отправляются в функцию, а одно проверяется внешне

Согласен, такой вариант должен увеличить стабильность интерактива, но пока я не сталкивался с проблемами.

П.С. Для себя рассматриваю PB как кроссплатформенный расширенный скриптовый язык. Так то использую Bash, Batch, PowerShell, Python, но подкупила самодостаточность пакета PB.

Отредактировано Akella (18.11.2022 19:44:22)

0

8

Akella

генерятся автоматически

я же в курсе что редактор форм сгенерировал такой код. По сути GUI самая ничтожная часть в программе, поэтому считать что это двойная работа, разве что к GUI. А то что он генерит десяток переменных, которые могли бы быть константами? Я думаю область констант и область переменных не одно и тоже. Когда прога ищет переменную в глобальном пространстве, она их перебирает пока не найдёт и плодить их нет смысла. Хотя опять же не критично, просто я возврат гаджетов использую в качестве дескрипторов, если номер задан явно, а не #PB_Any, то элемент возвращает дескриптор.

Согласен, такой вариант должен увеличить стабильность

Нет, только ради читабельности кода. Но кто как привык. Если мне не нравится шиворот навыворот, то я к этому привыкать не буду, хотя и так можно привыкнуть.

Так то использую Bash, Batch, PowerShell, Python,

Я тоже Python пробовал на Linux, желая програмить на Linux, но после AutoIt3 это было тяжело изучать, да и работало всё не как хотелось, через одно место. Ну и Python не родной для Windows, хоть и кроссплатформенный, он тянет библиотеки в Windows и код открытый, ведь по сути он скриптовый язык.

Для быстроты изучения ссылочки 1, 2, 3, 4, 5

Отредактировано AZJIO (19.11.2022 01:00:30)

0

9

AZJIO
Премного благодарен! В том числе закачал ваши программы для изучения.

0

10

О!  Я тоже интересуюсь этой темой уже очень давно.  Но так и не пришёл к единому мнению.

Пётр и AZJIO, скажите пожалуйста, вы всегда разделяете очередной свой проект на несколько файлов (формы, константы, основной код) ?
Хочу в конце концов начать уже делать правильно/по-науке, но возникает какой-то психологический барьер, когда нету всего кода перед глазами. Пишу всё в одном файле.  Основной цикл никогда не выносил в процедуры.  Да, это тяжко порой (разобраться в этой всей мешанине и поддерживать её в рабочем состоянии/вносить изменения), но такая вот привычка.  Вредная ?  Понимаю, что что-то крупное таким макаром не написать. Спрашиваю у вас, так как видел некоторые ваши проекты и там было именно разделение.

Вообще, можете поделиться секретом (по шагам), как начинаете писать что-то новое ?  Пожалуйста. Я записываю.   :writing:
В главном меню ide pb есть такой пункт:  проект -> новый проект...   (ctrl+shift+n).  Так ?

AZJIO, может возьмётесь сделать пошаговую инструкцию для новичков ?

P.S.  Кстати, до сих пор избегаю биндить события (опять же барьер), так как в древних версиях pb это приводило к утечкам памяти.

Отредактировано Пар (21.11.2022 12:37:04)

0

11

Пар написал(а):

вы всегда разделяете очередной свой проект на несколько файлов

Обычно разделяется на несколько файлов большой проект или если на это есть причины (например дизайнер форм сохраняет код в отдельном файле).

Пар написал(а):

возникает какой-то психологический барьер, когда нету всего кода перед глазами.

Код делится не случайно (часть процедур в одном, а часть в другом файле), а по функционалу. Обычно отдельный файл оформляется в виде модуля и из него доступны только функции из DeclareModule. Таким образом можно создать независимый от остального проекта код. В дальнейшем это упрощает использование кода в других проектах.

Пар написал(а):

Основной цикл никогда не выносил в процедуры.

Его можно разместить  где угодно. А события обрабатывать в процедурах назначенных через BindEvent().

0

12

1. У меня в одном файле, кроме... есть функции, которые уже универсальные, изолированные, которые применяю во всех проектах, которые не являются целенаправленными под проект. Вот их помещаю в файл ForProjectName, т.е. как пример "ДляКонвертер" и к этим функциям я уже не возвращаюсь, чтобы их улучшить и они мне не мешают в коде.
2. Если я сейчас изучаю Scintilla, то я не хотел бы иметь константы 500шт в своём коде, а также хоть из них используется с 10-ток констант я не хотел бы их добавлять по одной при каждом добавлении новых функций, я просто добавляю их все через инклуд и забываю об этой проблеме. В остальных случаях констант у меня не так много, чтобы делать в отдельном файле, учитывая что в процессе время от времени я добавляю очередную константу.
3. Также использование меток-комментариев ";- Метка" по смыслу ни чем не хуже чем разделение на файлы. То есть для всех участков кода я делаю метки для прыжка к ней. То есть обязательно метка "Перечисления", "Декларация",  "ini", GUI, "Цикл событий", причём цикл события может быть разделён ещё на блоки "Цикл - гаджеты", "Цикл меню", "Цикл ресайз", "Цикл - часть2". Далее идут функции, они сами добавляются на панель список, хотя их тоже можно разделить на блоки, типа "ФУНКЦИИ РЕЕСТРА", "ФУНКЦИИ  ListView (или выделить префиксом типа "=== ListView").
4. Биндить - были у меня на AutoIt3 такие проекты, смысл в том что в итоге после GUI идет пустой цикл, и он обрабатывался как бы вторым потоком, то есть там можно было вставить проверку иконки в трее. Хотя сейчас я не вижу проблемы сделать это двумя способами. И кстати функционал он работает таким же способом, просто незаметно для пользователя создаёт тот же цикл опроса событий с вызовом функции. В общем это только выглядит по разному, а компилируемый код будет одинаков.

Мне как удобно я так и делаю, когда я чувствую, что разделение облегчит мне жизнь, так сразу на него перейду, это же тоже вносит другие неудобства, если у меня открыто 10 файлов-программ и я по ним перемещаюсь беря код из одной программу в другую, если у меня будет каждый проект разделён на 3 файла, то у меня уже будет открыто 30 файлов и уже 3-5 рядов займут вкладки и перекроют мне полезную площадь окна кода, будут принуждать к мысли как бы лишний файл не открыть.

5. Как написать новое? Должна быть тяга, необходимость, вдохновение, а заставлять себя иногда бессмысленно. Бывает начал (попробовал) и затянуло. А что там объяснять, пишешь потихоньку и всё. У меня есть то что я повторяю функционал написанный на AutoIt3, то есть даже не новая идея, просто переписываю алгоритм. Естественно, если я уже понимаю, есть опыт работы на другом языке, то мне уже даётся легче чем если бы я с нуля изучал основы.

Отредактировано AZJIO (21.11.2022 23:01:00)

0

13

Здравствуйте, Уважаемые  Пётр  и  AZJIO.

Спасибо за ваши ответы/комментарии.

Пётр написал(а):

(например дизайнер форм сохраняет код в отдельном файле).

Никогда им полноценно не пользовался. Думаю, что его целесообразно использовать лишь для того, чтобы визуально видеть/проконтролировать как будут размещены гаджеты. Так сказать "превью".

Пётр написал(а):

Обычно отдельный файл оформляется в виде модуля и из него доступны только функции из DeclareModule.

У меня в программе есть довольно длительный и трудоёмкий процесс (вычисление хэшей файлов, в указанной(-ом) пользователем папке/диске). Это занимает, наверное 98% всего времени работы программы. Чтобы хоть как-то ускорить этот процесс было решено создавать дополнительные потоки. То есть за раз обсчитываются сразу несколько файлов. Естественно это всё не бесконтрольно. Максимальное кол-во работающих в конкретный момент времени потоков не должно превышать число n (это значение можно изменять).
Внимание вопрос:  потоки - это не про модули ?  Немного утрированно спросил. Хорошо. Возможно ли создание потоков в модуле ? И насколько это целесообразно ?
Разницу между понятиями поток и модуль - понимаю. Один из известнейших примеров - это, наверное тот, что предназначен для работы с реестром (на зарубежном форуме публиковался).

Пётр написал(а):

Его можно разместить  где угодно.

Пётр, если мне не изменяет память, то основной цикл, вида:

Код:
Repeat
  Event = WaitWindowEvent()
  
  Select Event
    Case ...
      ...
    Default
      ...
  EndSelect
Until Exit = #True

можно размещать только в основном потоке. + WaitWindowEvent() должен использоваться только единожды для всех окон. В процедуры данный цикл никогда не помещал (окна - да).

AZJIO написал(а):

Также использование меток-комментариев ";- Метка"

вот здесь категорически согласен.

AZJIO написал(а):

когда я чувствую, что разделение облегчит мне жизнь, так сразу на него перейду, это же тоже вносит другие неудобства...

ВотЪ...  поэтому и не могу принять эту парадигму.
Всё должно быть в пределах прямой видимости (перед глазами).

AZJIO написал(а):

Должна быть тяга, необходимость, вдохновение, а заставлять себя иногда бессмысленно.

категорически согласен. Это лишь хобби для души, не приносящее никакого дохода. К тому же безжалостно отнимающее время и глаза, НО...  как Вы правильно выразились - это затягивает. Бывает так, что ходишь несколько дней и вынашиваешь идеи, как лучше подступиться к той или иной задаче. Хорошо, если основной вид деятельности позволяет это делать. Собственно поэтому и опыта особого нет, так как пишу что-то очень редко. Что-то очень мелкое (микроскопическое). + всегда интереснее, когда задача прикладная, то есть будет использоваться где-то, а не просто лежать "на полке" (чтоб былО). Спортивный интерес - это не про меня (в данном контексте). Нет, конечно подглядываю временами у мастеров приёмчики разные. Нравятся лаконичные и простые решения. Откладываю их до времени, когда на подсознательном уровне понимаю, что в дальнейшем это можно будет где-то применить.

AZJIO, да, Ваш вклад в популяризацию AutoIt3 (на "русской доске") достоин уважения. Хотя я и не использую AutoIt3.
Также наблюдал некоторое время за развитием Вашей программы "ChkDskGui" на usbtor.ru (горячее обсуждение с nikzzzz'ом   :'( ).
Помню, что делали плагины для notepad++, но это для меня - верх понимания. То есть тёмный лес.

P. S.   Отдельная благодарность Вам за то, что не "зажимаете" исходники. Есть возможность (при желании) посмотреть как всё устроено.   :cool:

Отредактировано Пар (22.11.2022 22:00:27)

0

14

Пар написал(а):

потоки - это не про модули

Нет, потоки это thread-ы https://www.purebasic.com/documentation … index.html

Пар написал(а):

Возможно ли создание потоков в модуле ?

Модуль это обычный код, но изолированный от остальной программы. https://ru.wikipedia.org/wiki/Пространство_имён_(программирование)
Запускать потоки в модулях можно.

Пар написал(а):

основной цикл можно размещать только в основном потоке

В винде можно и в других потоках (нужно игнорировать предупреждение отладчика). В Linux работает не будет (программа завершится с ошибкой). В MacOS X не проверял.
Но я писал не об этом. Обработчик событий должен быть в том же потоке но не обязательно в нашем приложении. Стандартный пример из справки

Код:
If OpenWindow(0, 0, 0, 400, 100, "Timer Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ProgressBarGadget(0, 10, 10, 380, 20, 0, 100)
  AddWindowTimer(0, 123, 250)
  
  Value = 0
  
  MessageRequester("","")
  End
  
  Repeat
    Event = WaitWindowEvent()
    
    If Event = #PB_Event_Timer And EventTimer() = 123
      Value = (Value + 5) % 100
      SetGadgetState(0, Value)
    EndIf    
    
  Until Event = #PB_Event_CloseWindow
EndIf

Если его запустить, события таймера не обрабатываются - мешает MessageRequester(). Но если его немного переделать, события таймера обрабатываются несмотря на то что выполняется MessageRequester() и WaitWindowEvent() не вызывается.
Используется обработчик событий из MessageRequester().

Код:
Global Value = 0

Procedure Timer()
  Value = (Value + 5) % 100
  SetGadgetState(0, Value)
EndProcedure

If OpenWindow(0, 0, 0, 400, 100, "Timer Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ProgressBarGadget(0, 10, 10, 380, 20, 0, 100)
  AddWindowTimer(0, 123, 250)
  BindEvent(#PB_Event_Timer, @Timer(), 0, 123)
    
  MessageRequester("","")
  End
  
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf
Пар написал(а):

Всё должно быть в пределах прямой видимости (перед глазами).

Когда код состоит из десятков тысяч строк, держать в одном файле не только неудобно, но и сильно затрудняет работу с ним.

0

15

Пар

Всё должно быть в пределах прямой видимости (перед глазами).

Совершенно верно. Поэтому необходимо минимизировать ненужную информацию.

Если участок кода можно логически отделить в неизменяемую структуру - это нужно сделать, объявив процедуру с вводными переменными, а саму процедуру свернуть или вынести в другой файл. Структура закончена, работает, выдаёт ответ (результат или ошибка) и не должна более мозолить глаза.

В Powershell хорошая практика - там рекомендуется использовать имена функций (процедур)  в виде "глагол-существительное", например: Save-File(), Read-Data(), Draw-Graph(). Рекомендую. Позволяет быстро определить объем структуры для выноса в процедуру. И лучше дробить как можно сильнее, это не сильно увеличивает количество строк, но читаемость возрастает в разы.

Отредактировано Akella (23.11.2022 09:06:58)

0

16

Пётр, спасибо.

   Я бы сравнил модуль с процедурой (с некоторыми оговорками). Так как в процедуре переменные, явно объявленные как protected, не видны в основном коде (тоже своего рода изоляция). В модуле же изолировано всё (от и до). По завершении работы процедуры все переменные, списки, массивы, объявленные в ней автоматически уничтожаются. При завершении работы с модулем, как я понял, нужно вызвать специальную команду/директиву - UnuseModule. После этого, вызов задекларированных в модуле функций будет невозможен. Но что это нам даёт в итоге? Освобождает занятую память?

   Также есть сходство в той части, что модуль, ровно как и процедура, перехватывает обработчик событий основной программы "на себя". Выполняется последовательно. То есть, если в модуле есть затратная по времени процедура и она была вызвана из основной программы как имя модуля::имя задекларированной функции/процедуры(параметры функции/процедуры модуля), то это неизбежно/безусловно полвечёт за собой "фризы" (подвисания) основной программы/граф. интерфейса. Так? И что тогда? Получается, что такую заведомо "тяжёлую" модульную процедуру нужно вызывать из основной программы асинхронно (то есть в потоке)? Не встречал такого ещё, если честно.

   Хотел сказать здесь то, что управление интерфейсом программы вернётся/будет возможно лишь тогда, когда завершится эта "тяжёлая" процедура. И совсем не важно где она расположена (в модуле или в основном коде программы).

   Также модули, с некоторой натяжкой, можно сравнить с макросами. Только макросы видят все переменные из основного кода (без каких-либо дополнительных объявлений), а модуль о них ничего "не знает" (вероятно есть механизм их доставки туда, например, через параметры вызываемой функции или же через спец. директиву global). Макрос - это всё равно, что писать код с одной и той же проверкой чего-либо в разных местах кода. И предназначен он в основном для небольших/коротких цепочек.

   Пётр, ещё раз спасибо за ликбез (пространство имён).

   Akella, и Вам спасибо за рекомендации. Так и делаю (называю процедуры по смыслу). Так же пользуюсь парой

Код:
;{
;}

Это позволяет сворачивать любой текст, помещённый между фигурными скобками, ровно как процедуры. Естественно с комментарием поверх открывающей скобки (для быстрого доступа к нужному участку кода из окна, где перечислены все процедуры).

0

17

Пётр написал(а):

Нет, потоки это thread-ы https://www.purebasic.com/documentation … index.html

Если его запустить, события таймера не обрабатываются - мешает MessageRequester(). Но если его немного переделать, события таймера обрабатываются несмотря на то что выполняется MessageRequester() и WaitWindowEvent() не вызывается.
Используется обработчик событий из MessageRequester().

Когда код состоит из десятков тысяч строк, держать в одном файле не только неудобно, но и сильно затрудняет работу с ним.

Это наводит на мысль что таймер работает в отдельном потоке.
Иначе бы он зациклился на мессаге!

Код:
Global Value = 0
Global variant =1
Procedure.i calbac(h,m,w,l)
  
  If m=#WM_TIMER
    Debug"TIMER" +Str(variant)
      Value = (Value + 5) % 100
      SetGadgetState(0, Value)
  EndIf

ProcedureReturn #PB_ProcessPureBasicEvents 
EndProcedure

If OpenWindow(0, 0, 0, 400, 100, "Timer Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
   SetWindowCallback(@calbac(),0)
  ProgressBarGadget(0, 10, 10, 380, 20, 0, 100)
  AddWindowTimer(0, 123, 250)
  
  Value = 0
  
  MessageRequester("","")
Goto variant2
  
  Repeat
    Event = WaitWindowEvent()
    Debug 666
    If Event = #PB_Event_Timer And EventTimer() = 123
      Value = (Value + 5) % 100
      SetGadgetState(0, Value)
    EndIf    
    
  Until Event = #PB_Event_CloseWindow
EndIf
;=======================================================
Procedure Timer()
  Debug "=======Procedure Timer()======"
  Value = (Value + 5) % 100
  SetGadgetState(0, Value)
EndProcedure

variant2:
variant=2

If OpenWindow(0, 0, 0, 400, 100, "Timer Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  SetWindowCallback(@calbac(),0)
  ProgressBarGadget(0, 10, 10, 380, 20, 0, 100)
  AddWindowTimer(0, 123, 250)
  BindEvent(#PB_Event_Timer, @Timer(), 0, 123)
    
  MessageRequester("","")
Goto variant3
  
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf
variant3:

Отредактировано Sergeihik (23.11.2022 19:10:25)

0

18

Пар написал(а):

Но что это нам даёт в итоге?

Изолированное пространство имен. Например в разных пространствах могут быть переменные или процедуры с одинаковыми именами не вызывая конфликта имен.

Код:
DeclareModule m1
  Declare Test()
EndDeclareModule

Module m1
  Procedure Test()
    Debug "Процедра "+#PB_Compiler_Procedure+" из модуля "+#PB_Compiler_Module
  EndProcedure
EndModule

DeclareModule m2
  Declare Test()
EndDeclareModule

Module m2
  Procedure Test()
    Debug "Процедра "+#PB_Compiler_Procedure+" из модуля "+#PB_Compiler_Module
  EndProcedure
EndModule

Procedure Test()
  Debug "Процедра "+#PB_Compiler_Procedure+" из модуля "+#PB_Compiler_Module
EndProcedure

m1::Test()
m2::Test()
Test()
Пар написал(а):

Освобождает занятую память?

Нет.

Пар написал(а):

И что тогда?

Можно выполнить часть кода в другом потоке.

Sergeihik написал(а):

Это наводит на мысль что таймер работает в отдельном потоке.

Нет, в основном потоке.

0


Вы здесь » PureBasic - форум » Вопросы по PureBasic » Взаимодействие основного кода с Form