Python Plot in TKinter aktualisieren

#5318832
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!
Angehängte Dateien:
#5318849
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.
#5318860
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.
#5318874
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.
#5318876
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.
Gast #5319341
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...
#5319466
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.
#6344517
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()
#6344549
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 !
Gast #6345484
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.
Gast #6345941
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren