Seite 1 von 1
Tkinter in einem weiteren Modul
Verfasst: Sonntag 2. Dezember 2018, 19:25
von henry06
Hallo,
ich habe ein Programm erstellt, welches via Bluetooth Daten von einem Sensor ausliest und mit diesen Berechnungen durchführt.
Jetzt möchte ich ein weiteres Programm erstellen, welches diese in einer grafischen Oberfläche darstellt. Dazu habe ich folgendes erstellt:
gui.py:
Code: Alles auswählen
import tkinter as tk
import threading
from Main import i
class App(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.start()
def callback(self):
self.root.quit()
def run(self, i):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.callback)
w = tk.Scale(self.root, from_=0, to=200)
w.pack()
w.set(i)
self.root.mainloop()
app = App()
in der main.py sollen jetzt Berechnungen durchgeführt werden und dies ein der GUI visualisiert werden. Darüber wird auch alles gestartet:
main.py:
Code: Alles auswählen
import gui
import time
i = 1
def counter():
global i
while i < 20:
i = i + 1
time.sleep(1)
print(i)
counter()
Jetzt läuft nur der Timer ab, aber die GUI wird erst gestartet, wenn i = 20 ist und danach läuft der Timer erneut ab, aber die GUI aktualisiert sich nicht.
Ich hätte gerne, dass diese laufend aktualisert wird und sich gleich zu beginn startet. Wo ist mein Denkfehler?
Danke und Gruß
Re: Tkinter in einem weiteren Modul
Verfasst: Sonntag 2. Dezember 2018, 19:34
von __deets__
Der erste Denkfehler besteht darin, daass du planst die GUI in einem weiteren thread zu starten. Das geht nicht. Das MUSS der Maintgread sein, aus technischen Gründen. Der zweite besteht darin das du denst, da würde irgendwie magisch ein i in der run-Methode deines Threads auftauchen.
Die Lösung für dein Problem sind entweder Timer oder GUI-Variablen in tkinter. Erstere kannst du mit der after-Methode starten, und darin zB periodisch diene Abfrage &Berechnung einfach sofort durchführen. Zweitere kannst du benutzen um Werte in zB einem Label darstellen zu lassen, und den Wert aus einem Thread der die BT Kommunikation macht zu setzen.
Re: Tkinter in einem weiteren Modul
Verfasst: Sonntag 2. Dezember 2018, 20:07
von henry06
Danke für die Antwort. Das hilft mir weiter!
Ich habe ein wenig recherchiert und nun funktioniert ist. Ist das jetzt der richtige weg? Ein Problem habe ich noch. Ich kann das Programm nicht mehr abbrechen, da dies nun durch die update Schleife geblockt wird. Ist das jetzt der richtige weg? Gruß
Code: Alles auswählen
import tkinter as tk
import threading
import time
class App(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.callback)
w = tk.Scale(self.root, from_=0, to=200)
w.pack()
while True:
w.set(i)
self.root.update()
self.root.mainloop()
app = App()
print("Weiter gehts")
i = 0
def calculator():
global i
while i < 50:
i = i + 1
time.sleep(1)
print(i)
calculator()
Re: Tkinter in einem weiteren Modul
Verfasst: Sonntag 2. Dezember 2018, 20:25
von __deets__
Nein, das ist immer noch der komplett falsche Weg. Die GUI im Thread ist so nicht richtig. Die Dauerschleife mit Update ist so nicht richtig, da sie sowohl zu viel CPU Zeit verbrät, als auch eine nicht empfohlenes Vorgehen darstellt.
Re: Tkinter in einem weiteren Modul
Verfasst: Sonntag 2. Dezember 2018, 20:31
von __deets__
https://wiki.tcl-lang.org/page/Update+c ... ed+harmful
Das ist aus der Dokumentation zu der unterliegenden Technologie für die GUI.
Re: Tkinter in einem weiteren Modul
Verfasst: Sonntag 2. Dezember 2018, 20:59
von henry06
Okay vielen Dank nochmal!
1) keine Schleife
2) GUI nicht in einem Thread
3) mit after den Wert in der GUI neu setzen
Das probiere ich morgen mal aus.
Gruß,
Henry
Re: Tkinter in einem weiteren Modul
Verfasst: Montag 3. Dezember 2018, 18:41
von henry06
Hallo,
ich habe nun viele verschiedene Dinge ausprobiert und komme leider zu keinem Ergebnis.
Ich habe es jetzt wie folgt zum laufen gebracht, mit einer Auslastung von 70%. Mein Mainthread Listener Empfängt Werte via Bluetooth und übergibt diese an einen Rechner (Calculator). Die GUI soll anhand der berechneten Werte aktualisiert werden. Recherchen zum Thema after haben mich leider nicht zum Erfolg gebracht. Hat noch jemand neue Ideen/Anreize oder Beispiele wie ich das ganze sauber realisieren kann?
Code: Alles auswählen
import myo
from _thread import start_new_thread
import tkinter
import time
gyr_y = 0
def gui():
main = tkinter.Tk()
w = tkinter.Scale(main, from_=0, to=100)
w.pack()
LOOP_ACTIVE = True
while LOOP_ACTIVE == True:
w.set(gyr_y)
main.update()
time.sleep(0.1)
def calculator(gyr):
global gyr_y
gyr_y += gyr.x * 0.02
class Listener(myo.DeviceListener):
def on_paired(self, event):
print("Guten Tag, {}!".format(event.device_name))
event.device.vibrate(myo.VibrationType.short)
start_new_thread(gui,())
def on_unpaired(self, event):
return False # Stop the hub
def on_orientation(self, event):
orientation = event.orientation
acceleration = event.acceleration
gyroscope = event.gyroscope
print(gyroscope.x)
calculator(gyroscope)
if __name__ == '__main__':
myo.init(sdk_path='C:/Python_SDKs/myo-sdk-win-0.9.0/')
hub = myo.Hub()
listener = Listener()
while hub.run(listener.on_event, 500):
pass
Re: Tkinter in einem weiteren Modul
Verfasst: Montag 3. Dezember 2018, 18:43
von __deets__
Da du inzwischen zum dritten mal deine GUI in einem extra Thread startest, immer noch update verwendest, und damit alles ignorierst, was ich dir bis dato erzaehlt habe, bin ich raus. Viel Erfolg!
Re: Tkinter in einem weiteren Modul
Verfasst: Montag 3. Dezember 2018, 19:07
von henry06
Hallo,
deine Ansätze habe ich auch schon verfolgt. Ich werde diese morgen noch einmal raussuchen und hochladen.
Gruß Henry
Re: Tkinter in einem weiteren Modul
Verfasst: Montag 3. Dezember 2018, 19:15
von Sirius3
der Unterstrich bei `_thread` soll Dir sagen, dass das Modul nur für interne Zwecke verwendet werden soll. Jede zweite Zeile eine Leerzeile, macht den Code so gut wie unlesbar.
So sollte das aussehen:
Code: Alles auswählen
import myo
import tkinter as tk
from queue import Queue, Empty
class Listener(myo.DeviceListener):
def __init__(self, queue):
super().__init__()
self.queue = queue
def on_paired(self, event):
print("Guten Tag, {}!".format(event.device_name))
event.device.vibrate(myo.VibrationType.short)
def on_unpaired(self, event):
return False # Stop the hub
def on_orientation(self, event):
gyroscope = event.gyroscope
print(gyroscope.x)
queue.put(gyroscope.x)
class Gui(tk.Tk):
def __init__(self, queue):
super().__init__()
self.scale = tk.Scale(main, from_=0, to=100)
self.scale.pack()
self.queue = queue
self.gyr_y = 0
self.after(500, self.loop)
def loop(self):
try:
while True:
v = self.queue.get_nowait()
self.gyr_y += v * 0.02
self.scale.set(self.gyr_y)
except Empty:
pass
self.after(500, self.loop)
def main():
myo.init(sdk_path='C:/Python_SDKs/myo-sdk-win-0.9.0/')
hub = myo.Hub()
queue = Queue()
gui = Gui(queue)
listener = Listener(queue)
with hub.run_in_background(listener.on_event):
gui.mainloop()
if __name__ == '__main__':
main()
Re: Tkinter in einem weiteren Modul
Verfasst: Dienstag 4. Dezember 2018, 18:46
von henry06
Hallo,
ich habe es jetzt mit der After-Methode selber hinbekommen:
Code: Alles auswählen
import myo
import tkinter
gyr_x = 0
root = tkinter.Tk()
w = tkinter.Scale(root, from_=-100, to=100)
w.pack()
def requery():
w.set(gyr_x)
root.after(1, requery)
def calculator(gyr):
global gyr_x
gyr_x += gyr.x * 0.02
print(gyr_x)
class Listener(myo.DeviceListener):
def on_paired(self, event):
print("Guten Tag, {}!".format(event.device_name))
event.device.vibrate(myo.VibrationType.short)
def on_unpaired(self, event):
return False # Stop the hub
def on_orientation(self, event):
gyroscope = event.gyroscope
calculator(gyroscope)
def main():
myo.init(sdk_path='C:/Python_SDKs/myo-sdk-win-0.9.0/')
hub = myo.Hub()
listener = Listener()
with hub.run_in_background(listener.on_event, 500):
root.after(1000, requery)
root.mainloop()
if __name__ == '__main__':
main()
Danke auch dir Sirius, denn ohne das ändern von
auf
hat es nicht funktioniert. Warum weiß ich nicht. Dein Programm werde ich mir morgen einmal ansehen, aber ich habe es lieber selber ausprobiert, da ich es noch weiter entwickeln will und viele deiner verwendeten Funktionen mir fremd sind (queue, super() etc.)
Nun habe ich versucht, es mithilfe einer Klasse zu realisieren, aber das funktioniert noch nicht ganz, weil ich auf keine Werte zugreifen kann, aber da arbeite ich mich erstmal rein. Danke