Matplotlib, встроенный в wxPython: TextCtrl на панели инструментов навигации не работает на MacOS - PullRequest
0 голосов
/ 30 апреля 2018

Я делаю простой встроенный граф с API Matplotlib (2.2.2) в wxPython (Phoenix 4.0.1) и Python 3.6.4. Я разделил на подклассы панель инструментов WXAgg Navigation, чтобы я мог удалить инструмент «Настройка дочерних участков», и это работает нормально.

Кроме того, я добавил доступный только для чтения TextCtrl в свою панель инструментов с подклассами, чтобы показать координаты мыши (так же, как это выглядит в основанной на pyplot версии matplotlib, основанной на состоянии). Я реализовал простой обработчик событий перемещения мыши для документов Matplotlib, и все это прекрасно работает в Windows 10.

Однако этот код не полностью работает на macOS (10.13.4 High Sierra). График отображается очень хорошо, панель инструментов отображается нормально, кнопки панели инструментов работают нормально, но я не получаю никакого отображения моего TextCtrl с координатами мыши на панели инструментов (или даже с начальным значением, установленным при создании TextCtrl).

Может кто-нибудь пролить свет на то, почему TextCtrl на панели инструментов Matplotlib не работает на 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() 

1 Ответ

0 голосов
/ 20 мая 2018

Я понимаю, что уже поздно, но я думаю, что самое простое решение - вообще не создавать подкласс 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, который отображает события движения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...