Forum: PC-Programmierung Python Plot in TKinter aktualisieren


von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Hallo, ich bin Neuling in Sachen GUI, aber ich habe es schon fast 
geschafft (siehe Bildchen).

Was will ich machen/was habe ich?

Ich habe eine Hardware der ich über UART Befehle schicken kann und auch 
mit einem Befehl Samples eines ADCs lesen kann. Das klappt auch alles 
fein aus der GUI. Jetzt will ich die Samples auch plotten. Nicht nur 
einmal, sondern oft. Das wiederholt sich also, es werden neue Samples 
gelesen und diese sollen wieder geplottet werden.

Für die GUI habe ich TKinter verwendet, das ist ganz brauchbar wie ich 
finde. Darin habe ich dann eine Funktion aufgemacht die sich selbst 
zylisch wieder aufruft.
1
def task_plot():
2
#...
3
   root.after(1000,task_plot)
Sie wird einmalig gestartet mit
1
root.after(100,task_plot)
Das geht auch.

In dieser Funktion habe ich dann die Plotfunktion drinnen
1
fig = plt.figure()
2
ax1 = fig.add_subplot(211)
3
ax2 = fig.add_subplot(212)
Und es wird ein Canvas aufgemacht
1
canvas = FigureCanvasTkAgg(fig, master = root)
2
canvas._tkcanvas.place(x=100,y=10)

Das funktioniert auch, die neuen Daten werden gelesen, sie werden neu 
geplottet und schön angezeigt. Aber: Der RAM läuft schnell voll.
Klar, kann ich auch verstehen, das ist ja nach meinem Verständniss eine 
Funktion und immer wenn die neu aufgerufen wird, werden die Variablen 
lokal neu erzeugt und nicht nur mit neuen Inhalten gefüllt.

Tja ... wie macht man das richtig?

Den ganzen Code habe ich in den Anhang gepackt, da sind bestimmt viele 
unschöne Anfängerdinge drinnen. Die Serielle Schnittstelle ist dort 
leider nötig um Daten zu bekommen.

Vielen Dank!

von Sven B. (scummos)


Lesenswert?

Ja, also tatsächlich kenne ich mehrere Parteien inkl. mir die matplotlib 
für Echtzeit-Visualisierung benutzt haben und keine davon ist so 
wirklich glücklich geworden, aus Gründen wie dem von dir beschriebenen. 
Die Bibliothek ist einfach nicht dafür gedacht ...

Ich glaube, man muss da was anderes nehmen als matplotlib. So einen 
richtig guten Vorschlag hab ich allerdings auch nicht.

von Sheeva P. (sheevaplug)


Lesenswert?

Gustl B. schrieb:
> Hallo, ich bin Neuling in Sachen GUI, aber ich habe es schon fast
> geschafft (siehe Bildchen).

Das ist ja schonmal nicht schlecht für einen Anfänger. ;-)

> Den ganzen Code habe ich in den Anhang gepackt, da sind bestimmt viele
> unschöne Anfängerdinge drinnen.

Das Schlüsselwort "global" dient in Python dazu, eine globale Variable 
aus einem anderen Scope heraus anzulegen, zum Beispiel aus einer 
Funktion:
1
#!/usr/bin/env python
2
import traceback
3
4
def create_globals():
5
    global a, b
6
    a = 5
7
    b = 'hello'
8
9
def create_vars():
10
    return 5, 'hello'
11
    
12
13
if __name__ == '__main__':
14
    
15
    try: print('a =', a)
16
    except: traceback.print_exc()
17
        
18
    try: print('b =', b)
19
    except: traceback.print_exc()
20
21
    print("before call")
22
    create_globals()
23
    print("after call")
24
25
    print('a =', a)
26
    print('b =', b)
27
28
    c, d = create_vars()
29
    print('c =', c)
30
    print('d =', d)

Zudem würde ich für eine solche Applikation auf Matplotlib verzichten, 
die Library ist zum einmaligen Plotten gedacht und eignet sich daher 
nicht gut für Visualisierungen, die regelmäßig aktualisiert werden 
sollen. Dafür ist Tkinters Canvas besser geeignet -- oder vielleicht 
eine kleine Webanwendung mit einer Javascript-Plottinglibrary wie flot, 
jqWidgets oder jqplot.

von Gustl B. (-gb-)


Lesenswert?

OK, keine Matplotlib. Kann dieses Tkinters Canvas auch plotten?
Wie man globale Variablen anlegt weiß ich. Sollte ich alles global 
machen? Also auch fig und ax und den ganzen Rest?

von Sheeva P. (sheevaplug)


Lesenswert?

Sheeva P. schrieb:
> [Dinge]

Ach ja, noch so eine kleine Anregung: man kann Tkinter-Widgets natürlich 
auch objektorientiert benutzen, also einfach von Tkinter.Tk erben. Da 
Tkinter noch auf "old-style"-Klassen basiert, muß man dabei aber auf 
super() verzichten und den Konstruktor der Parent-Klasse namentlich 
aufrufen:
1
#!/usr/bin/env python
2
import Tix as T
3
4
class MainWin(T.Tk):
5
    def __init__(self, *args, **kwargs):
6
        T.Tk.__init__(self, *args, **kwargs)
7
8
        self.btn = T.Button(self, text="Exit", command=self.destroy)
9
        self.btn.pack(side=T.TOP)
10
        # oder in diesem speziellen Fall:
11
        #T.Button(self, text="Exit", command=self.destroy).pack(side=T.TOP)
12
13
if __name__ == '__main__':
14
    MainWin().mainloop()

Ich persönlich finde das wesentlich übersichtlicher.

von Sheeva P. (sheevaplug)


Lesenswert?

Gustl B. schrieb:
> OK, keine Matplotlib. Kann dieses Tkinters Canvas auch plotten?
> Wie man globale Variablen anlegt weiß ich.

Nein, eben nicht. Dieses ganze "global" auf der obersten Ebene kannst Du 
Dir komplett sparen!

> Sollte ich alles global machen? Also auch fig und ax und den ganzen Rest?

Lieber nicht. Ich arbeite seit mittlerweile über zehn Jahren fast 
täglich mit Python, und habe das Schlüsselwort "global" in dieser ganzen 
Zeit noch nicht wirklich gebraucht. Arbeite lieber mit Parametern und 
Rückgabewerten.

von Gustl B. (-gb-)


Lesenswert?

So, habe es jetzt mit pylab.figure gelöst und es funktioniert genau so 
wie es soll.

von CC (Gast)


Lesenswert?

Auch wenn es nicht direkt eine Antwort auf die eigentliche Frage ist, 
aber wie fixiert bist Du auf Python? Für QT/C++ gibt es beispielsweise 
qcustomplot, wobei es scheinbar recht aktuell auch etwas QT-"natives" 
gibt (Qt Charts, genutzt habe ich das nicht).

Es gibt ja auch irgendwie PyQt, eventuell lässt sich das damit 
verbinden? Wirft natürlich die Tk UI wieder über den Haufen...

von CC (Gast)


Lesenswert?

Nachtrag: https://www.riverbankcomputing.com/software/pyqtchart/intro
kann man auch von Python aus nutzen, wie es scheint...

von Gustl B. (-gb-)


Lesenswert?

Also ich kann auch C, aber in Python ist halt schon echt viel Zeug 
dabei. Ich will mir z. B. auch die FFT plotten lassen und mit Python 
geht das echt einfach. Ich weiß nicht wie ich da in C herangehen würde.

Qt habe ich auch gesehen, habe aber diese TkInter verwendet weil es bei 
Python dabei ist. Ich will möglichst wenig Abhängigkeiten haben.

von Sven B. (scummos)


Lesenswert?

Ne, wenn du in C einen Plot mit einer FFT drin haben willst, ist das der 
Tod. Biste drei Tage beschäftigt. Ich hab's dann am Ende so gemacht, 
aber wirklich zufrieden bin ich nicht, in Python wäre das alles 
einfacher und viel weniger Aufwand. Naja.

von Le X. (lex_91)


Lesenswert?

Ist immer nur eine Frage der Zeit bis auf eine Frage der Art "Wie löse 
ich Problem X in Sprache Y?" eine Antwort der Art "Schmeiß dein 
bisheriges Programm weg und nimm Sprache Y!" kommt.
Hauptsache seinen Senf dazugeben.

von Gustl B. (-gb-)


Lesenswert?

Ein Update der Vollständigkeit halber:

Das wird verwendet:
1
import pylab
2
from pylab import *
3
import tkinter
4
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

Das definiert das Fenster des Programms:
1
root = tkinter.Tk()
2
root.wm_title("Programmname")
3
root.minsize(width=1280, height=640)
4
root.maxsize(width=1280, height=640)

Das macht den Plot auf und definiert dessen Größe:
1
fig = pylab.figure(num=None, figsize=(16, 8), dpi=80, facecolor='w', edgecolor='k')
2
plt.subplots_adjust(left=0.05, bottom=0.1, right=0.98, top=0.96, wspace=0, hspace=0.1)
3
ax1 = fig.add_subplot(111)
4
5
canvas = FigureCanvasTkAgg(fig, master=root)
6
canvas.draw()
7
canvas.get_tk_widget().place(x=0,y=0)
8
canvas._tkcanvas.place(x=0,y=0)


Und das hier kommt in die Schleife. Daten ist dabei das Array das sich 
jeweils ändert und neu geplottet wird.
1
def Plotter():
2
  ax1.clear()
3
  ax1.set_xlim(xmin = 0, xmax = x-Max)
4
  ax1.set_ylim(ymin = 0, ymax = y-Max)
5
  ax1.set_title("Title")
6
  ax1.set_ylabel("y-Label")
7
  ax1.set_xlabel("x-Label")
8
  ax1.plot(Daten)
9
  canvas.draw()

von Frank K. (logisch_0)


Lesenswert?

Hallo,
noch eine Frage zur Struktur :
----------
Die Importe sind klar die stehen am Anfang des Programms.
----------
Tkinter ist Deine GUI und hat einen Mainloop.

root = tkinter.Tk()
root.wm_title("Programmname")
root.minsize(width=1280, height=640)
root.maxsize(width=1280, height=640)


root.mainloop()
-----------
Steht folgendes im Mainloop ? :

fig = pylab.figure(num=None, figsize=(16, 8), dpi=80, facecolor='w', 
edgecolor='k')
plt.subplots_adjust(left=0.05, bottom=0.1, right=0.98, top=0.96, 
wspace=0, hspace=0.1)
ax1 = fig.add_subplot(111)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().place(x=0,y=0)
canvas._tkcanvas.place(x=0,y=0)
-----------
Rufst Du dann die Funktion Plotter aus Deinem Mainloop auf und lässt die 
Funktion immer neu starten wie
zum Beispiel mit Plotter.after(1000,Plotter) ?

Danke für die Hilfe !

von Gustl B. (-gb-)


Angehängte Dateien:

Lesenswert?

Nur der Plotter ist in der Schleife. Ich lade mal das ganze Skript hoch. 
Das hat noch mehrere Funktionen. Ganz grob:
Das fragt über UART am FPGA an, bekommt Sampledaten, die werden dann 
jeweils ans Sampledatenarray angehängt und der älteste Wert entfernt. 
Die Arrays werden dann geplottet.

von Frank K. (logisch_0)


Lesenswert?

Danke Gustl,
bei mir war der Aufruf vom Backend in der Schleife. Nachdem der da raus 
war ist mein RAM nicht mehr angewachsen.

von imonbln (Gast)


Lesenswert?

> LTC2372_0_data = [0] * 1024
 > LTC2372_1_data = [0] * 1024
 > LTC2372_2_data = [0] * 1024
 > LTC2372_3_data = [0] * 1024
 > LTC2372_4_data = [0] * 1024
 > LTC2372_5_data = [0] * 1024
 > LTC2372_6_data = [0] * 1024
 > LTC2372_7_data = [0] * 1024

Sowas schreit doch gerade zu nach einer list() und Behandlung in 
Schleifen. Beim Programmieren ist unbedingt auf das Dry Prinzip zu 
achten. Außerdem suggeriert Uppercase erfahrenden Entwicklern das es 
sich um eine Konstante handelt.

von Gustl B. (-gb-)


Lesenswert?

Natürlich, das ist nur zum Testen der Hardware gedacht und von älteren 
Skripten kopiert. Bevor ist da mit list anfange habe ich das mehrmals 
kopiert.

imonbln schrieb:
> Außerdem suggeriert Uppercase erfahrenden Entwicklern das es
> sich um eine Konstante handelt.

Ist das so? Ja, sieht man oft, stimmt, aber ist das irgendwo so 
definiert?

von imonbln (Gast)


Lesenswert?

Gustl B. schrieb:
> Ist das so? Ja, sieht man oft, stimmt, aber ist das irgendwo so
> definiert?

Für Python in pep8 siehe 
https://www.python.org/dev/peps/pep-0008/#constants. Aber noch viel mehr 
ist es eine Art gutes Benehmen, ähnlich wie man grüßt wenn mein einen 
Raum betritt, es ist einfach eine Höflichkeit die dafür sorgt das andere 
sich in den eigen Sourcecode wohlfühlen.

von Gustl B. (-gb-)


Lesenswert?

Kannte ich noch nicht. Nun, das ist write-only Code. Den habe ich hier 
nur hochgeladen damit nachvollziehbar wird wie ich mein Problem gelöst 
habe.

Aber werde ich in Zukunft besser machen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.