How to make ttk.Treeview's rows editable?

后端 未结 4 831
攒了一身酷
攒了一身酷 2020-12-06 06:44

Is there any way to use ttk Treeview with editable rows?

I mean it should work more like a table. For example on double click on the it

相关标签:
4条回答
  • 2020-12-06 06:55

    After long research I haven't found such feature so I guess there's any. Tk is very simple interface, which allows programmer to build 'high-level' features from the basics. So my desired behaviour this way.

    def onDoubleClick(self, event):
        ''' Executed, when a row is double-clicked. Opens 
        read-only EntryPopup above the item's column, so it is possible
        to select text '''
    
        # close previous popups
        # self.destroyPopups()
    
        # what row and column was clicked on
        rowid = self._tree.identify_row(event.y)
        column = self._tree.identify_column(event.x)
    
        # get column position info
        x,y,width,height = self._tree.bbox(rowid, column)
    
        # y-axis offset
        # pady = height // 2
        pady = 0
    
        # place Entry popup properly         
        text = self._tree.item(rowid, 'text')
        self.entryPopup = EntryPopup(self._tree, rowid, text)
        self.entryPopup.place( x=0, y=y+pady, anchor=W, relwidth=1)
    

    This is method within a class which composes ttk.Treeview as self._tree

    And EntryPopup is then very simple sub-class of Entry:

    class EntryPopup(Entry):
    
        def __init__(self, parent, iid, text, **kw):
            ''' If relwidth is set, then width is ignored '''
            super().__init__(parent, **kw)
            self.tv = parent
            self.iid = iid
    
            self.insert(0, text) 
            # self['state'] = 'readonly'
            # self['readonlybackground'] = 'white'
            # self['selectbackground'] = '#1BA1E2'
            self['exportselection'] = False
    
            self.focus_force()
            self.bind("<Return>", self.on_return)
            self.bind("<Control-a>", self.select_all)
            self.bind("<Escape>", lambda *ignore: self.destroy())
    
        def on_return(self, event):
            self.tv.item(self.iid, text=self.get())
            self.destroy()
    
        def select_all(self, *ignore):
            ''' Set selection on the whole text '''
            self.selection_range(0, 'end')
    
            # returns 'break' to interrupt default key-bindings
            return 'break'
    
    0 讨论(0)
  • 2020-12-06 07:08

    You could also pop up a tool window with the editable fields listed with Entries to update the values. This example has a treeview with three columns, and does not use subclasses.

    Bind your double click to this:

    def OnDoubleClick(self, treeView):
        # First check if a blank space was selected
        entryIndex = treeView.focus()
        if '' == entryIndex: return
    
        # Set up window
        win = Toplevel()
        win.title("Edit Entry")
        win.attributes("-toolwindow", True)
    
        ####
        # Set up the window's other attributes and geometry
        ####
    
        # Grab the entry's values
        for child in treeView.get_children():
            if child == entryIndex:
                values = treeView.item(child)["values"]
                break
    
        col1Lbl = Label(win, text = "Value 1: ")
        col1Ent = Entry(win)
        col1Ent.insert(0, values[0]) # Default is column 1's current value
        col1Lbl.grid(row = 0, column = 0)
        col1Ent.grid(row = 0, column = 1)
    
        col2Lbl = Label(win, text = "Value 2: ")
        col2Ent = Entry(win)
        col2Ent.insert(0, values[1]) # Default is column 2's current value
        col2Lbl.grid(row = 0, column = 2)
        col2Ent.grid(row = 0, column = 3)
    
        col3Lbl = Label(win, text = "Value 3: ")
        col3Ent = Entry(win)
        col3Ent.insert(0, values[2]) # Default is column 3's current value
        col3Lbl.grid(row = 0, column = 4)
        col3Ent.grid(row = 0, column = 5)
    
        def UpdateThenDestroy():
            if ConfirmEntry(treeView, col1Ent.get(), col2Ent.get(), col3Ent.get()):
                win.destroy()
    
        okButt = Button(win, text = "Ok")
        okButt.bind("<Button-1>", lambda e: UpdateThenDestroy())
        okButt.grid(row = 1, column = 4)
    
        canButt = Button(win, text = "Cancel")
        canButt.bind("<Button-1>", lambda c: win.destroy())
        canButt.grid(row = 1, column = 5)
    

    Then confirm the changes:

    def ConfirmEntry(self, treeView, entry1, entry2, entry3):
        ####
        # Whatever validation you need
        ####
    
        # Grab the current index in the tree
        currInd = treeView.index(treeView.focus())
    
        # Remove it from the tree
        DeleteCurrentEntry(treeView)
    
        # Put it back in with the upated values
        treeView.insert('', currInd, values = (entry1, entry2, entry3))
    
        return True
    

    Here's how to delete an entry:

    def DeleteCurrentEntry(self, treeView):
        curr = treeView.focus()
    
        if '' == curr: return
    
        treeView.delete(curr)
    
    0 讨论(0)
  • 2020-12-06 07:09

    This is just for creating a tree for the specified path that is set in the constructor. you can bind your event to your item on that tree. The event function is left in a way that the item could be used in many ways. In this case, it will show the name of the item when double clicked on it. Hope this helps somebody.

        import ttk
        from Tkinter import*
        import os*
    
        class Tree(Frame):
    
        def __init__(self, parent):
            Frame.__init__(self, parent)
            self.parent = parent
            path = "/home/...."
            self.initUI(path)
    
        def initUI(self, path):
            self.parent.title("Tree")
            self.tree = ttk.Treeview(self.parent)
            self.tree.bind("<Double-1>", self.itemEvent)
            yScr = ttk.Scrollbar(self.tree, orient = "vertical", command = self.tree.yview)
            xScr = ttk.Scrollbar(self.tree, orient = "horizontal", command = self.tree.xview)
            self.tree.configure(yscroll = yScr.set, xScroll = xScr.set)
            self.tree.heading("#0", text = "My Tree", anchor = 'w')
            yScr.pack(side = RIGHT, fill = Y)
    
            pathy = os.path.abspath(path) 
            rootNode = self.tree.insert('', 'end', text = pathy, open = True)
            self.createTree(rootNode, pathy)
    
            self.tree.pack(side = LEFT, fill = BOTH, expand = 1, padx = 2, pady = 2)
    
            self.pack(fill= BOTH, expand = 1) 
    
        def createTree(self, parent, path)
            for p in os.listdir(path)
                pathy = os.path.join(path, p)
                isdir = os.path.isdir(pathy)
                oid = self.tree.insert(parent, 'end' text = p, open = False)
                if isdir:
                   self.createTree(oid, pathy)
    
        def itemEvent(self, event):
            item = self.tree.selection()[0] # now you got the item on that tree
            print "you clicked on", self.tree.item(item,"text")
    
    
    
        def main():
            root = Tk.Tk()
            app = Tree(root)
            root.mainloop()
    
        if __name__ == '__main__'
           main()
    
    0 讨论(0)
  • 2020-12-06 07:17

    I don't know about making the row editable, but to capture clicking on a row, you use the <<TreeviewSelect>> virtual event. This gets bound to a routine with the bind() method, then you use the selection() method to get the ids of the items selected.

    These are snippets from an existing program, but show the basic sequence of calls:

    # in Treeview setup routine
        self.tview.tree.bind("<<TreeviewSelect>>", self.TableItemClick)
    
    # in TableItemClick()
        selitems = self.tview.tree.selection()
        if selitems:
            selitem = selitems[0]
            text = self.tview.tree.item(selitem, "text") # get value in col #0
    
    0 讨论(0)
提交回复
热议问题