带入口的Tkinter列表框

时间:2013-07-30 01:44:13

标签: python tkinter listbox tkinter-entry

有没有办法让Tkinter Listbox的项目成为Entry Widgets?结果是您可以动态修改列表框条目中的文本。如果您的列表框如下所示:

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

那么你希望能够点击Apples并编写一些任意文本 - 然后你可以绑定Enter键,比如说,根据新文本触发一个函数。

4 个答案:

答案 0 :(得分:3)

我知道这个问题已经有一段时间了,但我创建了一个名为' ListboxEditable'的小部件,它可以充当列表框,当双击项目时,用户可以在条目中输入任何内容。然后,当用户单击另一行时,信息将保存在相应的已修改单元格中。请注意,用户可以使用向上和向下键浏览整个给定列表(所选行具有不同的背景颜色)。

此代码是根据Bryan Oakley的答案开发的。

我在这里留下一个最小的工作案例:

# 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类如下:

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

致以最诚挚的问候,

大卫

答案 1 :(得分:2)

不,tkinter不支持在列表框中就地编辑项目。当然,如果您确实不需要列表框,则可以始终将标签或条目小部件堆叠在一起以获得类似的效果。

答案 2 :(得分:0)

您可以为用户提供一些条目,然后从该输入中创建一个列表框

但你不能只改变那样的列表框文本

也许尝试不同的GUI库,如WX

修改

这是你可以做的事情:

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

这会在列表框中显示opt_list中的选项,然后当您输入例如5 hello条目并按下更改时,它会将选项hello添加到第五位

这是我能想到的唯一方式

答案 3 :(得分:0)

import tkinter as tk
root=tk.Tk()
# root.geometry('300x240')



sb = tk.Scrollbar(root)
sb.pack(side=tk.RIGHT,fill=tk.Y)
E1 = tk.Entry(root)
E1.pack()

mylist = [*range(15)]

v = tk.StringVar(value=mylist)
b1=tk.Listbox(root,activestyle='dotbox',yscrollcommand=sb.set,listvariable=v,selectmode='SINGLE')

sb.config(command=b1.yview)
# for i in range(1,15):
#     b1.insert(tk.END,i)
b1.pack()

def isfloat(s):
    try:
        return float(s)<float('inf')
    except:
        return False

def isInt(s):
    try:
        return int(s)<float('inf')
    except:
        return False

def set_entry_text(text):
    E1.delete(0,tk.END)
    E1.insert(0,text)


def set_item(event):
    text = E1.get()
    E1.delete(0,tk.END)
    index = b1.curselection()[0]
    b1.delete(index)
    b1.insert(index,text)
    print(v.get())

def set_item1(event):
    text = E1.get()
    E1.delete(0,tk.END)
    index = b1.curselection()[0]
    if isInt(text):
        mylist[index] = int(text)
    elif isfloat(text):
        mylist[index] = float(text)
    else:
        mylist[index] = text

    v.set(mylist)
    print(v.get())

def edit_item(event):
    text = E1.selection_get()
    # set_entry_text(text)
    E1.focus()


b1.bind('<Double-1>', edit_item)
E1.bind('<Return>',set_item1)
root.mainloop()