PureBasic

Руководство - Динамическая нумерация окон и Гаджетов, с помощью #PB_Any

Если вы просматривали справочные статьи для команды OpenWindow или какой-либо из команд создания Гаджетов (например, ButtonGadget()), или если Вы экспериментировали с инструментом Form Designer, Вы могли заметить ссылки на специальную константу с именем #PB_Any. В этой статье мы рассмотрим её подробней, чтобы вы поняли, почему она так важна.

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

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

Именно здесь приходит на помощь специальная константа #PB_Any. Когда эта константа используется в качестве аргумента для функций, которые ее поддерживают, автоматически генерируется уникальный ссылочный Номер, который возвращается в результате выполнения функции.

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

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

Есть несколько замечаний по этой программе:
В разделе 'Enumerations' обратите внимание, что для элементов меню есть только перечисления. Они будут доступны во всех окнах - хотя каждое окно будет иметь свою собственную панель меню.

В разделе Structures обратите внимание, что структура POLYGONWINDOW содержит четыре значения с типом Integer и, таким образом, предоставляет место для хранения ссылок на строку меню, метку, поле со списком и график изображения.

В разделе Переменные заметим, что в дополнение к обычным переменным для получения подробностей событий создается хеш-карта ActiveWindows с использованием ранее определенной структуры POLYGONWINDOW.

Посмотрите на процедуру CreatePolygonWindow.
Когда мы создаем окно, мы фиксируем результат функции OpenWindow() в переменной ThisWindow. Затем мы преобразуем это значение в строку и используем его как ключ для новой записи хеш-карты.

Когда мы тогда создаем меню, метку, Комбо-бокс и отображаем Гаджеты в новом окне, ссылки, возвращаемые этими функциями, также сохраняются на хеш-карте.

Посмотрите на процедуру ResizePolygonWindow.
Обратите внимание, что значение EventWindow передается из цикла обработки событий в эту процедуру. Это значение затем используется для извлечения ссылок на дочерние элементы управления из хеш-карты, и эти ссылки затем используются для изменения размеров Гаджетов.

Посмотрите на процедуру DestroyPolygonWindow.
Здесь ссылки дочернего элемента управления удаляются из хеш-карты, когда окно закрывается. Если размер хеш-карты достигнет нуля - больше нет открытых окон, и DestroyPolygonWindow задает флаг события, чтобы завершить программу.

Запустите программу.
- Используйте пункт меню "New Window", чтобы открыть два или три новых окна многоугольника.
- Используйте поле со списком в каждом, чтобы выбрать другую форму – обратите внимание, что каждое окно работает независимо от всех остальных.
- Измените размер некоторых окон - обратите внимание, что все они могут быть изменены независимо друг от друга и что полигон также будет изменять размер окна.
Наконец, обратите внимание, что хеш-карта не является единственным способом достижения этого эффекта, можно использовать Список или Массив хотя код для реализации этих альтернатив должен быть несколько иным, чем представленный здесь из-за различий в способах работы этих коллекций.

; Директивы компилятора.
EnableExplicit

; Константы
CompilerIf Defined(Blue, #PB_Constant) = #False
    #Blue = 16711680
    #Gray = 8421504
    #White = 16777215
CompilerEndIf

;- Перечисления
; Команды меню будут одни и те же, на всех окнах.
Enumeration
    #MenuNew
    #MenuClose
EndEnumeration

;- Структуры
; Эта структура будет содержать ссылки на уникальные элементы окна.
Structure POLYGONWINDOW
    Menu.i
    LabelSides.i
    ComboSides.i
    ImagePlot.i
EndStructure

;- Переменные

; Эта хеш-карта использует ранее объявленную структуру для содержания ссылок, для всех открытых окон.
NewMap ActiveWindows.POLYGONWINDOW()

; Переменные событий.
Define.i Event, EventWindow, EventGadget, EventType, EventMenu, EventQuit
Define.s EventWindowKey

; Реализация
Procedure.i CreatePolygonWindow()
    ; Создает новое окно и Гаджеты, добавляя его и его дочерние Гаджеты к хеш-карте отслеживания.
    Shared ActiveWindows()
    Protected.i ThisWindow
    Protected.s ThisKey

    ThisWindow = OpenWindow(#PB_Any, 50, 50, 300, 300, "Polygon", #PB_Window_SystemMenu - #PB_Window_SizeGadget - #PB_Window_MinimizeGadget - #PB_Window_TitleBar)

    WindowBounds(ThisWindow, 250, 250, #PB_Ignore, #PB_Ignore)

    If ThisWindow
        ; Карты принимают как ключ строковое значение, поэтому преобразуем целочисленное значение (.i) переменной ThisWindow в строку (string).
        ThisKey = StrU(ThisWindow)

        ; Добавить элемент хеш-карты для сохранения новых ссылок Гаджета.
        AddMapElement(ActiveWindows(), ThisKey)

        ; Создать строку меню.
        ActiveWindows(ThisKey)\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow))
        MenuTitle("Window")
        MenuItem(#MenuNew, "New Window")
        MenuItem(#MenuClose, "Close Window")

        ; Создать дочерние Гаджеты и сохраните их ссылки в хеш-карте.
        With ActiveWindows()
            ; Метка для комбо-бокса
            \LabelSides = TextGadget(#PB_Any, 5, 5, 150, 20, "Number of Sides:")

            ; Стороны комбо-бокса
            \ComboSides = ComboBoxGadget(#PB_Any, 160, 5, 100, 25)
            AddGadgetItem(\ComboSides, 0, "Triangle")
            AddGadgetItem(\ComboSides, 1, "Diamond")
            AddGadgetItem(\ComboSides, 2, "Pentagon")
            AddGadgetItem(\ComboSides, 3, "Hexagon")
            AddGadgetItem(\ComboSides, 4, "Heptagon")
            AddGadgetItem(\ComboSides, 5, "Octagon")
            AddGadgetItem(\ComboSides, 6, "Nonagon")
            AddGadgetItem(\ComboSides, 7, "Decagon")

            ; Выбрать Треугольник.
            SetGadgetState(\ComboSides, 0)

            ; Гаджет визуального изображения в окне.
            \ImagePlot = ImageGadget(#PB_Any, 5, 35, 290, 240, 0, #PB_Image_Border)
        EndWith
    EndIf

    ; Вернуть ссылку на новое окно.
    ProcedureReturn ThisWindow
EndProcedure

Procedure DestroyPolygonWindow(Window.i)
    ; Удалить окно с хеш-карты ActiveWindows, закройте окно и установите флаг выхода, если это необходимо.
    Shared EventQuit, ActiveWindows()
    Protected.s ThisKey

    ; Преобразует значение (.i) переменной Window в строку (string).
    ThisKey = StrU(Window)

    ; Удалить запись из хеш-карты.
    DeleteMapElement(ActiveWindows(), ThisKey)

    ; Закрыть Окно.
    CloseWindow(Window)

    ; Проверить, есть ли еще открытые окна.
    If MapSize(ActiveWindows()) = 0
        EventQuit = #True
    EndIf
EndProcedure

Procedure.i ResizePolygonWindow(Window.i)
    ; Изменить размеры дочерних Гаджетов в окне.
    ; На практике в этом примере только ImageGadget необходимо изменять.
    Shared ActiveWindows()
    Protected.i ThisImage
    Protected.i X, Y, W, H
    Protected.s ThisKey

    ; Получить ссылки на соответствующие Гаджеты с хеш-карты.
    ThisKey = StrU(Window)
    ThisImage = ActiveWindows(ThisKey)\ImagePlot

    ; Изменение размеров Гаджетов.
    W = WindowWidth(Window) - 15
    H = WindowHeight(Window) - 70
    ResizeGadget(ThisImage, #PB_Ignore, #PB_Ignore, W, H)
EndProcedure

Procedure PlotPolygon(Window.i)
    ; Нарисовать многоугольник и перенесите его в Гаджет изображения.
    Shared ActiveWindows()
    Protected.f Radius, OriginX, OriginY, StartX, StartY, EndX, EndY
    Protected.i Sides, Vertex, Width, Height, ThisCombo, ThisImage, ThisPlot
    Protected.s ThisKey

    ; Проверить, соответствует ли это событие правильному окну.
    If Not IsWindow(Window) : ProcedureReturn : EndIf

    ; Получить ссылки на соответствующие Гаджеты с хеш-карты.
    ThisKey = StrU(Window)
    ThisCombo = ActiveWindows(ThisKey)\ComboSides
    ThisImage = ActiveWindows(ThisKey)\ImagePlot

    ; Вычислить размеры и происхождение.
    Sides = GetGadgetState(ThisCombo) + 3
    Width = GadgetWidth(ThisImage) - 4
    Height = GadgetHeight(ThisImage) - 4
    OriginX = Width/2
    OriginY = Height/2
    If Width < Height
        Radius = OriginX - 50
    Else
        Radius = OriginY - 50
    EndIf

    ; Создать новое изображение.
    ThisPlot = CreateImage(#PB_Any, Width, Height)
    StartDrawing(ImageOutput(ThisPlot))

    ; Нарисовать белый фон.
    Box(0, 0, Width, Height, #White)

    ; Нарисовать ограничивающий круг серого цвета.
    Circle(OriginX, OriginY, Radius, #Gray)

    ; Нарисовать многоугольник.
    For Vertex = 0 To Sides

        ; Вычислить начальную точку стороны.
        StartX =  OriginX + (Radius * Cos(2 * #PI * Vertex/Sides))
        StartY = OriginY + (Radius * Sin(2 * #PI * Vertex/Sides))

        ; И конечную точку.
        EndX = OriginX + (Radius * Cos(2 * #PI * (Vertex + 1)/Sides))
        EndY = OriginY + (Radius * Sin(2 * #PI * (Vertex + 1)/Sides))

        ; Нарисовать сторону синим цветом.
        LineXY(StartX, StartY, EndX, EndY, #Blue)

    Next Vertex

    ; Заполнить многоугольник синим цветом.
    FillArea(OriginX, OriginY, #Blue, #Blue)

    StopDrawing()

    ; Перенос содержимого изображения в отображаемый Гаджет.
    SetGadgetState(ThisImage, ImageID(ThisPlot))

    ; Уничтожить временное изображение.
    FreeImage(ThisPlot)
EndProcedure

;- Главный код

; Создать первое окно.
EventWindow = CreatePolygonWindow()
ResizePolygonWindow(EventWindow)
PlotPolygon(EventWindow)

;- Цикл событий.
Repeat
    Event = WaitWindowEvent()
    EventWindow = EventWindow()
    EventWindowKey = StrU(EventWindow)
    EventGadget = EventGadget()
    EventType = EventType()
    EventMenu = EventMenu()

    Select Event
        Case #PB_Event_Gadget
            ; Произошло событие Гаджета.
            If EventGadget = ActiveWindows(EventWindowKey)\LabelSides
                ; Ничего не делать.

            ElseIf EventGadget = ActiveWindows(EventWindowKey)\ComboSides
                ; Если поле со списком изменилось, перерисовать изображение.
                PlotPolygon(EventWindow)

                ; Обновить заголовок окна, чтобы отобразить новую фигуру.
                SetWindowTitle(EventWindow, GetGadgetText(ActiveWindows(EventWindowKey)\ComboSides))

            ElseIf EventGadget = ActiveWindows(EventWindowKey)\ImagePlot
                ; Ничего не делать.

            EndIf

        Case #PB_Event_Menu
            ; Произошло событие меню.
            If EventMenu = #MenuNew
                EventWindow = CreatePolygonWindow()
                ResizePolygonWindow(EventWindow)
                PlotPolygon(EventWindow)

            ElseIf EventMenu = #MenuClose
                DestroyPolygonWindow(EventWindow)

            EndIf

        Case #PB_Event_Repaint
            ; Содержимое окна было аннулировано.
            PlotPolygon(EventWindow)

        Case #PB_Event_SizeWindow
            ; Размеры окна были изменены.
            ResizePolygonWindow(EventWindow)
            PlotPolygon(EventWindow)

        Case #PB_Event_CloseWindow
            ; Окно было закрыто.
            DestroyPolygonWindow(EventWindow)

    EndSelect

Until EventQuit = #True

Навигация Руководства

< Доступ к памяти - Обзор - Управление множеством окон >