Forum: PC-Programmierung Python Plot in TKinter aktualisieren


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Gustl B. (-gb-)


Angehängte Dateien:

Bewertung
1 lesenswert
nicht 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.
def task_plot():
#...
   root.after(1000,task_plot)
Sie wird einmalig gestartet mit
root.after(100,task_plot)
Das geht auch.

In dieser Funktion habe ich dann die Plotfunktion drinnen
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
Und es wird ein Canvas aufgemacht
canvas = FigureCanvasTkAgg(fig, master = root)
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)


Bewertung
1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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:
#!/usr/bin/env python
import traceback

def create_globals():
    global a, b
    a = 5
    b = 'hello'

def create_vars():
    return 5, 'hello'
    

if __name__ == '__main__':
    
    try: print('a =', a)
    except: traceback.print_exc()
        
    try: print('b =', b)
    except: traceback.print_exc()

    print("before call")
    create_globals()
    print("after call")

    print('a =', a)
    print('b =', b)

    c, d = create_vars()
    print('c =', c)
    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-)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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:
#!/usr/bin/env python
import Tix as T

class MainWin(T.Tk):
    def __init__(self, *args, **kwargs):
        T.Tk.__init__(self, *args, **kwargs)

        self.btn = T.Button(self, text="Exit", command=self.destroy)
        self.btn.pack(side=T.TOP)
        # oder in diesem speziellen Fall:
        #T.Button(self, text="Exit", command=self.destroy).pack(side=T.TOP)

if __name__ == '__main__':
    MainWin().mainloop()

Ich persönlich finde das wesentlich übersichtlicher.

von Sheeva P. (sheevaplug)


Bewertung
0 lesenswert
nicht 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-)


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

von CC (Gast)


Bewertung
-2 lesenswert
nicht 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)


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

von Gustl B. (-gb-)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
2 lesenswert
nicht 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-)


Bewertung
0 lesenswert
nicht lesenswert
Ein Update der Vollständigkeit halber:

Das wird verwendet:
import pylab
from pylab import *
import tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

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

Das macht den Plot auf und definiert dessen Größe:
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)


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

von Frank K. (logisch_0)


Bewertung
0 lesenswert
nicht 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:

Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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-)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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-)


Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.