Matplotlib: line style je nach Zoomstufe ändern

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
Bernhard_42
User
Beiträge: 4
Registriert: Freitag 16. Dezember 2011, 15:03

Hallo zusammen!

Ich würde gerne in einem pyplot dynamisch den Linienstil ändern. Anfänglich soll also die Linie gezeichnet werden, wie es im oberen subplot dargestellt ist und wenn noch weniger als eine Periode des Sinus zu sehen ist, dann möchte ich den unteren Linienstil erhalten.

Wo und wie kann ich die entsprechenden Zoom Messages abfangen und wie ändere ich dann den Linienstil?

Herzlichen Dank für die Hilfe
Bernhard

Code: Alles auswählen

import matplotlib.pyplot as plt
from pylab import sin, arange, pi, show

x = arange(0.0, 10.0, 0.01)
y = sin(2*pi*x)

f = plt.figure()
p1 = f.add_subplot(211)
p1.plot(x, y)
p2 = f.add_subplot(212, sharex=p1)
p2.plot(x, y, '+-')

show()
Rekrul
User
Beiträge: 78
Registriert: Dienstag 7. Dezember 2010, 16:23

So kannst du den Liniestil und andere Attribute verändern:

Code: Alles auswählen

lines = plot(x,y)
lines[0].set_linestyle(':')
Das Abfangen des Zoom-Events wird etwas komplizierter. Du kannst die Mausevents abfangen (am besten das hier durchlesen) und dann die neuen x-Limits und y-Limits abfragen. (Irgendwie lässt sich sicherlich auch ermitteln in welchem Modus du bist: normal, pan; zoom etc.)

Eine andere Möglichkeit ist deine eigene Toolbar (backendabhängig) mit eigener Zoom-Funktion zu implementieren. (kleines Beispiel).
Bernhard_42
User
Beiträge: 4
Registriert: Freitag 16. Dezember 2011, 15:03

Hallo Rekrul,

danke für den Tip. Es ist nun folgendes Script entstanden:

Code: Alles auswählen

import matplotlib.pyplot as plt
from pylab import sin, arange, pi, show

def OnDraw( event ):
    if p1.get_xlim()[1] - p2.get_xlim()[0] < 1:
        p1Line.set_marker('+')
    else:
        p1Line.set_marker('')

x = arange(0.0, 10.0, 0.01)
y = sin(2*pi*x)

f = plt.figure()
p1 = f.add_subplot(211)
p1Line, = p1.plot(x, y)
p2 = f.add_subplot(212, sharex=p1)
p2.plot(x, y, '+-')

f.canvas.mpl_connect('draw_event', OnDraw)
show()
Anmerkungen dazu:
- Die 'resize_event' gehörige Funktion wurde bei mir nicht ausgeführt. Daher bin ich auf 'draw_event' ausgewichen.
- Es ist scheinbar nicht möglich über set_linestyle die Marker zu ändern. Ich hatte mich immer gewundert, warum meine Linie ganz verschwindet. Wenn ich mit plot zeichne funktioniert dies nämlich.
- Leider werden die Marker oder eine Linienstiländerung erst dann übernommen, wenn das ganze Fenster neu gezeichnet wird. Ich dachte zuerst mit canvas.draw Abhilfe schaffen zu können. Dies funktioniert wohl nicht, weil sich dann OnDraw dauernd selbst aufrufen würde. Gibt es hier eine Möglichkeit erst meine Änderungen einzupflegen und danach zu zeichnen (pre_draw_event)?
- Die Idee mit der eigenen Toolbar gefällt mir. Aus zeitlichen Gründen muß dies aber hinten angestellt werde.

Viele Grüße
Bernhard
Rekrul
User
Beiträge: 78
Registriert: Dienstag 7. Dezember 2010, 16:23

Bernhard_42 hat geschrieben: Die 'resize_event' gehörige Funktion wurde bei mir nicht ausgeführt. Daher bin ich auf 'draw_event' ausgewichen.
Dieses Event wird auch nur getriggered wenn du die Größe deines Canvas veränderst und nicht wenn du zoomst.
Bernhard_42 hat geschrieben: Es ist scheinbar nicht möglich über set_linestyle die Marker zu ändern. Ich hatte mich immer gewundert, warum meine Linie ganz verschwindet. Wenn ich mit plot zeichne funktioniert dies nämlich.
Bei mir funktioniert das einwandfrei:

Code: Alles auswählen

def OnDraw( event ):
    if p1.get_xlim()[1] - p2.get_xlim()[0] < 1:
        p1Line.set_linestyle(':')
    else:
        p1Line.set_linestyle('-')
Bernhard_42 hat geschrieben:Leider werden die Marker oder eine Linienstiländerung erst dann übernommen, wenn das ganze Fenster neu gezeichnet wird. Ich dachte zuerst mit canvas.draw Abhilfe schaffen zu können. Dies funktioniert wohl nicht, weil sich dann OnDraw dauernd selbst aufrufen würde. Gibt es hier eine Möglichkeit erst meine Änderungen einzupflegen und danach zu zeichnen (pre_draw_event)?
Das Event das du haben möchtest ist das 'xlim_changed' / 'ylim_changed' event von Axes. Beispiel:

Code: Alles auswählen

from matplotlib import pyplot as plt
from pylab import sin, arange, pi, show

def on_limit_changed(ax):
    print 'The limit has changed'
        
x = arange(0.0, 10.0, 0.01)
y = sin(2*pi*x)

f = plt.figure()
axes = f.add_subplot(111)
axes.callbacks.connect('xlim_changed', on_limit_changed)
axes.plot(x, y)
show()
Bernhard_42
User
Beiträge: 4
Registriert: Freitag 16. Dezember 2011, 15:03

Hallo Rekrul,

herzlichen Dank für Deine Hilfe!
Jetzt funktioniert das Ganze so wie ich es mir vorstelle.
Bei "set_linestyle" kann ich Dir allerdings nur beipflichten, wenn ich Deinen Vorschlag nutze. Wenn ich das gewünschte '+' oder '+-' einsetze wird die Linie nicht angezeigt.

Viele Grüße
Bernhard
Antworten