GUI Subplots erneuern

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
Nick94
User
Beiträge: 10
Registriert: Dienstag 16. Juni 2020, 14:14

Hallo Zusammen,

ich habe mit dem Qt Designer eine GUI erstellt und in dieser ein Matplotlib Widget eingebunden.

Mit der Tastatur kann ich dann die Skalierung der Kurven interaktiv ändern.

Mein Problem ist, dass ich die Linien nicht einzeln ändern kann, wenn diese in einem einzigen Subplot sind.
Die Y-Achse wird geändert aber der Bezug zur Linie fehlt.
Da ich zu beginn nicht weiß, wie viele Plots der Anwender machen möchte kann ich nicht ax1, ax2... schreiben und muss es in einer for-schleife machen.

Könnt ihr mir da vllt. helfen oder einen Tipp geben?

So erstelle ich meinen Plot:

Code: Alles auswählen

        farben=['b', 'g', 'r', 'c', 'm', 'y', 'k', 'orange', 'purple', 'saddlebrown']
        ax1=ax2=ax3=ax4=ax5=ax6=ax7=ax8=ax9=ax10=0
        var=[ax1,ax2,ax3,ax4,ax5,ax6,ax7]
        subplots=[]
        
        y_max = 0
        y_min = 0
        
        for i in range(0,anzahl_plot):
            subplots.append(var[i])
        
        for i in range(0,anzahl_plot):
            if(max(spalten[y_achse[i]])>y_max):
                y_max = max(spalten[y_achse[i]])
                
        subplots[0] = object.MplWidget.canvas.axes = object.MplWidget.canvas.figure.add_subplot(111)
        object.MplWidget.canvas.axes.plot(spalten[x_achse[0]],spalten[y_achse[0]], label = spalten_namen[1], color = farben[0])
        subplots[0].set_ylim(y_min,y_max)
        
        for i in range(1,anzahl_plot):
            subplots[i]=subplots[0].twinx()#x-achse wird geteilt und neue y-achse wird erzeugt
            subplots[i] = object.MplWidget.canvas.axes.plot(spalten[x_achse[i]],spalten[y_achse[i]], label = spalten_namen[i+1], color = farben[i])
        object.MplWidget.canvas.axes.legend(loc='upper right')
        object.MplWidget.canvas.draw()
Und so ändere ich die Skalierung:

Code: Alles auswählen

def on_press(key):
    linien = window.MplWidget.canvas.figure.get_axes()
    if(window.radioButton_Ch1.isChecked()):
        x=0
    elif(window.radioButton_Ch2.isChecked()):
        x=1
    elif(window.radioButton_Ch3.isChecked()):
        x=2
    elif(window.radioButton_Ch4.isChecked()):
        x=3
    elif(window.radioButton_Ch5.isChecked()):
        x=4
    elif(window.radioButton_Ch6.isChecked()):
        x=5
    elif(window.radioButton_Ch7.isChecked()):
        x=6
    elif(window.radioButton_Ch8.isChecked()):
        x=7
    window.MplWidget.canvas.axes = linien[x]
    
    delta = max(window.MplWidget.canvas.axes.get_xlim()) - min(window.MplWidget.canvas.axes.get_xlim())
    step = delta/10
    pos_min, pos_max = window.MplWidget.canvas.axes.get_xlim()
    delta_y = max(window.MplWidget.canvas.axes.get_ylim()) - min(window.MplWidget.canvas.axes.get_ylim())
    step_y = delta_y/10
    pos_min_y, pos_max_y = window.MplWidget.canvas.axes.get_ylim()
    
    if(key == Key.f1):    
        cur_ylim = window.MplWidget.canvas.axes.get_ylim()
        print(cur_ylim)
        scale_factor = 1/1.1
        new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor
        ydata = (cur_ylim[1] - cur_ylim[0])/2
        rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])
        window.MplWidget.canvas.axes.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
        window.MplWidget.canvas.draw_idle()

    if(key == Key.f2)
        cur_ylim = window.MplWidget.canvas.axes.get_ylim()
        scale_factor = 1.1
        new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor
        ydata = (cur_ylim[1] - cur_ylim[0])/2
        rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])
        window.MplWidget.canvas.axes.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
        window.MplWidget.canvas.draw()
Wäre für jede Hilfe dankbar.
Sirius3
User
Beiträge: 18220
Registriert: Sonntag 21. Oktober 2012, 17:20

Was ist der Sinn dahinter, ax1 bis ax10 mit 0 zu initialisieren, und dann von diesen Nullen 7 davon in eine Liste `var` zu packen, um die dann später kompliziert über eine for-Schleife mit Index in die Liste `subplots` zu packen? Später überschreibst Du dann diese Nullen mit anderen Werten. Warum erzeugst Du die Liste subplots nicht gleich mit den richtigen Werten und läßt den ganzen Quatsch am Anfang einfach weg?
Die Klammern um die if-Bedingungen sind allesamt unnötig und gehören weg.
`object` ist der eingebaute Objekt-Typ und sollte nicht durch irgendetwas anderes überschrieben werden.
Warum greifst Du mal über subplots[0] und mal über object.MplWidget.canvas.axes auf diesen ersten Subplot zu? Zweiteres sieht mir dank der Verschachtelung falsch aus.
Das ganze mal aufgeräumt:

Code: Alles auswählen

canvas = object.MplWidget.canvas # woher kommt diese object? sollte das etwas self sein?
farben = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'orange', 'purple', 'saddlebrown']

y_min = 0
y_max = max(max(spalten[y] for y in y_achse))

subplots = []
for x, y, farbe, spalten_name in zip(x_achse, y_achse, farben, spalten_namen[1:]):
    if not subplots:
        subplot = canvas.figure.add_subplot(111)
        subplot.set_ylim(y_min, y_max)
    else:
        subplot = subplots[0].twinx() # x-achse wird geteilt und neue y-achse wird erzeugt
    subplot.plot(spalten[x],spalten[y], label=spalten_name, color=farbe)
    subplots.append(subplot)
canvas.axes.legend(loc='upper right')
canvas.draw()
Bei diesem on_keypress fehlt mir auch irgendwie ein `self`, das man ja bei einem GUI-Fenster normalerweise hat. Heißt hier irgendwie `window` und kommt aus dem nichts.
Während ich beim ersten Code-Schnippsel noch denken konnte, das wurde irgendwie in dem Teil der Methode gelöst, den ich nicht sehe, scheint es sich hier um globale Variablen zu handeln.
Bevor Du also mit GUI und Matplotlib weiter machst, lerne erst einmal Klassendefinitionen, wiederhole nochmal, wie man richtige for-Schleifen programmiert und wie wirkliche Funktionen aufgebaut sind. Hört sich jetzt hart an, aber ohne diese Grundlagen wirst Du hier mit Deinem Projekt nicht weiter kommen.
Die Radio-Buttons gehören in eine Liste. In den beiden key-if-Blöcken steht bis auf scale_factor identischer Code, das kann also zusammengefasst werden.
Benutzeravatar
__blackjack__
User
Beiträge: 13927
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Statt in eine Liste, würde ich die Radiobuttons in eine `QButtonGroup` stecken, denn dann braucht man die nicht alle durchgehen, um den zu finden der ausgewählt ist, sondern kann einfach die Gruppe nach einer selbst vergebenen, numerischen ID des ausgewählten Radiobuttons fragen. Also nur noch ``x = self.christmas_button_group.checkedId()``.

Eventuell noch eine Abfrage danach, falls es möglich ist, dass gar keiner der Radiobuttons ausgewählt ist. Dann hat `x` den Wert -1.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
Nick94
User
Beiträge: 10
Registriert: Dienstag 16. Juni 2020, 14:14

Vielen Dank für die schnellen Antworten :)

Ich muss zugeben, dass es am Anfang ein wenig chaotisch ist.
Das 'Object' bzw. 'window' kommt daher, dass ich den Programmteil in einer separaten Funktion geschrieben habe.

Klammern in der if-Bedingung ist doch nicht falsch und kann man machen oder nicht?

Aber wahrscheinlich habt ihr recht, dass ich mich nochmal ein bisschen mehr mit den Grundlagen auseinander setzen muss... :roll:

Den Vorschlag mit der Liste bzw. der 'QButtonGroup' finde ich super, und werde ich versuchen umzusetzen.
Sirius3
User
Beiträge: 18220
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn du globale Varianten benutzt, hast du keine Funktionen geschrieben, sondern nur Sprungmarken wie es sie in BASIC gab.
Überflüssige Klammern kann man an vielen Stellen hinschreiben. In den meisten Fällen erschweren sie das Lesen und sind damit potentielle Fehlerquellen und damit falsch. In wenigen Fällen tragen sie zur Lesbarkeit bei.
Antworten