在 matplotlib 中添加新的导航模式 [英] Add new navigate modes in matplotlib

查看:54
本文介绍了在 matplotlib 中添加新的导航模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写wx/matplotlib应用程序,并且很难在matplotlib NavigationToolbar中添加新工具.

I'm writing a wx/matplotlib application, and I'm having considerable difficulty adding a new tool to the matplotlib NavigationToolbar.

基本上我想添加用于切换受控子图鼠标模式的选择工具(选取框、套索等).到目前为止,我还没有找到任何可以让我轻松完成此操作的功能.

Basically I want to add tools for selection (marquee, lasso, etc) that will toggle the controlled subplots mouse mode. As of yet I have been unable to find any features that will let me do this easily.

然而,我发现这个函数看起来很有帮助:http://matplotlib.sourceforge.net/api/axes_api.html?highlight=set_navigate_mode#matplotlib.axes.Axes.set_navigate_mode

I did, however, just discover this function that looked like it would be helpful: http://matplotlib.sourceforge.net/api/axes_api.html?highlight=set_navigate_mode#matplotlib.axes.Axes.set_navigate_mode

不幸的是,正如警告所暗示的那样,它并没有真正帮助我.

Unfortunately, as the warning implies, it doesn't really help me.

有人知道如何做到这一点吗?下面是一个简化的示例,显示了我走了多远.书签图标用于代替我的套索图标,为了简洁起见,我删除了套索功能.

Does anybody have a clue of how to do this? Below is a stripped down example showing how far I've gotten. The bookmark icon is used in place of my lasso icon, and I've removed the lasso functionality for brevity.

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()

谢谢,亚当

推荐答案

好吧,丑陋但实用.我会让文档字符串来讲话,这已经浪费了我很多的时间.

Well here it is, ugly but functional. I'll let the docstrings do the talking, this has wasted enough of my time.

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()

这篇关于在 matplotlib 中添加新的导航模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆