【问题标题】:Matplotlib embedded in wxPython: TextCtrl in Navigation toolbar not working on macos嵌入在 wxPython 中的 Matplotlib:导航工具栏中的 TextCtrl 在 macos 上不起作用
【发布时间】:2018-04-30 20:55:13
【问题描述】:

我正在 wxPython(Phoenix 4.0.1)和 Python 3.6.4 中使用 Matplotlib API(2.2.2)做一个简单的嵌入式图表。我对 WXAgg 导航工具栏进行了子类化,因此我可以删除“配置子图”工具,这工作正常。

此外,我在子类工具栏中添加了一个只读 TextCtrl 以显示鼠标坐标(就像它出现在基于 pyplot 状态的 matplotlib 版本中一样)。我根据 Matplotlib 文档为鼠标移动事件实现了一个简单的处理程序,这在 Windows 10 上一切正常。

但是,此代码无法在 macOS(10.13.4 High Sierra)上完全运行。图形显示得很好,工具栏显示得很好,工具栏按钮工作得很好,但是我没有在工具栏中显示任何带有鼠标坐标的 TextCtrl(甚至是创建 TextCtrl 时设置的初始值)。

谁能解释为什么 Matplotlib 工具栏中的 TextCtrl 在 Mac 上不起作用?有没有办法在mac上做到这一点?如果这根本不可能,那么在我的 Matplotlib 画布的其他地方显示鼠标坐标的替代方法是什么?

这是我的示例代码:

import wx
from matplotlib.figure import Figure
from matplotlib import gridspec
import numpy as np
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx as NavigationToolbar

class MyToolbar(NavigationToolbar): 
    def __init__(self, plotCanvas):
        # create the default toolbar
        NavigationToolbar.__init__(self, plotCanvas)

        # Add a control to display mouse coordinates
        self.info = wx.TextCtrl(self, -1, value = 'Coordinates', size = (100,-1),
                                style = wx.TE_READONLY | wx.BORDER_NONE)
        self.AddStretchableSpace()
        self.AddControl(self.info)

        # Remove configure subplots
        SubplotsPosition = 6
        self.DeleteToolByPos(SubplotsPosition)
        self.Realize()

class Graph(wx.Frame):
    def __init__(self, parent, title='Coordinates Test'):
        super().__init__(parent, title=title) 

        self.SetSize((900, 500))

        # A simple embedded matplotlib graph
        self.fig = Figure(figsize = (8.2,4.2), facecolor = 'gainsboro')
        self.canvas = FigureCanvas(self, -1, self.fig)
        gs = gridspec.GridSpec(2, 1, left = .12, right = .9, bottom = 0.05, top = .9, height_ratios = [10, 1], hspace = 0.35)
        ax = self.fig.add_subplot(gs[0])

        t = np.arange(0.0, 2.0, 0.01)
        s = 1 + np.sin(2 * np.pi * t)
        ax.plot(t, s)

        ax.set(xlabel='time (s)', ylabel='voltage (mV)',
               title='About as simple as it gets, folks')
        ax.grid()
        ax.set_navigate(True)

        # Get a toolbar instance
        self.toolbar = MyToolbar(self.canvas)
        self.toolbar.Realize()

        # Connect to matplotlib for mouse movement events
        self.canvas.mpl_connect('motion_notify_event', self.onMotion)
        self.toolbar.update()

        # Layout the frame
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.EXPAND)
        self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
        self.SetSizer(self.sizer)

    def onMotion(self, event):
        if event.inaxes:
            xdata = event.xdata
            ydata = event.ydata
            self.toolbar.info.ChangeValue(f'x = {xdata:.1f},  y = {ydata:.1f}')
        else:
            self.toolbar.info.ChangeValue('')


class MyFrame(wx.Frame):
    def __init__(self, parent, title=""):
        super().__init__(parent, title=title) 
        self.SetSize((800, 480))

        self.graph = Graph(self)
        self.graph.Show()

class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, title='Main Frame')
        self.frame.Show()
        return True

if __name__ == "__main__":
    app = MyApp(False)
    app.MainLoop() 

【问题讨论】:

    标签: macos matplotlib wxpython


    【解决方案1】:

    我意识到这已经晚了,但我认为最简单的解决方案是根本不继承 NavigationToolbar,而只是添加您自己的 TextCtrl。

    也就是说,完全摆脱您的MyToolbar 并将您的代码修改为

        # Get a toolbar instance
        self.toolbar = NavigationToolbar(self.canvas)
        self.info = wx.TextCtrl(self, -1, value = 'Coordinates', size = (100,-1),
                                style = wx.TE_READONLY | wx.BORDER_NONE)
    
        self.canvas.mpl_connect('motion_notify_event', self.onMotion)
        self.toolbar.update()
    
        # Layout the frame
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas, 1, wx.LEFT | wx.EXPAND)
    
        bottom_sizer = wx.BoxSizer(wx.HORIZONTAL)
        bottom_sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
        bottom_sizer.Add(self.info, 1, wx.LEFT | wx.EXPAND)
        self.sizer.Add(bottom_sizer, 0, wx.LEFT | wx.EXPAND)
    
        self.SetSizer(self.sizer)
    
    def onMotion(self, event):
        if event.inaxes is not None:
            xdata = event.xdata
            ydata = event.ydata
            self.info.ChangeValue(f'x = {xdata:.1f},  y = {ydata:.1f}')
        else:
            self.info.ChangeValue('')
    

    将提供显示运动事件的 TextCtrl。

    【讨论】:

    • 是的,这将显示坐标,但不将导航工具栏子类化会消除删除子图配置工具的可能性。无论如何,建议的解决方案意味着我想避免的工具栏下方有一行单独的信息。最后,我决定将鼠标坐标放在底部的 StatusBar 中,并将 Matplotlib 工具栏移到窗口布局的顶部。
    • 其实我给出的解决方案并没有在导航工具栏下方添加坐标文本框,而是放在导航工具栏的右侧。你可能会发现 wxmplot (wxPython + matplotlib) 很有用。它的 PlotFrame 根本不使用导航工具栏,但确实提供了良好的缩放(具有放大/缩小历史记录)、保存和打印功能,并且具有可用于自定义颜色、字体等的配置框架。用于基本 2D 绘图和伪彩色图像显示的便利功能太多,无法在此处列出。见newville.github.io/wxmplot
    猜你喜欢
    • 2019-03-23
    • 1970-01-01
    • 2018-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-12
    • 2020-04-20
    • 1970-01-01
    相关资源
    最近更新 更多