2011-01-19 27 views
8

Bir wx/matplotlib uygulaması yazıyorum ve matplotlib NavigationToolbar'a yeni bir araç eklerken büyük zorluk yaşıyorum.Matplotlib'de yeni gezinme modları ekleme

Temel olarak, kontrol edilen alt piksel fare modunu değiştirecek seçim için (seçim çerçevesi, kement vb.) Araçlar eklemek istiyorum. Şu ana kadar bunu kolayca yapmamı sağlayacak hiçbir özellik bulamadım.

Ben, ancak, sadece yararlı olacaktır benziyordu bu işlevi keşfettiniz: http://matplotlib.sourceforge.net/api/axes_api.html?highlight=set_navigate_mode#matplotlib.axes.Axes.set_navigate_mode

Maalesef uyarı da anlaşılacağı gibi, gerçekten bana yardım etmez.

Bunu nasıl yapacaklarına dair bir ipucu var mı? Aşağıda ne kadar yol aldığımı gösteren soyulmuş bir örnek verilmiştir. Yer imi simgesi, kement simgemin yerine kullanılır ve kement işlevini kısalık için kaldırdım.

import wx 
from matplotlib.patches import Rectangle 
from matplotlib.widgets import Lasso 
from matplotlib.figure import Figure 
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg 
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar 

class ScatterPanel(FigureCanvasWxAgg): 
    ''' 
    Contains the guts for drawing scatter plots. 
    ''' 
    def __init__(self, parent, **kwargs): 
     self.figure = Figure() 
     FigureCanvasWxAgg.__init__(self, parent, -1, self.figure, **kwargs) 
     self.canvas = self.figure.canvas 
     self.SetMinSize((100,100)) 
     self.figure.set_facecolor((1,1,1)) 
     self.figure.set_edgecolor((1,1,1)) 
     self.canvas.SetBackgroundColour('white') 

     self.subplot = self.figure.add_subplot(111) 
     self.navtoolbar = None 
     self.lasso = None 
     self.redraw() 

     self.canvas.mpl_connect('button_press_event', self.on_press) 
     self.canvas.mpl_connect('button_release_event', self.on_release) 

    def lasso_callback(self, verts): 
     pass 

    def on_press(self, evt): 
     if evt.button == 1: 
      if self.canvas.widgetlock.locked(): 
       return 
      if evt.inaxes is None: 
       return 
      if self.navtoolbar.mode == 'lasso': 
       self.lasso = Lasso(evt.inaxes, (evt.xdata, evt.ydata), self.lasso_callback) 
       self.canvas.widgetlock(self.lasso) 

    def on_release(self, evt): 
     # Note: lasso_callback is not called on click without drag so we release 
     # the lock here to handle this case as well. 
     if evt.button == 1: 
      if self.lasso: 
       self.canvas.draw_idle() 
       self.canvas.widgetlock.release(self.lasso) 
       self.lasso = None 
     else: 
      self.show_popup_menu((evt.x, self.canvas.GetSize()[1]-evt.y), None) 

    def redraw(self): 
     self.subplot.clear() 
     self.subplot.scatter([1,2,3],[3,1,2]) 

    def toggle_lasso_tool(self, evt): 
     if evt.Checked(): 
      self.navtoolbar.mode = 'lasso' 
      #self.subplot.set_navigate_mode('lasso') 
      # Cheat: untoggle the zoom and pan tools 
      self.navtoolbar.ToggleTool(self.navtoolbar._NTB2_ZOOM, False) 
      self.navtoolbar.ToggleTool(self.navtoolbar._NTB2_PAN, False) 
     else: 
      self.navtoolbar.mode = '' 
      self.lasso = None 
      #self.subplot.set_navigate_mode('') 

    def get_toolbar(self): 
     if not self.navtoolbar: 
      self.navtoolbar = NavigationToolbar(self.canvas) 
      self.navtoolbar.DeleteToolByPos(6) 
      ID_LASSO_TOOL = wx.NewId() 
      lasso = self.navtoolbar.InsertSimpleTool(5, ID_LASSO_TOOL, 
          wx.ArtProvider.GetBitmap(wx.ART_ADD_BOOKMARK), 
          isToggle=True) 
      self.navtoolbar.Realize() 
      self.navtoolbar.Bind(wx.EVT_TOOL, self.toggle_lasso_tool, id=ID_LASSO_TOOL) 
     return self.navtoolbar 

if __name__ == "__main__": 
    app = wx.PySimpleApp() 
    f = wx.Frame(None, size=(600,600)) 
    p = ScatterPanel(f) 
    f.SetToolBar(p.get_toolbar())    
    f.Show() 
    app.MainLoop() 

sayesinde Adam Eh burada

cevap

2

o, çirkin ama işlevseldir. Belgelerin konuşmasını yapmasına izin vereceğim, bu benim zamanımı yeterince harcadı.

import wx 
from matplotlib.patches import Rectangle 
from matplotlib.widgets import Lasso 
from matplotlib.figure import Figure 
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg 
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg 

class MyNavToolbar(NavigationToolbar2WxAgg): 
    """wx/mpl NavToolbar hack with an additional tools user interaction. 
    This class is necessary because simply adding a new togglable tool to the 
    toolbar won't (1) radio-toggle between the new tool and the pan/zoom tools. 
    (2) disable the pan/zoom tool modes in the associated subplot(s). 
    """ 
    ID_LASSO_TOOL = wx.NewId() 
    def __init__(self, canvas): 
     super(NavigationToolbar2WxAgg, self).__init__(canvas) 

     self.pan_tool = self.FindById(self._NTB2_PAN) 
     self.zoom_tool = self.FindById(self._NTB2_ZOOM) 

     self.lasso_tool = self.InsertSimpleTool(5, self.ID_LASSO_TOOL, 
          wx.ArtProvider.GetBitmap(wx.ART_ADD_BOOKMARK), 
          isToggle=True) 
     self.Bind(wx.EVT_TOOL, self.on_toggle_lasso_tool, self.lasso_tool) 
     self.Bind(wx.EVT_TOOL, self.on_toggle_pan_zoom, self.zoom_tool) 
     self.Bind(wx.EVT_TOOL, self.on_toggle_pan_zoom, self.pan_tool) 

    def get_mode(self): 
     """Use this rather than navtoolbar.mode 
     """ 
     if self.lasso_tool.IsToggled(): 
      return 'lasso' 
     else: 
      return self.mode 

    def untoggle_mpl_tools(self): 
     """Hack city: Since I can't figure out how to change the way the 
     associated subplot(s) handles mouse events: I generate events to turn 
     off whichever tool mode is enabled (if any). 
     This function needs to be called whenever any user-defined tool 
     (eg: lasso) is clicked. 
     """ 
     if self.pan_tool.IsToggled(): 
      wx.PostEvent(
       self.GetEventHandler(), 
       wx.CommandEvent(wx.EVT_TOOL.typeId, self._NTB2_PAN) 
      ) 
      self.ToggleTool(self._NTB2_PAN, False) 
     elif self.zoom_tool.IsToggled(): 
      wx.PostEvent(
       self.GetEventHandler(), 
       wx.CommandEvent(wx.EVT_TOOL.typeId, self._NTB2_ZOOM) 
      ) 
      self.ToggleTool(self._NTB2_ZOOM, False) 

    def on_toggle_lasso_tool(self, evt): 
     """Lasso tool handler. 
     """ 
     if evt.Checked(): 
      self.untoggle_mpl_tools() 

    def on_toggle_pan_zoom(self, evt): 
     """Called when pan or zoom is toggled. 
     We need to manually untoggle user-defined tools. 
     """ 
     if evt.Checked(): 
      self.ToggleTool(self.ID_LASSO_TOOL, False) 
     # Make sure the regular pan/zoom handlers get the event 
     evt.Skip() 

class ScatterPanel(FigureCanvasWxAgg): 
    """Contains the guts for drawing scatter plots. 
    """ 
    def __init__(self, parent, **kwargs): 
     self.figure = Figure() 
     FigureCanvasWxAgg.__init__(self, parent, -1, self.figure, **kwargs) 
     self.canvas = self.figure.canvas 
     self.SetMinSize((100,100)) 
     self.figure.set_facecolor((1,1,1)) 
     self.figure.set_edgecolor((1,1,1)) 
     self.canvas.SetBackgroundColour('white') 

     self.subplot = self.figure.add_subplot(111) 
     self.navtoolbar = None 
     self.lasso = None 
     self.redraw() 

     self.canvas.mpl_connect('button_press_event', self.on_press) 
     self.canvas.mpl_connect('button_release_event', self.on_release) 

    def lasso_callback(self, verts): 
     pass 

    def on_press(self, evt): 
     """canvas mousedown handler 
     """ 
     if evt.button == 1: 
      if self.canvas.widgetlock.locked(): 
       return 
      if evt.inaxes is None: 
       return 
      if self.navtoolbar and self.navtoolbar.get_mode() == 'lasso': 
       self.lasso = Lasso(evt.inaxes, (evt.xdata, evt.ydata), self.lasso_callback) 
       self.canvas.widgetlock(self.lasso) 

    def on_release(self, evt): 
     """canvas mouseup handler 
     """ 
     # Note: lasso_callback is not called on click without drag so we release 
     # the lock here to handle this case as well. 
     if evt.button == 1: 
      if self.lasso: 
       self.canvas.draw_idle() 
       self.canvas.widgetlock.release(self.lasso) 
       self.lasso = None 
     else: 
      self.show_popup_menu((evt.x, self.canvas.GetSize()[1]-evt.y), None) 

    def redraw(self): 
     self.subplot.clear() 
     self.subplot.scatter([1,2,3],[3,1,2]) 

    def get_toolbar(self): 
     if not self.navtoolbar: 
      self.navtoolbar = MyNavToolbar(self.canvas) 
      self.navtoolbar.Realize() 
     return self.navtoolbar 

if __name__ == "__main__": 
    app = wx.PySimpleApp() 
    f = wx.Frame(None, size=(600,600)) 
    p = ScatterPanel(f) 
    f.SetToolBar(p.get_toolbar())    
    f.Show() 
    app.MainLoop() 
+1

Sadece bunu okuyan herkes için bir FYI. Bu benim için de çalışıyordu, ama şimdi Matplotlib 1.2.0 ile iki çizgiyi değiştirmek zorundayım. 'self._NTB2_PAN'' self.wx_ids [ 'Pan'] ' ve ' self._NTB2_ZOOM' olur 'self.wx_ids [ 'Yakınlaştırma']' –

7

İşte MyNavToolbar geliştirilmiş bir versiyonu. Dikkat edilmesi gereken en önemli şey, add_user_tool yönteminin eklenmesidir. Bunu __init__ içinden çağıracağım, ancak muhtemelen MyNavToolbar sınıfının dışından aramak isteyebilirsiniz. Bu şekilde arsa türleri için farklı araçlara sahip olabilirsiniz.

class MyNavToolbar(NavigationToolbar2WxAgg): 
    """wx/mpl NavToolbar hack with an additional tools user interaction. 
    This class is necessary because simply adding a new togglable tool to the 
    toolbar won't (1) radio-toggle between the new tool and the pan/zoom tools. 
    (2) disable the pan/zoom tool modes in the associated subplot(s). 
    """ 
    def __init__(self, canvas): 
     super(NavigationToolbar2WxAgg, self).__init__(canvas) 
     self.pan_tool = self.FindById(self._NTB2_PAN) 
     self.zoom_tool = self.FindById(self._NTB2_ZOOM) 
     self.Bind(wx.EVT_TOOL, self.on_toggle_pan_zoom, self.zoom_tool) 
     self.Bind(wx.EVT_TOOL, self.on_toggle_pan_zoom, self.pan_tool) 

     self.user_tools = {} # user_tools['tool_mode'] : wx.ToolBarToolBase 

     self.InsertSeparator(5) 
     self.add_user_tool('lasso', 6, icons.lasso_tool.ConvertToBitmap(), True, 'Lasso') 
     self.add_user_tool('gate', 7, icons.gate_tool.ConvertToBitmap(), True, 'Gate') 

    def add_user_tool(self, mode, pos, bmp, istoggle=True, shortHelp=''): 
     """Adds a new user-defined tool to the toolbar. 
     mode -- the value that MyNavToolbar.get_mode() will return if this tool 
       is toggled on 
     pos -- the position in the toolbar to add the icon 
     bmp -- a wx.Bitmap of the icon to use in the toolbar 
     isToggle -- whether or not the new tool toggles on/off with the other 
        togglable tools 
     shortHelp -- the tooltip shown to the user for the new tool 
     """ 
     tool_id = wx.NewId() 
     self.user_tools[mode] = self.InsertSimpleTool(pos, tool_id, bmp, 
          isToggle=istoggle, shortHelpString=shortHelp) 
     self.Bind(wx.EVT_TOOL, self.on_toggle_user_tool, self.user_tools[mode]) 

    def get_mode(self): 
     """Use this rather than navtoolbar.mode 
     """ 
     for mode, tool in self.user_tools.items(): 
      if tool.IsToggled(): 
       return mode 
     return self.mode 

    def untoggle_mpl_tools(self): 
     """Hack city: Since I can't figure out how to change the way the 
     associated subplot(s) handles mouse events: I generate events to turn 
     off whichever tool mode is enabled (if any). 
     This function needs to be called whenever any user-defined tool 
     (eg: lasso) is clicked. 
     """ 
     if self.pan_tool.IsToggled(): 
      wx.PostEvent(
       self.GetEventHandler(), 
       wx.CommandEvent(wx.EVT_TOOL.typeId, self._NTB2_PAN) 
      ) 
      self.ToggleTool(self._NTB2_PAN, False) 
     elif self.zoom_tool.IsToggled(): 
      wx.PostEvent(
       self.GetEventHandler(), 
       wx.CommandEvent(wx.EVT_TOOL.typeId, self._NTB2_ZOOM) 
      ) 
      self.ToggleTool(self._NTB2_ZOOM, False) 

    def on_toggle_user_tool(self, evt): 
     """user tool click handler. 
     """ 
     if evt.Checked(): 
      self.untoggle_mpl_tools() 
      #untoggle other user tools 
      for tool in self.user_tools.values(): 
       if tool.Id != evt.Id: 
        self.ToggleTool(tool.Id, False) 

    def on_toggle_pan_zoom(self, evt): 
     """Called when pan or zoom is toggled. 
     We need to manually untoggle user-defined tools. 
     """ 
     if evt.Checked(): 
      for tool in self.user_tools.values(): 
       self.ToggleTool(tool.Id, False) 
     # Make sure the regular pan/zoom handlers get the event 
     evt.Skip() 

    def reset_history(self): 
     """More hacky junk to clear/reset the toolbar history. 
     """ 
     self._views.clear() 
     self._positions.clear() 
     self.push_current() 
+0

Vay, bu benim için fevkalade yararlı olur, teşekkürler çok olur cevaplamak için Kimsenin cevap vermediği veya hiç yorumlamadığı için üzgünüm. –

+0

Teşekkürler Adam, reset_history() işleviniz son derece yardımcı oldu. –

+0

Matplotlib 1.2.0 ile birkaç şeyi değiştirmek zorundayım. 'self._NTB2_PAN',' self.wx_ids ['Pan'] ' ve ve self_NTB2_ZOOM'' 'self.wx_ids ['Zoom']' olur. –

İlgili konular