2013-09-01 16 views
6

Czy istnieje sposób na wykorzystanie widoku drzewa ttk z edytowalnymi wierszami?Jak dokonać edycji wierszy ttk.Treeview?

Mam na myśli, że powinien działać bardziej jak stół. Np. Dwukrotne kliknięcie elementu powoduje, że kolumna # 0 jest "edytowalna".

Jeśli nie jest to możliwe, każdy sposób, aby umożliwić wybór myszy na produkcie, byłby w porządku. Nie znalazłem żadnej wzmianki o tym w tkdocs lub innych dokumentach.

+0

I opracowali sposób do klikania komórka w widoku drzewa i utwórz pole na górze klikniętej komórki, aby można było edytować wartość komórki. Jednak jedna z metod przeglądania drzewa, którą użyłem do jej wyłączenia, działa tylko na moim Macu, ale nie na Windows. Dziwne jest to, że technicznie rzecz biorąc nie powinno to działać na Macu, a jednak tak jest. Nie podałeś swojej platformy, ale jeśli używasz komputera Mac (i nie będziesz go uruchamiał w systemie Windows), daj mi znać, a ja opublikuję odpowiedź ze szczegółami. –

+1

Zrobiłem to samo i działa zarówno na systemie Linux, jak i na Windowsie, nie mam okazji wypróbować go na Macu. Nie muszę już edytować tekstu, stworzyłem widżet Entry tylko do odczytu. Więc jeśli twoje "Mac-only solution" ma problem z wyświetlaniem popupu Entry, być może moje rozwiązanie może cię zainspirować. Zobacz moją odpowiedź na to pytanie dla przykładu kodu. – dakov

+0

Mam podobne ograniczenia, głównie używając Treeview do naśladowania tabeli, ponieważ nie ma widgetu podobnego do tabeli w tkinter/ttk. Jeśli nie używasz widoku drzewa jako "drzewa", możesz wypróbować tkintertable (https://code.google.com/p/tkintertable/). Zasadniczo pozwala na funkcjonalność arkusza kalkulacyjnego i jest stosunkowo aktualny, dobrze udokumentowany i dość bogaty w funkcje. – Fiver

Odpowiedz

2

Po długich badaniach nie znalazłem takiej funkcji, więc myślę, że to istnieje. Tk jest bardzo prostym interfejsem, który pozwala programistom na budowanie funkcji "wysokiego poziomu" od podstaw. Tak więc moje pożądane zachowanie w ten sposób.

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) 

    # clicked row parent id 
    parent = self._tree.parent(rowid) 

    # do nothing if item is top-level   
    if parent == '': 
     return 

    # get column position info 
    x,y,width,height = self._tree.bbox(rowid, column) 

    # y-axis offset 
    pady = height // 2 

    # place Entry popup properly   
    url = self._tree.item(rowid, 'text') 
    self.entryPopup = EntryPopup(self._tree, url) 
    self.entryPopup.place(x=0, y=y+pady, anchor=W, relwidth=1) 

Jest to metoda w klasie, która komponuje ttk.Treeview jak self._tree

I EntryPopup jest wtedy bardzo proste podklasa wpisu:

class EntryPopup(Entry): 

    def __init__(self, parent, text, **kw): 
     ''' If relwidth is set, then width is ignored ''' 
     super().__init__(parent, **kw) 

     self.insert(0, text) 
     self['state'] = 'readonly' 
     self['readonlybackground'] = 'white' 
     self['selectbackground'] = '#1BA1E2' 
     self['exportselection'] = False 

     self.focus_force() 
     self.bind("<Control-a>", self.selectAll) 
     self.bind("<Escape>", lambda *ignore: self.destroy()) 

    def selectAll(self, *ignore): 
     ''' Set selection on the whole text ''' 
     self.selection_range(0, 'end') 

     # returns 'break' to interrupt default key-bindings 
     return 'break' 
+0

To bardzo przypomina proces, którego używam. W moim przypadku używam 'ident_region_' w celu określenia, gdzie użytkownik kliknął w widoku drzewa i wyświetla tylko pole wprowadzania, jeśli użytkownik kliknął komórkę. Dokumenty, których początkowo używałem, nie wspominały o tym, że ta metoda działa tylko z Tk 8.6 i wyżej, więc spróbowałem. Cudownie to działało ... na moim Macu. Dopiero kiedy rzucił wyjątek na system Windows mojego klienta, odkryłem, że moduły Tkinter i ttk Pythona używają obecnie Tk 8.5. Dlaczego to działa dla mnie? Brak pomysłu. W każdym razie dałeś mi kilka pomysłów, jak to naprawić. Dzięki! –

+0

Proszę, możesz podać przykład pracy, mam na myśli ten kod nie działa, jeśli próbuję go uruchomić. – nbro

0

Nie wiem, jak ustawić wiersz do edycji, ale aby przechwycić kliknięcie wiersza, należy użyć zdarzenia wirtualnego . Zostanie to przypisane do procedury za pomocą metody bind(), a następnie użyj metody selection(), aby uzyskać identyfikatory wybranych elementów.

Są to fragmenty z istniejącego programu, ale wykazują podstawową sekwencję połączeń:

# 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

To właśnie do tworzenia drzewo dla określonej ścieżki ustawionej w konstruktorze. możesz powiązać swoje wydarzenie z przedmiotem na tym drzewie. Funkcja zdarzenia jest pozostawiona w taki sposób, aby przedmiot mógł być wykorzystany na wiele sposobów. W takim przypadku po dwukrotnym kliknięciu będzie wyświetlać nazwę przedmiotu. Mam nadzieję, że to pomaga komuś.

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

Można również wyświetlić okno narzędzia z polami do edycji wymienionymi za pomocą wpisów, aby zaktualizować wartości. Ten przykład ma widok drzewa z trzema kolumnami i nie używa podklas.

powiązać podwójnego kliknięcia do tego:

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) 

następnie potwierdź zmiany:

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 

Oto jak usunąć wpis:

def DeleteCurrentEntry(self, treeView): 
    curr = treeView.focus() 

    if '' == curr: return 

    treeView.delete(curr) 
Powiązane problemy