如何在 wx.Notebook 中调整多个画布的大小,浮动大小和重叠问题(WxPython、Matplotlib)

How to size multiple canvases in wx.Notebook, issue with float sizes and overlap (WxPython, Matplotlib)

我正在尝试制作一个 wx.Notebook,每个页面上都有一个 FigureCanvas,并且在这些画布中是独一无二的 figures/axes。问题是,如果图形大小(按 fig.set_size_inches)是小数点后 2 位或更多位的浮点数,不同页面上的图形将稍微重叠 。我一辈子都无法理解为什么会发生这种情况以及如何解决它。

要查看重叠问题,您必须切换到选项卡 2,然后返回选项卡 1,重叠出现在第一个图表的底部。

Picture of what I'm talking about

import wx

# Import Matplotlib
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas

class MyFrame ( wx.Frame ):

    def __init__( self, parent ):

        # FORM BUILDER OUTPUT
        #########################
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 600,500 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )

        bSizer9 = wx.BoxSizer( wx.VERTICAL )

        self.m_notebook2 = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_panel5 = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer10 = wx.BoxSizer( wx.VERTICAL )

        self.m_panel5.SetSizer( bSizer10 )
        self.m_panel5.Layout()
        bSizer10.Fit( self.m_panel5 )
        self.m_notebook2.AddPage( self.m_panel5, u"Page1", False )
        self.m_panel6 = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer11 = wx.BoxSizer( wx.VERTICAL )

        self.m_panel6.SetSizer( bSizer11 )
        self.m_panel6.Layout()
        bSizer11.Fit( self.m_panel6 )
        self.m_notebook2.AddPage( self.m_panel6, u"Page2", False )

        bSizer9.Add( self.m_notebook2, 1, wx.EXPAND |wx.ALL, 5 )

        self.SetSizer( bSizer9 )
        self.Layout()

        self.Centre( wx.BOTH )

        #########################

        graph1 = self.create_graph( self.m_panel5, size=(3, 2.255252) )
        graph2 = self.create_graph( self.m_panel6, size=(5.5556, 3.5) )

        ####################

    def create_canvas( self, panel, size ):
        fig = Figure()
        ax = fig.add_subplot(111)
        ax.set_xlim([0,10])
        ax.set_ylim([0,100])
        ax.axis('off')

        fig.set_size_inches( size )  # If this is a float to 3 decimals is causes problems

        canvas = FigureCanvas(panel, -1, fig)
        sizer = panel.GetSizer()
        sizer.Add(canvas)
        return(canvas)

    def create_graph( self, panel, size ):
        canvas = self.create_canvas(panel,size)
        ax = canvas.figure.axes[0]
        xdata = range(1,10)
        ydata = [x*x for x in xdata]
        ax.scatter( xdata, ydata )
        ax.bar( xdata, [100]*9, 0.5, 0, color='red' )
        ax.plot()
        return(ax)


class MyApp(wx.App):
    def OnInit(self):
        main = MyFrame(None)
        main.Show()
        return True

app = MyApp(0)
app.MainLoop()

print('Done')

已尝试 Windows (wxPython 2.9.5.1/mpl 1.3.1) 上的代码并得到相同的结果(第一个图底部的红色标记)。

我会以不同的方式解决这个问题,并指定绘图的大小,而不是在 matplotlib 中,而是在 wxPython 中。

修改代码时如下:

def __init__( self, parent ):
    ...
    graph2 = self.create_graph(#...
    self.Layout() # put this at the end

def create_canvas( self, panel, size ): # size can be omitted
    fig = Figure(dpi=96) # specify the dpi instead of the size
    ...
    # comment this out
    # fig.set_size_inches( size )

一切都画得很好,尺寸也更合理。

编辑:问题出现了如何为 mpl canvas 设置固定的宽高比而不具体指定以英寸为单位的尺寸。添加到 sizer 并写入

时,您可以使用 wx.SHAPED 标志而不是 wx.EXPAND
    bSizer9.Add( self.m_notebook2, 1, wx.SHAPED |wx.ALL, 5 )

这将保持 __init__ 中绘制的纵横比。

一种解决方法是在页面之间切换时调用页面的 Layout() 方法。这是一个对我有用的例子 Windows 7 with wxPython 3 and Python 2.7:

import wx

# Import Matplotlib
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas

class MyFrame ( wx.Frame ):

    def __init__( self, parent ):

        # FORM BUILDER OUTPUT
        #########################
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 600,500 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize )

        bSizer9 = wx.BoxSizer( wx.VERTICAL )

        self.m_notebook2 = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_notebook2.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.onPageChanged)
        self.m_panel5 = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer10 = wx.BoxSizer( wx.VERTICAL )

        self.m_panel5.SetSizer( bSizer10 )
        self.m_panel5.Layout()
        bSizer10.Fit( self.m_panel5 )
        self.m_notebook2.AddPage( self.m_panel5, u"Page1", False )
        self.m_panel6 = wx.Panel( self.m_notebook2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
        bSizer11 = wx.BoxSizer( wx.VERTICAL )

        self.m_panel6.SetSizer( bSizer11 )
        self.m_panel6.Layout()
        bSizer11.Fit( self.m_panel6 )
        self.m_notebook2.AddPage( self.m_panel6, u"Page2", False )

        bSizer9.Add( self.m_notebook2, 1, wx.EXPAND |wx.ALL, 5 )

        self.SetSizer( bSizer9 )
        self.Layout()

        self.Centre( wx.BOTH )

        #########################

        graph1 = self.create_graph( self.m_panel5, size=(3, 2.255252) )
        graph2 = self.create_graph( self.m_panel6, size=(5.5556, 3.5) )

        ####################

    def create_canvas( self, panel, size ):
        fig = Figure()
        ax = fig.add_subplot(111)
        ax.set_xlim([0,10])
        ax.set_ylim([0,100])
        ax.axis('off')

        fig.set_size_inches( size )  # If this is a float to 3 decimals is causes problems

        canvas = FigureCanvas(panel, -1, fig)
        sizer = panel.GetSizer()
        sizer.Add(canvas)
        return(canvas)

    def create_graph( self, panel, size ):
        canvas = self.create_canvas(panel,size)
        ax = canvas.figure.axes[0]
        xdata = range(1,10)
        ydata = [x*x for x in xdata]
        ax.scatter( xdata, ydata )
        ax.bar( xdata, [100]*9, 0.5, 0, color='red' )
        ax.plot()
        return(ax)

    def onPageChanged(self, event):
        self.m_panel5.Layout()
        self.m_panel6.Layout()


class MyApp(wx.App):
    def OnInit(self):
        main = MyFrame(None)
        main.Show()
        return True

app = MyApp(0)
app.MainLoop()