Tkinter Listbox with Entry

10,772

Solution 1

you could give the user some entries then create a listbox from that input

but you cant just change a listboxes text like that

maybe try a different GUI Library like WX

EDIT

here is something you can do:

from Tkinter import *


root = Tk()
opt_list = ['opt1','opt2','opt3','opt4','opt5']
sel_list = []

def get_sel():
    sel_list.append(Lb1.curselection())
    root.destroy()

def change_opt():
    entry = E.get()
    change = entry.split(" ")
    print change
    Lb1.insert(int(change[0]),change[1])
    root.update()


def cancel():
    root.destroy()
E = Entry(root)
A = Button(root, text ="Change", command = change_opt)
B = Button(root, text ="Submit", command = get_sel)
C = Button(root, text ="Cancel", command = cancel)
Lb1 = Listbox(root, selectmode=MULTIPLE)


for i,j in enumerate(opt_list):
    Lb1.insert(i,j)


Lb1.pack()
B.pack()
C.pack()
E.pack()
A.pack()

root.mainloop()

this will make a listbox with the options in opt_list then when you type for example 5 hello the entry and press Change it will add the option hello to the fifth place

thats the only way i can think of

Solution 2

I know it has been a while since this question, but I have created a widget called 'ListboxEditable', which is able to act as a listbox and, when double-clicking on an item, the user can type anything inside an entry. Then, when the user clicks another row, the information is saved on the corresponding modified cell. Note that the user can use the up and down keys to browse the entire given list (the selected row has a different background color).

This code has been developed based on the answer from @Bryan Oakley.

Minimal working case

# Imports
from tkinter import *
from tkinter.ttk import *
# Import for the listboxEditable
from ListboxEditable import *

# Colors
colorActiveTab="#CCCCCC" # Color of the active tab
colorNoActiveTab="#EBEBEB" # Color of the no active tab
# Fonts
fontLabels='Calibri'
sizeLabels2=13

# Main window
root = Tk()

# *** Design *****
frame_name=Frame(root,bg=colorActiveTab) # Column frame
frame_name_label=Frame(frame_name,bg='blue') # Label frame
label_name=Label(frame_name_label, text="Header", bg='blue', fg='white', font=(fontLabels, sizeLabels2, 'bold'), pady=2, padx=2, width=10)
frame_name_listbox=Frame(frame_name,bg='blue') # Label frame
list_name=['test1','test2','test3']
listBox_name=ListboxEditable(frame_name_listbox,list_name)

# *** Packing ****
frame_name.pack(side=LEFT,fill=Y)
frame_name_label.pack(side=TOP, fill=X)
label_name.pack(side=LEFT,fill=X)
frame_name_listbox.pack(side=TOP, fill=X)
listBox_name.placeListBoxEditable()

# Infinite loop
root.mainloop()

ListboxEditable class

# Author: David Duran Perez
# Date: May 26, 2017

# Necessary imports
from tkinter import *
from tkinter import ttk

# Colors
colorActiveTab="#CCCCCC" # Color of the active tab
colorNoActiveTab="#EBEBEB" # Color of the no active tab
# Fonts
fontLabels='Calibri'
sizeLabels2=13

class ListboxEditable(object):
    """A class that emulates a listbox, but you can also edit a field"""
    # Constructor
    def __init__(self,frameMaster,list):
        # *** Assign the first variables ***
        # The frame that contains the ListboxEditable
        self.frameMaster=frameMaster
        # List of the initial items
        self.list=list
        # Number of initial rows at the moment
        self.numberRows=len(self.list)

        # *** Create the necessary labels ***
        ind=1
        for row in self.list:
            # Get the name of the label
            labelName='label'+str(ind)
            # Create the variable
            setattr(self, labelName, Label(self.frameMaster, text=self.list[ind-1], bg=colorActiveTab, fg='black', font=(fontLabels, sizeLabels2), pady=2, padx=2, width=10))

            # ** Bind actions
            # 1 left click - Change background
            getattr(self, labelName).bind('<Button-1>',lambda event, a=labelName: self.changeBackground(a))
            # Double click - Convert to entry
            getattr(self, labelName).bind('<Double-1>',lambda event, a=ind: self.changeToEntry(a))
            # Move up and down
            getattr(self, labelName).bind("<Up>",lambda event, a=ind: self.up(a))
            getattr(self, labelName).bind("<Down>",lambda event, a=ind: self.down(a))

            # Increase the iterator
            ind=ind+1

    # Place
    def placeListBoxEditable(self):
        # Go row by row placing it
        ind=1
        for row in self.list:
            # Get the name of the label
            labelName='label'+str(ind)
            # Place the variable
            getattr(self, labelName).grid(row=ind-1,column=0)

            # Increase the iterator
            ind=ind+1


    # Action to do when one click
    def changeBackground(self,labelNameSelected):
        # Ensure that all the remaining labels are deselected
        ind=1
        for row in self.list:
            # Get the name of the label
            labelName='label'+str(ind)
            # Place the variable
            getattr(self, labelName).configure(bg=colorActiveTab)

            # Increase the iterator
            ind=ind+1

        # Change the background of the corresponding label
        getattr(self, labelNameSelected).configure(bg=colorNoActiveTab)
        # Set the focus for future bindings (moves)
        getattr(self, labelNameSelected).focus_set()


    # Function to do when up button pressed
    def up(self, ind):
        if ind==1: # Go to the last
            # Get the name of the label
            labelName='label'+str(self.numberRows)
        else: # Normal
            # Get the name of the label
            labelName='label'+str(ind-1)

        # Call the select
        self.changeBackground(labelName)


    # Function to do when down button pressed
    def down(self, ind):
        if ind==self.numberRows: # Go to the last
            # Get the name of the label
            labelName='label1'
        else: # Normal
            # Get the name of the label
            labelName='label'+str(ind+1)

        # Call the select
        self.changeBackground(labelName)


    # Action to do when double-click
    def changeToEntry(self,ind):
        # Variable of the current entry
        self.entryVar=StringVar()
        # Create the entry
        #entryName='entry'+str(ind) # Name
        self.entryActive=ttk.Entry(self.frameMaster, font=(fontLabels, sizeLabels2), textvariable=self.entryVar, width=10)
        # Place it on the correct grid position
        self.entryActive.grid(row=ind-1,column=0)
        # Focus to the entry
        self.entryActive.focus_set()

        # Bind the action of focusOut
        self.entryActive.bind("<FocusOut>",lambda event, a=ind: self.saveEntryValue(a))

    
    # Action to do when focus out from the entry
    def saveEntryValue(self,ind):
        # Find the label to recover
        labelName='label'+str(ind)
        # Remove the entry from the screen
        self.entryActive.grid_forget()
        # Place it again
        getattr(self, labelName).grid(row=ind-1,column=0)
        # Change the name to the value of the entry
        getattr(self, labelName).configure(text=self.entryVar.get())

Some sreenshots

enter image description here

enter image description here

enter image description here

Solution 3

No, tkinter doesn't support in-place editing of items in a listbox. Of course, if you don't really need a listbox, you can always stack labels or entry widgets on top of each other to get a similar effect.

Share:
10,772
en_Knight
Author by

en_Knight

Updated on June 04, 2022

Comments

  • en_Knight
    en_Knight almost 2 years

    Is there a way to have the items of a Tkinter Listbox be Entry Widgets? The result would be that you could dynamically modify the text in an Listbox entry. If your Listbox looks like:

     --------
    | Apples  |
    | Pears   |
    | Oranges |
     ---------
    

    then you would want to be able to click on Apples and write some arbitrary text - you could then bind the Enter key, say, to trigger a function based on the new text.

  • en_Knight
    en_Knight almost 11 years
    This is what I assumed, but I'll have the itch until I see why not - does it say this somewhere?
  • Serial
    Serial almost 11 years
    hmm i dont think it is really written anywhere its more of like Tk isnt advanced enough to do that kind of thing... ill look around though
  • en_Knight
    en_Knight almost 11 years
    This is what I had (more or less), but if the listbox was far away from the entries it was slow to click back and forth, and if it was right next to them it looked messy. Not a big deal, and this style is a pretty good way of writing the solution. Thanks!
  • Serial
    Serial almost 11 years
    you can edit the layout i just typed that up real quick from some code i had glad i could help:)
  • lukehawk
    lukehawk over 5 years
    I know its been years - but, you made the ListBoxEditable a subclass of object instead of Frame. I have seen this before, but curious why? If I want to use this class, how do I use pack or grid to place it in the master frame? Is it as easy as changing the declaration to class ListboxEditable(Frame) instead of class ListboxEditable(object):? I apologize if this is a stupid question - I am new here.
  • David Duran
    David Duran over 5 years
    @lukehawk, in this case, the listbox I created had a method to place it called 'placeListBoxEditable()'. You just have to call it when you want to place it. It is based on a grid system.
  • razdi
    razdi over 3 years
    It would be great if you could add some explanation around your code and how it is different from the accepted answer. Just dumping code is not always the best.