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

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

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

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


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


Ответы (4)


arrow_upward
0
arrow_downward

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

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

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

arrow_upward
5
arrow_downward

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

arrow_upward
2
arrow_downward

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

person Bryan Oakley    schedule 30.07.2013

arrow_upward

arrow_downward

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