代码之家  ›  专栏  ›  技术社区  ›  snark

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

  •  1
  • snark  · 技术社区  · 6 年前

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

    用于说明问题的伪代码:

    导入matplotlib.pyplot as plt 从matplotlib.table导入自定义单元格 从matplotlib.widgets导入文本框 选择(事件)时的定义: 如果IsInstance(event.artist,customcell): 单元格=事件.艺术家 #不起作用,因为cell.get_y()为负: #text_box_axes=plt.axes([cell.get_x(),cell.get_y(),cell.get_width(),cell.get_height()]) #这也不起作用,但至少你能看到图上的文本框! text_box_axes=plt.axes([cell.get_x(),-cell.get_y(),cell.get_width(),cell.get_height()]) cell_text=cell.get_text().get_text()) 文本框(文本框轴,初始=单元格文本) 请画()) 列标签=(‘长度’、‘宽度’、‘高度’、‘售出?’) 行_labels=[“法拉利”,“保时捷”] 数据=[[2.2,1.6,1.2,真], [2.1、1.5、1.4、假]] table=plt.table(celltext=data,collabels=column_labels,rowlabels=row_labels,cellloc='center',loc='bottom') 文本框=无 celld=table.get-celld()。 对于celld.keys()中的键: #每个键都是表单的元组(行、列)。 #列标题在第0行。行标题在列-1中。 #所以表中的第一项数据实际上是(1,0)。 如果键[0]>0和键[1]>-1: 单元格=单元格d[键] 单元格.设置选取器(真) canvas=plt.gcf().canvas canvas.mpl_connect(“pick_event”,on_pick) PLT.轴(‘关闭’) 请显示())
    
    

    但是,如果我运行这个,然后点击,比如说,单元格中的

    那么,如何获取文本框的边界以精确匹配用户单击的单元格的边界?

    文本框的轴似乎是相对于整个图形的,而不是相对于表本身的。here我已经为表中的每个实际数据单元格注册了一个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

    那么我该如何得到文本框要精确匹配用户单击的单元格的边界吗?

    似乎文本框的轴与整个图形相关,而不是与表本身相关。

    2 回复  |  直到 6 年前
        1
  •  0
  •   ImportanceOfBeingErnest    6 年前

    单元格的位置确实是以轴坐标表示的,而 TextBox

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

    当然,您还需要确保根据

    import matplotlib.pyplot as plt
    
    from matplotlib.table import CustomCell
    from matplotlib.widgets import TextBox
    
    class EditableTable():
        def __init__(self, table):
            self.table = table
            self.ax = self.table.axes
            celld = table.get_celld()
            for key in celld.keys():
                if key[0] > 0 and key[1] > -1:
                    cell = celld[key]
                    cell.set_picker(True)
            self.canvas = self.table.get_figure().canvas
            self.cid = self.canvas.mpl_connect('pick_event', self.on_pick)
            self.tba = self.ax.figure.add_axes([0,0,.01,.01])
            self.tba.set_visible(False)
            self.tb = TextBox(self.tba, '', initial="")
            self.cid2 = self.tb.on_submit(self.on_submit)
            self.currentcell = celld[(1,0)]
    
        def on_pick(self, event):
            if isinstance(event.artist, CustomCell):
                # clear axes and delete textbox
                self.tba.clear()
                del self.tb
                # make textbox axes visible
                self.tba.set_visible(True)
                self.currentcell = event.artist
                # set position of textbox axes to the position of the current cell
                trans = self.ax.figure.transFigure.inverted()
                trans2 = self.ax.transAxes
                bbox = self.currentcell.get_bbox().transformed(trans2 + trans)
                self.tba.set_position(bbox.bounds)
                # create new Textbox with text of the current cell
                cell_text = self.currentcell.get_text().get_text()
                self.tb = TextBox(self.tba, '', initial=cell_text)
                self.cid2 = self.tb.on_submit(self.on_submit)
    
                self.canvas.draw()
    
        def on_submit(self, text):
            # write the text box' text back to the current cell
            self.currentcell.get_text().set_text(text)
            self.tba.set_visible(False)
            self.canvas.draw_idle()
    
    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]]
    
    fig, ax = plt.subplots()
    table = ax.table(cellText=data, colLabels=column_labels, rowLabels=row_labels, 
                     cellLoc='center', loc='bottom')
    
    et = EditableTable(table)
    
    ax.axis('off')
    
    plt.show()
    

    但是请注意,有时会有一些错误阻止单元格正确更新。我还没有找到原因。

        2
  •  0
  •   snark    6 年前

    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()