wxPython while loop beenden

Plattformunabhängige GUIs mit wxWidgets.
Antworten
BaseBallBatBoy
User
Beiträge: 15
Registriert: Mittwoch 18. April 2007, 13:21

Hi!

Ich hab ein kleines GUI erstellt. Das Ziel ist, dass man zuerst die Parameter eingibt (Duration, Interval, Jobfile) und danach per 'Start' Button den while loop startet. Dieser loop sollte sich dann per 'Stop' Button beenden lassen. Allerdings scheint aber der loop keine Eingaben zuzulassen, denn wenn Start gedrückt wurde ist alles andere blockiert.

Ich habe Python 2.5, wxPython 2.8.3, Eclipse 3.2.2, PyDev 1.3.2

GUI.py

Code: Alles auswählen

import wx, re, os, time

#global vars
duration = ""
interval = ""
path = ""
running = True

class MyFrame(wx.Frame):
    def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=(280, 200)):
        wx.Frame.__init__(self, parent, id, title, pos, size)

        self.CreateStatusBar()
        
        panel = wx.Panel(self)
        
        sizer = wx.GridBagSizer(vgap=5, hgap=5)
        bt = wx.Button(panel, 0, "Start", wx.DefaultPosition, wx.DefaultSize)
        sizer.Add(bt, pos=(1,1), span=(1,2), flag=wx.EXPAND) 
        bt = wx.Button(panel, 1, "Stop", wx.DefaultPosition, wx.DefaultSize)
        sizer.Add(bt, pos=(1,3), span=(1,2), flag=wx.EXPAND) 
        
        self.displayDur = wx.TextCtrl(panel, 2, '',  style=wx.TE_LEFT)
        sizer.Add(self.displayDur, pos=(2,1), span=(1,3), flag=wx.EXPAND)
        bt = wx.Button(panel, 3, "Add Duration", wx.DefaultPosition, wx.DefaultSize)
        sizer.Add(bt, pos=(2,4), flag=wx.EXPAND) 
        
        self.displayInt = wx.TextCtrl(panel, 4, '',  style=wx.TE_LEFT)
        sizer.Add(self.displayInt, pos=(3,1), span=(1,3), flag=wx.EXPAND)
        bt = wx.Button(panel, 5, "Add Interval", wx.DefaultPosition, wx.DefaultSize)
        sizer.Add(bt, pos=(3,4), flag=wx.EXPAND)
        
        bt = wx.Button(panel, 6, "Add Jobfile", wx.DefaultPosition, wx.DefaultSize)
        sizer.Add(bt, pos=(4,1), flag=wx.EXPAND)
        
        panel.SetSizer(sizer)
        panel.Layout()

        self.Bind(wx.EVT_BUTTON, self.OnStart, id=0)
        self.Bind(wx.EVT_BUTTON, self.OnStop, id=1)
        self.Bind(wx.EVT_BUTTON, self.OnAddDuration, id=3)
        self.Bind(wx.EVT_BUTTON, self.OnAddInterval, id=5)
        self.Bind(wx.EVT_BUTTON, self.OnAddJobfile, id=6)
        

    def OnStart(self, event):
        if duration and path and interval >= 0:
            #prints because of test reasons
            print duration
            print interval
            print path

            self.SetStatusText("Beginning with Encoding Sequence")
            
            helix="C:/Programme/Helix/HelixMobileProducer_11.1.0/hmprod.exe"
            
            #ATTENTION this is actualy an endless loop !!!
            global running
            running = True
            while running:
                
                command = helix + " -job " + path + " -d " + duration
                
                print command
                
                #provides the command to the shell (helix mobile producer needed)
                #but isn't necessary to demonstrate my problem
                #os.system(command)

                actual = time.ctime()
                self.SetStatusText("Encoding completed at %s" % actual)
                
                time.sleep(interval)
            
        else:
            self.SetStatusText("Not able to Start because not every Value is set")
        

    def OnStop(self, event):        
        global running
        running = False
        
        self.SetStatusText("Job has been aborted")


    def OnAddDuration(self, event):
        global duration
        duration = self.displayDur.GetValue()
        
        #check if intervall is not empty
        if duration:
            #check if intervall is a correct duration number
            if re.search("^\d\d:\d\d:\d\d$", duration):
                
                print duration 
                
                self.SetStatusText("Duration is set to %s" % duration)
            else:
                self.displayDur.Clear()
                duration = ""
        

    def OnAddInterval(self, event):
        global interval
        interval = self.displayInt.GetValue()
        
        #check if intervall is not empty
        if interval:
            #check if intervall is a number
            if re.search("^\d+$", interval):
                
                interval = int(interval)
                print interval  
                
                self.SetStatusText("Interval is set to %i" % interval)
            else:
                self.displayInt.Clear()
                interval = ""


    def OnAddJobfile(self, event):
        dlg = wx.FileDialog(self, "Choose a Jobfile", os.getcwd(), "", "*.xml", wx.OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            getpath = dlg.GetPath()
            jobfile = os.path.basename(getpath)
        dlg.Destroy()
    
        #Regex: C:\file -> C:/file
        global path
        path = re.sub(r"\\", r"/", getpath)
        
        print "File Path %s" % path
        print "Selected Jobfile %s" % jobfile
        
        self.SetStatusText("Jobfile is %s" % path)


class MyApp(wx.App):
    def OnInit(self):
        frame = MyFrame(parent=None, id=-1, title="Test GUI")
        self.SetTopWindow(frame)
        frame.Show(True)
        return True


if __name__ == "__main__":
    app = MyApp(0)
    app.MainLoop()

Also, hat jemand eine Idee wie ich beim Klick auf den Stop Button aus dem 'endless' loop rauskomme? Ich brauche aber leider einen endless loop der nur auf eine Useraktion beendet werden soll.

THX BBBB
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Wahrscheinlich musst Du die Schleife in einem eigenen Thread ausführen. Du musst dann aber aufpassen, dass in diesem Thread keine GUI-Aktionen durchgeführt werden.
MfG
HWK
BaseBallBatBoy
User
Beiträge: 15
Registriert: Mittwoch 18. April 2007, 13:21

HWK hat geschrieben:Wahrscheinlich musst Du die Schleife in einem eigenen Thread ausführen. Du musst dann aber aufpassen, dass in diesem Thread keine GUI-Aktionen durchgeführt werden.
MfG
HWK
da ich python noch nicht lange kenne: wie sieht so ein "eigener thread" aus? kann mir darunter gerade nix vorstellen...
und was die gui aktionen angeht: das sind doch solche die mit wx beginnen. also so wie ich dass sehe, habe ich davon keine im loop.
cime
User
Beiträge: 152
Registriert: Dienstag 24. Mai 2005, 15:49

so ein "eigener thread" ist umgs. ein hintergrundprozess, aber das kann dir hier jemand anders betimmt besser erklären, deshalb versuch ich es nicht weiter ....

ich nutze meistens das hier: http://docs.python.org/lib/module-thread.html
ich denke mal es gibt noch andere möglichkeiten, aber damit solltest du gut klar kommen ...

mfg
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

BaseBallBatBoy hat geschrieben:und was die gui aktionen angeht: das sind doch solche die mit wx beginnen. also so wie ich dass sehe, habe ich davon keine im loop.
Z.B. self.SetStatusText(...) ist eine GUI-Operation. Also Vorsicht!
MfG
HWK
BaseBallBatBoy
User
Beiträge: 15
Registriert: Mittwoch 18. April 2007, 13:21

stimmt, den statusbar hab ich vergessen...

aber das mit den threads hab ich noch nicht ganz kapiert. kann ich denn von da aus auch parameter zurück an die funktion geben, um dan eben zb. den statusbar anzusprechen?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BaseBallBatBoy hat geschrieben:aber das mit den threads hab ich noch nicht ganz kapiert. kann ich denn von da aus auch parameter zurück an die funktion geben, um dan eben zb. den statusbar anzusprechen?
Hi BaseBallBatBoy!

Ja das geht. Mit ``wx.CallAfter()``. Ich habe zu diesem Thema ein paar Links zusammen getragen.

- http://www.python-forum.de/post-49728.html (ganz unten)

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BaseBallBatBoy
User
Beiträge: 15
Registriert: Mittwoch 18. April 2007, 13:21

woooha!

danke für den link! hat mir echt weitergeholfen.
hab jetzt mal zum test einen simplen versuch gemacht - allerdings mit thread und nicht threading. kann das aber auch noch ändern wenn nötig.

Code: Alles auswählen

import wx, re, os, time, thread 

#global vars 
running = False 

class MyFrame(wx.Frame): 
    def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=(280, 200)): 
        wx.Frame.__init__(self, parent, id, title, pos, size) 

        self.CreateStatusBar() 
         
        panel = wx.Panel(self) 
         
        sizer = wx.GridBagSizer(vgap=5, hgap=5) 
        bt = wx.Button(panel, 0, "Start", wx.DefaultPosition, wx.DefaultSize) 
        sizer.Add(bt, pos=(1,1), span=(1,2), flag=wx.EXPAND)  
        bt = wx.Button(panel, 1, "Stop", wx.DefaultPosition, wx.DefaultSize) 
        sizer.Add(bt, pos=(1,3), span=(1,2), flag=wx.EXPAND)  
         
        panel.SetSizer(sizer) 
        panel.Layout() 

        self.Bind(wx.EVT_BUTTON, self.OnStart, id=0) 
        self.Bind(wx.EVT_BUTTON, self.OnStop, id=1) 
         

    def OnStart(self, event): 
        self.SetStatusText("Beginning loop") 
        global running 
        running = True 
        thread.start_new_thread(self.ThreadLoop, ()) 

            
    def ThreadLoop(self):
        global running 
        while running: 
            print "hello world"
    

    def OnStop(self, event): 
        global running 
        running = False 
        self.SetStatusText("Job has been aborted") 


class MyApp(wx.App): 
    def OnInit(self): 
        frame = MyFrame(parent=None, id=-1, title="simple gui") 
        self.SetTopWindow(frame) 
        frame.Show(True) 
        return True 


if __name__ == "__main__": 
    app = MyApp(0) 
    app.MainLoop()  
macht nix anderes als whileloop starten und beenden

falls ich noch fragen habe sollte, werde ich mich bestimmt melden!
aber fürs erste: DANKE
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Nur so als Gedankenschupser: :-)

Code: Alles auswählen

        btn_start = wx.Button(panel, -1, "Start")
        btn_start.Bind(wx.EVT_BUTTON, self.OnStart)
        ...
        btn_stop = wx.Button(panel, -1, "Stop")
        btn_stop.Bind(wx.EVT_BUTTON, self.OnStop) 
        ...
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BaseBallBatBoy
User
Beiträge: 15
Registriert: Mittwoch 18. April 2007, 13:21

@gerold: ja, hab ich geändert!

noch ne kurze frage zu den globals: gibts da auch eine andere variante um vars die in der einen funktion erstellt/befüllt werden in einer anderen zu verwenden? oder ist das mit den globals die sicherste/schönste/schnellste variante?
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

BaseBallBatBoy hat geschrieben:zu den globals: gibts da auch eine andere variante
Hi BBBB!

Die Variable ``running`` wird ja nur innerhalb des Objekts ``MyFrame`` gebraucht, richtig? Dann würde ich die Variable an das Objekt ``MyFrame`` binden und keine globale Variable draus machen.

Code: Alles auswählen

def __init__(self,...):
    ...
    self.running = False
    ...

def OnStart(self, event):
    ...
    self.running = True
    ...

def OnStop(self, event):
    ...
    self.running = False
    ...
Wenn du später mal eine Variable brauchst, die für die gesamte Applikation wichtig ist, dann kannst du diese an das ``MyApp``-Objekt binden.

Mit ``wx.GetApp()`` kommst du von überall aus zum App-Objekt.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten