Seite 1 von 1

Probleme mit Threads

Verfasst: Montag 17. April 2006, 20:15
von cime
Hallo,

ich habe ein Programm, dass im Hintergrund (mit Hilfe von threads)einiges bearbeitet ... sobald der Prozess beendet ist, soll ein MessageDialog kommen, dass er fertig ist ... wenn ich dieses Fenster aber im thread starte hängt sich das Programm auf ... Kann mir jemand sagen, wie ich das umsetze, da ich einfach kein Lösung finde?

Danke schon im voraus!

hier ein BeispielCode:

Code: Alles auswählen

import wx,time

from thread import start_new


def arbeite_ganz_doll(parent):
    busy = wx.BusyInfo("Ich arbeite hier gerade ganz doll im Hintergrund...")
    time.sleep(2)
    busy.Destroy()
    dlg=wx.MessageDialog(self, 'Bin Fertig',
                        'Titel', wx.OK | wx.ICON_INFORMATION)
    dlg.CenterOnScreen()
    dlg.ShowModal()
    dlg.Destroy()


class app(wx.App):
    def OnInit(self):
        frame = myframe(None)
        self.SetTopWindow(frame)
        frame.CenterOnScreen()
        frame.Show(True)
        return True

class myframe(wx.Frame):
    def __init__(self,parent):
        wx.Frame.__init__(self,parent,-1,'Hallo Welt',size=(100,100))
        btn=wx.Button(self,-1,'Hier klicken',(10,10),(80,80))
        self.Bind(wx.EVT_BUTTON,lambda event: start_new(arbeite_ganz_doll,(self,)),btn)
        
        
x=app()
x.MainLoop()
mfg
cime

Re: Probleme mit Threads

Verfasst: Dienstag 18. April 2006, 10:12
von Francesco
Das ist wieder das Problem, ein Gui Control in einem Thread aufzurufen.

Ich frage mal in der wxPyhton mailing list nach der
einfachsten Möglichkeit, dies zu lösen.

Re: Probleme mit Threads

Verfasst: Dienstag 18. April 2006, 10:36
von gerold
Francesco hat geschrieben:Das ist wieder das Problem, ein Gui Control in einem Thread aufzurufen.
Hi!

Ich frage mich, ob es überhaupt sinnvoll ist, hier einen Thread zu verwenden.

wx.BusyInfo zeigt ein kleines Fenster ohne Rahmen an, in dem steht, dass gerade etwas getan wird.

Wenn nach dem Aufruf von wx.BusyInfo noch ein "wx.Yield()" angehängt wird, dann hat das Fenster genug Zeit, den Text anzuzeigen. Danach kann der lange Prozess abgearbeitet werden. Während dieser Zeit bleibt das BusyInfo-Fenster einfach stehen und zeigt an, dass etwas getan wird... Vielleicht mal zwischendurch im langen Prozess ein kleines wx.Yield() rein um eventuellen Anzeigeschwierigkeiten vorzubeugen. So lange, bis der Prozess fertig ist. Danach kann das BusyInfo-Fenster wieder zerstört werden.

Was ich sagen will: "Warum dann auch noch der zusätzliche Thread?" Macht man sich da nicht ein Problem, wo eigentlich keines wäre?

Es könnte natürlich auch einen wichtigen Grund für den Thread geben, aber darauf kann ich erst eingehen, wenn ich weiß warum.

Da es anscheinend Schwierigkeiten bereitet, ein Fenster in einem eigenen Thread anzuzeigen, empfehle ich mit Events zu arbeiten und vom arbeitenden Thread aus, Events an das Hauptprogramm zu schicken. Dieses kann dann darauf reagieren und die dementsprechenden Fenster anzeigen. Ich weiß zwar noch nicht wie das geht (ich bin noch beim Lernen von wxPython), aber zur Not kann man immer noch eine Queue zur Signalübermittlung einsetzen. Siehe http://www.python-forum.de/post-32524.html#32524

Falls ich Blödsinn geschrieben habe, bitte ich um Rücksicht, da ich noch am Anfang von wxPython stehe. :P

lg
Gerold
:-)

Re: Probleme mit Threads

Verfasst: Dienstag 18. April 2006, 10:47
von Francesco
gerold hat geschrieben:
Francesco hat geschrieben:Das ist wieder das Problem, ein Gui Control in einem Thread aufzurufen.
Hi!

Ich frage mich, ob es überhaupt sinnvoll ist, hier einen Thread zu verwenden.


Ich glaube, der cime hatte das Problem mit Threads und
versuchte nur ein vereinfachtes kurzes Sample zu posten,
das das Problem verdeutlicht.

Kann mich aber auch irren. :)
gerold hat geschrieben:
Falls ich Blödsinn geschrieben habe, bitte ich um Rücksicht, da ich noch am Anfang von wxPython stehe. :P

Nein, auf keinen Fall, ich glaube, das ist schon richtig, was du gesagt hast.

Verfasst: Dienstag 18. April 2006, 11:57
von pr0stAta
Öhm, bei mir funktioniert das so:

Code: Alles auswählen

import wx,time

import thread


def arbeite_ganz_doll(parent):
    busy = wx.BusyInfo("Ich arbeite hier gerade ganz doll im Hintergrund...")
    time.sleep(2)
    busy.Destroy()
    dlg=wx.MessageDialog(None, 'Bin Fertig',
                        'Titel', wx.OK | wx.ICON_INFORMATION)
    dlg.CenterOnScreen()
    dlg.ShowModal()
    dlg.Destroy()


class app(wx.App):
    def OnInit(self):
        frame = myframe(None)
        self.SetTopWindow(frame)
        frame.CenterOnScreen()
        frame.Show(True)
        return True

class myframe(wx.Frame):
    def __init__(self,parent):
        wx.Frame.__init__(self,parent,-1,'Hallo Welt',size=(100,100))
        btn=wx.Button(self,-1,'Hier klicken',(10,10),(80,80))
        self.Bind(wx.EVT_BUTTON,lambda event:thread.start_new_thread(arbeite_ganz_doll,(self,)),btn)
       
       
x=app()
x.MainLoop() 

Verfasst: Dienstag 18. April 2006, 12:26
von Francesco
pr0stAta hat geschrieben:Öhm, bei mir funktioniert das so:

Benutzt du Linux?

bei WinXP gibt es einen Crash, nachdem ich die Messagebox beendet habe.

Code: Alles auswählen

Busy: pythonw.exe - Fehler in Anwendung
Die Anwendung "0x0000007C"verweist auf den Speicher in "0x0000007C". Der Vorgang "read" konnte nicht ....

Verfasst: Dienstag 18. April 2006, 12:49
von pr0stAta
Habe es gerade auf einem XP Rechner getestet.
Dort geht es wirklich nicht :>
Konnte den Fehler allerdings eingrenzen, es liegt anscheinend
an
busy.Destroy()
Jedenfalls funktioniert alles sobald man diese Zeile auskommentiert.
Aber ob dies nun wirklich der Grund ist, und warum das so ist, weiss ich selbst noch nicht :>
*edit* Hmm ich glaube diese BusyInfo Methode ist noch nicht so ganz ausgereift. Gibt kaum Hilfe dazu. Ruft man das busy Objekt mit der
.__del__ Methode auf, crasht das Programm auch. Ich vermute einfach mal, das es für Windows einfach nicht geht ;)
Gruss

Verfasst: Dienstag 18. April 2006, 12:56
von Francesco
pr0stAta hat geschrieben:Habe es gerade auf einem XP Rechner getestet.
Dort geht es wirklich nicht :>
Konnte den Fehler allerdings eingrenzen, es liegt anscheinend
an
busy.Destroy()
Jedenfalls funktioniert alles sobald man diese Zeile auskommentiert.
Aber ob dies nun wirklich der Grund ist, und warum das so ist, weiss ich selbst noch nicht :>
Das ist interessant, danke für die Info.

Robin Dunn meint dazu:

You probably want to just do a "del self.busy" instead of calling
Destroy. The wx.BusyInfo class is already setup to destroy itself when
the python proxy object is garbage collected.

Dann funktioniert das auch tatsächlich ohne Crash.

Verfasst: Sonntag 23. April 2006, 17:22
von cime
okay, ich muss mich entschuldigen, ich hatte mein kleines testprog für euch nich noch einmal ausführlich getestet ... ich hatte gedacht, der Fehler liegt dort auch, wie im folgenden, an der Anzeige des Dialogs ... aber leider scheint ein wx.Dialog Unterschiede zum wx.MessageDialog in dieser Hinsicht zu haben. Hier mal ein wx.Dialog, so ähnlich wie ich ihn auch in meinem Programm benutze in einem thread:

Code: Alles auswählen

# -*- coding: cp1252 -*-
import wx,time,os,sys

from thread import start_new

class mydialog_save(wx.Dialog):
    def __init__(self,parent,name,txt):
        self.txt=txt
        self.path,self.name=os.path.split(name)
        pre = wx.PreDialog()
        pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP)
        pre.Create(parent,-1,"Speichern ... %s"%name,wx.DefaultPosition,(700,700))
        self.PostCreate(pre)
        
        self.outp = wx.TextCtrl(self, -1,txt,(10,10),size=(674, 620), style=wx.TE_MULTILINE|wx.TE_READONLY)
        self.outp.SetBackgroundColour(wx.NamedColor('WHITE'))

        btn=wx.Button(self,-1,"Speichern",(300,640))
        btn.SetDefault()
        self.Bind(wx.EVT_BUTTON,self.save_as,btn)
        btn = wx.Button(self, wx.ID_OK,"Fertig",(450,640))

    def save_as(self,event=None):
        if self.path and not os.path.isdir(self.path):
            os.makedirs(self.path)
        while True:
            dlg = wx.FileDialog(
                self, defaultDir=self.path, 
                defaultFile=self.name, wildcard=wildcard, style=wx.SAVE | wx.CHANGE_DIR
                )
            if dlg.ShowModal() == wx.ID_OK:
                path=dlg.GetPaths()[0]
                if os.path.isfile(path):
                    dlg = wx.MessageDialog(self,'Diese Datei existiert schon. Wollen SIe die Daten anfügen?',
                                'ERROR', wx.YES_NO|wx.NO_DEFAULT)
                    ret=dlg.ShowModal()
                    dlg.Destroy()
                    if ret==wx.ID_YES:
                        try:
                            f=open(path,'a')
                        except:
                            dlg.Destroy()
                            dlg = wx.MessageDialog(self,'Datei kann nicht geschrieben werden',
                                        'ERROR', wx.OK)
                            dlg.ShowModal()
                            dlg.Destroy()
                            continue
                    else:
                        continue
                else:
                    try:
                        f=open(path,'w')
                    except:
                        dlg.Destroy()
                        dlg = wx.MessageDialog(self,'Datei kann nicht geschrieben werden',
                                    'ERROR', wx.OK)
                        dlg.ShowModal()
                        dlg.Destroy()
                        continue
                f.write(self.outp.GetValue())
                f.close()
            break
        pass

def arbeite_ganz_doll(parent):
    busy = wx.BusyInfo("Ich arbeite hier gerade ganz doll im Hintergrund...")
    time.sleep(2)
    del busy

    dlg=mydialog_save(parent, os.path.join(os.path.split(sys.argv[0])[0],'Testordner','Testdatei.txt'),
                        'hier ist halt der Text.')
    dlg.CenterOnScreen()
    print 'bis hier gehts'
    dlg.ShowModal()
    print 'hier schon nicht mehr'
    dlg.Destroy()


class app(wx.App):
    def OnInit(self):
        frame = myframe(None)
        self.SetTopWindow(frame)
        frame.CenterOnScreen()
        frame.Show(True)
        return True

class myframe(wx.Frame):
    def __init__(self,parent):
        wx.Frame.__init__(self,parent,-1,'Hallo Welt',size=(100,100))
        btn=wx.Button(self,-1,'Hier klicken',(10,10),(80,80))
        self.Bind(wx.EVT_BUTTON,lambda event: start_new(arbeite_ganz_doll,(self,)),btn)
        
        
x=app(redirect=False)
x.MainLoop()