Nebenläufigkeit von GUI und Messgerätkalibrierung realisiere

Fragen zu Tkinter.
Antworten
dominik123
User
Beiträge: 35
Registriert: Montag 10. Februar 2014, 08:48

Hallo,

ich möchte mit Hilfe eines Pythonscripts mit visa Messgeräte ansteuern.
Um die Übersichtlichkeit zu verbessern möchte ich außerdem mit Hilfe von TKinter eine GUI programmieren.
Was wäre die beste Methode, um zu verhindern, dass das Interface "einfriert", sobald bei der Messgerätkalibrierung Wartezeiten entstehen.
Vielen Dank für eure Hilfe!

Gruß,
Dominik
BlackJack

@dominik123: Du könntest die Kalibrierung in einem eigenen Thread (`threading`-Modul) laufen lassen. Wichtig ist, dass man nur vom Hauptthread aus Veränderungen an der GUI vornehmen darf. Falls der Arbeitsthread etwas in der GUI verändern soll, dann verwendet man üblicherweise `Queue.Queue`-Objekte um zwischen den beiden Threads zu kommunizieren und fragt im Hauptthread mittels `after()`-Methode regelmässig ob neue Daten in der Queue vorliegen.
dominik123
User
Beiträge: 35
Registriert: Montag 10. Februar 2014, 08:48

Vielen Dank für die Antwort!
Zur Kalibrierung nutze ich momentan ein eigenes Script, das sehr umfangreich ist. Kann ich tatsächlich das gesamte Script als Thread aufrufen? Muss die Kalibrierung im gleichen Script ablaufen, wie die Erstellung des GUI?
Gruß,
Dominik
BlackJack

@dominik123: Du kannst streng genommen nicht das Skript als Thread aufrufen aber Du kannst einen Thread starten in dem das Skript dann aufgerufen wird. Wenn es tatsächlich als eigener Prozess laufen soll gibt es dafür das `subprocess`-Modul. Ansonsten würde ich versuchen das Skript so zu schreiben das man es auch als Modul importieren kann und darin dann die entsprechende Funktion(en) aufrufen kann.
dominik123
User
Beiträge: 35
Registriert: Montag 10. Februar 2014, 08:48

Ich habe versucht, dass Kalibierungsscript mithilfe der import- Funktion in das GUI zu importieren. Wenn ich nun allerdings mein GUI starten möchte, so öffnet sich nur die Kalibrierung und sobald diese fertig ist öffnet sich das GUI. Worauf muss ich achten, wenn ich das Script als Modul importieren möchte?
Wie kann ich denn das ganze Script mithilfe eines Threads aufrufen?
Vielen Dank!
BlackJack

@dominik123: Du musst das Modul so schreiben das man es importieren kann ohne das es als Programm los läuft. Dafür wertet man in einem Modul `__name__` aus, was an den Modulnamen gebunden ist, ausser wenn man ein Modul direkt ausführt, dann ist es an den Wert '__main__' gebunden. Das Idiom sieht so aus:

Code: Alles auswählen

if __name__ == '__main__':
    main()
Und in der `main()`-Funktion steht dann das Hauptprogramm.
dominik123
User
Beiträge: 35
Registriert: Montag 10. Februar 2014, 08:48

Vielen Dank!
Habe die Kalibrierung jetzt so umgeschrieben, dass ich sie als Modul importieren kann, ohne dass sie direkt los läuft.
Wenn ich nun jedoch (mittels Button) eine Funktion aus dem "Kalibrierungsmodul" aufrufe, friert mein GUI solange ein, bis diese Funktion vollständig abgearbeitet wurde. Hier kommt nun die eigentliche Nebenläufigkeit ins Spiel und diese bereitet mir immer noch Probleme!
Wie könnte ich eine Funktion wie z.B. "calibration.start_measurement()" in einen Thread auslagern, sodass mein GUI nicht mehr einfriert?
Mit dem Befehl thread.start_new_thread() komme ich momentan leider nicht weiter...
BlackJack

@dominik123: Das `thread`-Modul sollte man sowieso nicht mehr verwenden, deshalb hatte ich ja auf das `threading`-Modul verwiesen.

So ganz allgemein ohne mehr zu wissen kann man nur das hier sagen:

Code: Alles auswählen

    thread = Thread(target=calibration.start_measurement)
    thread.start()
Dann läuft die Funktion nebenläufig.
dominik123
User
Beiträge: 35
Registriert: Montag 10. Februar 2014, 08:48

Hallo,
um eine Progressbar zu installieren habe ich folgende Funktion:

Code: Alles auswählen

def progress():
      progressbar = ttk.Progressbar(orient=HORIZONTAL, length=140, mode='indeterminate', maximum=10)
      progressbar.grid(row=7, column=5, sticky=W)
      progressbar.start(1000)
um sie nebenläufig ausführen zu lassen, habe ich sie folgendermaßen aufgerufen:

Code: Alles auswählen

      Thread = thread(target=progress())
      Thread.start()
die Progressbar läuft auch los, allerdings wird folgende Fehlermeldung ausgegeben und das Programm beendet:

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
    return self.func(*args)
  File "C:\Python27\GUI\GUI_1802.py", line 109, in consistency_check
    Thread = thread(target=progress())
TypeError: 'module' object is not callable
was ich nicht verstehe ist, dass die Progressbar überhaupt losläuft... wie kann ich diesen Fehler umgehen?
Vielen Dank!
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

1) Du sollst nicht das thread-Modul verwenden, sondern das threading-Modul. BlackJack hat dir das bereits mitgeteilt.

2) In threading gibt es eine Klasse Thread. Die musst du verwenden. Sie heißt nicht thread.

3) Das ganze tkinter-Zeugs muss im Haupt-Thread laufen. Aus einem zweiten Thread heraus direkt GUI-Manipulationen durchzuführen wird zu Problemen führen, weil tkinter, wie alle GUI Libs, bei sowas durcheinander kommt. Verwende dazu queue.Queue und verschicke darüber Nachrichten und Daten zwischen den einzelnen Threads.

4) Du musst die Funktion progress an Thread(...) übergeben. Statt dessen rufst du sie momentan auf und übergibst das Ergebnis dieses Aufrufs. Guckstu:

Code: Alles auswählen

>>> def foo():
...     return 'hey!'
...
>>> foo
<function foo at 0x7fedda04>
>>> foo()
'hey!'
5) Es gibt eine Code-Formatierung im Forum. Verwende sie.
In specifications, Murphy's Law supersedes Ohm's.
dominik123
User
Beiträge: 35
Registriert: Montag 10. Februar 2014, 08:48

Zunächst mal vielen Dank für die Antworten.
Ich importiere nun das threading- Modul.
Ist folgender Code richtig, um die Funktion progress an Thread() zu übergeben?

Code: Alles auswählen

thread= threading.Thread(target=progress)
thread.start()
Momentan sieht meine progress- Funktion folgender Maßen aus:
def progress():
progressbar = ttk.Progressbar(orient=HORIZONTAL, length=140, mode='indeterminate', maximum=10)
progressbar.grid(row=7, column=5, sticky=W)
progressbar.start(1000)
momentan würde ich also ein Teil des tkinter- Zeugs in einen extra Thread auslagern. Wie könnte ich meine progressbar auslagern, ohne automatisch einen teil des tkinter- Zeugs auszulagern und ohne dass diese sobald länger rechnende Funktionen abgearbeitet werden einfriert?
Ich hoffe meine Frage ist verständlich.
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Hier ein kurzes Beispiel:

Code: Alles auswählen

try:
    import Tkinter as tk  # Python 2
    import ttk
except ImportError:
    import tkinter as tk  # Python 3
    import tkinter.ttk as ttk

from threading import Thread
from queue import Queue, Empty
import time

def make_progress(queue, maximum, step):
    for i in range(maximum):
        queue.put(1)
        time.sleep(step)

class MainApp(tk.Frame):

    def __init__(self, master, queue, maximum, update_interval):
        self.queue = queue
        self.update_interval = update_interval
        tk.Frame.__init__(self, master)
        self.progress = ttk.Progressbar(self, maximum=maximum)
        self.progress.pack(expand=1, fill=tk.BOTH)
        self.show_progress()

    def show_progress(self):
        try:
            width = self.queue.get(False)
        except Empty:
            return
        else:
            self.progress.step(width)
            self.update()
        finally:
            self.after(self.update_interval, self.show_progress)

if __name__ == '__main__':
    q = Queue()
    m = 10
    u = 1
    s = 2
    t = Thread(target=make_progress, args=(q, m, s))
    t.start()
    root = tk.Tk()
    app = MainApp(root, q, m, u)
    app.pack(expand=1, fill=tk.BOTH)
    root.mainloop()
    t.join()
Es ist bloß auf die Schnelle und ohne viel Nachdenken zusammengehackt.
In specifications, Murphy's Law supersedes Ohm's.
dominik123
User
Beiträge: 35
Registriert: Montag 10. Februar 2014, 08:48

Vielen Dank! Jetzt läuft alles parallel...
Antworten