我已经使用 matplotlib 绘制了一个矩阵。我想知道是否可以使用一些工具包/模块对该图产生一些交互性。
根据我们的分析,我们先验地知道矩阵的哪个单元与其他单元相连。我们想要做的是当用户用鼠标指针将鼠标悬停在矩阵单元上时,它应该通过(指针或任何其他方式)突出显示它连接到的其他单元格。它是一种图形数据结构,但我希望用户获得交互式体验。
我已经使用 matplotlib 绘制了一个矩阵。我想知道是否可以使用一些工具包/模块对该图产生一些交互性。
根据我们的分析,我们先验地知道矩阵的哪个单元与其他单元相连。我们想要做的是当用户用鼠标指针将鼠标悬停在矩阵单元上时,它应该通过(指针或任何其他方式)突出显示它连接到的其他单元格。它是一种图形数据结构,但我希望用户获得交互式体验。
Matplotlib 有一个事件处理 API,您可以将其用于交互式图形。
下面的示例脚本使用matshow
. 矩阵的值是彩色编码的。
您可以在连接字典中设置连接:键是要为其添加连接(作为元组)的矩阵位置,连接在连接点列表中给出(再次作为元组)。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import patches
class MatrixBrowser(object):
def __init__(self, matrix, matrix_ax, connections):
self.matrix = matrix
self.matrix_ax = matrix_ax
self.con = connections
self.index = (0, 0)
self.rect = patches.Rectangle((0, 0), 1.1, 1.1,
linewidth=3, fill=False, visible=False)
self.con_rects = self.add_connection_rects()
def add_connection_rects(self):
max_cons = max([len(_) for _ in self.con.values()])
rects = []
for con in range(max_cons):
con_rect = patches.Rectangle((0, 0), 1.1, 1.1, linewidth=5,
fill=False, visible=False, edgecolor='red')
rects.append(con_rect)
self.matrix_ax.add_patch(con_rect)
return rects
def update_connections(self, event):
current_ax = event.inaxes
cx = event.xdata
cy = event.ydata
# only if the cursor is on the matrix ax
if current_ax == self.matrix_ax:
rx = round(abs(cx))
ry = round(abs(cy))
if not self.index == (rx, ry):
# make every previous rect invisible
for rect in self.con_rects:
rect.set_visible(False)
cons = self.con.get((rx, ry), [])
for rect, con in zip(self.con_rects, cons):
rect.set_xy((con[0] - 0.55, con[1] - 0.55))
rect.set_visible(True)
self.index = (rx, ry)
self.rect.set_visible(True)
self.rect.set_xy((rx - 0.55, ry - 0.55))
else:
self.rect.set_visible(False)
plt.draw()
def main(matrix, connections):
fig, ax = plt.subplots()
im = ax.matshow(matrix, aspect='auto', cmap=plt.cm.winter)
plt.colorbar(im, use_gridspec=True)
browser = MatrixBrowser(matrix, ax, connections)
ax.add_patch(browser.rect)
fig.canvas.mpl_connect('motion_notify_event', browser.update_connections)
plt.tight_layout()
plt.show()
if __name__ == '__main__':
matrix = np.random.rand(15, 15) * 10
connections = {(0, 0): [(1, 1), (2, 2), (10, 2), (8, 5)],
(3, 2): [(3, 3)],
(14, 14): [(0, 0), (0, 14), (14, 0)]}
main(matrix, connections)
为了给你一个印象,我添加了一个屏幕截图。黑色矩形随着鼠标光标移动,如果当前鼠标位置存在连接,则会出现红色矩形。
在此屏幕截图中,光标位于(0, 0)
矩阵中的点上。由于为此点定义了一个连接(请参阅连接字典:)(0, 0): [(1, 1), (2, 2), (10, 2), (8, 5)]
,因此定义的连接用红色矩形突出显示。
例如,您可以将 gtk 与 matplotlib 一起使用以使其成为应用程序。那里有很多教程(这里有一个)。如果您选择 gtk,您可以将侦听器连接到对鼠标运动作出反应的 matplotlib-widget。该侦听器的回调函数将获得一个事件,该事件将具有图形画布上鼠标位置的坐标以及您正在绘制的数据空间 (event.ydata
和) 中的坐标。event.xdata
至于悬停效果,使用补丁(matplotlib)可能是一种解决方案,如果您希望它们位于图表顶部,或者您可以将信息放在 gtk 中的另一个小部件中。
一些代码在路上(编写整个应用程序有点太多了,也就是说它不会做太多但会产生一些错误来复制和粘贴它们)。
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
self.image_fig = plt.Figure(figsize=figsize, dpi=75)
image_plot = self.image_fig.add_subplot(111)
image_canvas = FigureCanvas(self.image_fig)
self.image_fig.canvas.mpl_connect('motion_notify_event', self.plot_drag
self.plots_vbox.pack_start(image_canvas, False, False, 2)
最后,一旦您更新了绘图,您需要重新绘制画布。在我的示例行中,我会这样做
self.image_fig.canvas.draw()
matplotlib 有一些较低层的 API 和钩子,您可以使用它们来为绘图添加交互性。API 示例展示了一些方法来做到这一点。确切的方法可能取决于您使用的后端。