Problem mit matplotlib und Legende

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
rmanske
User
Beiträge: 26
Registriert: Freitag 30. September 2016, 13:26

Hallo,

in einem Plot zeige ich 4 Graphen an. Von zwei Graphen werden die Werte an der linken Y-Achse angezeigt und von den anderen beiden an der rechten Seite. Jetzt habe ich aber Probleme mit dem anzeigen der Labels in der Legende. Es sollen alle 4 Graphen in einer Legende angezeigt werden. Google hat mir gesagt, dass man sich das "zusammenbasteln" muss, weil nicht nur eine der Y-Achsen sondern beide gleichzeitig verwendet werden. Das zusammenbaseln funktioniert auch soweit.

Die Anzeige der Legende soll sich an- und abschalten lassen. Ist soweit kein Problem.
Zudem soll sich aber die Beschriftung der Graphen, also der Labels, ändern lassen. Eigentlich funktioniert das auch noch.

Nun kommt aber das eigentliche Problem. Wenn ich nach dem ändern der Labels die Anzeige der Legende ab- und wieder anschalte, wird immer die erste Beschriftung der Labels, mit dem die Graphen ursprünglich erstellt wurden, angezeigt und nicht die aktuell geänderte. Es sieht so aus als wenn beim Ändern der Labels nur der Textes der Legende geändert wird und nicht die Label der Graphen selbst.

Kann ich das irgndwie ändern oder muss ich mir die aktuellen Marker irgendwo zwischenspeichern und diese dann anzeigen. Schöner fände ich, wenn ich direkt die Label und nicht nur die Texte ändern könnte.

Mein Code dazu sieht folgendermassen aus.

Hier zum ersten Anzeigen der Legende:

Code: Alles auswählen

def show_legend(self):
        if self.LegendOnOffState:
            handles, labels, = self.axes['left'].get_legend_handles_labels()
            handles2, labels2, = self.axes['right'].get_legend_handles_labels()
            for lab in labels:
                print("leg:", lab )
            for lab in labels2:
                print("leg:", lab )
            self.axes['left'].legend(handles + handles2, labels + labels2,
                                loc='best', shadow=True)
Im nachfolgenden Teil das ändern der Labels:

Code: Alles auswählen

def changelabel(self, labellist):
        handles, labels, = self.axes['left'].get_legend_handles_labels()
        handles2, labels2, = self.axes['right'].get_legend_handles_labels()
        for lab in labels:
                print("ch:", lab )
        for lab in labels2:
                print("ch:", lab )           
        by_label = OrderedDict(zip(labellist, handles + handles2))
        self.axes['left'].legend(by_label.values(), by_label.keys(), loc='best', shadow=True)
Hier zum Abschalten der Legende:

Code: Alles auswählen

def legendonoff(self, state):
        self.LegendOnOffState = state
        self.axes['left'].legend().set_visible(state)
        self.show_legend()
Nach dem Abschalten und anschliessendem erneutem Anzeigen der Legende muss ich self.show_legend() wieder aufrufen, da sonst nur die Legende für die Graphen der linken Achse angezeigt werden. Und dies auch nicht an der Stelle, an der die Legende beim ersten Mal angezeigt wurde.

Die Hilfsanzeigen bitte ignorieren. Darüber habe ich rausgefunden, dass die Label nicht geändert werden sondern nur die Texte der Legende.

Danke schon mal für jeglichen Hinweis.
Zuletzt geändert von Anonymous am Freitag 7. Oktober 2016, 19:47, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@rmanske: um besser helfen zu können, wäre lauffähiger Code sehr hilfreich. So muß man erst Deinen anderen Thread lesen und dort die passenden Klassen herauskopieren und mit dem, was Du hier versuchst kombinieren, um dann drauf zu kommen, dass das eigentliche Problem darin liegt, dass Du in »legendonoff« »show_legend« statt »changelabel« aufrufst. Und warum diese ganze Verwirrung? Weil die Methoden nicht so heißen, wie das, was sie tun. »show_legend« müßte eigentlich heißen »create_and_show_legend_but_only_if_LegendOnOffState_is_True« und »changelabel« müßte heißen »create_and_show_legend_with_custom_labels«.

Dabei haben Deine Graph-Objekte schon Labels, warum änderst Du nicht einfach die?

[Codebox=python file=Unbenannt.py]
class Graph(object):
MARKERS = {
'left': 'o',
'right': 's',
}
def __init__(self, values, label, xaxis='left', linewidth=1):
self.values = values
self._label = label
self.xaxis = xaxis
self.graph = None
self._linewidth = linewidth

def plot(self, axes, color):
if not self.values:
return
self.graph = axes[self.xaxis].plot(self.values,
marker=self.MARKERS[self.xaxis],
label=self.label,
lw=self.linewidth,
color=color)[0]

@property
def linewidth(self):
return self._linewidth

@linewidth.setter
def linewidth(self, linewidth):
self._linewidth = linewidth
if self.graph:
self.graph.set_linewidth(linewidth)

@property
def label(self):
return self._label

@label.setter
def label(self, label):
self._label = label
if self.graph:
self.graph.set_label(label)
[/Codebox]

Dann brauchst Du nur noch eine »create_legend«-Methode, die Du halt nach jeder Label-Änderung aufrufen mußt.

[Codebox=python file=Unbenannt.py]
class PlotBaseClass(object):
def __init__(self, fig, canv, graphs):
self.figure = fig
self.canvas = canv
self.axes = None
self.legend = None
self.graphs = graphs
self._legend_shown = True

def plot(self):
self.axes = {}
self.axes['left'] = self.figure.add_subplot(111)
self.axes['right'] = self.axes['left'].twinx()
for graph, color in zip(graphs, COLORS):
graph.plot(self.axes, color)
self.create_legend()
self.canvas.draw()

def change_linewidth(self, linewidth):
for graph in graphs:
graph.linewidth = linewidth

def create_legend(self):
self.legend = self.axes['left'].legend(
[g.graph for g in self.graphs],
[g.label for g in self.graphs],
loc='best', shadow=True)
self.legend.set_visible(self._legend_shown)

@property
def legend_shown(self):
return self._legend_shown

@legend_shown.setter(self, shown):
self._legend_shown = shown
self.legend.set_visible(self._legend_shown)
[/Codebox]
rmanske
User
Beiträge: 26
Registriert: Freitag 30. September 2016, 13:26

Sirius3 hat geschrieben:@rmanske: um besser helfen zu können, wäre lauffähiger Code sehr hilfreich. So muß man erst Deinen anderen Thread lesen und dort die passenden Klassen herauskopieren und mit dem, was Du hier versuchst kombinieren,
Sorry, dachte der Teil wäre ausreichend zum Verständnis meines Problems. Werde mich bessern.

Den Teil mit dem ändern der Labels hatte ich schon in meinen vorherigen Veruschen, bevor Du mir die Hilfestellung mit den Klassen gegeben hattest. Daher habe ich das in Deine Lösung kopiert. Mein Gedanke war, wenn ich mit "self.axes['left'].legend" die Label ändere, dass diese dann übernommen werden und ich diese dann auch beim nächsten Aufruf von "get_legend_handles_labels()" erhalte. Der Gedanke war wohl falsch.

Die Legende neuaufbauen musste ich deshalb, weil nach dem set_visible(False) und anschliessendem set_visible(True) diese an ganz anderer Position wieder angezeigt wurde. Es war auch egal ob ich loc=best, loc=upper oder loc=sonstwo gemacht hatte. Daher kam mir dann der Gedanke, dass ich diese dann neu Anzeigen muss.
Sirius3 hat geschrieben: Dabei haben Deine Graph-Objekte schon Labels, warum änderst Du nicht einfach die?
Habe ich jetzt gemacht.

Code: Alles auswählen

[g.graph for g in self.graphs],
[g.label for g in self.graphs]
Das ist natürlich die elegantere Lösung, wäre ich selber nicht drauf gekommen.

Danke für Deine Hilfe.
Antworten