Seite 1 von 2

[Programm] Bilder in Threads laden

Verfasst: 30.01.2014 19:19
von es_91
Hier geht es zwar um Grafik, der Inhalt ist aber ein Thread-Problem, deshalb kommt dies hier nicht in die Grafik- und Sound-Abteilung.

Ich versuche ein Programm zu schreiben, was Bilddateien, Bitmaps, einlädt. Die Besonderheit: das Laden soll als Thread ablaufen - jedes Bild ein einzelner Thread - damit das Programm sich entfalten kann, bevor die Bilder in dem MDIGadget angezeigt werden (jedes Bild ein MDIWindow).

Warum funktioniert mein Code nicht?

Code: Alles auswählen

  Structure IMAGE
    ID.i
  EndStructure
  
  Structure MDIWINDOW
    ID.i
    ImageGadget.i
  EndStructure
  
  Global NewList Images.IMAGE()
  Global NewList MDIWindows_Main.MDIWINDOW()
  Global NewList ProgramParams$()
  Global NewList LoadingThreads()
  Global NewList ThreadEntriesToDelete()
  NewList ProgramParams$()
  
  Procedure LoadingThread(*FileName$)
    
    AddElement(Images())
    Images()\ID = LoadImage(#PB_Any, *FileName$)
    
  EndProcedure
  
  While ProgramParameter(x)
    AddElement(ProgramParams$())
    ProgramParams$() = ProgramParameter(x)
    x + 1
  Wend
  
  Window_Main_Title$ = "just a little test program..."
  
  Window_Main = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 640, 400, Window_Main_Title$, #PB_Window_Invisible|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget)
  
  MDIGadget_Main = MDIGadget(#PB_Any, 0, 0, 0, 0, 0, 0)
  
  HideWindow(Window_Main, #False)
  
  ForEach ProgramParams$()
    AddElement(LoadingThreads())
    LoadingThreads() = CreateThread(@LoadingThread(), @ProgramParams$())
  Next
  
  Repeat
    
    MenuHeight_Main = 0
    ToolBarHeight_Main = 0
    
    ForEach LoadingThreads()
      
      If Not IsThread(LoadingThreads())
        SelectElement(Images(), ListIndex(LoadingThreads()))
        AddElement(MDIWindows_Main())
        MDIWindows_Main()\ID = AddGadgetItem(MDIGadget_Main, #PB_Any, "", ImageID(Images()\ID))
        MDIWindows_Main()\ImageGadget = ImageGadget(#PB_Any, 0, 0, WindowWidth(MDIWindows_Main()), WindowHeight(MDIWindows_Main()), Images()\ID)
        AddElement(ThreadEntriesToDelete())
        ThreadEntriesToDelete() = LoadingThreads()
      EndIf
      
    Next
    
    WindowEvent = WindowEvent()
    Select WindowEvent
      Case #PB_Event_SizeWindow
        ResizeGadget(MDIGadget_Main, 0, 0, WindowWidth(Window_Main), WindowHeight(Window_Main))
      
      Case #PB_Event_CloseWindow
        
        End
        
    EndSelect
    
    ForEach ThreadEntriesToDelete()
      ForEach LoadingThreads()
        If LoadingThreads() = ThreadEntriesToDelete()
          DeleteElement(LoadingThreads())
          Break
        EndIf
      Next
    Next
        
  ForEver
Die Images()\ID wird bei mir immer null sein. Warum? Was mache ich falsch?

Schönen Abend und Danke im Vorraus,

es_91.

Re: [Programm] Bilder in Threads laden

Verfasst: 30.01.2014 19:26
von NicTheQuick
Weil du falsch mit Pointern umgehst.
'*FileName$' sollte in PB verboten werden. :mrgreen:

Und weil deine LinkedList nicht threadsafe ist. Es kann nämlich durchaus passieren, dass der eine Thread 'AddElement()' macht und dann ein anderer auch nochmal ein 'AddElement()' macht. Wenn dann der erste wiederum 'Images()\ID = LoadImage(...)' ausführt, dann hat er ein Element übersprungen.
Du musst ein Array benutzen und jedem Thread sagen an welchem Index er die ImageID zu speichern hat. (Oder du nutzt komplizierte Lock/Semaphoren-Mechanismen) :wink:

Re: [Programm] Bilder in Threads laden

Verfasst: 30.01.2014 19:36
von es_91
Danke Dir. Das mit dem Array leuchtet mir ein, aber wie mache ich das mit dem Pointer?

/EDIT: Jetzt geht das mit den Images, aber sie werden nicht angezeigt ????? :?

Code: Alles auswählen

  Structure IMAGE
    ID.i
  EndStructure
  
  Structure MDIWINDOW
    ID.i
    ImageGadget.i
  EndStructure
  
  Structure THREADINFO
    ID.i
    FileName$
  EndStructure
  
  Global NewList ProgramParams$()
  
  While ProgramParameter(x)
    AddElement(ProgramParams$())
    ProgramParams$() = ProgramParameter(x)
    x + 1
  Wend
  
  If x - 1 => 0
    Global Dim Images.IMAGE(x - 1)
  EndIf
  
  Global NewList MDIWindows_Main.MDIWINDOW()
  Global NewList LoadingThreads()
  Global NewList ThreadEntriesToDelete()
  Global NewList ThreadInfos.THREADINFO()
  
  Procedure LoadingThread(*ThreadInfo.THREADINFO)
    
    Images(*ThreadInfo\ID)\ID = LoadImage(#PB_Any, *ThreadInfo\FileName$)
    
  EndProcedure
  
  
  Window_Main_Title$ = "just a little test program..."
  
  Window_Main = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 640, 400, Window_Main_Title$, #PB_Window_Invisible|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget)
  
  MDIGadget_Main = MDIGadget(#PB_Any, 0, 0, 0, 0, 0, 0)
  
  HideWindow(Window_Main, #False)
  
  ForEach ProgramParams$()
    AddElement(LoadingThreads())
    AddElement(ThreadInfos())
    ThreadInfos()\ID = ListIndex(ProgramParams$())
    ThreadInfos()\FileName$ = ProgramParams$()
    LoadingThreads() = CreateThread(@LoadingThread(), @ThreadInfos())
  Next
  
  Repeat
    
    MenuHeight_Main = 0
    ToolBarHeight_Main = 0
    
    ForEach LoadingThreads()
      
      If Not IsThread(LoadingThreads())
        x = ListIndex(LoadingThreads())
        AddElement(MDIWindows_Main())
        MDIWindows_Main()\ID = AddGadgetItem(MDIGadget_Main, #PB_Any, "", ImageID(Images(x)\ID))
        ResizeWindow(MDIWindows_Main()\ID, 0, 0, 400, 200)
        MDIWindows_Main()\ImageGadget = ImageGadget(#PB_Any, 0, 0, WindowWidth(MDIWindows_Main()\ID), WindowHeight(MDIWindows_Main()\ID), Images(x)\ID)
        AddElement(ThreadEntriesToDelete())
        ThreadEntriesToDelete() = LoadingThreads()
      EndIf
      
    Next
    
    WindowEvent = WindowEvent()
    Select WindowEvent
        
      Case #PB_Event_SizeWindow
        If IsToolBar(Toolbar_Main)
          ToolBarHeight_Main = ToolBarHeight(Toolbar_Main)
        EndIf
        If IsMenu(Menu_Main)
          MenuHeight_Main = MenuHeight()
        EndIf
        ResizeGadget(MDIGadget_Main, Const_ToolBarWidth, ToolBarHeight, WindowWidth(Window_Main) - Const_ToolBarWidth, WindowHeight(Window_Main) - ToolBarHeight - MenuHeight)
      
      Case #PB_Event_CloseWindow
        
        End
        
    EndSelect
    
    ForEach ThreadEntriesToDelete()
      ForEach LoadingThreads()
        If LoadingThreads() = ThreadEntriesToDelete()
          DeleteElement(LoadingThreads())
          Break
        EndIf
      Next
    Next
        
  ForEver

Re: [Programm] Bilder in Threads laden

Verfasst: 30.01.2014 20:59
von Bisonte
Ehrlich gesagt, entzieht sich mir der Sinn, jedes Bild in einem eigenem Thread in eine Linklist einzulesen. Damit sich die Threads nicht gegenseitig verwirren, müsste man ja Locks usw nutzen und dadurch kann man dann gleich alles in einem Rutsch lesen ... ohne threads... Schneller wird das auch nicht dadurch....

Dann eher das Window erst anzeigen lassen, wenn alles soweit fertig ist...

Re: [Programm] Bilder in Threads laden

Verfasst: 30.01.2014 22:18
von NicTheQuick
Du solltest mal konsequent überall Überprüfungen einbauen. Also wurde das Bild im Thread tatsächlich fehlerfrei geladen (Ist die ID ungleich 0 bzw. IsImage())? Wurde das Element korrekt zur Liste hinzugefügt (AddElement() ungleich 0)? ThreadSafe in den Compiler-Optionen aktiviert? Und was soll eigentlich dieses 'ThreadEntriesToDelete()'? Lösch' den Eintrag doch gleich in der ersten ForEach-Schleife, wo du die neuen MDI-Fenster erstellst. ;)

Re: [Programm] Bilder in Threads laden

Verfasst: 30.01.2014 22:53
von es_91
Überprüfungen einbauen ... mach ich!
NicTheQuick hat geschrieben:Und was soll eigentlich dieses 'ThreadEntriesToDelete()'? Lösch' den Eintrag doch gleich in der ersten ForEach-Schleife, wo du die neuen MDI-Fenster erstellst. ;)
Das würde die Zählschleife manipulieren und Elemente überspringen. Stell dir vor, du bist bei Element 2, löscht dieses ... welches Element kommt als nächstes? Die 3. Die war aber vorher die vier. Also wird die vorherige 3 übersprungen. Hoffe, das beschreibt es gut. [s]Und mit PreviousElement() kann ich da nicht ran gehen, weil ich ja nicht ausschließen kann, dass es sich nicht um das erste Element handelt.[/s]

/EDIT: Oh Mann, bin ich blöd, bin ich blöd, bin ich blöd, bin ich blöd ... Was habe ich vergessen ? Was hat mich 45 Minuten lang angestrengt suchen lassen ??? IMAGEID() hat gefehlt!

So geht der Text jetzt:

Code: Alles auswählen

  Structure IMAGE
    ID.i
  EndStructure
  
  Structure MDIWINDOW
    ID.i
    ImageGadget.i
  EndStructure
  
  Structure THREADINFO
    ID.i
    FileName$
  EndStructure
  
  Global NewList ProgramParams$()
  
  While ProgramParameter(x)
    AddElement(ProgramParams$())
    ProgramParams$() = ProgramParameter(x)
    x + 1
  Wend
  
  If x - 1 => 0
    Global Dim Images.IMAGE(x - 1)
  EndIf
  
  Global NewList MDIWindows_Main.MDIWINDOW()
  Global NewList LoadingThreads()
  Global NewList ThreadEntriesToDelete()
  Global NewList ThreadInfos.THREADINFO()
  
  Procedure LoadingThread(*ThreadInfo.THREADINFO)
    
    Images(*ThreadInfo\ID)\ID = LoadImage(#PB_Any, *ThreadInfo\FileName$)
    
  EndProcedure
  
  
  Window_Main_Title$ = "just a little test program..."
  
  Window_Main = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 640, 400, Window_Main_Title$, #PB_Window_Invisible|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget)
  
  MDIGadget_Main = MDIGadget(#PB_Any, 0, 0, 0, 0, 0, 0)
  
  HideWindow(Window_Main, #False)
  
  ForEach ProgramParams$()
    AddElement(LoadingThreads())
    AddElement(ThreadInfos())
    ThreadInfos()\ID = ListIndex(ProgramParams$())
    ThreadInfos()\FileName$ = ProgramParams$()
    LoadingThreads() = CreateThread(@LoadingThread(), @ThreadInfos())
  Next
  
  Repeat
    
    MenuHeight_Main = 0
    ToolBarHeight_Main = 0
    
    ForEach LoadingThreads()
      
      If Not IsThread(LoadingThreads())
        x = ListIndex(LoadingThreads())
        AddElement(MDIWindows_Main())
        MDIWindows_Main()\ID = AddGadgetItem(MDIGadget_Main, #PB_Any, "", ImageID(Images(x)\ID))
        
        MDIWindows_Main()\ImageGadget = ImageGadget(#PB_Any, 0, 0, 600, 300, ImageID(Images(x)\ID))
        AddElement(ThreadEntriesToDelete())
        ThreadEntriesToDelete() = LoadingThreads()
      EndIf
      
    Next
    
    WindowEvent = WindowEvent()
    Select WindowEvent
        
      Case #PB_Event_SizeWindow
        If IsToolBar(Toolbar_Main)
          ToolBarHeight_Main = ToolBarHeight(Toolbar_Main)
        EndIf
        If IsMenu(Menu_Main)
          MenuHeight_Main = MenuHeight()
        EndIf
        ResizeGadget(MDIGadget_Main, Const_ToolBarWidth, ToolBarHeight, WindowWidth(Window_Main) - Const_ToolBarWidth, WindowHeight(Window_Main) - ToolBarHeight - MenuHeight)
      
      Case #PB_Event_CloseWindow
        
        End
        
    EndSelect
    
    ForEach ThreadEntriesToDelete()
      ForEach LoadingThreads()
        If LoadingThreads() = ThreadEntriesToDelete()
          DeleteElement(LoadingThreads())
          Break
        EndIf
      Next
    Next
        
  ForEver
So, jetzt noch irgendwas falsch dran? Jetzt kann ich endlich weiterarbeiten. :bounce:

Re: [Programm] Bilder in Threads laden

Verfasst: 30.01.2014 23:20
von es_91
... will ich Bisontes Thread mal auseinander nehmen :mrgreen:
Bisonte hat geschrieben:Ehrlich gesagt, entzieht sich mir der Sinn, jedes Bild in einem eigenem Thread in eine Linklist einzulesen. Damit sich die Threads nicht gegenseitig verwirren, müsste man ja Locks usw nutzen
Wie Du an meinem letzten Code siehst geht es auch so. :)
Bisonte hat geschrieben:und dadurch kann man dann gleich alles in einem Rutsch lesen ... ohne threads... Schneller wird das auch nicht dadurch....
Schneller nicht, aber kontrollierbarer. Ich kann eine ProgressBar einbauen, die den Ladefortschritt anzeigt, deren Mutterfenster mit frischen WindowEvents() versorgt wird und nicht "abhängt" bis das Bild geladen ist. Außerdem - man stelle sich vor - es wird ein Image von 8192*8192 Pixeln auf einem uralt-Rechner geladen und der Anwender entscheidet sich, dass es ihm zu lange dauert => Klick auf Abbrechen und der Thread ist tot. Sonst müsste er warten, bis das Bild geladen ist.
Bisonte hat geschrieben:Dann eher das Window erst anzeigen lassen, wenn alles soweit fertig ist...
Nö. :D

Re: [Programm] Bilder in Threads laden

Verfasst: 31.01.2014 00:15
von NicTheQuick
es_91 hat geschrieben:Überprüfungen einbauen ... mach ich!
NicTheQuick hat geschrieben:Und was soll eigentlich dieses 'ThreadEntriesToDelete()'? Lösch' den Eintrag doch gleich in der ersten ForEach-Schleife, wo du die neuen MDI-Fenster erstellst. ;)
Das würde die Zählschleife manipulieren und Elemente überspringen. Stell dir vor, du bist bei Element 2, löscht dieses ... welches Element kommt als nächstes? Die 3. Die war aber vorher die vier. Also wird die vorherige 3 übersprungen. Hoffe, das beschreibt es gut. [s]Und mit PreviousElement() kann ich da nicht ran gehen, weil ich ja nicht ausschließen kann, dass es sich nicht um das erste Element handelt.[/s]
Wenn du dir die Hilfe zu 'DeleteElement()' durchliest wirst du bemerken, dass nach einem 'DeleteElement()' standardmäßig das vorherige Element das neue wird. Existiert kein vorheriges Element, dann wird kein Element das neue sein. Demnach wird im nächsten Durchlauf der ForEach-Schleife immer das Element aktuell sein, was nach dem gelöschten kam.

Re: [Programm] Bilder in Threads laden

Verfasst: 31.01.2014 07:45
von es_91
Dann möchte ich mich gehörig entschuldigen. Ich versuche, das ThreadEntriesToDelete() auszubauen.

/EDIT: Irgendetwas mache ich falsch ... in meiner ProgramParams$()-Liste sind zwei Bilder, aber es wird zweimal das erste Bild angezeigt . . .

Code: Alles auswählen

  Structure IMAGE
    ID.i
  EndStructure
  
  Structure MDIWINDOW
    ID.i
    ImageGadget.i
  EndStructure
  
  Structure THREADINFO
    ID.i
    FileName$
  EndStructure
  
  Global NewList ProgramParams$()
  
  While ProgramParameter(x)
    AddElement(ProgramParams$())
    ProgramParams$() = ProgramParameter(x)
    x + 1
  Wend
  
  If Not (x - 1) < 0
    Global Dim Images.IMAGE(x - 1)
  EndIf
  
  Global NewList MDIWindows_Main.MDIWINDOW()
  Global NewList LoadingThreads()
  Global NewList ThreadEntriesToDelete()
  Global NewList ThreadInfos.THREADINFO()
  
  Procedure LoadingThread(*ThreadInfo.THREADINFO)
    
    Images(*ThreadInfo\ID)\ID = LoadImage(#PB_Any, *ThreadInfo\FileName$)
    
  EndProcedure
  
  
  Window_Main_Title$ = "just a little test program . . ."
  
  Window_Main = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 640, 400, Window_Main_Title$, #PB_Window_Invisible|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget)
  
  MDIGadget_Main = MDIGadget(#PB_Any, 0, 0, 0, 0, 0, 0)
  
  HideWindow(Window_Main, #False)
  
  ForEach ProgramParams$()
    AddElement(LoadingThreads())
    AddElement(ThreadInfos())
    ThreadInfos()\ID = ListIndex(ProgramParams$())
    ThreadInfos()\FileName$ = ProgramParams$()
    LoadingThreads() = CreateThread(@LoadingThread(), @ThreadInfos())
  Next
  
  Repeat
    
    MenuHeight_Main = 0
    ToolBarHeight_Main = 0
    
    ForEach LoadingThreads()
      
      If Not IsThread(LoadingThreads())
        x = ListIndex(LoadingThreads())
        AddElement(MDIWindows_Main())
        MDIWindows_Main()\ID = AddGadgetItem(MDIGadget_Main, #PB_Any, "", ImageID(Images(x)\ID))
        
        MDIWindows_Main()\ImageGadget = ImageGadget(#PB_Any, 0, 0, 600, 300, ImageID(Images(x)\ID))
        DeleteElement(LoadingThreads())
      EndIf
      
    Next
    
    WindowEvent = WindowEvent()
    Select EventWindow()
        Case Window_Main
          Select WindowEvent
              
            Case #PB_Event_SizeWindow
              If IsToolBar(Toolbar_Main)
                ToolBarHeight_Main = ToolBarHeight(Toolbar_Main)
              EndIf
              If IsMenu(Menu_Main)
                MenuHeight_Main = MenuHeight()
              EndIf
              ResizeGadget(MDIGadget_Main, Const_ToolBarWidth, ToolBarHeight, WindowWidth(Window_Main) - Const_ToolBarWidth, WindowHeight(Window_Main) - ToolBarHeight - MenuHeight)
            
            Case #PB_Event_CloseWindow
              
              End
              
          EndSelect
    EndSelect
  ForEver
Geändert wurde:


If Not IsThread(LoadingThreads())
x = ListIndex(LoadingThreads())
AddElement(MDIWindows_Main())
MDIWindows_Main()\ID = AddGadgetItem(MDIGadget_Main, #PB_Any, "", ImageID(Images(x)\ID))

MDIWindows_Main()\ImageGadget = ImageGadget(#PB_Any, 0, 0, 600, 300, ImageID(Images(x)\ID))
DeleteElement(LoadingThreads())
EndIf

und es wurde die alte Löschungsabfrage gelöscht.

Re: [Programm] Bilder in Threads laden

Verfasst: 31.01.2014 11:58
von CSHW89
Es liegt nun daran, dass

Code: Alles auswählen

x = ListIndex(LoadingThreads())
nach dem Löschen ein falschen Index haben kann. Wenn du das 0te Element löscht, haben alle nachfolgenden einen um 1 kleineren Index.
Das mit einer zusätzlichen Liste zu bereinigen, halte ich aber für Falsch, oder zumindest für schlechten Stil. Du hast sowieso für mein Geschmack zu viele Listen. Denn die Werte in den einzelnen Listen korrespondieren ja. Also z.b. das zweite Element der Liste ThreadInfos() speichert den Index der Liste und den Dateinamen des zweiten Bildes. Das zweite Element der Liste ProgramParams$() speichert ebenfalls den Dateinamen. Und das zweite Element der Liste LoadingThreads() speichert von genau diesem Bild die Thread-Nummer.
Für solche Fälle sollte man nur eine Liste und eine Structure verwenden. Also ProgramParams$(), LoadingThreads() und TreahEntriesToDelete() rausschmeißen, und stattdessen in die Structure THREADINFO ein zusätzlichen Eintrag "thread.i" einfügen, der die Rückgabe von CreateThread speichert. Am Anfang wird dann nicht ProgramParams$ befüllt, sondern ThreadInfos() und beim Laden wird auch genau in dieser Liste geprüft, ob der Thread noch existiert. Dabei kann man dann x gleich den Index in ThreadInfo() setzen.
Das sähe dann so aus (ungetestet):

Code: Alles auswählen

  Structure IMAGE
    ID.i
  EndStructure
 
  Structure MDIWINDOW
    ID.i
    ImageGadget.i
  EndStructure
 
  Structure THREADINFO
    ID.i
    Thread.i
    FileName$
  EndStructure
 
  Global NewList MDIWindows_Main.MDIWINDOW()
  Global NewList ThreadInfos.THREADINFO()
 
  While ProgramParameter(x)
    AddElement(ThreadInfos())
    ThreadInfos()\ID = ListIndex(ThreadInfos())
    ThreadInfos()\FileName$ = ProgramParameter(x)
    x + 1
  Wend
 
  If Not (x - 1) < 0
    Global Dim Images.IMAGE(x - 1)
  EndIf
 
  Procedure LoadingThread(*ThreadInfo.THREADINFO)
   
    Images(*ThreadInfo\ID)\ID = LoadImage(#PB_Any, *ThreadInfo\FileName$)
   
  EndProcedure
 
 
  Window_Main_Title$ = "just a little test program . . ."
 
  Window_Main = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 640, 400, Window_Main_Title$, #PB_Window_Invisible|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget)
 
  MDIGadget_Main = MDIGadget(#PB_Any, 0, 0, 0, 0, 0, 0)
 
  HideWindow(Window_Main, #False)
 
  ForEach ThreadInfos()
    ThreadInfos()\Thread = CreateThread(@LoadingThread(), @ThreadInfos())
  Next
 
  Repeat
   
    MenuHeight_Main = 0
    ToolBarHeight_Main = 0
   
    ForEach ThreadInfos()
     
      If Not IsThread(ThreadInfos()\Thread)
        x = ThreadInfos()\ID
        AddElement(MDIWindows_Main())
        MDIWindows_Main()\ID = AddGadgetItem(MDIGadget_Main, #PB_Any, "", ImageID(Images(x)\ID))
       
        MDIWindows_Main()\ImageGadget = ImageGadget(#PB_Any, 0, 0, 600, 300, ImageID(Images(x)\ID))
        DeleteElement(ThreadInfos())
      EndIf
     
    Next
   
    WindowEvent = WindowEvent()
    Select EventWindow()
        Case Window_Main
          Select WindowEvent
             
            Case #PB_Event_SizeWindow
              If IsToolBar(Toolbar_Main)
                ToolBarHeight_Main = ToolBarHeight(Toolbar_Main)
              EndIf
              If IsMenu(Menu_Main)
                MenuHeight_Main = MenuHeight()
              EndIf
              ResizeGadget(MDIGadget_Main, Const_ToolBarWidth, ToolBarHeight, WindowWidth(Window_Main) - Const_ToolBarWidth, WindowHeight(Window_Main) - ToolBarHeight - MenuHeight)
           
            Case #PB_Event_CloseWindow
             
              End
             
          EndSelect
    EndSelect
  ForEver