Список Tkinter с записью

Есть ли способ, чтобы элементы списка Tkinter были виджетами ввода? В результате вы можете динамически изменять текст в записи Listbox. Если ваш список выглядит так:

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

тогда вы хотели бы иметь возможность нажимать на яблоки и писать произвольный текст - затем вы могли бы привязать клавишу Enter, скажем, для запуска функции, основанной на новом тексте.


person en_Knight    schedule 30.07.2013    source источник


Ответы (4)


вы можете дать пользователю несколько записей, а затем создать список из этого ввода

но вы не можете просто так изменить текст списка

возможно, попробуйте другую библиотеку графического интерфейса, например 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 на пятое место.

это единственный способ, о котором я могу думать

person Serial    schedule 30.07.2013
comment
Это то, что я предполагал, но у меня будет зуд, пока я не увижу, почему нет - это где-то сказано? - person en_Knight; 30.07.2013
comment
хм, я не думаю, что это действительно где-то написано, это больше похоже на то, что Tk недостаточно продвинут, чтобы делать такие вещи ... хотя я плохо оглядываюсь - person Serial; 30.07.2013
comment
Это то, что у меня было (более или менее), но если список был далеко от записей, он медленно щелкал назад и вперед, а если он был прямо рядом с ними, он выглядел грязным. Ничего страшного, и этот стиль — довольно хороший способ написания решения. Спасибо! - person en_Knight; 30.07.2013
comment
вы можете отредактировать макет, который я только что набрал очень быстро, из некоторого кода, который я был рад, что смог помочь :) - person Serial; 30.07.2013

Я знаю, что прошло некоторое время с момента этого вопроса, но я создал виджет под названием «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())

Некоторые скриншоты

введите здесь описание изображения

введите здесь описание изображения

введите здесь описание изображения

person David Duran    schedule 26.05.2017
comment
Я знаю, что прошли годы, но вы сделали ListBoxEditable подклассом object вместо Frame. Я видел это раньше, но интересно, почему? Если я хочу использовать этот класс, как мне использовать pack или grid, чтобы поместить его в мастер-фрейм? Это так же просто, как изменить объявление на class ListboxEditable(Frame) вместо class ListboxEditable(object):? Прошу прощения, если это глупый вопрос - я здесь новичок. - person lukehawk; 14.11.2018
comment
@lukehawk, в этом случае созданный мной список имел метод для его размещения под названием «placeListBoxEditable()». Вам просто нужно позвонить, когда вы хотите разместить его. Он основан на системе сетки. - person David Duran; 15.11.2018

Нет, tkinter не поддерживает редактирование элементов в списке. Конечно, если вам на самом деле не нужен список, вы всегда можете сложить метки или виджеты ввода друг над другом, чтобы получить аналогичный эффект.

person Bryan Oakley    schedule 30.07.2013

person    schedule
comment
Было бы здорово, если бы вы добавили пояснение к своему коду и чем он отличается от принятого ответа. Просто сбрасывать код не всегда лучше. - person razdi; 06.10.2020