Reputation: 487
Is there a way to edit a listbox item instead deleting it?
I am using a tkinter GUI with a listbox. Now my listbox could contain for example 10 items. If I would like to change the text of one item, is there a way to do it?
I know I can create a button to delete a single item, but is there any way to also jut edit an item?
Upvotes: 1
Views: 2697
Reputation: 127
modified version fo code shared by @Bryan Oakley, this version removes the scrollbar when editing is started and place back the scrollbar after editing is cancelled or done
import tkinter as tk
class EditableListbox:
def __init__(self):
self.editItemIndex = None
self.root = tk.Tk()
self.lb = tk.Listbox(self.root)
self.vsb = tk.Scrollbar(self.root, command=self.lb.yview)
self.vsb.pack(side="right", fill="y")
self.lb.configure(yscrollcommand=self.vsb.set)
self.lb.pack(side="left", fill="both", expand=True)
for i in range(100):
self.lb.insert("end", f"Item #{i + 1}")
self.lb.bind("<Double-1>", self.startEdit)
def startEdit(self, event):
index = self.lb.index(f"@{event.x},{event.y}")
self.vsb.pack_forget()
self._start_edit(index)
return "break"
def _start_edit(self, index):
self.editItemIndex = index
text = self.lb.get(index)
y0 = self.lb.bbox(index)[1]
entry = tk.Entry(self.root, borderwidth=0, highlightthickness=1)
entry.bind("<Return>", self.accept_edit)
entry.bind("<Escape>", self.cancel_edit)
entry.insert(0, text)
entry.selection_from(0)
entry.selection_to("end")
entry.place(relx=0, y=y0, relwidth=1, width=-20)
entry.focus_set()
entry.grab_set()
def cancel_edit(self, event):
event.widget.destroy()
self.vsb.pack(side="right", fill="y")
def accept_edit(self, event):
new_data = event.widget.get()
self.lb.delete(self.editItemIndex)
self.lb.insert(self.editItemIndex, new_data)
event.widget.destroy()
self.vsb.pack(side="right", fill="y")
def runGUI(self):
self.root.mainloop()
if __name__ == "__main__":
flc_app = EditableListbox()
flc_app.runGUI()
Upvotes: 2
Reputation: 1
Another variant, when dynamically populating listbox items from events, with use of EditableListbox(). If the listbox item is outside the visible area bbox() looks for, it raises a NoneType error on self.bbox(index)[1]. This is due to the newly added item being outside the visible window.
The below simply checks if the item is visible and calls see(index) respectively. However, it will manipulate the original visible state of the Listbox items (in regards to vscroll).
#TODO: Incorporate bbox() state preservation and restore after.
class EditableListbox(tk.Listbox):
"""A listbox where you can directly edit an item via double-click
listbox-text-edit-in-gui """
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.edit_item = None
self.bind("<Double-1>", self._start_edit)
def _start_edit(self, event):
index = self.index(f"@{event.x},{event.y}")
self.start_edit(index)
return "break"
def start_edit(self, index, accept_func=None,cancel_func=None):
if self.bbox(index) == None:
self.see(index)
self.edit_item = index
text = self.get(index)
y0 = self.bbox(index)[1]
entry = tk.Entry(self, borderwidth=0, highlightthickness=1)
entry.bind("<Return>", self.accept_edit)
entry.bind("<Escape>", self.cancel_edit)
entry.insert(0, text)
entry.selection_from(0)
entry.selection_to("end")
entry.place(relx=0, y=y0, relwidth=1, width=-1)
entry.focus_set()
entry.grab_set()
def cancel_edit(self, event):
event.widget.destroy()
def accept_edit(self, event):
new_data = event.widget.get()
self.delete(self.edit_item)
self.insert(self.edit_item, new_data)
event.widget.destroy()
Upvotes: 0
Reputation: 385900
The listbox doesn't support directly editing an item inside the listbox. You have to provide a mechanism for the user to enter data, and then you can replace just a single item in the listbox.
That being said, tkinter gives you all the tools you need to let the user double-click on an item and change it. Here's a quick hack of an example. In a nutshell, when you double-click, it will superimpose an entry widget over the item you clicked on, and when you press the return key it saves the item to the listbox.
import tkinter as tk
class EditableListbox(tk.Listbox):
"""A listbox where you can directly edit an item via double-click"""
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.edit_item = None
self.bind("<Double-1>", self._start_edit)
def _start_edit(self, event):
index = self.index(f"@{event.x},{event.y}")
self.start_edit(index)
return "break"
def start_edit(self, index):
self.edit_item = index
text = self.get(index)
y0 = self.bbox(index)[1]
entry = tk.Entry(self, borderwidth=0, highlightthickness=1)
entry.bind("<Return>", self.accept_edit)
entry.bind("<Escape>", self.cancel_edit)
entry.insert(0, text)
entry.selection_from(0)
entry.selection_to("end")
entry.place(relx=0, y=y0, relwidth=1, width=-1)
entry.focus_set()
entry.grab_set()
def cancel_edit(self, event):
event.widget.destroy()
def accept_edit(self, event):
new_data = event.widget.get()
self.delete(self.edit_item)
self.insert(self.edit_item, new_data)
event.widget.destroy()
root = tk.Tk()
lb = EditableListbox(root)
vsb = tk.Scrollbar(root, command=lb.yview)
lb.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
lb.pack(side="left", fill="both", expand=True)
for i in range(100):
lb.insert("end", f"Item #{i+1}")
root.mainloop()
Upvotes: 5