9

Создание пользовательского интерфейса

В этой главе я расскажу, как создать графический пользовательский интерфейс (далее я буду называть GUI) для своих программ. PureBasic с помощью своих простых команд делает эту задачу невыразимо легкой. Я поясню код создания меню и графики. Мы так же рассмотрим обработку событий интерфейса вашей программы, например, когда пользователь нажимает на кнопку или выбирает пункт меню. Надеюсь, что после прочтения этой главы, вы будете хорошо осведомлены для создания GUI любой программы, а так же уже сами будете развиваться в этом направлении.

Консольные программы

Мы начнем с простого, а именно с консоли. Консольные программы, как следует из названия, это программы, которые используют консоль для своего пользовательского интерфейса. Консоль представляет собой текстовый интерфейс, который может принимать вводные параметры и отображать с помощью текстовых символов. В некоторых операционных системах консоль может отображать графические символы. Они взяты из набора символов ASCII и попросту заменяют простые символы.  Интерфейс консоли обычно используются в программах, где мало требуется вмешательство пользователя. Это обычно утилиты командной строки, которые запускаются из консоли или к примеру CGI программы, которые выполняются в фоновом режиме на веб-серверах и т.д. В основном, консоль используется для вывода информации из программы а так же для того чтобы принять ввод текста от пользователя. Команды, которые создают и работают с консольным интерфейсом описаны в справке PureBasic  (Helpfile:ReferenceManual->General Libraries->Console). Эта библиотека предлагает программистам различные команды для печати текста в консоли, ввода информации от пользователя, очистки консоли и даже изменения цвета. Вот пример того, как создать консольную программу в PureBasic:

If OpenConsole()
  Print("This is a test console program, press return to exit...")
  Input()
  CloseConsole()
EndIf
End

 

В этом примере я использовал команды OpenConsole() и CloseConsole() , чтобы открыть и закрыть окно консоли, фактически они говорят сами за себя. Второй командой Print со  строковым параметром, я указал программе напечатать строку в консоли. Эта команда практически идентична команде PrintN() , которая также будет печатать строку текста, но она будет добавлять конец строки после последнего символа в параметре. То есть следующая команда Print или PrintN будет печатать с новой строки.Это очень похоже на поведение команд WriteString()  и WriteStringN(). Третьей командой, используемой в приведенном выше примере, является Input(). Эта команда останавливает выполнение программы до  нажатия клавиши ENTER. Кроме того перед нажатием клавиши ENTER можно вывести любой символ(ы) на экран, и это будет параметром возврата команды INPUT(). В моем примере, я использую эту команду исключительно для того чтобы держать окно консоли открытым, чтобы люди смогли прочитать текст, который я вывел в окно консоли. Если в данном примере исключить эту команду, то консоль почти сразу же закрывается после открытия. Использование Input() позволяет держать консоль открытой, для чтения, а потом сообщить пользователю, что для продолжения выполнения программы нужно  нажать на клавишу ENTER.

 Чтение пользовательского ввода

Иногда в консоли нужно получить то, что ввел пользователь. Это может быть простым числом, либо строкой текста. Принять ввод от пользователя несложно, но вы всегда должны помнить о том, что команда Input()  возвращает только строки. Следующий фрагмент кода показывает это, представляя новые консольные команды:

If OpenConsole()
 EnableGraphicalConsole(#True)
   Repeat
     ConsoleColor(10, 0)
     PrintN("TIMES TABLES GENERATOR")
     PrintN("")
     ConsoleColor(7, 0)
     PrintN("Please enter a number, then press Return...")
     PrintN("")
     Number.q = ValQ(Input())
     If Number = 0
        ClearConsole()
        Continue
     Else
        Break
    EndIf
  ForEver

PrintN("")

  For x.l = 1 To 10
    PrintN(Str(x) + " x " + StrQ(Number) + " = " + StrQ(x * Number))
  Next x

   PrintN("")
   Print("Press Return to exit...")
   Input()
   CloseConsole()
EndIf
End

Этот пример выглядит довольно сложным, на первый взгляд. Я использовал здесь новую команду  EnableGraphicalConsole(). Она включает или выключает отображение графических возможностей консоли, путем передачи этой команде констант #TRUE или #False. Поскольку мы хотим  использовать команду ClearConsole(), которая работает только в графическом режиме консоли, мы зададим команде EnableGraphicalConsole() значение #TRUE.

Различия между текстовым и графическим режимами консоли

Использование команды EnableGraphicalConsole() дает возможность переключаться между режимом текста и графическим режимом в консоли. Ниже приведены различия для каждого режима:

Текстовый режим (по умолчанию):

ASCII символы отображаются правильно (ASCII в диапазоне от ‘0’ до ‘31’).

Редиректы(перенаправление) работают правильно (необходимость для программ CGI).

Длинные строки напечатанного текста переносятся на новую строку, при достижении границы окна консоли.

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

Графический режим (меняется с помощью EnableGraphicalConsole (# True)):

 Текстовые символы ACSII вне диапазона от  ‘33’ до ‘126’ отображены в режиме малой графики.

Длинные строки напечатанного текста обрезаются, при достижении границы окна консоли.

ClearConsole() полностью очищает консоль вывода информации.

ConsoleLocate() перемещает курсор  в любое заданное место в окне консоли.

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

С помощью команды ConsoleColor() я изменил цвет текста консоли в некоторых местах . Первым параметром является цвет текста, вторым цвет фона текста. Параметры цифры в диапазоне от 0 до 15 которые представляют различные цвета.  Чтобы понять какое число с каким цветом ассоциируется см. справку (Helpfile:Reference Manual->General Libraries->Console->ConsoleColor). Кроме того, в этом примере я использовал цикл, для  проверки вводимой пользователем информации. Это нужно, если пользователь введет неверное значение. То есть мне нужно, чтобы пользователь вводил  число. Даже если пользователь введет число, это все равно будет строковым типом данных, возвращенным командой INPUT(). Для того, чтобы преобразовать его в число, я использую функцию ValQ(), которая преобразует его в тип Quad. Это возвращаемое значение проверяется. Если оно равно 0, то программа очистит консоль и выведет на печать те строки, которые были сразу после запуска и конечно снова перейдет в режим ожидания ввода от пользователя. В том же случае, если пользователь введет число, программа прервет цикл и перейдет к остальной части программы, в которой нарисует таблицу умножения для вводимого числа.

 Чтение пользовательского ввода в режиме реального времени

Последний пример продемонстрировал работу команды Input(), которая хороша, если вам необходимо ввести строку, и все же у нее есть единственное ограничение: обязательное нажатие клавиши ENTER. Что делать, если вы хотите получить ввод пользователя в реальном времени, то есть сразу же обнаружить, когда клавиша была нажата, чтобы вызвать действие? Это может быть достигнуто с помощью  Inkey() и RawKey() команд. Посмотрите на следующий кусок кода для примера использования их обоих:

Procedure DisplayTitle()
  ConsoleColor(10, 0)
  PrintN("KEY CODE FINDER")
  PrintN("")
  ConsoleColor(7, 0)
EndProcedure

Procedure DisplayEscapeText()
  PrintN("")
  ConsoleColor(8, 0)
  PrintN("Press another key or press Escape to exit")
  ConsoleColor(7, 0)
EndProcedure

If OpenConsole()
     EnableGraphicalConsole(#True)
     DisplayTitle()
     PrintN("Press a key...")
  Repeat
    KeyPressed.s = Inkey()
    RawKeyCode.l = RawKey()
    If KeyPressed <> ""
       ClearConsole()
       DisplayTitle()
       PrintN("Key Pressed: " + KeyPressed)
       PrintN("Key Code: " + Str(RawKeyCode))
       DisplayEscapeText()
    ElseIf RawKeyCode
       ClearConsole()
       DisplayTitle()
       PrintN("Key Pressed: " + "Non-ASCII")
       PrintN("Key Code: " + Str(RawKeyCode))
       DisplayEscapeText()
    Else
      Delay(1)
    EndIf
  Until KeyPressed = #ESC$
    CloseConsole()
EndIf
End

Этот пример похож на прошлый. Мы так же открываем консоль, устанавливаем графический режим, печатаем текст. Основное различие в цикле Repeat, где идет отлов клавиши с помощью команд Inkey() и RawKey(). Первая команда Inkey() возвращает строковой параметр: сам символ при нажатии на него. Так если я нажму клавишу D на клавиатуре, то  команда Inkey() вернет строку D. Если я нажму на эту клавишу с нажатой клавишей Shift, то команда вернет d. Так же если я переключу на русский язык, команда будет возвращать уже русские символы Если при нажатии будет не-ASCII символ, то команда вернет пустую строку. Вторая команда RawKey() тоже работает в режиме реального времени и  возвращает код нажатой клавиши. Важно понимать, что команда RawKey() не различает русские и английские символы, а так же строчные и прописные. Она считывает  ASCII код лишь в одном регистре (он совпадает с прописными английскими клавишами). В приведенном выше примере, можно увидеть, как использовать эти две команды. Это отличный пример ожидания ввода от пользователя в режиме реального времени и соответствующим образом реагировать, когда конкретная клавиша нажата.

Использование команды DELAY()

Вы могли заметить, что в приведенном выше примере я использовал команду Delay() с помощью которой я определил программе задержку на 1 миллисекунду. Хотя это выглядит немного странным и ненужным, команда Delay() разумна для данного кода. Поскольку вы будете использовать многозадачность операционной системы для запуска программ, практика использования команды задержки позволит не монополизировать(только для себя) процессорное время. Использование задержки дает возможность другим программам использовать процессор в это время. Даже задержка 1 миллисекунда освобождает ресурсы процессора для других программ. Эта команда особенно полезна при использовании циклов. Данная команда может использоваться даже для заморозки своей программы на нужное время.

Правильная компиляция консольных программ

Запуск консольной программы  в IDE, будет строить программы, которые выглядят и действуют как реальные консольные программы, но не будут консольными программами в прямом смысле, пока вы их не скомпилируете  правильно в формат консоли.

 

 

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

 

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

В этом разделе я собираюсь показать вам, как создавать привычные вам пользовательские интерфейсы. PureBasic использует API операционной системы для привлечения компонентов интерфейса. В качестве примера такого интерфейса, посмотрите на PureBasic IDE. Среда IDE  написана полностью на языке PureBasic, который в свою очередь, использует API операционной системы. Все эти разговоры об API операционной системы могут вас обескуражить , заставит вас думать, что все  очень сложно, но не паникуйте. PureBasic всю сложность превратил в набор понятных команд и функций. По набору этих функций PureBasic может соперничать с любым другим языком программирования. В среднем программы, написанные на PureBasic оказываются быстрее и имеют меньшие размеры, чем у многих языков программирования. Это было доказано много раз через небольшие соревнования, созданные пользователями на официальных форумах PureBasic. Итак, давайте продолжим и создадим наш первый пользовательский интерфейс.

ПРИВЕТ МИР!

Как это принято в мире программирования, первый пример интерфейса команд на любом языке программирования обычно программа Hello World(ПРИВЕТ МИР!)  Он состоит из простого окна, приветствующего пользователя и предоставляющего возможность выхода из программы. И так:

#WINDOW_MAIN = 1
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WINDOW_MAIN, 0, 0, 300, 200, "Hello World", #FLAGS)
  Repeat
    Event.l = WaitWindowEvent()
 Until Event = #PB_Event_CloseWindow
EndIf
End
 

Если вы запустите этот кусок кода, вы увидите окно, похожее на то, что ниже на рисунке. Чтобы закрыть это окно нажмите кнопку Закрыть в верхнем правом углу. Этот  пример состоит из пустого окна и это станет началом пользовательских интерфейсов в PureBasic. В приведенном выше примере, я использую функцию OpenWindow() для открытия окна и определения ее атрибутов. Если открыть справку и найти функцию OpenWindow() (Helpfile:Reference Manual->General Libraries->Window->OpenWindow), можно четко увидеть  синтаксис этой функции. В данном примере мы использовали флаги #PB_Window_SystemMenu и #PB_Window_ScreenCentered, которые определяют, что окна должны иметь функционирующую систему меню, в комплекте с кнопкой Закрыть и быть в центре экрана, после запуска. При использовании нескольких флагов, нужно использовать битовый оператор OR  ( | ). Этот оператор объединяет все значения (более полное объяснение см. главу 3). После команды OpenWindow()  я использовал цикл Repeat  в качестве основного цикла для обработки любых событий.

 

Почему параметры позиции окна нулевые? При открытии окна с помощью OpenWindow() параметры команд определения координат игнорируются, поскольку используется флаг  #PB_Window_ScreenCentered.  Как и флаг #PB_Window_WindowCentered (для дочернего окна)они имеют больший приоритет. И поскольку это так, вы можете смело использовать параметры  #PB_Ignore для этих двух позиций.

Основной цикл

Основной цикл в программе необходим, чтобы программа непрерывно работала. Это позволяет ей иметь неизменный вид при перерисовке интерфейса (если он двигался на рабочем столе) и обрабатывать любые события, генерируемые интерфейсом. К счастью PureBasic обрабатывает все автоматически, при правильном использовании основного цикла. В программе ПРИВЕТ МИР я использовал цикл Repeat - Until  в качестве моего главного цикла , это дает мне возможность держать цикл, пока определенное условие соблюдено, в нашем случае, пока программа не обнаружила тип события  # PB_Event_CloseWindow, которое срабатывает при нажатии на кнопку Закрыть. После получения этого события, цикл завершает свою работу, таким образом идет прекращение работы программы.

Понимание событий

Программа, написанная на любом из языков программирования, будет использовать примерно одинаковый способ обработки событий. Все основное действие происходит в главном цикле, и лишь в зависимости от событий, программа может перейти к нужной нам части кода. Какого рода события существуют? События могут быть вызваны многими действиями в рамках вашей программы, например событие нажатия на гаджет, или нажатие кнопки на клавиатуре, выбор пункта из меню, или же просто закрытие программы. Самое главное, надо уметь привязать к нужному событию нужный кусок кода. Но для этого надо его идентифицировать. В Purebasic для этого есть команды: WaitWindowEvent() и WindowEvent().

Что такое гаджет?

В PureBasic гаджет представляет собой графический объект пользовательского интерфейса, который нужен для интерактивности вашей программы. Microsoft Windows называет их  Controls, а некоторых дистрибутивы Linux называет их  Widgets. Гаджеты представляют собой кнопки, поля ввода, ползунки, фреймы, прогресс бары и т.д. Все различные интерактивные компоненты, которые составляют интерфейс.

Эти две команды практически идентичны, поскольку обе они возвращают идентификатор в виде длинного номера, когда происходит событие. Разница между ними в том, что команда WaitWindowEvent() циклично приостанавливает на короткие промежутки времени вашу программу до обнаружения события, и лишь когда оно происходит, возвращает его идентификатор  и позволяет программе продолжиться как обычно. (Это очень удобно для использования в GUI, так как позволяет программе использовать мало ресурсов процессора.). Команда WindowEvent() не останавливает программу, то есть она на всю мощь работает, отлавливая события, при этом съедая невыразимо много ресурсов процессора. Эта команда очень редко используется в пользовательских интерфейсах так как это уменьшает вычислительные мощности компьютера по отношению к другим запущенным процессам в системе. WindowEvent() может использоваться, когда необходимо сохранить работу основного цикла на всю мощь, например, при показе динамической графики в вашей программе, при ее постоянной перерисовке и т.д. Если же все таки вы используете WindowEvent(), то лучше это делать в паре с командой Delay (1). Если нет событий осуществляется задержка  1 миллисекунда, для освобождения ресурсов процессора.

В программе "Hello World" я использовал команду WaitWindowEvent() в цикле Repeat для обнаружения вызова события. После вызова события WaitWindowEvent(), возвращает идентификатор, который передается на хранение в переменную с типом Long и названием Event.l . Цикл проходя по коду, сравнивает возвращенный идентификатор со всеми событиями имеющимися в коде, и если он равен числу в константе # PB_Event_CloseWindow, которая содержит идентификатор закрытия окна, то окно закрывается. Поскольку ниже цикла нет кода, программа завершает свою работу.

События происходят все время

Хотя мы не назначили никаких других действий программе, это не означает, что не происходит никаких других событий. Если мы возьмем ту же программу "Hello World" и добавим еще одну строку, содержащую команду Debug Event,мы увидим, как программа будет отображать идентификаторы событий в окне вывода отладки во время выполнения. Запустите следующий кусок кода, а затем переместите мышку  или щелкните внутри и вокруг окна. Вы увидите, что происходит много событий.

#WINDOW_MAIN = 1
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WINDOW_MAIN, 0, 0, 300, 200, "Hello World", #FLAGS)
  Repeat
    Event.l = WaitWindowEvent()
    Debug Event
 Until Event = #PB_Event_CloseWindow
EndIf
End

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

Добавление гаджетов

До сих пор мы видели простое окно, которое отображает Hello Word в заголовке. В следующем разделе я покажу вам, как вы можете добавить гаджет к окну вашей программы, чтобы повысить его функциональность и интерактивность. В следующий кусок кода я добавил две кнопки и продемонстрировал, как выявлять события от нажатия на них.

Enumeration
  #WIN_MAIN
  #BUTTON_INTERACT
  #BUTTON_CLOSE
EndEnumeration

Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WIN_MAIN, 0, 0, 300, 200, "Interaction", #FLAGS)
  If CreateGadgetList(WindowID(#WIN_MAIN))
    ButtonGadget(#BUTTON_INTERACT, 10, 170, 100, 20, "Click me")
    ButtonGadget(#BUTTON_CLOSE, 190, 170, 100, 20, "Close window")
    Repeat
        Event.l = WaitWindowEvent()
        Select Event
          Case #PB_Event_Gadget
            Select EventGadget()
               Case #BUTTON_INTERACT
                   Debug "The button was pressed."
               Case #BUTTON_CLOSE
                   Quit = #True
           EndSelect
       EndSelect
   Until Event = #PB_Event_CloseWindow Or Quit = #True
  EndIf
EndIf
End

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

В этом примере после команды создания основного окна, я использовал команду CreateGadgetList(). Это необходимо, чтобы ваше окно могло размещать гаджеты. Эта команда принимает только один параметр, который является идентификатором ОС: ID окна. Я использую команду WINDOWID()  для его получения:

...
CreateGadgetList(WindowID(#WIN_MAIN))
...

ОС идентификаторы были рассмотрены в главе 7 (PB числа и ОС идентификаторы). После создания списка гаджетов, можно размещать гаджеты на окно. Я создал две кнопки, при помощи команды  ButtonGadget(). Если открыть файл помощи и посмотреть на синтаксис этой команды (Helpfile:Reference Manual->General Libraries->Gadget->ButtonGadget) вы сможете увидеть все возможные параметры относящиеся к этой команде.

С помощью параметров к команде ButtonGadget() я разместил кнопки по нижнему краю окна. Как и в первом примере с пустым окном, мы переменной Event присваиваем идентификаторы, возвращаемые  функцией WaitWindowEvent(), а командой SELECT мы тестируем какому числу равен идентификатор:

Select Event
Case #PB_Event_Gadget
...
EndSelect

Вы заметили, что первый тест с помощью SELECT указывает на константу  #PB_Event_Gadget, которая является идентификатором события гаджета. Другими словами, проверяется равенство события константе  #PB_Event_Gadget. Далее, когда мы знаем, что событие произошло от гаджета, мы должны узнать от какого именно. Для этого применяется функция EventGadget(), которая возвращает номер гаджета задействованного в событии. И я тестировал его с помощью другого SELECT.

...
Select EventGadget()
  Case #BUTTON_INTERACT
     Debug "The button was pressed."
  Case #BUTTON_CLOSE
     Quit = #True
EndSelect
...

 

Здесь у меня два тестируемых значения, одно #BUTTON_INTERACT другое  #BUTTON_CLOSE. Если возвращаемое значение из функции EventGadget() равно #BUTTON_INTERACT, то была нажата кнопка с названием  Click me. А значит по условию будет выполняться код: Debug "The button was pressed." Если возвращаемое значение равно  #BUTTON_CLOSE, то значит что была нажата кнопка Close window и переменной Quit будет назначено значение #True, а значит произойдет выход из цикла и завершение программы. Именно поэтому использование констант в блоке Enumeration очень  полезно. После определения числовых констант для всех гаджетов в начале исходного кода, можно сослаться на каждый из них, вместо номера, что делает исходный код несравненно более четким и читаемым.

Принимаем ввода текста

Давайте изменим последний пример и добавим StringGadget,  для того чтобы мы могли ввести некоторый текст, который можно использовать в программе. StringGadget очень удобен, поскольку он обеспечивает простой способ программе принимать строку текста. Следующий пример покажет вам, как можно получить значение этого гаджета т.е. строку. и использовать ее в вашей программе. Давайте создадим один StringGadget практически на полную ширину окна:

Enumeration
    #WIN_MAIN
    #TEXT_INPUT
    #STRING_INPUT
    #BUTTON_INTERACT
    #BUTTON_CLOSE
EndEnumeration
 

Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WIN_MAIN, 0, 0, 300, 200, "Interaction", #FLAGS)
    If CreateGadgetList(WindowID(#WIN_MAIN))
        TextGadget(#TEXT_INPUT, 10, 10, 280, 20, "Enter text here:")
        StringGadget(#STRING_INPUT, 10, 30, 280, 20, "")
        ButtonGadget(#BUTTON_INTERACT, 10, 170, 120, 20, "Echo text")
        ButtonGadget(#BUTTON_CLOSE, 190, 170, 100, 20, "Close window")
        SetActiveGadget(#STRING_INPUT)
        Repeat
            Event.l = WaitWindowEvent()
            Select Event
             Case #PB_Event_Gadget
                Select EventGadget()
                    Case #BUTTON_INTERACT
                        Debug GetGadgetText(#STRING_INPUT)
                    Case #BUTTON_CLOSE
                        Quit = #True
                EndSelect
            EndSelect
        Until Event = #PB_Event_CloseWindow Or Quit = #True
    EndIf
EndIf
End

 

В этом обновленном коде, я добавил еще пару констант в блок Enumeration, в результате я могу легко добавить еще два гаджета . Один из гаджетов, который я добавил, является TextGadget. Он выводит не изменяемую для пользователя строку текста в окно. Я его использовал для отображения строки: Enter text here: Вторым  гаджетом является  StringGadget  Он создает поле ввода, которое позволяет вам ввести свой собственный текст.

Если вы откроете справку PureBasic  для StringGadget() (Helpfile:Reference Manual->GeneralLibraries->Gadget->StringGadget) то можете увидеть параметры этого гаджета. С помощью параметров, я поставил StringGadget  в верхней части окна, чуть ниже TextGadget. В качестве последнего параметра я принял пустую строку, которая выражается как набор  двойных кавычек, между ними ничего нет. Это тоже строка, которая не имеет содержания. Мне захотелось, чтобы изначально в нашем поле ввода ничего не было.

 

Как только эта программа будет запущена, вы заметите, что текстовый курсор мигает в StringGadget. Это не по умолчанию, как правило, вам придется нажать на гаджете, для того чтобы появился курсор. Но в моей программе я установил так, что гаджет станет активным при запуске с помощью команды SetActiveGadget(). Эта команда имеет всего один параметр: гаджет, который вы хотите сделать активным. Я сделал это для удобства пользователя. Для проверки интерактивности этой программы, введите текст в  StringGadget, а затем нажмите на кнопку Echo text. Теперь вы увидите, что текст появился в окне вывода отладки. Этот текст может изменяться по вашему желанию и каждый раз будет выводиться в окно вывода отладки, то что вы напечатали. Считывает текст из StringGadget функция  GetGadgetText, у которой всего один параметр: гаджет, из которого надо считать текстовую информацию. Эта функция может быть использована со многими гаджетами в PureBasic, но со StringGadget она просто возвращает строку.

Отображение текста

Давайте расширим эту программу еще дальше и вместо того, чтобы выводить текст из StringGadget в окно вывода отладки, давайте добавим ListViewGadget для отображения текста внутри нашей программы, а не в процессе отладки. В следующем примере я добавил  ListViewGadget для отображения ввода текста, с невозможностью редактировать строки. ListViewGadget является отличным способом для отображения списка строк, поскольку каждая строка имеет свою собственную линию в рамках гаджета. PureBasic предлагает множество команд для работы с  ListViewGadget, начиная с добавления и удаления, далее для подсчета и сортировки элементов, содержащихся в этом типе гаджета. В следующем небольшом примере, я использовал только команду AddGadgetItem() , чтобы добавлять новые строки:

Enumeration
    #WIN_MAIN
    #TEXT_INPUT
    #STRING_INPUT
    #LIST_INPUT
    #BUTTON_INTERACT
    #BUTTON_CLOSE
EndEnumeration

Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WIN_MAIN, 0, 0, 300, 200, "Interaction", #FLAGS)
    If CreateGadgetList(WindowID(#WIN_MAIN))
        TextGadget(#TEXT_INPUT, 10, 10, 280, 20, "Enter text here:")
        StringGadget(#STRING_INPUT, 10, 30, 280, 20, "")
        ListViewGadget(#LIST_INPUT, 10, 60, 280, 100)
        ButtonGadget(#BUTTON_INTERACT, 10, 170, 120, 20, "Enter text")
        ButtonGadget(#BUTTON_CLOSE, 190, 170, 100, 20, "Close window")
        SetActiveGadget(#STRING_INPUT)

        Repeat
            Event.l = WaitWindowEvent()
            Select Event
                Case #PB_Event_Gadget
                    Select EventGadget()
                        Case #BUTTON_INTERACT
                            AddGadgetItem(#LIST_INPUT, -1, GetGadgetText(#STRING_INPUT))
                            SetGadgetText(#STRING_INPUT, "")
                            SetActiveGadget(#STRING_INPUT)
                        Case #BUTTON_CLOSE
                            Quit = #True
                    EndSelect
            EndSelect
        Until Event = #PB_Event_CloseWindow Or Quit = #True
    EndIf
EndIf
End

Этот пример очень похож на предыдущий. На самом деле единственное реальное различие является включение  ListViewGadget. Чтение справки PureBasic для ListViewGadget() (HelpFile: ReferenceManual->General Libraries->Gadget->ListViewGadget) даст вам хороший обзор этого гаджета. Вы узнаете о синтаксисе и параметрах этой команды. Я использовал параметры для этого гаджета таким образом, чтобы этот гаджет оказался в центре главного окна, над кнопками, но под StringGadget.

 

После запуска программы, введите какой-нибудь текст в  StringGadget и нажмите  на кнопку Enter text. Это действие добавит в наш ListViewGadget() строку, которую мы ввели. После этого программа очищает StringGadget и он готов к дальнейшему вводу. Все это достигается благодаря тому, что я добавил в наш код добавление в StringGadget  пустой строки и фокусировку гаджета, после нажатия на кнопку #BUTTON_INTERACT

...
Case #BUTTON_INTERACT
AddGadgetItem(#LIST_INPUT, -1, GetGadgetText(#STRING_INPUT))
SetGadgetText(#STRING_INPUT, "")
SetActiveGadget(#STRING_INPUT)
...

В первой строке после команды CASE идет команда ADDGadgetItem. Если вы откроете страницу в справке  для AddGadgetItem() (Helpfile:Reference Manual->General Libraries->Gadget->AddGadgetItem), вы увидите параметры, которые использует эта команда. Первым параметром является номер гаджета, в который вы хотели бы добавить элемент, в нашем случае это константа #LIST_INPUT, которая содержит номер нашего гаджета. Второй параметр определяет позицию в списке (существующего или несуществующего значения). Когда будете добавлять, вы должны помнить, что индексы начинаются с нуля. В приведенном выше примере, я хочу, чтобы этот текст был добавлен в конец списка независимо от того, какой индекс, поэтому я задал параметр -1. Такой способ помогает не возиться с индексами, просто добавляет пункт в конец текущего списка каким бы большим он ни был. Третий параметр является фактической строкой, которую вы хотите добавить в гаджет. Я использовал команду GetGadgetText() возвращающую значение из StringGadget и передающую его в качестве параметра.  AddGadgetItem() может иметь больше параметров, чем я использовал здесь, но другие не являются обязательными. Второй строкой после команды Case является команда SetGadgetText(), которая устанавливает значение StringGadget в пустую строку. Это так же делается для удобства пользователя. Последней строкой идет команда SetActiveGadget(), которая делает  StringGadget активным.

Чему мы научились?

Надеюсь, последние несколько примеров были хорошим введением в GUI и обеспечивает хорошую отправную точку для дальнейшего обучения. В этой книге я не могу дать вам примеры работы каждого гаджета. Все, что могу сделать, это дать вам основные так сказать строительные блоки интерфейса, и это будет вам опорой для дальнейших ваших разработок. Принцип GUI одинаков. Сначала необходимо открыть окно с помощью команды OpenWindow() . Далее, по желанию нужно разместить гаджеты в этом окне. Но перед этим надо создать список гаджетов с помощью команды CreateGadgetList(). И конечно же создать цикл, в котором будут обрабатываться все события программы. Не забываем установить в цикл команду WaitWindowEvent() или WindowEvent() для обнаружения событий.

Внимание: в новых версиях компилятора, начиная с версии 4.30 команда CreateGadgetList() встроена в компиляцию GUI приложений, поэтому для размещения гаджетов ее использовать нет необходимости!

Изучаем гаджеты в справке.

Если вы поняли, что было написано до сих пор, вам не трудно будет создать свой собственный интерфейс. Для того чтобы больше узнать о гаджетах, нужно обратиться в справку PureBasic  (Helpfile:Reference Manual->General Libraries->Gadget). Я рекомендую прочесть этот раздел, поскольку он даст знания о том как взаимодействуют различные команды с различными гаджетами. Возьмем, к примеру команду SetGadgetText(). Она может быть использована с различными гаджетами. Если вы посмотрите в справку для этой функции (Helpfile:Reference Manual->General Libraries->Gadget->SetGadgetText), там есть  список всех гаджетов, с которыми эта команда взаимодействует.Чтобы охватить все гаджеты и команды, которые работают с ними, нужно написать еще одну книгу, поэтому пока она не написана, мой вам совет: ознакомиться с разделом GADGET в справке. C другой стороны, если вам нужно поверхностно узнать о каком то гаджете, вы можете обратиться к приложению В этой книги.  Там есть полный список всех гаджетов и краткое описание каждого из них.

Добавление меню

Добавить меню в интерфейс так же просто, как и добавление гаджетов. Когда меню создано, события для него обрабатываются так же как и у гаджетов в главном цикле.Все пункты меню определяются по номерам, как и гаджеты, и так же тестируются для дальнейших действий, связанных с ним. Вот небольшой пример:

Enumeration
   #WINDOW_MAIN
   #MENU_MAIN
   #MENU_QUIT
   #MENU_ABOUT
EndEnumeration


Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WINDOW_MAIN, 0, 0, 300, 200, "Menu Example", #FLAGS)
    If CreateMenu(#MENU_MAIN, WindowID(#WINDOW_MAIN))
        MenuTitle("File")
        MenuItem(#MENU_QUIT, "Quit")
        MenuTitle("Help")
        MenuItem(#MENU_ABOUT, "About...")
        Repeat
            Event.l = WaitWindowEvent()
            Select Event
                Case #PB_Event_Menu
                    Select EventMenu()
                        Case #MENU_QUIT
                            Quit = #True
                        Case #MENU_ABOUT
                            MessageRequester("About", "This is where you describe your program.")
                    EndSelect
            EndSelect
        Until Event = #PB_Event_CloseWindow Or Quit = #True
    EndIf
EndIf

End

Этот маленький пример показывает создание меню в пустом окне. Есть два названия меню и каждое название содержит один пункт меню. Если вы посмотрите на приведенном выше примере, можно увидеть, как создать такое меню. Для начала, вам необходимо открыть окно. После того как окно было создано, мы создаем основу меню с помощью команды CreateMenu() . Эта команда имеет два параметра, первый номер PB этого меню, а второй идентификатор окна ОС, к которому оно должно быть привязано. Это очень похоже на  синтаксис команды CreateGadgetList(), там  я также использовал команду WINDOWID() чтобы получить идентификатор ОС для нашего окна. После того как основа меню была успешно создана, можно заполнить ее заголовками меню. Для этого я использую команду MenuTitle(). Эта команда создает заголовок меню, которое отображается в верхней части окна (или в случае с Mac OS X, в верхней части экрана). Для этой команды предусмотрен всего один параметр-название меню. После того, как заголовок определен, можно создать пункты меню. Пункты меню создаются с помощью команды MenuItem(). Эта команда имеет два параметра, первый номер PB,  а второй его фактическое название. В моем примере, я определил по одному пункту меню для каждого заголовка. Для заголовка FILE   пункт Quit, а для заголовка HELP пункт ABOUT.

Обнаружение событий меню

После того как было создано меню, включая все меню и названия, мы можем отлавливать события этого меню с помощью команды #PB_Event_Menu. Все в точности так же как и с гаджетами, разница лишь в том что в случае с гаджетами мы использовали #PB_Event_Gadget.  Если событие отлавливаемое с помощью WaitWindowEvent() равно #PB_Event_Menu то это событие из пункта меню.

...
Select Event
  Case #PB_Event_Menu
    Select EventMenu()
      Case #MENU_QUIT
        Quit = #True
      Case #MENU_ABOUT
        MessageRequester("About", "This is where you describe your program.")
   EndSelect
EndSelect

...

Для определения, какой именно пункт меню был вызван,  мы должны использовать команду EventMenu(). Эта команда возвращает номер меню, который был вызван данным событием. И так сначала мы тестируем: равно ли событие #PB_Event_Menu, и если равно, тогда тестируем какой именно пункт меню был вызван с помощью команды EventMenu() . Все тесты в моей программе были сделаны с помощью оператора SELECT.

Подпункты меню

До сих пор я продемонстрировал, как создать стандартное меню, но вы также можете создать так называемое SUBMENU(подпункт меню). Данное  меню реализовано в меню ПУСК Microsoft Windows. Получается так сказать древовидная структура. События для этих меню отслеживаются так же как и для основного меню. Вот пример:

Enumeration
    #WINDOW_MAIN
    #MENU_MAIN
    #MENU_CHILD
    #MENU_QUIT
    #MENU_ABOUT

EndEnumeration

Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WINDOW_MAIN, 0, 0, 300, 200, "Menu Example", #FLAGS)
  If CreateMenu(#MENU_MAIN, WindowID(#WINDOW_MAIN))
    MenuTitle("File")
    OpenSubMenu("Parent Menu Item")
      MenuItem(#MENU_CHILD, "Sub Menu Item")
    CloseSubMenu()
    MenuBar()
    MenuItem(#MENU_QUIT, "Quit")
    MenuTitle("Help")
    MenuItem(#MENU_ABOUT, "About...")
    Repeat
      Event.l = WaitWindowEvent()
      Select Event
        Case #PB_Event_Menu
          Select EventMenu()
            Case #MENU_CHILD
              Debug "Sub menu item selected."
            Case #MENU_QUIT
              Quit = #True
            Case #MENU_ABOUT
              MessageRequester("About", "This is where you describe your program.")
         EndSelect
     EndSelect
   Until Event = #PB_Event_CloseWindow Or Quit = #True
 EndIf
EndIf
End

Подпункт меню создается с помощью команды OpenSubMenu() после команды MenuTitle(). Это объясняется тем, подпункт меню, связан с заголовком меню так же, как и любой другой пункт меню. Команда OpenSubMenu()  принимает всего один параметр, которым является строка. Она отображается как и пункт меню, но со стрелкой(сноской). После того, как мы определили команду OpenSubMenu(), мы можем добавлять пункты с помощью MenuItem() ниже этой команды, но выше команды CloseSubMenu(), которая закрывает блок SUBMENU. Дальнейшее добавление пунктов вне блока, будут обыкновенными без сносок. Кроме того если нам надо в SUBMENU создать еще одно SUBMENU, то это реализуется точно так же: в блок записывается еще один дополнительный блок со своими названиями. 

Отделение пунктов меню

Если вы внимательнее посмотрите на приведенный выше пример, то увидите, что я добавил еще одну новую команду. Эта команда есть не что иное, как графический бар или разделитель меню. Название этой команды MenuBar(). Он не принимает никаких параметров и используется там, где вы хотите разместить разделитель в текущем меню. Обычно он разделяет пункт "Quit" от остальных пунктов меню, что я и сделал в прошлом примере.

Сочетание гаджетов и меню в одной программе

Включение меню и гаджетов в одной программе очень просто. Во-первых, вы открываете окно и создайте меню. Затем вы создаете список гаджетов. Дальше связываете события гаджетов и меню с нужными действиями. Вот пример кода:

Enumeration
 #WINDOW_MAIN
 #MENU_MAIN
 #MENU_QUIT
 #MENU_ABOUT
 #TEXT_INPUT
 #STRING_INPUT
 #LIST_INPUT
 #BUTTON_INTERACT
 #BUTTON_CLOSE
EndEnumeration
Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WINDOW_MAIN, 0, 0, 300, 222, "Interaction", #FLAGS)
  If CreateMenu(#MENU_MAIN, WindowID(#WINDOW_MAIN))
    MenuTitle("File")
    MenuItem(#MENU_QUIT, "Quit")
    MenuTitle("Help")
    MenuItem(#MENU_ABOUT, "About...")
     If CreateGadgetList(WindowID(#WINDOW_MAIN))
       TextGadget(#TEXT_INPUT, 10, 10, 280, 20, "Enter text here:")
       StringGadget(#STRING_INPUT, 10, 30, 280, 20, "")
       ListViewGadget(#LIST_INPUT, 10, 60, 280, 100)
       ButtonGadget(#BUTTON_INTERACT, 10, 170, 120, 20, "Enter text")
       ButtonGadget(#BUTTON_CLOSE, 190, 170, 100, 20, "Close window")
       SetActiveGadget(#STRING_INPUT)
       Repeat
         Event.l = WaitWindowEvent()
         Select Event
           Case #PB_Event_Menu
             Select EventMenu()
                Case #MENU_QUIT
                   Quit = #True
                Case #MENU_ABOUT
                   MessageRequester("About", "This is your program description.")
            EndSelect
         Case #PB_Event_Gadget
           Select EventGadget()
              Case #BUTTON_INTERACT
                 AddGadgetItem(#LIST_INPUT, -1, GetGadgetText(#STRING_INPUT))
                 SetGadgetText(#STRING_INPUT, "")
                 SetActiveGadget(#STRING_INPUT)
             Case #BUTTON_CLOSE
                Quit = #True
          EndSelect
      EndSelect
    Until Event = #PB_Event_CloseWindow Or Quit = #True
   EndIf
 EndIf
EndIf
End

Для обработки событий пунктов меню или гаджетов, желательно использовать константы событий, а тестировать все с помощью условных операторов:

...
Select Event
  Case #PB_Event_Menu
     Select EventMenu()
       ...
     ;Menu events are handled here
       ...
    EndSelect
       ...
  Case #PB_Event_Gadget
    Select EventGadget()
       ...
   ;Gadget events are handled here
       ...
    EndSelect
EndSelect
...

Если событие вызывается из пункта меню, то есть событие будет равно  #PB_Event_Menu, то далее определяется точный пункт меню с помощью команды EventMenu(). Если событие срабатывает от гаджета, то есть событие равно #PB_Event_Gadget, то  точный гаджет устанавливается с помощью команды EventGadget(). Это позволяет обрабатывать все сразу и  меню и гаджеты  в одной программе, с помощью условного оператора SELECT.

Почему я не могу поставить иконки в меню?

Вы вероятно заметили, что  в меню PureBasic IDE расположены иконки. Но нет встроенных команд для расположения  иконок в меню. PureBasic является кросс-платформенным языком программирования, а значит его код,  должен поддерживаться на любой из поддерживаемых платформ. Таким образом, все поддерживаемые команды должны достичь такого же желаемого эффекта на каждой платформе. Иконки в меню были расценены как слишком трудными для поддержки кросс-платформенности, поэтому они были исключены из окончательного набора команд. Иконки были включены в PureBasic IDE  с использованием API каждой платформы (например, WinAPI в случае с Microsoft Windows). Чтобы узнать больше  о WinAPI, см. главу 13.

 

Сочетания клавиш в меню

 

Большинство программ поддерживают горячие клавиши для выбора пунктов меню. Еще их называют клавишами быстрого вызова,  нажав на которые, можно вызвать меню, не заходя в него. Вы можете вставить их в программу с легкостью. Для демонстрации этого мы должны создать окно с меню. В этом меню мы определим наши пункты меню, но на этот раз мы будем определять их вместе с акселераторами(надписями сочетаний клавиш). Принято писать акселераторы по правому краю меню, при том не забывая отделять их от названия пункта пробелами. Все это делается для правильного визуального восприятия:

Enumeration
    #WIN_MAIN
    #MENU_MAIN
    #M_QUIT
    #M_ABOUT
EndEnumeration
Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WIN_MAIN, 0, 0, 300, 200, "Menu Example", #FLAGS)
    If CreateMenu(#MENU_MAIN, WindowID(#WIN_MAIN))
        MenuTitle("File")
        MenuItem(#M_QUIT, "Quit" + #TAB$ + "Ctrl+Q")
        MenuTitle("Help")
        MenuItem(#M_ABOUT, "About..." + #TAB$ + "Ctrl+A")
        AddKeyboardShortcut(#WIN_MAIN,#PB_Shortcut_Control|#PB_Shortcut_Q,#M_QUIT)
        AddKeyboardShortcut(#WIN_MAIN,#PB_Shortcut_Control|#PB_Shortcut_A,#M_ABOUT)
        Repeat
            Event.l = WaitWindowEvent()
            Select Event
                Case #PB_Event_Menu
                    Select EventMenu()
                        Case #M_QUIT
                            Quit = #True
                        Case #M_ABOUT
                            MessageRequester("About", "This is where you describe your program.")
                    EndSelect
            EndSelect
        Until Event = #PB_Event_CloseWindow Or Quit = #True
    EndIf
EndIf
End

Если вы запустите пример, вы увидите, что в пункты меню вставлены акселераторы. Они разделяются от меню с помощью символов табуляции:

...
MenuItem(#M_QUIT, "Quit" + #TAB$ + "Ctrl+Q")
...

 

Эта команда, как вы знаете, имеет два параметра, но второй параметр строка состоит из трех частей. Первая часть пункта меню строка, собственно название Quit. Затем мы объединяем  с помощью константы табуляции #TAB$ строки "Quit" и "Ctrl+Q". Эта константа имеет значение 9 в ASCII, то есть запись #TAB$ равноценна записи CHR(9) . Чтобы добавить сочетания клавиш акселераторов, мы должны использовать команду AddKeyboardShortcut():

...
AddKeyboardShortcut(#WIN_MAIN,#PB_Shortcut_Control|#PB_Shortcut_Q,#M_QUIT)
AddKeyboardShortcut(#WIN_MAIN,#PB_Shortcut_Control|#PB_Shortcut_A,#M_ABOUT)
...

Эта команда имеет три параметра, первый номер PB нашего окна, с которым связана горячая клавиша, в данном случае это  #WIN_MAIN. Вторым параметром является значение, которое представляет собой фактическую комбинацию клавиш. Эта величина состоит из встроенных констант, которые представляют различные комбинации клавиш на клавиатуре. В первой строке в приведенном выше фрагменте, я объединил константы  #PB_Shortcut_Control и #PB_Shortcut_Q, чтобы указать, что я хочу получить горячую клавишу Ctrl + Q на клавиатуре. Эти константы комбинируются обычным способом, с помощью побитового оператора | (or). Все доступные константы для сочетаний горячих клавиш перечислены в справке PureBasic для команды AddKeyboardShortcut() (Helpfile:Reference Manual->General Libraries->Window->AddKeyboardShortcut). Третий параметр PB, это номер пункта меню, который вы хотите ассоциировать с горячей клавишей. В первой строке в приведенном выше фрагменте, я использовал #M_QUIT. Это номер одного из пунктов меню. Это означает, что после нажатия на клавиатуре Ctrl+Q, будет вызвано меню #M_QUIT, которое связано с событием присваивания переменной Quit значения #TRUE. Что в свою очередь вызовет выход из цикла и завершение программы. На следующей строке кода, я повторил эту команду, но с другими параметрами, чтобы предоставить клавишу быстрого вызова для пункта меню с названием About и номером #M_ABOUT. Эти события сочетаний клавиш обрабатываются точно так же, как если бы они были спровоцированы из выбора пункта меню и не существует абсолютно никакой разницы для обработки их в основном цикле, как вы могли видеть из прошлого примера.

Сочетания клавиш без меню

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

#WIN_MAIN = 1
#SC_EVENT = 2
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WIN_MAIN, 0, 0, 300, 200, "Hello World", #FLAGS)
  AddKeyboardShortcut(#WIN_MAIN,#PB_Shortcut_Control|#PB_Shortcut_Z, #SC_EVENT)
    Repeat
      Event.l = WaitWindowEvent()
      Select Event
        Case #PB_Event_Menu
            Select EventMenu()
               Case #SC_EVENT
                  Debug "The shortcut was pressed"
           EndSelect
     EndSelect
   Until Event = #PB_Event_CloseWindow
EndIf
End

Приведенный выше пример работает даже при отсутствии меню. При нажатии на сочетание клавиш Ctrl+Z выводится строка в окно вывода отладки. Этот пример работает, потому что команда AddKeyboardShortcut() вызывает меню с помощью его номера, который обрабатывается в основном цикле. То есть мы как бы подменили вызов меню нашим действием(вывод строки). Я думаю,что вам эти знания могут пригодится, особенно когда нужно будет добавить горячие клавиши в свои программы, даже если в вашей программе не будет меню.

Включение графики в вашу программу

Иногда, может понадобится в ваших программах  включать определенные изображения, и PureBasic делает это очень просто. В следующем разделе я покажу вам, как добавить изображения на свой интерфейсы  двумя способами. Первый способ:  вы можете загрузить из внешнего источника, например, изображение может быть в той же директории программы. Второй способ: можно вставлять изображения напрямую в вашу программу из ресурсов исполняемого файла. Оба способа дают нужный результат, но все же от вас  зависит, как вы хотите загрузить изображение. Оба этих метода требуют ImageGadget, размещенный на вашем окне для отображения изображения. Oн находится на вашем окне так же, как и любой другой гаджет, и соответствуют тем же правилам.

Загрузка изображений в Вашей программе

Этот метод загрузки изображений для использования в вашей интерфейса зависит от внешних изображений. В отличие от вложенных изображений, этот метод требует от вас распространять используемые изображения,  вместе с исполняемым файлом. Вот пример:

Enumeration
    #WIN_MAIN
    #IMAGE_FILE
    #IMAGE_DISPLAY
    #BUTTON_CLOSE
EndEnumeration
Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WIN_MAIN, 0, 0, 300, 200, "Image Example", #FLAGS)
    If CreateGadgetList(WindowID(#WIN_MAIN))
        If LoadImage(#IMAGE_FILE, "image.bmp")
            ImageGadget(#IMAGE_DISPLAY, 10, 10, 280, 150, ImageID(#IMAGE_FILE))
            ButtonGadget(#BUTTON_CLOSE, 100, 170, 100, 20, "Close window")
            Repeat
                Event.l = WaitWindowEvent()
                Select Event
                    Case #PB_Event_Gadget
                        Select EventGadget()
                            Case #BUTTON_CLOSE
                                Quit = #True
                        EndSelect
                EndSelect
           Until Event = #PB_Event_CloseWindow Or Quit = #True
        EndIf
    EndIf
EndIf
End

Для этого примера для правильной работы, вам необходим файл изображения 280х150  в формате .bmp в той же папке, что и исходный код или скомпилированный файл EXE. После этого, в действии, программа будет выглядеть примерно так (см. ниже). В этом примере я использовал команду LoadImage() для загрузки изображения с жесткого диска в память:

...
LoadImage(#IMAGE_FILE, "image.bmp")
...

Если вы посмотрите на команду LoadImage() в справке (Helpfile:Reference Manual->General Libraries->Image->LoadImage) вы можете увидеть в синтаксисе этой команды, что она принимает три параметра, первый номер PB, который будет связан с загруженным изображением. Второй имя и путь к файлу на жестком диске. Если прописывать только имя файла, то программа будет считать, что файл находится в той же папке, что и исполняемый файл. Я не использовал третий дополнительный параметр в данном примере. Как обычно, я проверяю возвращаемое значение этой команды с  помощью команды IF, дабы  убедиться, что файл изображения загружен, прежде чем продолжить работу остальной части программы. После того, как изображения были загружены корректно, вы можете поместить его в ImageGadget().  Пример определения ImageGadget() в списке гаджетов:

...
ImageGadget(#IMAGE_DISPLAY, 10, 10, 280, 150, ImageID(#IMAGE_FILE))
...

 

Первым параметром ImageGadget() является  число PB(идентификатор гаджета). Следующие четыре параметра связаны с расположением и размерами изображения. В пятом параметре мы указываем идентификатор ОС этого изображения. Поскольку мы загрузили подходящее изображение, мы уже можем использовать команду ImageID()  для получения  ОС идентификатора. В приведенном выше коде я использовал команду  ImageID( # image_file)  для получения идентификатора ОС. В свою очередь #image_file стал действенным, после того как мы загрузили изображение с помощью команды LoadImage().

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

Вложенное изображение в вашей программе  

Предыдущий метод загрузки изображений хорош, но иногда необходимо создать исполняемый файл со встроенными  изображениями в нем.Этот метод не потребует внешних носителей для вашего изображения, поскольку файл будет в ресурсах файла EXE. Этот метод использует разделы секции ресурса файла, в него вы можете вставлять бинарные файлы и использовать при необходимости. Вот пример:

Enumeration
  #WIN_MAIN
  #IMAGE_MEMORY
  #IMAGE_DISPLAY
  #BUTTON_CLOSE
EndEnumeration

Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WIN_MAIN, 0, 0, 300, 200, "Image Example", #FLAGS)
  If CreateGadgetList(WindowID(#WIN_MAIN))
    If CatchImage(#IMAGE_MEMORY, ?Image)
        ImageGadget(#IMAGE_DISPLAY, 10, 10, 280, 150, ImageID(#IMAGE_MEMORY))
        ButtonGadget(#BUTTON_CLOSE, 100, 170, 100, 20, "Close window")
        Repeat
            Event.l = WaitWindowEvent()
            Select Event
                Case #PB_Event_Gadget
                    Select EventGadget()
                        Case #BUTTON_CLOSE
                            Quit = #True
                    EndSelect
            EndSelect
       Until Event = #PB_Event_CloseWindow Or Quit = #True
    EndIf
  EndIf
EndIf
End

DataSection
Image:
IncludeBinary "image.bmp"
EndDataSection

 

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

...
DataSection
Image:
IncludeBinary "image.bmp"
EndDataSection

Команды DataSection и EndDataSection являются началом и концом раздела. Вы  можете вставлять бинарные файлы в его пределах его с помощью команды IncludeBinary. Эта команда не использует каких-либо скобок, она просто использует строку, в которой записано имя вставляемого файла.

В моем случае я использую для вложения файл под названием image.bmp. Вы также заметили, что я использовал метку перед командой IncludeBinary. Этот знак позволяет вашей программе найти начало этого рисунка в памяти, когда он загружен с этой программой. После того как изображение было вставлено, мы можем легко использовать его в основном коде. Мы больше не используем команду LoadImage(), чтобы загрузить изображение в память, поскольку она загружается из ресурсов программы. Но загрузить из ресурсов все же надо, и для этого я использую команду  CatchImage():

...
CatchImage(#IMAGE_MEMORY, ?Image)
...

Если вы посмотрите на команду CatchImage() в справке (Helpfile:Reference Manual->General Libraries->Image->CatchImage) вы можете увидеть что команда  принимает три параметра: Первый номер PB, с которым будет связан образ изображения. Вторым параметром является адрес памяти, где этот образ находится. Если вы помните, я использовал метку в разделе данных, и мне при загрузке изображения нужен адрес памяти. Делается это с помощью знака вопроса перед названием метки, когда я использую его в качестве параметра. Этот знак нужен для того чтобы вернуть адрес памяти нашей метки. Более подробно про указатели и метки в главе 13 (указатели). Я не использовал третий параметр в этом примере, потому что он не является обязательным.  Изображения загружаются с  помощью команды CatchImage() точно так же, как при помощи оператора LoadImage().Ну а далее мы опять используем ImageGadget и заполняем его параметры таким же образом, как и прежде:

...
ImageGadget(#IMAGE_DISPLAY, 10, 10, 280, 150, ImageID(#IMAGE_MEMORY))
...

 

После запуска данного примера она будет  выглядеть таким же образом.  Это потому, что это та же программа, но теперь она больше не нуждается в внешнем файле image.bmp.

Какие графические форматы можно использовать?

В последних  примерах, использовались изображения в формате Bitmap (*. BMP) , но вы не ограничены в этом одном формате.

Стандартно можно загружать и отображать Bitmap (*. BMP) и Icon (*. ICO) форматы файлов, но иногда нужно использовать другие форматы. Для этого можно использовать декодеры. Использование декодеров чрезвычайно просто в использовании, вы просто используете команду установки декодера для нужного формата в начале вашего исходного кода и с тех пор в вашей программе, все команды связанные с загрузкой изображений в этом формате выполняются без каких либо ограничений. Более подробную информацию о декодерах вы можете найти в справке (Helpfile:Reference Manual->General Libraries->ImagePlugin). Вот команды декодеров:

UseJPEGImageDecoder()  - поддерживаются (*.jpg/*.jpeg) форматы

UsePNGImageDecoder()  - поддерживаеся (*.png) формат

UseTIFFImageDecoder() - поддерживаются (*.tif/*.tiff) форматы

UseTGAImageDecoder() - поддерживается (*.tga) формат

Как отмечалось ранее, использование этих декодер команд просто. Возьмем последний пример вложения изображений и добавим в него поддержку формата Jpeg для того, чтобы мы могли вставлять Jpeg файл вместо файла Bitmap:

 

UseJPEGImageDecoder()


Enumeration
  #WIN_MAIN
  #IMAGE_MEMORY
  #IMAGE_DISPLAY
  #BUTTON_CLOSE
EndEnumeration


Global Quit.b = #False
#FLAGS = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
If OpenWindow(#WIN_MAIN, 0, 0, 300, 200, "Image Example", #FLAGS)
  If CreateGadgetList(WindowID(#WIN_MAIN))
    If CatchImage(#IMAGE_MEMORY, ?Image)
      ImageGadget(#IMAGE_DISPLAY, 10, 10, 280, 150, ImageID(#IMAGE_MEMORY))
      ButtonGadget(#BUTTON_CLOSE, 100, 170, 100, 20, "Close window")
      Repeat
        Event.l = WaitWindowEvent()
        Select Event
          Case #PB_Event_Gadget
            Select EventGadget()
              Case #BUTTON_CLOSE
                Quit = #True
            EndSelect
        EndSelect
    Until Event = #PB_Event_CloseWindow Or Quit = #True
  EndIf
 EndIf
EndIf
End


DataSection
  Image:
  IncludeBinary "image.jpg"
EndDataSection

Это прошлый пример, только сделана поддержка формата JPEG.  Я вставил в исходный код декодер первой строкой. Эта программа сейчас вкладывает изображение в формате Jpeg вместо изображения в формате .bmp. Все остальные декодеры используются таким же образом. Достаточно добавить команду декодер  в верхней части исходного кода, чтобы добавить поддержку этих дополнительных форматов файлов.

 

Первый взгляд на визуальный редактор

В комплекте с PureBasic идет мощный визуальный конструктор, чтобы вы могли построить интерфейс визуально и динамически. Это означает, то, что вы можете разместить ваши гаджеты в окне визуально, создать свой интерфейс, а код за вас сгенерируется автоматически в реальном времени. Использование таких инструментов, поможет сэкономить много времени при проектировании интерфейсов, регулируя вид и поведение интерфейса, просматривая немедленные результаты. Вы также можете сохранить проект на случай, если вам нужно будет что-то добавить или предоставить дополнительные функции в вашей программе. Существует одно ограничение в настоящее время в отношении загрузки существующих источников, код которых написан не в визуальном конструкторе, он может показать неправильно результат. Это происходит потому, визуальный конструктор генерирует код, определенным образом и все рукописные отклоненные от его формата тексты, будет принимать не как должно.

Почему ты не говорил об нем раньше?

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

Главное окно

При запуске Visual Designer будет выглядеть см ниже. Главное окно содержит все инструменты, для того чтобы вам легко было создать свой интерфейс. Визуальный конструктор состоит из меню в верхней панели инструментов, и под ним. В левой части экрана находится панель инструментов, содержащие множество гаджетов и панели Свойства. В центре будет текущий интерфейс разработки и ниже автоматически сгенерированный код.

Использование Visual Designer

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

После того как вы поставили нужные размеры окна(они регулируются) ,вы можете начать размещение гаджетов на нем. Для этого на панели с гаджетами нужно выделить необходимый гаджет , а после нарисовать его на окне мышкой прямо по точкам. После того как вы установили гаджет, вы увидите, что он окружен большими синими точками. Это так называемые ручки, позволяющие изменять размер. Кроме того вы можете перемещать гаджет в любое место. При добавлении гаджетов в интерфейсе можно заметить, что код генерируется автоматически в нижней части экрана. После того как все гаджеты размещены и у вас все готово, вы можете сохранить ваш проект. Позднее вы всегда можете его открыть в визуальном редакторе и отредактировать. Ну и конечно открыв его в IDE продолжить разработку кода, ведь для этого мы его и создавали.

За и против визуальный конструктор

Хотя визуальный конструктор это отличный инструмент для быстрой и простой разработки интерфейса, в нем сгенерированный код не может быть изменен. Таким образом, создавая в нем вы должны будете придерживаться его встроенного формата. Конечно не проблема, если вы являетесь опытным программистом в PureBasic, вы легко потом подправите в IDE код так как вам надо, но и для начинающего пользователя, экспортированный код может показаться довольно сложным. Главным козырем визуального конструктора является скорость. Вы можете размещать гаджеты так как вам надо, до тех пор пока не будет достигнут желаемый результат. И уж конечно это намного быстрее, чем если бы вы создавали это в IDE. Некоторые люди говорят, что лучше проект составлять в редакторе IDE, другие говорят что незачем тратить время на составление кода для интерфейса. Ну а вы решите сами за себя, что вам удобнее и практичнее.

Узнать больше о визуальном конструкторе

Визуальный конструктор поставляется в комплекте с обширной справкой, которая охватывает всю работу с ним. Для просмотра справки нажмите клавишу F1 при включенном  визуальном конструкторе и справка загрузится. Я рекомендую прочитать ее полностью по крайней мере один раз. Новые версии визуального конструктора, наверняка более  развитые, так что если есть возможность, то лучше обновить с сайта PureBasic визуальный дизайнер. В приложении указаны все необходимые и полезные интернет-ссылки.

Графика и звук

Программы, использующие графику и звук чаще всего являются компьютерными играми и интерактивными приложениями. При программировании такого, вам нужны инструменты, необходимые для реализации ваших идей. PureBasic предлагает простые и в то же время мощные и полнофункциональные команды, которые могут создать качественные игры, демосцены, скринсейверы и другие интерактивные программы. В этом разделе я покажу вам основы использования графики и звука в ваших программах. Сначала рассказ пойдет о 2D командах. Они нужны для рисования простых форм, линий и текста. И конечно я продемонстрирую, как сохранять эти образы. Затем я покажу вам, как сделать полный размер графического экрана. Кроме того, я познакомлю вас со спрайтами и как использовать их для создания графических эффектов. После этого мы перейдем на 3D-графику, где я представлю вам имеющийся 3D-движок и покажу вам основную информацию об использовании 3D-графики в PureBasic. Этот движок называется OGRE и стоит на третьем месте по созданию качественной и профессиональной графики и эффектов. В последней главе данного раздела, пойдет речь о звуке. Я покажу как загружать и воспроизводить звуки в форматах  WAV, Tracker модули, MP3 и CD Audio. Главы, содержащиеся в этом разделе, не являются полным руководством по достижению каждого графического эффекта и уж тем более не дадут вам полных знаний для написания серьезных игр. Эти главы для того, чтобы познакомить вас с библиотеками команд, которые дадут вам плацдарм и основу. Но даже этого хватит, чтобы вы могли поэкспериментировать. Надеюсь, этот раздел вдохновит вас на создание демосцен или небольшой игры.


 Содержание                  Учебник                Главная

Сайт создан в системе uCoz