有没有办法让 Tkinter 列表框的项目成为条目小部件?结果将是您可以动态修改列表框条目中的文本。如果您的列表框看起来像:
--------
| Apples |
| Pears |
| Oranges |
---------
那么您可能希望能够单击 Apple 并编写一些任意文本 - 然后您可以绑定 Enter 键,例如,触发基于新文本的功能。
有没有办法让 Tkinter 列表框的项目成为条目小部件?结果将是您可以动态修改列表框条目中的文本。如果您的列表框看起来像:
--------
| Apples |
| Pears |
| Oranges |
---------
那么您可能希望能够单击 Apple 并编写一些任意文本 - 然后您可以绑定 Enter 键,例如,触发基于新文本的功能。
我知道这个问题已经有一段时间了,但是我创建了一个名为“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()
# 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())
不,tkinter 不支持对列表框中的项目进行就地编辑。当然,如果您真的不需要列表框,您可以随时将标签或条目小部件堆叠在一起以获得类似的效果。
您可以给用户一些条目,然后从该输入创建一个列表框
但你不能只改变这样的列表框文本
也许尝试不同的 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
到第五位
这是我能想到的唯一方法
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()