(wxpython) 使滚动面板内图形的 matplotlib NavigationToolbar 静态化
(wxpython) make the matplotlib NavigationToolbar static for a figure inside scrolledpanel
我有一个 Matplotlib 图,其中有很多需要 ScrolledPanel
的子图。现在我想要一个 navigation toolbar
的图形,它在滚动图形 panel
时保持静态。
我尝试将它添加到另一个面板而不是 canvas 的父面板,但这不起作用(导航工具栏仅保留在 canvas 的父面板内)。
有没有办法让 matplotlib 导航工具栏保持静态,但让 canvas 的其余部分可滚动?
这是我的完整代码:
import wx
import wx.lib.scrolledpanel
import matplotlib
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar
from matplotlib.figure import Figure
import wx.lib.inspection
matplotlib.use('WXAgg')
class PlotDemoApp(object):
def __init__(self, data):
self.app = wx.App()
self.frame = PlotCanvas(None, -1, "PlotCanvas", data)
self.frame.Show(True)
wx.lib.inspection.InspectionTool().Show()
self.app.MainLoop()
class PlotCanvas(wx.Frame):
def __init__(self, parent, wxid, title, data):
wx.Frame.__init__(self, parent, wxid, title)
self.box_main = wx.BoxSizer(wx.VERTICAL)
panel_lower = wx.lib.scrolledpanel.ScrolledPanel(self, size=(2500,-1))
panel_lower.SetupScrolling()
panel_lower.SetBackgroundColour("White")
self.box_lower = wx.BoxSizer(wx.HORIZONTAL)
box_local = wx.BoxSizer(wx.VERTICAL)
plt = Figure(figsize=(95,10))
num_columns = len(data.columns)
axes_1 = plt.add_subplot(1, num_columns, 1)
data_numpy = data[data.columns[0]].to_numpy()
depth = data.index.to_numpy()
plt.gca().invert_yaxis()
plt.subplots_adjust(left=0.01, right=1.00, top=0.95, bottom=0.05)
axes_1.plot(data_numpy, depth)
for i in range(1, num_columns):
axes_tmp = plt.add_subplot(1, num_columns, i+1, sharey=axes_1)
axes_tmp.set(xlabel=data.columns[i], ylabel='Depth', title='Depth vs ' + data.columns[i])
data_numpy = data[data.columns[i]].to_numpy()
plt.gca().invert_yaxis()
axes_tmp.plot(data_numpy, depth)
canvas = FigureCanvas(panel_lower, -1, plt)
canvas.draw()
box_local.Add(canvas, 1, wx.EXPAND)
panel_nav = wx.Panel(self)
box_nav = wx.BoxSizer(wx.HORIZONTAL)
toolbar = NavigationToolbar(canvas)
box_nav.Add(toolbar)
panel_nav.SetSizer(box_nav)
toolbar.Realize()
self.box_main.Add(panel_nav, 0, wx.CENTER)
self.box_lower.Add(box_local, 1, wx.EXPAND)
self.box_main.Add(panel_lower, 1, wx.EXPAND)
panel_lower.SetSizer(self.box_lower)
self.SetSizer(self.box_main)
self.box_main.Layout()
data
这是一个 pandas 数据帧
示例数据:
| index | BS | CAL |
|-------|------|------|
| 162 | 17.5 | 17.4 |
| 163 | 17.8 | 17.7 |
| 164 | 17.8 | 17.9 |
希望这就是您所追求的,或者至少为您指明了正确的方向。
matplotlib 导航工具栏似乎与绘图本身焊接在一起,但它可以隐藏。
虽然它保持隐藏状态,但它的功能仍然可用,因此我们可以创建我们自己的 toolbar
并将隐藏工具栏中的功能分配给它。
像这样:
import wx
import wx.lib.scrolledpanel as scrolled
import pandas as pd
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar
from matplotlib.figure import Figure
# images in /usr/share/matplotlib/mpl-data/images
# Your system may differ!
class PlotPanel(scrolled.ScrolledPanel):
def __init__(self,parent):
scrolled.ScrolledPanel.__init__(self, parent)
self.SetupScrolling(scroll_x=True, scroll_y=True, scrollToTop=False, scrollIntoView=False)
self.ShowScrollbars(True,True)
self.plt = Figure(figsize=(95,10))
self.canvas = FigureCanvas(self,-1, self.plt)
self.toolbar = NavigationToolbar(self.canvas)
#Hide the Matplotlib toolbar because we are going to create own own
#but use the functions of this toolbar.
self.toolbar.Hide()
def plot(self):
d = {'BS':[17.54,17.55,17.54,17.53,17.55,17.54],'CAL':[17.46,17.47,17.49,17.44,17.47,17.49]}
data = pd.DataFrame(d, index=['1','2','3','4','5','6'])
num_columns = len(data.columns)
axes_1 = self.plt.add_subplot(1, num_columns, 1)
#data_numpy = data[data.columns[0]].to_numpy() # My version of pandas doesn't have .to_numpy()
data_numpy = data[data.columns[0]]
#depth = data.index.to_numpy()
depth = data.index
self.plt.gca().invert_yaxis()
self.plt.subplots_adjust(left=0.01, right=0.50, top=0.95, bottom=0.05)
axes_1.plot(data_numpy, depth)
for i in range(0, num_columns):
axes_tmp = self.plt.add_subplot(1, num_columns, i+1, sharey=axes_1)
axes_tmp.set(xlabel=data.columns[i], ylabel='Depth', title='Depth vs ' + data.columns[i])
#data_numpy = data[data.columns[i]].to_numpy()
data_numpy = data[data.columns[i]]
self.plt.gca().invert_yaxis()
axes_tmp.plot(data_numpy, depth)
self.canvas.draw()
self.SetSize(self.canvas.GetSize())
class TestFrame(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self,parent,title=title,size=(600,600))
self.p1 = PlotPanel(self)
#Create our own toolbar
toolbar = self.CreateToolBar(style=wx.TB_HORIZONTAL|wx.TB_DOCKABLE|wx.TB_TEXT)
hometool = toolbar.AddTool(wx.ID_ANY, 'Home', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/home.png'))
backtool = toolbar.AddTool(wx.ID_ANY, 'Back', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/back.png'))
fwdtool = toolbar.AddTool(wx.ID_ANY, 'Forward', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/forward.png'))
pantool = toolbar.AddTool(wx.ID_ANY, 'Pan', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/move.png'))
zoomtool = toolbar.AddTool(wx.ID_ANY, 'Zoom', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/zoom_to_rect.png'))
subtool = toolbar.AddTool(wx.ID_ANY, 'Subplots', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/subplots.png'))
savetool = toolbar.AddTool(wx.ID_ANY, 'Save', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/filesave.png'))
self.Bind(wx.EVT_TOOL, self.home, hometool)
self.Bind(wx.EVT_TOOL, self.back, backtool)
self.Bind(wx.EVT_TOOL, self.fwd, fwdtool)
self.Bind(wx.EVT_TOOL, self.pan, pantool)
self.Bind(wx.EVT_TOOL, self.zoom, zoomtool)
self.Bind(wx.EVT_TOOL, self.sub, subtool)
self.Bind(wx.EVT_TOOL, self.save, savetool)
toolbar.Realize()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.p1,1,wx.EXPAND,10)
self.statusbar=self.CreateStatusBar()
self.SetSizer(sizer)
self.Show()
self.plot()
#Self defined Navigation Toolbar controls used to call hidden matplotlib Toolbar functions
def home(self,event):
self.statusbar.SetStatusText("Home")
self.p1.toolbar.home()
# Also scroll panel to start position
self.p1.Scroll(0,0)
def back(self,event):
self.statusbar.SetStatusText("Back")
self.p1.toolbar.back()
def fwd(self,event):
self.statusbar.SetStatusText("Fwd")
self.p1.toolbar.forward()
def pan(self,event):
self.statusbar.SetStatusText("Pan")
self.p1.toolbar.pan()
def zoom(self,event):
self.statusbar.SetStatusText("Zoom")
self.p1.toolbar.zoom()
def sub(self,event):
self.statusbar.SetStatusText("Subplots")
self.p1.toolbar.configure_subplots(event)
def save(self,event):
self.statusbar.SetStatusText("Save")
self.p1.toolbar.save_figure()
def plot(self):
self.p1.plot()
app = wx.App(redirect=False)
frame = TestFrame(None,"Plot in Scrolled panel with replacement Navigation")
app.MainLoop()
您会注意到我将替换 toolbar
设置为可停靠,因此我可以将 toolbar
移动到屏幕上任何我想要的位置。它是可重新连接的。
因为我们正在定义我们自己的工具栏,所以它可以在我们想要的任何地方,只需查看 wx.Toolbar
的选项。
在这里,我把文字弄丢了,改成了竖排的。
我有一个 Matplotlib 图,其中有很多需要 ScrolledPanel
的子图。现在我想要一个 navigation toolbar
的图形,它在滚动图形 panel
时保持静态。
我尝试将它添加到另一个面板而不是 canvas 的父面板,但这不起作用(导航工具栏仅保留在 canvas 的父面板内)。
有没有办法让 matplotlib 导航工具栏保持静态,但让 canvas 的其余部分可滚动?
这是我的完整代码:
import wx
import wx.lib.scrolledpanel
import matplotlib
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar
from matplotlib.figure import Figure
import wx.lib.inspection
matplotlib.use('WXAgg')
class PlotDemoApp(object):
def __init__(self, data):
self.app = wx.App()
self.frame = PlotCanvas(None, -1, "PlotCanvas", data)
self.frame.Show(True)
wx.lib.inspection.InspectionTool().Show()
self.app.MainLoop()
class PlotCanvas(wx.Frame):
def __init__(self, parent, wxid, title, data):
wx.Frame.__init__(self, parent, wxid, title)
self.box_main = wx.BoxSizer(wx.VERTICAL)
panel_lower = wx.lib.scrolledpanel.ScrolledPanel(self, size=(2500,-1))
panel_lower.SetupScrolling()
panel_lower.SetBackgroundColour("White")
self.box_lower = wx.BoxSizer(wx.HORIZONTAL)
box_local = wx.BoxSizer(wx.VERTICAL)
plt = Figure(figsize=(95,10))
num_columns = len(data.columns)
axes_1 = plt.add_subplot(1, num_columns, 1)
data_numpy = data[data.columns[0]].to_numpy()
depth = data.index.to_numpy()
plt.gca().invert_yaxis()
plt.subplots_adjust(left=0.01, right=1.00, top=0.95, bottom=0.05)
axes_1.plot(data_numpy, depth)
for i in range(1, num_columns):
axes_tmp = plt.add_subplot(1, num_columns, i+1, sharey=axes_1)
axes_tmp.set(xlabel=data.columns[i], ylabel='Depth', title='Depth vs ' + data.columns[i])
data_numpy = data[data.columns[i]].to_numpy()
plt.gca().invert_yaxis()
axes_tmp.plot(data_numpy, depth)
canvas = FigureCanvas(panel_lower, -1, plt)
canvas.draw()
box_local.Add(canvas, 1, wx.EXPAND)
panel_nav = wx.Panel(self)
box_nav = wx.BoxSizer(wx.HORIZONTAL)
toolbar = NavigationToolbar(canvas)
box_nav.Add(toolbar)
panel_nav.SetSizer(box_nav)
toolbar.Realize()
self.box_main.Add(panel_nav, 0, wx.CENTER)
self.box_lower.Add(box_local, 1, wx.EXPAND)
self.box_main.Add(panel_lower, 1, wx.EXPAND)
panel_lower.SetSizer(self.box_lower)
self.SetSizer(self.box_main)
self.box_main.Layout()
data
这是一个 pandas 数据帧
示例数据:
| index | BS | CAL |
|-------|------|------|
| 162 | 17.5 | 17.4 |
| 163 | 17.8 | 17.7 |
| 164 | 17.8 | 17.9 |
希望这就是您所追求的,或者至少为您指明了正确的方向。
matplotlib 导航工具栏似乎与绘图本身焊接在一起,但它可以隐藏。
虽然它保持隐藏状态,但它的功能仍然可用,因此我们可以创建我们自己的 toolbar
并将隐藏工具栏中的功能分配给它。
像这样:
import wx
import wx.lib.scrolledpanel as scrolled
import pandas as pd
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar
from matplotlib.figure import Figure
# images in /usr/share/matplotlib/mpl-data/images
# Your system may differ!
class PlotPanel(scrolled.ScrolledPanel):
def __init__(self,parent):
scrolled.ScrolledPanel.__init__(self, parent)
self.SetupScrolling(scroll_x=True, scroll_y=True, scrollToTop=False, scrollIntoView=False)
self.ShowScrollbars(True,True)
self.plt = Figure(figsize=(95,10))
self.canvas = FigureCanvas(self,-1, self.plt)
self.toolbar = NavigationToolbar(self.canvas)
#Hide the Matplotlib toolbar because we are going to create own own
#but use the functions of this toolbar.
self.toolbar.Hide()
def plot(self):
d = {'BS':[17.54,17.55,17.54,17.53,17.55,17.54],'CAL':[17.46,17.47,17.49,17.44,17.47,17.49]}
data = pd.DataFrame(d, index=['1','2','3','4','5','6'])
num_columns = len(data.columns)
axes_1 = self.plt.add_subplot(1, num_columns, 1)
#data_numpy = data[data.columns[0]].to_numpy() # My version of pandas doesn't have .to_numpy()
data_numpy = data[data.columns[0]]
#depth = data.index.to_numpy()
depth = data.index
self.plt.gca().invert_yaxis()
self.plt.subplots_adjust(left=0.01, right=0.50, top=0.95, bottom=0.05)
axes_1.plot(data_numpy, depth)
for i in range(0, num_columns):
axes_tmp = self.plt.add_subplot(1, num_columns, i+1, sharey=axes_1)
axes_tmp.set(xlabel=data.columns[i], ylabel='Depth', title='Depth vs ' + data.columns[i])
#data_numpy = data[data.columns[i]].to_numpy()
data_numpy = data[data.columns[i]]
self.plt.gca().invert_yaxis()
axes_tmp.plot(data_numpy, depth)
self.canvas.draw()
self.SetSize(self.canvas.GetSize())
class TestFrame(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self,parent,title=title,size=(600,600))
self.p1 = PlotPanel(self)
#Create our own toolbar
toolbar = self.CreateToolBar(style=wx.TB_HORIZONTAL|wx.TB_DOCKABLE|wx.TB_TEXT)
hometool = toolbar.AddTool(wx.ID_ANY, 'Home', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/home.png'))
backtool = toolbar.AddTool(wx.ID_ANY, 'Back', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/back.png'))
fwdtool = toolbar.AddTool(wx.ID_ANY, 'Forward', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/forward.png'))
pantool = toolbar.AddTool(wx.ID_ANY, 'Pan', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/move.png'))
zoomtool = toolbar.AddTool(wx.ID_ANY, 'Zoom', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/zoom_to_rect.png'))
subtool = toolbar.AddTool(wx.ID_ANY, 'Subplots', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/subplots.png'))
savetool = toolbar.AddTool(wx.ID_ANY, 'Save', wx.Bitmap('/usr/share/matplotlib/mpl-data/images/filesave.png'))
self.Bind(wx.EVT_TOOL, self.home, hometool)
self.Bind(wx.EVT_TOOL, self.back, backtool)
self.Bind(wx.EVT_TOOL, self.fwd, fwdtool)
self.Bind(wx.EVT_TOOL, self.pan, pantool)
self.Bind(wx.EVT_TOOL, self.zoom, zoomtool)
self.Bind(wx.EVT_TOOL, self.sub, subtool)
self.Bind(wx.EVT_TOOL, self.save, savetool)
toolbar.Realize()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.p1,1,wx.EXPAND,10)
self.statusbar=self.CreateStatusBar()
self.SetSizer(sizer)
self.Show()
self.plot()
#Self defined Navigation Toolbar controls used to call hidden matplotlib Toolbar functions
def home(self,event):
self.statusbar.SetStatusText("Home")
self.p1.toolbar.home()
# Also scroll panel to start position
self.p1.Scroll(0,0)
def back(self,event):
self.statusbar.SetStatusText("Back")
self.p1.toolbar.back()
def fwd(self,event):
self.statusbar.SetStatusText("Fwd")
self.p1.toolbar.forward()
def pan(self,event):
self.statusbar.SetStatusText("Pan")
self.p1.toolbar.pan()
def zoom(self,event):
self.statusbar.SetStatusText("Zoom")
self.p1.toolbar.zoom()
def sub(self,event):
self.statusbar.SetStatusText("Subplots")
self.p1.toolbar.configure_subplots(event)
def save(self,event):
self.statusbar.SetStatusText("Save")
self.p1.toolbar.save_figure()
def plot(self):
self.p1.plot()
app = wx.App(redirect=False)
frame = TestFrame(None,"Plot in Scrolled panel with replacement Navigation")
app.MainLoop()
您会注意到我将替换 toolbar
设置为可停靠,因此我可以将 toolbar
移动到屏幕上任何我想要的位置。它是可重新连接的。
因为我们正在定义我们自己的工具栏,所以它可以在我们想要的任何地方,只需查看 wx.Toolbar
的选项。
在这里,我把文字弄丢了,改成了竖排的。