2014-05-16 15 views
9

Na podstawie this solution, chciałem zrobić suwak, w którym dozwolone są tylko określone wartości, sam suwak jest również dyskretny, suwak przesuwa się tylko wtedy, gdy został wybrany nowy punkt (czyli w zasadzie suwaka wersja Przycisk radiowy). Na przykład, jeśli kliknę w pobliżu, ale nie dokładnie w bieżącym punkcie, suwak nie powinien się zmienić, a wykres nie powinien przerysowywać.Dyskretny suwak w widgecie matplotlib

Mam dość dużo pracy, ale wynik jest opóźniony: jeśli kliknę na przemian od 1 do 10 w poniższym przykładzie, suwak aktualizuje się poprawnie, ale ruchomy punkt zawsze przeskakuje do poprzedniej wartości. Jak mogę to naprawić:

import matplotlib.pyplot as plt 
import numpy as np 
from matplotlib.widgets import Slider 

class ChangingPlot(object): 
    def __init__(self): 
     self.draw_counter = 0 
     x = np.logspace(0, 1, 10) 

     self.fig, self.ax = plt.subplots() 
     self.sliderax  = self.fig.add_axes([0.2, 0.02, 0.6, 0.03],axisbg='yellow') 
     self.slider  = DiscreteSlider(self.sliderax,'Value', 0, 10,\ 
              allowed_vals=x, valinit=x[0]) 

     self.slider.on_changed(self.update) 

     self.ax.plot(x, x, 'ro') 
     self.dot, = self.ax.plot(x[0], x[0], 'bo', markersize=18) 
     self.text = self.ax.text(2,8,str(self.draw_counter)) 

    def update(self, value): 
     self.draw_counter += 1 
     self.dot.set_data([[value],[value]]) 
     self.text.set_text(str(self.draw_counter)+' draws, value = '+str(value)) 

    def show(self): 
     plt.show() 

class DiscreteSlider(Slider): 
    """A matplotlib slider widget with discrete steps.""" 
    def __init__(self, *args, **kwargs): 
     """ 
     Identical to Slider.__init__, except for the new keyword 'allowed_vals'. 
     This keyword specifies the allowed positions of the slider 
     """ 
     self.allowed_vals = kwargs.pop('allowed_vals',None) 
     self.previous_val = kwargs['valinit'] 
     Slider.__init__(self, *args, **kwargs) 
     if self.allowed_vals==None: 
      self.allowed_vals = [self.valmin,self.valmax] 

    def set_val(self, val): 
     discrete_val = self.allowed_vals[abs(val-self.allowed_vals).argmin()] 
     xy = self.poly.xy 
     xy[2] = discrete_val, 1 
     xy[3] = discrete_val, 0 
     self.poly.xy = xy 
     self.valtext.set_text(self.valfmt % discrete_val) 
     if self.drawon: 
      self.ax.figure.canvas.draw() 
     self.val = val 
     if self.previous_val!=discrete_val: 
      self.previous_val = discrete_val 
      if not self.eventson: 
       return 
      for cid, func in self.observers.iteritems(): 
       func(discrete_val) 

p = ChangingPlot() 
p.show() 
+1

Dziękujemy za udostępnienie tego. Właśnie musiałem zmienić linię 'self.val = val' na' self.val = discrete_val' w funkcji 'set_val'. – rerx

+0

@JohnSmith To jest sprytny! Dokładnie to, czego ja (i prawdopodobnie inni) potrzebowałem. Powinieneś zastosować poprawkę zaproponowaną przez HYRY i przesłać ją jako żądanie ściągnięcia do repozytorium github matplotlib. – tel

Odpowiedz

5

Bardzo ładny przykład, trzeba zadzwonić Figure.canvas.draw() do reflush ekranu:

def update(self, value): 
    self.draw_counter += 1 
    self.dot.set_data([[value],[value]]) 
    self.text.set_text(str(self.draw_counter)+' draws, value = '+str(value)) 
    self.fig.canvas.draw() # <--- Add this line 
+0

Świetnie, że działało. Dzięki! –