Editable table in Matplotlib: How to superimpose a TextBox widget on a table cell?

后端 未结 2 2038
北海茫月
北海茫月 2021-01-24 07:18

I\'m progressing towards creating an interactive table in Matplotlib. I want the user to be able to click on a data cell in the table so they can edit its value. Based on the ad

2条回答
  •  感情败类
    2021-01-24 08:00

    The cell's position is indeed given in axes coordinates, while the TextBox's axes lives in figure coordinates. You may transform in between the two coordinate systems as

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

    Of course you then also need to make sure the cell text is updated according to the content of the TextBox, each time it is submitted.

    The following would be a fully functional editable matplotlib table.

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

    Note however that there is some bug sometimes preventing the cell to be correctly updated. I haven't found out the reason for this yet. Note that in a previous version of this, a single TextBox instance was used. However this led to untraceable errors. Instead one would need to create a new instance each time a cell is clicked, as in the above updated version.

提交回复
热议问题