Einsteiger-Kapitel - Dynamische Nummerierung von Fenstern und Gadgets mittels #PB_Any

Wenn Sie in die Hilfe-Artikel des OpenWindow Befehls oder eines der Befehle zur Gadget-Erstellung (zum Beispiel ButtonGadget()) geschaut haben, oder mit dem Form-Designer experimentiert haben, werden Sie Verweise auf eine spezielle Konstante genannt #PB_Any bemerkt haben. In diesem Artikel schauen wir uns diese etwas näher an, um herauszufinden, warum diese so bedeutsam ist.

Bisher haben alle unserer Beispiele ein Gruppen von Konstanten verwendet, eine Enumeration (Aufzählung), um ein einzelnes Fenster und jedes Gadget auf diesem Fenster zu identifizieren. Dies ist gut in einfachen Programmen, welche wir bisher präsentiert haben, aber führt zu einem Problem in komplexeren Programmen - nur eines dieser Fenster kann zur gleichen Zeit existieren.

Was passiert also, wenn ein Programm mehr als ein Exemplar eines Fenster bieten muss? Vielleicht möchten Sie mehrere Dateien gleichzeitig geöffnet haben oder möglicherweise möchten Sie mehrere verschiedene Ansichten der gleichen Datei anbieten.

Dies ist der Punkt, wo die spezielle #PB_Any Konstante zum Einsatz kommt. Wenn diese Konstante als Argument bei den Funktionen zum Einsatz kommt, welche dies unterstützen, dann wird automatisch eine eindeutige Referenznummer generiert und als Ergebnis der Funktion zurückgegeben.

Vorausgesetzt, wir achten auf all diese Referenzen, können wir dies zu unserem Vorteil nutzen. Dessen Organisation ist ein wenig komplizierter als die Beispiele, die wir bisher gesehen haben - aber die erhöhte Flexibilität, die es uns bieten kann, ist diese Mühe absolut wert.

Dieses Beispiel-Programm bietet ein Fenster, auf dem ein regelmäßiges Vieleck (Polygon) in blau auf einem umlaufenden grauen Kreis gezeichnet wird. Eine Combobox ist dazu da, uns eine Auswahl der zu zeichnenden Polygone zu ermöglichen. Ein Menü wird angeboten, um das Erstellen neuer Fenster oder das Schließen des aktuellen Fensters zu ermöglichen.

Es gibt mehrere Dinge, die zu diesem Programm angemerkt werden sollen:
Im Enumerations Abschnitt beachten Sie, dass es nur Aufzählungen für die Menü-Einträge gibt. Diese werden auf allen Fenstern gemeinsam genutzt - obwohl jedes Fenster seine eigene Menüleiste hat.

Im Structures Abschnitt beachten Sie, dass die POLYGONWINDOW Struktur vier Integer-Werte beinhaltet und somit einen Platz bietet, die Verweise auf die Menüleiste, einen Text, eine Auswahlbox und einen Zeichenbereich zu speichern.

Im Variablen-Abschnitt beachten Sie, dass in Ergänzung zu den üblichen Variablen zum Empfangen von Ereignis-Details eine Map namens ActiveWindows mittels der zuvor definierten POLYGONWINDOW Struktur erstellt wird.

Schauen Sie auf die CreatePolygonWindow Prozedur.
Wenn wir das Fenster erstellen, speichern wir das Ergebnis der @openwindowFunktion in einer Variable genannt ThisWindow. Dann konvertieren wir diese in einen String-Wert und verwenden diesen als Schlüssel für einen neuen Map-Eintrag.

Wenn wir dann das Menü und die Text-, Combobox- und Image-Gadgets auf dem neuen Fenster erstellen, werden die von diesen Funktionen zurückgegebenen Referenzen ebenfalls in der Map gespeichert.

Werfen Sie dann einen Blick auf die ResizePolygonWindow Prozedur.
Beachten Sie, wie der Wert von EventWindow von der Ereignis-Schleife in diese Prozedur übergeben wird. Dieser Wert wird dann verwendet, um Referenzen auf die untergeordneten Steuerelemente aus der Map zu erhalten, und diese Referenzen werden dann verwendet, um die Gadgets in der Größe zu verändern.

Schauen Sie auf die DestroyPolygonWindow Prozedur.
Hier werden die Referenzen auf die untergeordneten Steuerelemente aus der Map entfernt, wenn ein Fenster geschlossen wird. Wenn die Größe der Map Null erreicht - gibt es keine weiteren offenen Fenster und DestroyPolygonWindow setzt ein Ereignis-Flag, was dem Programm mitteilt, dass es beendet werden kann.

Starten Sie nun das Programm.
- Verwenden Sie den 'New Window' Menüeintrag, um zwei oder drei neue Polygon-Fenster zu öffnen.
- Verwenden Sie die Combobox in jedem Fenster, um ein unterschiedliches Aussehen auszuwählen – beachten Sie, dass jedes Fenster unabhängig von allen anderen arbeitet.
- Verändern Sie einige der Fenster in der Größe – beachten Sie, dass sie alle unabhängig von einander in der Größe verändert werden können und sich auch das Polygon mit dem Fenster in der Größe anpasst.
Zum Schluss bleibt anzumerken, dass eine Map nicht den einzigen Weg darstellt, diesen Effekt zu erreichen. Eine verknüpfte Liste oder ein Array kann für diesen Job ebenfalls verwendet werden, wenn Sie möchten - auch wenn der Code für das Implementieren dieser Alternativen wegen der Unterschiede wie diese arbeiten etwas anders gegenüber dem hier präsentierten aussehen muss.
  ; Compiler Direktiven
  EnableExplicit
  
  ; Konstanten
  CompilerIf Defined(Blue, #PB_Constant) = #False
    #Blue = 16711680
    #Gray = 8421504
    #White = 16777215
  CompilerEndIf
  
  ;- Enumerations
  ; Die Menü-Befehle werden bei allen Fenstern die gleichen sein.
  Enumeration
    #MenuNew
    #MenuClose
  EndEnumeration
  
  ;- Structures
  ; Diese Struktur wird alle Referenzen auf die einmaligen Elemente eines Fensters beinhalten.
  Structure POLYGONWINDOW
    Menu.i
    LabelSides.i
    ComboSides.i
    ImagePlot.i
  EndStructure
  
  ;- Variablen
  
  ; Diese Map verwendet die zuvor definierte Struktur, um die Referenzen auf alle geöffneten Fenster festzuhalten.
  NewMap ActiveWindows.POLYGONWINDOW()
  
  ; Ereignis-Variablen.
  Define.i Event, EventWindow, EventGadget, EventType, EventMenu, EventQuit
  Define.s EventWindowKey
  
  ; Implementierung.
  Procedure.i CreatePolygonWindow()
    ; Erstellt ein neues Fenster und Gadgets, fügt dieses und seine untergeordneten Gadgets zur Tracking-Map hinzu.
    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
      ; Maps erhalten einen String-Wert als Schlüssel, weshalb wir die Integer-Zahl 'ThisWindow' in einen String umwandeln.
      ThisKey = StrU(ThisWindow)
      
      ; Ein Map-Element hinzufügen, um alle neuen Gadget-Referenzen festzuhalten.
      AddMapElement(ActiveWindows(), ThisKey)
      
      ; Erstelle die Menüleiste.
      ActiveWindows(ThisKey)\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow))
      MenuTitle("Window")
      MenuItem(#MenuNew, "New Window")
      MenuItem(#MenuClose, "Close Window")
      
      ; Erstelle die untergeordneten Gadgets und speichere ihre Referenzen in der Map.
      With ActiveWindows()
        ; Ein Beschriftungstext für die Auswahlbox.
        \LabelSides = TextGadget(#PB_Any, 5, 5, 150, 20, "Number of Sides:")
        
        ; Die Auswahlbox.
        \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")
        
        ; Dreieck auswählen.
        SetGadgetState(\ComboSides, 0)
        
        ; Das sichtbare Image-Gadget auf dem Fenster.
        \ImagePlot = ImageGadget(#PB_Any, 5, 35, 290, 240, 0, #PB_Image_Border)
      EndWith
    EndIf
    
    ; Gibt die Referenz auf das neue Fenster zurück.
    ProcedureReturn ThisWindow
  EndProcedure
  
  Procedure DestroyPolygonWindow(Window.i)
    ; Entfernt das Fenster 'Window' aus der 'ActiveWindows' Map, schließt das Fenster und setzt das Quit Flag, wenn nötig.
    Shared EventQuit, ActiveWindows()
    Protected.s ThisKey
    
    ; Konvertiert den Integer-Wert 'Window' in einen String.
    ThisKey = StrU(Window)
    
    ; Löscht den Map-Eintrag.
    DeleteMapElement(ActiveWindows(), ThisKey)
    
    ; Schließt das Fenster.
    CloseWindow(Window)
    
    ; Überprüft, ob es noch offene Fenster gibt.
    If MapSize(ActiveWindows()) = 0
      EventQuit = #True
    EndIf
  EndProcedure
  
  Procedure.i ResizePolygonWindow(Window.i)
    ; Verändert die untergeordneten Gadgets auf dem Fenster in ihrer Größe.
    ; In der Praxis muss bei diesem Beispiel nur das ImageGadget in der Größe verändert werden.
    Shared ActiveWindows()
    Protected.i ThisImage
    Protected.i X, Y, W, H
    Protected.s ThisKey
    
    ; Ermittelt Referenzen auf die betroffenen Gadgets aus der Map.
    ThisKey = StrU(Window)
    ThisImage = ActiveWindows(ThisKey)\ImagePlot
    
    ; Verändert die Gadgets in der Größe.
    W = WindowWidth(Window) - 15
    H = WindowHeight(Window) - 70
    ResizeGadget(ThisImage, #PB_Ignore, #PB_Ignore, W, H)
  EndProcedure
  
  Procedure PlotPolygon(Window.i)
    ; Zeichnet das Polygon-Bild und überträgt es auf das Image-Gadget.
    Shared ActiveWindows()
    Protected.f Radius, OriginX, OriginY, StartX, StartY, EndX, EndY
    Protected.i Sides, Vertex, Width, Height, ThisCombo, ThisImage, ThisPlot
    Protected.s ThisKey
    
    ; Überprüft, ob das Ereignis für das richtige Fenster ist.
    If Not IsWindow(Window) : ProcedureReturn : EndIf
    
    ; Ermittelt Referenzen auf die betroffenen Gadgets aus der Map.
    ThisKey = StrU(Window)
    ThisCombo = ActiveWindows(ThisKey)\ComboSides
    ThisImage = ActiveWindows(ThisKey)\ImagePlot
    
    ; Berechnet Abmessungen und Herkunft.
    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
    
    ; Erstellt ein neues Bild.
    ThisPlot = CreateImage(#PB_Any, Width, Height)
    StartDrawing(ImageOutput(ThisPlot))
    
    ; Zeichnet einen weißen Hintergrund.
    Box(0, 0, Width, Height, #White)
    
    ; Zeichnet einen grauen umlaufenden Kreis.
    Circle(OriginX, OriginY, Radius, #Gray)
    
    ; Zeichnet das Polygon.
    For Vertex = 0 To Sides
      
      ; Berechnet den Startpunkt.
      StartX =  OriginX + (Radius * Cos(2 * #PI * Vertex/Sides))
      StartY = OriginY + (Radius * Sin(2 * #PI * Vertex/Sides)) 
      
      ; und den Endpunkt.
      EndX = OriginX + (Radius * Cos(2 * #PI * (Vertex + 1)/Sides))
      EndY = OriginY + (Radius * Sin(2 * #PI * (Vertex + 1)/Sides))
      
      ; Zeichnet die Seite in blau.
      LineXY(StartX, StartY, EndX, EndY, #Blue)
      
    Next Vertex
    
    ; Füllt das Polygon in blau.
    FillArea(OriginX, OriginY, #Blue, #Blue)
    
    StopDrawing()
    
    ; Übertragt den Bild-Inhalt in das sichtbare Gadget.
    SetGadgetState(ThisImage, ImageID(ThisPlot))
    
    ; Löscht das temporäre Bild.
    FreeImage(ThisPlot)
  EndProcedure
  
  ;- Hauptteil
  
  ; Erstellt das erste Fenster.
  EventWindow = CreatePolygonWindow()
  ResizePolygonWindow(EventWindow)
  PlotPolygon(EventWindow)
  
  ;- Ereignis-Schleife
  Repeat
    Event = WaitWindowEvent()
    EventWindow = EventWindow()
    EventWindowKey = StrU(EventWindow)
    EventGadget = EventGadget()
    EventType = EventType()
    EventMenu = EventMenu()
    
    Select Event
      Case #PB_Event_Gadget
        ; Ein Gadget-Ereignis ist aufgetreten.
        If EventGadget = ActiveWindows(EventWindowKey)\LabelSides
          ; Tue nichts.
          
        ElseIf EventGadget = ActiveWindows(EventWindowKey)\ComboSides
          ; Wenn sich die Auswahlbox verändert hat, das Bild neu zeichnen.
          PlotPolygon(EventWindow)
          
          ; Aktualisiert den Fenstertitel, um das neue Aussehen wiederzuspiegeln.
          SetWindowTitle(EventWindow, GetGadgetText(ActiveWindows(EventWindowKey)\ComboSides))
          
        ElseIf EventGadget = ActiveWindows(EventWindowKey)\ImagePlot
          ; Tue nichts.
          
        EndIf
        
      Case #PB_Event_Menu
        ; Ein Menü-Ereignis ist aufgetreten.
        If EventMenu = #MenuNew
          EventWindow = CreatePolygonWindow()
          ResizePolygonWindow(EventWindow)
          PlotPolygon(EventWindow)
          
        ElseIf EventMenu = #MenuClose
          DestroyPolygonWindow(EventWindow)
          
        EndIf
        
      Case #PB_Event_Repaint
        ; Ein Fenster-Inhalt wurde für ungültig erklärt.
        PlotPolygon(EventWindow)
        
      Case #PB_Event_SizeWindow
        ; Ein Fenster wurde in der Größe verändert.
        ResizePolygonWindow(EventWindow)
        PlotPolygon(EventWindow)
        
      Case #PB_Event_CloseWindow
        ; Ein Fenster wurde geschlossen.
        DestroyPolygonWindow(EventWindow)
        
    EndSelect
    
  Until EventQuit = #True

Einsteiger-Kapitel Navigation

< Vorheriges: Speicher-Zugriff | Überblick | Nächstes: Verwalten mehrerer Fenster >