Skript läuft nur, wenn ich es in der IDE starte (Mod thread)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
ChristofArn
User
Beiträge: 3
Registriert: Mittwoch 2. Januar 2008, 15:08

Bitte entschuldigt, falls dieses Problem schon besprochen wurde. Es geht um das Modul "thread", aber wenn ich nach "Thread" suche, gibt es viel zu viele Suchergebnisse, die damit nichts zu tun haben.

Ich experimentiere ein bisschen, u.a. mit Threads. Folgendes Skript läuft, wenn ich es von von der Python IDE "Eric" aus starte tiptop. Wenn ich das Skript aber von einer Konsole aus starte, geht es nicht. Woran liegt das?

Skript:

#!/usr/bin/python

import thread
from Tkinter import *

class Projektor(object):
def __init__(self):
self.fenster = Tk()
self.fenster.mainloop()

def starter():
p = Projektor()

thread.start_new_thread(starter, ())



Wenn ich das Skript von Eric aus starte, zeigt es brav ein Fenster ohne nichts an.
Wenn ich das Skript von der Konsole aus starte, kriege ich diese nicht sehr hilfreiche Fehlermeldung:

Unhandled exception in thread started by
Error in sys.excepthook:

Original exception was:

Also, damit man mich richtig verstehen kann: Nach den Doppelpunkten ist einfach leer. Kann mir da jemand weiterhelfen?

Schönen Abend!

Christof


PS: Wenn ich das Skript so abändere, dass es ohne thread gemacht ist, läuft es auch wenn ich es von der Konsole aus starte. Darum denke ich, hängt es mit dem Modul "thread" zusammen. So läuft es tiptop:

#!/usr/bin/python

import thread
from Tkinter import *

class Projektor(object):
def __init__(self):
self.fenster = Tk()
self.fenster.mainloop()

def starter():
p = Projektor()

starter()
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

1. Willkommen im Forum
2. Nutze bei Code Beispielen bitte die dafür vorgesehenen Python-Tags
3. GUIs vertragen sich recht selten mit verschiedenen Threads, da sie immer ihre eigene Mainloop haben.
4. Es ist sehr unklug in Tkinter überhaupt eine Mainloop an ein Objekt zu binden, es sollte Grundsätzlich nur eine Mainloop existieren und diese in der Main - das selbe gilt für ein Tkinter.Tk-Widget

Dies ist so ziemlich das schlechteste Beispiel um in Tkinter mit Threads arbeiten zu können, was willst du eigentlich machen? - Um mit Threads einfach nur zu üben solltest du die GUI raushalten.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
BlackJack

Anstelle von `thread` sollte man das `threading`-Modul verwenden. Da wird in der Dokumentation auch drauf hingewiesen.
ChristofArn
User
Beiträge: 3
Registriert: Mittwoch 2. Januar 2008, 15:08

Grossen Dank für die raschen Reaktionen! Das ist ja super.

@Xynon1:
1. Danke!
2. Merk ich mir, sorry, hätte ich wissen können.
3. Schade
4. Da bin ich nicht sicher, ob ich Dich richtig verstehe. Heisst das: Besser keine mainloop innerhalb einer Klasse/einem Objekt? Die Tk-Beispiele, die ich im Python-Buch von Michael Weigend gefunden habe, sind aber immer so. Oder verstehe ich Dich falsch?

PS: Machen will ich eine Diashow, die immer mehrere Bilder gleichzeitig anzeigt und diese zufallsmässig und zeitgesteuert laufend gegen andere austauscht. - Bin aber auch sonst dabei, mit Python zu experimentieren. Django läuft jetzt auf meinem Server und eine erste dynamische Seite mit spezieller Kalenderfunktion habe ich dort hingekriegt.
BlackJack

@ChristofArn: Für die Diaschau brauchst Du keine Threads. Das Bilder austauschen kannst Du mittels der `after()`-Methode, die es auf jedem Widget gibt, zeitverzögert ausführen lassen.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Dieses Buch kenne ich leide nicht, aber kurze Grundlage. Du brauchst in Tkinter nur eine einzige "tkinter.Tk" Instanz, diese wird meist als "root" oder "main" bezeichnet. Diese hat ausschließlich in deiner Main im Programm zu stehen und sollte auch als einzige Instanz die "mainloop" ausführen.

Kurzes Beispiel:

Code: Alles auswählen

import Tkinter as tkinter

if __name__ == "__main__":
    root = tkinter.Tk()
    lb = tkinter.Label(root, text="Hello World")
    lb.pack() 
    #...
    root.mainloop()
Alles was du an Widgets auf deinem Hauptfenster brauchst sollte nun direkt oder indirekt auf "root" gelegt werden. Sollte man ein zweites Fenster benötigen, so sollte man das "tkinter.Toplevel" Widget nutzen.

Eine Ableitung von einem Widget, wenn man sich an die Tkinter API hält könnte zB. so aussehen.

Code: Alles auswählen

import Tkinter as tkinter

class LabelEntry(tkinter.Frame):
    
    def __init__(self, master, text="", cnf=()):
        tkinter.Frame.__init__(self, master, cnf)
        self._value = ""
        self._control = tkinter.StringVar()
        
        self.label = tkinter.Label(self, text=text.title())
        self.label.pack(side="left")
        
        options = {"textvariable" : self._control,
                   "justify" : "right",
                   "width" : 30,
                   "relief" : "flat"}
        self.entry = tkinter.Entry(self, options)
        self.entry.bind("<FocusIn>", self._focus_in)
        self.entry.bind("<Escape>", self._escape)
        self.entry.bind("<Any-Enter>", self._forget_focus)
        self.entry.pack(side="right")
    
    def _focus_in(self, event=None):
        self._value = self._control.get()
        
    def _forget_focus(self, event=None):
        self.master.focus_set()
        self.master.update()
    
    def _escape(self, event):
        self.control.set(self._value)
        self.forget_focus()
        
    def get(self):
        return self._control.get()

    def set(self, value):
        self._control.set(value)


if __name__ == "__main__":
    root = tkinter.Tk()
    le = LabelEntry(root, "Eingabe:")
    le.pack()
    root.mainloop()
Und deine Diashow sollte auch ohne Threads funktionieren, sie dir mal die "after"-Methode an.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
ChristofArn
User
Beiträge: 3
Registriert: Mittwoch 2. Januar 2008, 15:08

Super, herzlichen Dank für die Hilfe. Ich habe es nun mit der Methode "after" gemacht und es klappt wunderbar. Damit kann ich auf threads verzichten.

Es ist toll, in einem Forum eine solche Unterstützung so rasch zu kriegen.

Noch eine Frage zum Schluss: Gibt es irgendwelche Ideen, warum das Skript gelaufen ist, wenn ich es von Eric aus gestartet habe - und sonst nicht?
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ich würde jetzt einfach mal behaupten, das Eric die Exception ignoriert hat.
In PyDev geht es genauso wenig wie im Terminal.
und wenn man es von IDLE aus startet, wird die Exception meist ignoriert, aber da IDLE eine Tkinter Application ist und eine eigene Mainloop besitzt wo durch es manchmal wenn man Tkinter davon aus startet ein paar miese Problem gibt, führte das in ungefähr 2 von 3 Startversuchen zum Absturz von IDLE.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Antworten