tk hängt

Fragen zu Tkinter.
Antworten
snowleopard
User
Beiträge: 7
Registriert: Mittwoch 6. Oktober 2010, 08:08

sicherlich eine einfache frage die sicherlich auch 1000 mal im
forum beantwortet wurde, jedoch weiß ich einfach nicht wonach ich explizit
suchen muss, da ich noch absoluter anfänger in tk bin ;)

mein problem ist, dass tk hängt und während einer berechnung nichtmehr reagiert.
aufbau ist wie folgt:


Code: Alles auswählen

funktion start()
  button start wird rot
  startet über die com-schnittstelle einen 2minütigen prozess
  startet über die com-schnittstelle einen 5minütigen prozess
  startet über die com-schnittstelle einen 1minütigen prozess

tk wird geladen
button start löst funktion start() aus
tk loop


mein problem ist, dass sich der button start/stop erst zum ende
aller com-aufrufe rot verfärbt. zudem würde ich ihn gerne erneut
drücken können um nach einem com aufruf das programm zu beenden,
letzteres ist aber erstmal nebensächlich. mir würde es vorerst genügen,
wenn meine gui nicht über minuten einfriert :)
wär toll wenn mir wer helfen könnte, oder mir sagen kann wonach ich
suchen muss =)
BlackJack

@snowleopard: Solange die `start()`-Funktion läuft, läuft die Hauptschleife der GUI nicht, solange wird die also auch nicht aktualisiert oder kann auf Ereignisse vom Benutzer reagieren. Du müsstest die lang laufenden Sachen also asynchron, zum Beispiel in einem Thread ablaufen lassen. Dabei ist zu beachten, dass nur aus dem Hauptthread, in dem die GUI-Hauptschleife läuft, auf die GUI zugegriffen werden darf.
snowleopard
User
Beiträge: 7
Registriert: Mittwoch 6. Oktober 2010, 08:08

wenn ich also die funktionen die über die com-schnittstelle abgearbeitet
werden in nen thread packe müsste es funktionieren? =)
wäre klasse, hat jemand nen für anfänger gut geeignetes tutorial über
threads? =)
problembär

Ich steh' nicht sehr auf Threads und versuche sie zu vermeiden, wenn's geht:
Deine Funktion start() kann auch den COM-Prozeß starten, und dann

Code: Alles auswählen

mainwindow.after(120000, check)
auslösen (2 Minuten). Danach bleibt Tk erstmal im Mainloop und check() wird in 2 Minuten automatisch ausgelöst.
snowleopard
User
Beiträge: 7
Registriert: Mittwoch 6. Oktober 2010, 08:08

also wird quasi die gui aktualisiert und danach würden die com prozesse ausgeführt?
problem ist, dass während der ausführung der com-prozesse die grafische oberfläche
weiterhin aktiv bleiben soll. habe es jetzt mittels thread "gelöst".

Es ergibt sich nun allerdings das Problem, dass er nur einmal die comaufrufe tätigt,
beim zweiten mal button start druecken passiert nichts:

Code: Alles auswählen

funktion comaufrufe():
  from win32com.client import Dispatch
  anwendung = Dispatch('DATPROG.Application')   

  startet über die com-schnittstelle einen 2minütigen prozess
  startet über die com-schnittstelle einen 5minütigen prozess
  startet über die com-schnittstelle einen 1minütigen prozess



funktion starten():
  button start wird rot

  c.Threadaufruf()   #thread aus klasse
  c.start()



import threading
class Threadaufruf(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)        
    def run(self):     
        comaufrufe()

tk wird geladen
button start löst funktion starten() aus
tk loop
wär toll wenn mir wer nen tipp geben könnte woran das liegt =)
BlackJack

@snowleopard: Da wir Deinen Code nicht kennen, können wir auch schlecht sagen was da vielleicht nicht richtig ist. Dein Pseudocode ist offensichtlich nicht dass was Du da tatsächlich hast, denn mir ist nicht so ganz klar was `c` in `starten()` sein soll!?

Importe werden üblicherweise alle am Anfang eines Moduls getätigt, dann sieht man einfacher wovon das Modul abhängt. Und um eine Funktion in einem Thread zu starten muss man keine eigene Klasse schreiben. Dafür hat `threading.Thread` das `target` Argument, und falls nötig `args` und `kwargs`.
problembär

snowleopard hat geschrieben:also wird quasi die gui aktualisiert und danach würden die com prozesse ausgeführt?
problem ist, dass während der ausführung der com-prozesse die grafische oberfläche
weiterhin aktiv bleiben soll.
Tut sie auch, wenn man .after() verwendet. Das Blockieren des GUIs wäre ein Problem von time.sleep().

Ansonsten stimme ich BlackJack zu.
snowleopard
User
Beiträge: 7
Registriert: Mittwoch 6. Oktober 2010, 08:08

ich hab den pseudocode mal umgesetzt:

Code: Alles auswählen

def PartWechseln():
    time.sleep(3)
    #params=[]
    #CATIA.SystemService.ExecuteScript ("C:/Users/thf/Desktop", 1, "PartFensterWaehlen.CATScript", "CATMain", params)
    print "ausgefuehrt"    

def StartStop():
    th=threading.Thread(target = PartWechseln)
    th.run()

#==================================
from Tkinter import *
import threading
import time
#from win32com.client import Dispatch
#CATIA = Dispatch('CATIA.Application')


root=Tk()
ButStartStop = Button(root, text="start", command=StartStop).grid(row=0, column=0)
root.mainloop()
die fuer mich wichtigen 4 zeilen habe ich ausgeblendet (würden bei euch sicher nicht
funktionieren, zugriff über com auf anderes prog.) und das commando in der com habe ich durch
time.sleep(3) ersetzt, damit sollte mein problem klar werden ;)

ich weiß leider nicht wie ich das .after() hier anwende :(
BlackJack

@snowleopard: `Thread`\s startet man mit `start()` und nicht mit `run()`.
snowleopard
User
Beiträge: 7
Registriert: Mittwoch 6. Oktober 2010, 08:08

ja, allerdings funktioniert der aufruf der com mit start() nicht, mit run() schon.
jedoch blockiert run() die gui
BlackJack

@snowleopard: `run()` wird ja auch nicht asynchron ausgeführt. Kann man diesen `com`-Kram überhaupt asynchron verwenden!? Ist der thread-safe? Muss man da vielleicht bestimmte Massnahmen ergreifen?

Und was heisst "funktioniert nicht"? Fehlermeldung? Programmabsturz?
snowleopard
User
Beiträge: 7
Registriert: Mittwoch 6. Oktober 2010, 08:08

ja, eine (für mich nicht aufschlussreiche) fehlermeldung, die erst nach beenden
der gui (kreuzchen) erscheint.

Traceback (most recent call last):
File "C:\Python26\lib\threading.py", line 532, in __bootstrap_inner
self.run()
File "C:\Python26\lib\threading.py", line 484, in run
self.__target(*self.__args, **self.__kwargs)
File "C:\Users\thf\Desktop\catiaaufruftest.py", line 5, in PartWechseln
CATIA.SystemService.ExecuteScript ("C:/Users/thf/Desktop", 1, "PartFensterWaehlen.CATScript", "CATMain", params)
File "C:\Python26\lib\site-packages\win32com\client\dynamic.py", line 512, in __getattr__
raise AttributeError("%s.%s" % (self._username_, attr))
AttributeError: CATIA.Application.SystemService
problembär

Wenn so viel neben dem Mainloop zu tun ist, daß das Python-Skript in diesen anderen Aktivitäten feststeckt, so daß das dann doch "time.sleep()" nahe kommt, dann muß man tatsächlich Threads neben Tkinter verwenden.
Nicht ganz leicht zu kontrollieren. Hier mal ein Beispiel (das Du vielleicht abwandeln kannst):

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-

import threading
import Tkinter as tk
import time

class TkWin:

    def __init__(self):

        self.root = tk.Tk()
        self.ButStartStop = tk.Button(self.root,
                                      text = 'waiting',
                                      command = self.StartStop)
        self.ButStartStop.pack()
        self.ButExit = tk.Button(self.root,
                                 text = 'exit',
                                 command = self.exit)
        self.ButExit.pack()
        self.cms = ComStarter()
        self.root.mainloop()

    def StartStop(self):

        if self.cms.running == 0:
            if self.cms.isAlive():
                self.ButStartStop.configure(fg = 'red', text = 'continued')
            if not self.cms.isAlive():
                self.ButStartStop.configure(fg = 'red', text = 'started')
                self.cms.start()
            self.cms.running = 1
        elif self.cms.running == 1:
            self.ButStartStop.configure(fg = 'black', text = 'stopped')
            print
            self.cms.running = 0

    def exit(self):
        self.cms.running = 2
        del self.cms
        self.root.destroy()

class ComStarter(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.nr = 1
        self.running = 0

    def run(self):
        while True:
            if self.running == 2:
                break
            if self.running == 0:
                self.nr = 1
                continue
            print self.nr
            time.sleep(0.3)
            self.nr += 1
TkWin()
HTH
snowleopard
User
Beiträge: 7
Registriert: Mittwoch 6. Oktober 2010, 08:08

leider kann ich mir das zur zeit nicht genauer anschauen,
da ich mitte nächster woche mündl. prüfung habe.
werde es danach aber mal probieren!
vielen dank schon mal :)
Antworten