OpenWindow... in HauptThread

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
Joel
Beiträge: 851
Registriert: 21.04.2006 19:22

OpenWindow... in HauptThread

Beitrag von Joel »

Hey,

seit PB 5.x kann man ja OpenWindow, WaitWindowEvent ... nur noch im MainThread benutzen. Ich habe hier aktuell in PB Programm das ca 3000 Zeilen groß ist (und das ist gerade mal der Server). Wenn ich nun alle Window Dinge in den MainThread packe, wo außerdem schon die ganzen NetworkEvents sind, dann sieht der Code extrem unübersichtlich aus.

Ok, man kann natürlich die Compiler kurzzeitig ausschalten und dann trotzdem OpenWindow in einem Thread aufrufen. Da meine Software aber auch unter Linux laufen soll, möchte ich das trotzdem nicht verwenden. Das wurde ja scheinbar eingeführt, weil andere Betriebssysteme damit nicht klar kommen.

Ich poste jetzt einfach mal die Struktur von meinem Code (gekürzt aufs nötigste). Fällt euch da eine bessere und übersichtlichere Strukturierung für ein?

Code: Alles auswählen

Procedure Key() 
  For n=0 To 255 
    If GetAsyncKeyState_(n) 
      ProcedureReturn n 
    EndIf 
  Next 
EndProcedure

Enumeration
  #ScrollArea_1
  #Abmelden
  #NeuStarten
  #Herunterfahren
  #VPN
  #ScrollArea_2
  #Ok_Abmelden
  #Ok_NeuStarten
  #Ok_Herunterfahren
  #Progress_Download
  #Menu_Downloaden
  #Menu_Umbenennen
  #Menu_Loschen
EndEnumeration

Procedure GadgetCleaner()
  If IsGadget(#VPN)
    FreeGadget(#VPN)
  EndIf 
EndProcedure 


Procedure Abmelden_Area()
  GadgetCleaner()
  UseGadgetList(WindowID(0))
  ScrollAreaGadget(#ScrollArea_2, 300, 0, 300, 650, 290, 640, 1)
  ButtonGadget(#Ok_Abmelden, 50, 610, 210, 30, "Abmelden OK")
  CloseGadgetList()
EndProcedure 


Procedure NeuStarten_Area()
  GadgetCleaner()
  UseGadgetList(WindowID(0))
  ScrollAreaGadget(#ScrollArea_2, 300, 0, 300, 650, 290, 640, 1)
  ButtonGadget(#Ok_NeuStarten, 50, 610, 210, 30, "NeuStarten OK")
  CloseGadgetList()
EndProcedure


Procedure Herunterfahren_Area()
  GadgetCleaner()
  UseGadgetList(WindowID(0))
  ScrollAreaGadget(#ScrollArea_2, 300, 0, 300, 650, 290, 640, 1)
  ButtonGadget(#Ok_Herunterfahren, 50, 610, 210, 30, "Herunterfahren OK")
  CloseGadgetList()
EndProcedure

If InitNetwork() = 0 
  MessageRequester("Error", "Can't initialize the network !", 0) 
  End 
EndIf 

Port = 4000
Buffer = AllocateMemory(1000000) 
If CreateNetworkServer(0, Port) 
  Repeat 
    Delay(200)
    SEvent = NetworkServerEvent() 
    If SEvent 
      Debug "event"
      ClientID = EventClient() 
      Select SEvent 
          
        Case #PB_NetworkEvent_Connect          
          
          If OpenWindow(0, 30, 60, 600, 650, "PureBasic - Gadget Demonstration", #PB_Window_SystemMenu | #PB_Window_SizeGadget |  #PB_Window_MinimizeGadget)
            
            ScrollAreaGadget(#ScrollArea_1, 0, 0, 300, 650, 295, 625, 1)
            ButtonGadget(#Abmelden, 10, 15, 250, 30, "Abmelden") 
            ButtonGadget(#NeuStarten, 10, 50, 250, 30, "#NeuStarten") 
            ButtonGadget(#Herunterfahren, 10, 85, 250, 30, "Herunterfahren")
            CloseGadgetList()
            ProgressBarGadget(#Progress_Download, 10, 620, 250, 25, 0, 99)
            If CreatePopupMenu(0)      
              MenuItem(#Menu_Downloaden, "Downloaden")     
              MenuItem(#Menu_Umbenennen, "Umbenennen")      
              MenuItem(#Menu_Loschen, "Löschen")
            EndIf
            
            
            Repeat
              EventID = WaitWindowEvent()
              
              If Key() = #VK_F5
                If IsGadget(#VPN)
                  SendNetworkString(ClientID, "VPN"+Seperator$+VPN_Aktueller_Pfad$)
                  ClearGadgetItems(#VPN) 
                  Delay(100)
                EndIf 
              EndIf 
              
              If EventID = #PB_Event_Gadget
                Select EventGadget()
                    
                  Case #Abmelden
                    Abmelden_Area()
                    
                  Case #NeuStarten
                    NeuStarten_Area()
                    
                  Case #Herunterfahren
                    Herunterfahren_Area()
                    
                  ;Case xxxxxxx
                  ;..........
                  ;........
                  ;......
                  ;Case xxxxxxx
                  ;.....
                    
                  Case #Ok_Abmelden
                    SendNetworkString(ClientID, "Abmelden")
                    
                  Case #Ok_NeuStarten
                    SendNetworkString(ClientID, "Neustarten")
                    
                  Case #Ok_Herunterfahren
                    SendNetworkString(ClientID, "Herunterfahren")
                    
                  ; Case OK_xxxxxxxxxxx
                  ;......
                  ;.....
                  ; Case OK_xxxxxxxxxxx
                  ;.......
                        
                   
                EndSelect
              EndIf
              
              If EventID = #PB_Event_Menu
                Select EventMenu()
                    
                  Case #Menu_Loschen
                    If MessageRequester("Löschen?", "Wollen sie das Objekt wirklich löschen?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes     
                      Loschen_Dateiname$ = VPN_Aktueller_Pfad$+GetGadgetText(#VPN)
                      If FindString(Loschen_Dateiname$, ".", 0)
                        SendNetworkString(ClientID, "LöschenDatei"+Seperator$+Loschen_Dateiname$)
                      Else 
                        SendNetworkString(ClientID, "LöschenOrdner"+Seperator$+Loschen_Dateiname$)
                      EndIf 
                    EndIf 
                    
                    
                  ; Case #Menu_xxx
                  ; .........
                  ;Case #Menu_xxx
                  ;..........
                    
                    
                EndSelect 
              EndIf 
            Until EventID = #PB_Event_CloseWindow
            SendNetworkString(ClientID, "Close")
            CloseWindow(0)
          EndIf
          End 
      
        Case #PB_NetworkEvent_Data 
          *Buffer = AllocateMemory(1024)
          If *Buffer
            RcvBytes = ReceiveNetworkData(ClientID, *Buffer, 1024)
            If RcvBytes > 0
              Line$ = PeekS(*Buffer, 5)
              If Left(Line$, 5) = "FILE:"  
                
                  ;If NetworkReceiveFile(ClientID, *Buffer, RcvBytes, Tempdir$+"Empf\"+Str(Aktuelle_DownloadDatei)+".teil")
                  ;......
                  ;....
                  ;......
                ;EndIf 
                
                
              Else
                
                String$ = PeekS(*Buffer)
                Debug String$
                Name$ = StringField(String$, 2, Seperator$)
                Debug String$+Name$
                Select StringField(String$, 1, Seperator$)
                    
                  Case "Fehler"
                    
                    If Name$ = "Prozess Dll nicht gefunden"
                      MessageRequester("xxx", "xxx")
                    EndIf 
                    
                    If Name$ = "Prozess existiert nicht"
                      MessageRequester("xxx", "xxx")
                    EndIf 
                    
                    If Name$ = "Prozess konnte nicht beendet werden"
                      MessageRequester("xxx", "xxx")
                    EndIf 
                    
                    If Name$ = "Erfolgreich!!!"
                      MessageRequester("xxx", "xxx")
                    EndIf 
                    
                    ; Case xxxxxxxx
                    ;.............
                    ; Case xxxxxxxxx
                    
                EndSelect 
                
                
              EndIf
            EndIf
            FreeMemory(*Buffer)
          EndIf
          
          
        Case #PB_NetworkEvent_Disconnect
          Debug "endeee der connection case 4"
          CloseNetworkConnection(ClientID)
          CloseNetworkServer(0)
          ; MessageRequester("Trennug", "Der Client wurde getrennt", 0)
          End 
          
          
      EndSelect 
    EndIf 
  Until Quit = 1      
EndIf 
End  
----------------------------------------------------------

PB 5.20 Beta 10 | Windows 7
Benutzeravatar
Chimorin
Beiträge: 451
Registriert: 30.01.2013 16:11
Computerausstattung: MSI GTX 660 OC mit TwinFrozr III
6Gb DDR 3 RAM
AMD Phenom II X4 B55 @ 3,6GHz
Windows 7 Home Premium 64-bit

Re: OpenWindow... in HauptThread

Beitrag von Chimorin »

Joel hat geschrieben: Ok, man kann natürlich die Compiler kurzzeitig ausschalten und dann trotzdem OpenWindow in einem Thread aufrufen. Da meine Software aber auch unter Linux laufen soll, möchte ich das trotzdem nicht verwenden. Das wurde ja scheinbar eingeführt, weil andere Betriebssysteme damit nicht klar kommen.
Ich glaube, du meinst den Debugger... Wenn du den Compiler ausschaltest, gibt's auch keine Exe ;)
Hmm, ich meine, es gab auch unter Windows Probleme.
Bild

- formerly known as Bananenfreak -
Benutzeravatar
Joel
Beiträge: 851
Registriert: 21.04.2006 19:22

Re: OpenWindow... in HauptThread

Beitrag von Joel »

Bananenfreak hat geschrieben:
Joel hat geschrieben: Ok, man kann natürlich die Compiler kurzzeitig ausschalten und dann trotzdem OpenWindow in einem Thread aufrufen. Da meine Software aber auch unter Linux laufen soll, möchte ich das trotzdem nicht verwenden. Das wurde ja scheinbar eingeführt, weil andere Betriebssysteme damit nicht klar kommen.
Ich glaube, du meinst den Debugger... Wenn du den Compiler ausschaltest, gibt's auch keine Exe ;)
Hmm, ich meine, es gab auch unter Windows Probleme.
Meine Debugger. Ja kann sein, das das auch unter Windows Probleme gab. Hat jemand Ideen, was man da machen kann?
----------------------------------------------------------

PB 5.20 Beta 10 | Windows 7
Benutzeravatar
KeyKon
Beiträge: 1412
Registriert: 10.09.2004 20:51
Computerausstattung: Laptop: i5 2,8 Ghz, 16GB DDR3 RAM, GeForce 555GT 2GB VRAM
PC: i7 4,3 Ghz, 32GB DDR3 RAM, GeForce 680 GTX 4GB VRAM
Win10 x64 Home/Prof
PB 5.30 (64bit)
Wohnort: Ansbach
Kontaktdaten:

Re: OpenWindow... in HauptThread

Beitrag von KeyKon »

Habe deinen Code nur kurz überflogen, habe daher nur diese zwei fixen Ideen:

Also zum einen würde ich das GUI immer im Hauptthread anlegen, und sämtliche andere Sachen (also die Netzwerkgeschichte) in Threads auslagern.
Zum anderen würde ich gerade wenn ich ein Fenster nicht immer offen habe dieses trotzdem am Anfang meines Programms versteckt erstellen und dann eben erst zeigen wenn ich es brauche, oder zumindest das ganze in eine Prozedur auslagern, damit das beim Aufrufen im Main Code nicht so stört.

LG KeyKon
(\/) (°,,,°) (\/)
Benutzeravatar
Joel
Beiträge: 851
Registriert: 21.04.2006 19:22

Re: OpenWindow... in HauptThread

Beitrag von Joel »

OK danke! gute Idee.

1. Ich werde jetzt einen Network-Thread anlegen und das Fenster schon mal im voraus anlegen (versteckt) und erst bei einem Event sichtbar machen. Sonst noch Ideen, Tipps etc?

Es gibt ja diesen PostEvent Befehl. Meint ihr ich könnte das damit alles etwas vereinfachen? Wenn ich es richtig verstehe kann ich alles in eine Eventschleife Packen un so z.B. einerseits nach

Code: Alles auswählen

Case #PB_NetworkEvent_Connect
und andererseits nach

Code: Alles auswählen

CasE #Ok_Herunterfahren_Gadget
abfragen. Ist das sinnvoll, oder ehr nicht?
----------------------------------------------------------

PB 5.20 Beta 10 | Windows 7
Benutzeravatar
KeyKon
Beiträge: 1412
Registriert: 10.09.2004 20:51
Computerausstattung: Laptop: i5 2,8 Ghz, 16GB DDR3 RAM, GeForce 555GT 2GB VRAM
PC: i7 4,3 Ghz, 32GB DDR3 RAM, GeForce 680 GTX 4GB VRAM
Win10 x64 Home/Prof
PB 5.30 (64bit)
Wohnort: Ansbach
Kontaktdaten:

Re: OpenWindow... in HauptThread

Beitrag von KeyKon »

Mit Postevent habe ich persönlich noch nie gearbeitet, aber es ist durchaus eine gute Möglichkeit um zwischen Thread und GUI zu kommunizieren.
So wie ich die Hilfe verstanden habe kannst du dort aber nur Window-Events oder eigene Events nutzen, das heißt du musst dir eigene Konstanten anlegen (Die größer als #PB_Event_FirstCustomValue sein müssen) und kannst nicht einfach die Eventnummern des Network verwenden, da die ja bei den WindowEvents für andere Sachen stehen.

Ich regele Kommunikation im übrigen immer über strukturierte Variablen, auf die beide Threads Zugriff haben, auch wenn ich mir bewusst bin, dass das nicht die sauberste Lösung ist^^

LG KeyKon
(\/) (°,,,°) (\/)
auser
Beiträge: 58
Registriert: 17.05.2011 10:56

Re: OpenWindow... in HauptThread

Beitrag von auser »

Joel hat geschrieben:wo außerdem schon die ganzen NetworkEvents sind, dann sieht der Code extrem unübersichtlich aus.
Sehe ich auch so. Damit wird man gezwungen Code zusammenzumischen der eigentlich gar nicht zusammen sein müsste. Angenommen ich hab in einem Hauptfenster eine Art Windows-Explorer der mir lediglich eine Liste mit Items anbietet die ich anklicken darf und bei Doppelklick ein oder mehrere eigentlich automome Programmteile (mit weiteren Fenstern) aufmacht die sehr gut ohne einander leben könnten. Ich müsste trotz der Unabhängigkeit und trotz daß diese Programteile (inkl. der Fenster) sich untereinander eigentlich nichts zu erzählen haben die Modularität aufgeben und eine serielle lange Wurst pressen und hoffen das alles was da sonst noch so mitspielt nicht so doof wie meine GUI ist und im Hauptthread laufen muss. Das fängt ja schon an wenn ich ein simples "Connecting to..." visualisieren will damit sich der User nicht grün und blau wundert und 100x startet weil er meint da passiert nix. Selbst wenn dieses "Connecting to"-Fensterl der Micky-Maus-Anteil von der Arbeit ist den es zu bewältigen gibt muss ich nur wegen diesen kleinen Fensters den Code zusammen wurschteln... weil... das Fenster soll schließlich im Hauptthread laufen. Oder aber ich muss überhaupt statt verschiedenen Threads verschiedene Programme starten - was wiederum blöd ist wenn ich zumindest beim Starten doch einen kleinen Teil an Speicher zum initialisieren weiterreichen muss und der Sicherheitstechnisch nicht ganz unproblematisch ist. Ans Ziel kommt man schon irgendwie ...

Das Problem ist aber nicht erst seit PB 5.0 und eigentlich gar nicht die Schuld von PB. Als Schuld rechne ich hier den Entwicklern von PB lediglich an daß sie diesen Umstand viel zu spät Rechnung getragen haben und die Hilfe in Windows eigentlich (irreführender Weise) suggeriert hat man müsse lediglich aufpassen daß man das WindowEvent im selben Thread machen müsste in dem das Fenster erstellt wurde. Das funktioniert(e) in Windows ja auch tatsächlich. Ich glaube die haben diesen Umstand für die grafischen Umgebungen von Linux und MacOS schlicht zu spät bedacht. Irgend wann hat dann nämlich leider jemand (ich glaube das war ich) tatsächlich ernsthaft versucht sein Program in Linux mit Threads und Fenster zu kompilieren und hat festgestellt daß das mit den Threads so überhaupt nicht stabil gelaufen ist und einen Bug reportet. Die Lösung war aber leider nicht der erhoffte Fix für Linux sondern die Funktionalität der Windows-Version wurde so weit beschnitten daß es der von den GUIs in Linux und MacOS entspricht. Und ich würd da jetzt ja auch noch gerne über PB und so schimpfen... >_< - nützt aber nix weil ich letztlich festgestellt habe daß es auch in QT und so nicht besser ist. Also wird dadurch meine Linux-Version auch nicht gerettet.

Und weil Bananenfreak von wegen Problemen unter Windows meinte: Ich hab tatsächlich auch noch einen OpenWindow-Bug reportet und zwar ziemlich zeitgleich. Dieses Problem wurde aber (nach einem sehr ausgedehnten frustrierenden "No Bug - never ever"-Thread) letztlich (mehr oder weniger - siehe unten) gefixt. Also trotz dessen daß ich eigentlich ein rechter Linux-Verfechter bin muss ich eingestehen daß das in der Windows-Welt offensichtlich wesentlich unabhängiger funktioniert. Das man in der Zeit wo man PCs mit mehreren Kernen noch gezwungen wird hier durch eine Überpriorisierung von einer Aufgabe (GUI) Spagetti ... oder sagen wir zumindest "Objektorientierten Nudel-code" zu produzieren das finde ich rückständiger als es der Zeit entsprechen müsste. Ich fände es cool wenn die GUIs in Linux und MacOS irgendwann mal die GUI vom Hauptthread-zwang befreien.

Da meine Software aber auch unter Linux laufen soll, möchte ich das trotzdem nicht verwenden.
Sollte meine Software ursprünglich auch. Ich hab aber schon viel mehr als 3000 Codezeilen reingesteckt und die zu geringe Anzahl an Linux-Usern hat letzlich den Tod für die Linux-Version besiegelt. In Windows verwende ich den ziemlich ekeligen Workaround mit DisableDebugger / EnableDebugger. Und bis auf einige Kleinigkeiten läuft das eigentlich sauber. Bei den MessageRequestern muss man etwas aufpassen (wird in einem Thread ein Fenster geschlossen und zur gleichen Zeit im Hauptthread ein Messagerequester erzeugt kann es sehr gut passieren daß der Messagerequester nicht erscheint oder sofort wieder geschlossen wird ... als ob der Messagerequester sich einfach irgend ein Fenster sucht an den er sich anheften kann und beim Tod von diesem einfach mitstirbt).


Mfg,
auser
Benutzeravatar
mk-soft
Beiträge: 3902
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: OpenWindow... in HauptThread

Beitrag von mk-soft »

Hat noch nie gut geklappt GUI-Element aus Threads anzulegen. Ich habe es schon damit geschaft Linux abzuschiessen.

Bis auch ein paar ausnahmen ist es immer besser die GUI im Main zu erstellen und zu bearbeiten.
Für die Kommunikation mit den User aus einen Thread herraus verwende ich dieses...

http://www.purebasic.fr/german/viewtopi ... =8&t=26219

FF :wink:
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Antworten