Matplotlib中的可编辑表格:如何在表格单元格上叠加文本框小部件?

2021-02-25 21:07:21 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在努力在Matplotlib中创建一个交互式表。我希望用户能够单击表中的数据单元格,以便编辑其值。根据@ImportanceOfBeingErnesthere的建议,我为表中的每个实际数据单元注册了一个pick事件处理程序。然后我可以检测到用户点击了哪个单元格。但是我不能将一个TextBox对象完全叠加在选中的单元格上,这样对用户来说就像是在编辑他们选中的单元格。在

说明问题的伪代码:

import matplotlib.pyplot as plt

from matplotlib.table import CustomCell
from matplotlib.widgets import TextBox

def on_pick(event):

    if isinstance(event.artist, CustomCell):
        cell = event.artist
        # Doesn't work because cell.get_y() is negative:
        #text_box_axes = plt.axes([cell.get_x(), cell.get_y(), cell.get_width(), cell.get_height()])

        # This doesn't work either but at least you can see the TextBox on the figure!
        text_box_axes = plt.axes([cell.get_x(), -cell.get_y(), cell.get_width(), cell.get_height()])

        cell_text = cell.get_text().get_text()
        TextBox(text_box_axes, '', initial=cell_text)
        plt.draw()

column_labels = ('Length', 'Width', 'Height', 'Sold?')
row_labels = ['Ferrari', 'Porsche']
data = [[2.2, 1.6, 1.2, True],
        [2.1, 1.5, 1.4, False]]
table = plt.table(cellText=data, colLabels=column_labels, rowLabels=row_labels, cellLoc='center', loc='bottom')
text_box = None

celld = table.get_celld()
for key in celld.keys():
    # Each key is a tuple of the form (row, column).
    # Column headings are in row 0. Row headings are in column -1.
    # So the first item of data in the table is actually at (1, 0).
    if key[0] > 0 and key[1] > -1:
        cell = celld[key]
        cell.set_picker(True)

canvas = plt.gcf().canvas
canvas.mpl_connect('pick_event', on_pick)
plt.axis('off')

plt.show()

但是如果我运行这个,然后点击,比如,有1.2的单元格,我会看到:

enter image description here

那么我如何获得TextBox的边界,使之与用户单击的单元格的边界完全匹配呢?在

文本框的轴似乎是相对于整个图形而不是相对于表格本身。在

2条回答
网友
1楼 ·

单元格的位置确实是在坐标轴中给出的,而TextBox的坐标则是图形坐标。您可以在两个坐标系之间转换为

trans = figure.transFigure.inverted()
trans2 = ax.transAxes
bbox = cell.get_bbox().transformed(trans2 + trans)
text_box_axes.set_position(bbox.bounds)

当然,您还需要确保每次提交单元格文本时都根据TextBox的内容进行更新。在

下面是一个功能齐全的可编辑matplotlib表。在

^{pr2}$

但是请注意,有时会有一些错误阻止单元格正确更新。我还没找到原因。 注意,在以前的版本中,使用了一个TextBox实例。然而,这导致了无法追踪的错误。相反,每次单击单元格时都需要创建一个新实例,如上面更新的版本所示。在

网友
2楼 ·

使用@ImportanceOfBeingErnest的非常有用的答案,我能够使我的原始代码适应一个有效的解决方案。是的,我知道它使用可怕的全局变量,等等,但至少它是有效的!在

import matplotlib.pyplot as plt

from matplotlib.table import CustomCell
from matplotlib.widgets import TextBox

def on_pick(event):

    if isinstance(event.artist, CustomCell):

        global text_box, current_cell, table
        if text_box is not None:
            plt.gcf().delaxes(text_box.ax)

        current_cell = event.artist
        table_axes = table.axes

        axes_to_display = table_axes.transAxes
        display_to_figure = table_axes.figure.transFigure.inverted()

        bbox = current_cell.get_bbox().transformed(axes_to_display + display_to_figure)
        text_box_axes = plt.axes(bbox.bounds)

        cell_text = current_cell.get_text().get_text()
        text_box = TextBox(text_box_axes, '', initial=cell_text)
        text_box.on_submit(update_table_cell)
        plt.draw()


def update_table_cell(new_value):

    global text_box, current_cell
    # Get rid of the textbox:
    plt.gcf().delaxes(text_box.ax)
    current_cell.get_text().set_text(new_value)
    text_box = None
    current_cell = None

    # TODO: Update the table data...
    plt.draw()


column_labels = ('Length', 'Width', 'Height', 'Sold?')
row_labels = ['Ferrari', 'Porsche']
data = [[2.2, 1.6, 1.2, True],
        [2.1, 1.5, 1.4, False]]
table = plt.table(cellText=data, colLabels=column_labels, rowLabels=row_labels, cellLoc='center', loc='bottom')
text_box = None
current_cell = None

celld = table.get_celld()
for key in celld.keys():
    # Each key is a tuple of the form (row, column).
    # Column headings are in row 0. Row headings are in column -1.
    # So the first item of data in the table is actually at (1, 0).
    if key[0] > 0 and key[1] > -1:
        cell = celld[key]
        cell.set_picker(True)

canvas = plt.gcf().canvas
canvas.mpl_connect('pick_event', on_pick)
plt.axis('off')

plt.show()

相关问题