【问题标题】:Tkinter Listbox with Entry带有条目的 Tkinter 列表框
【发布时间】:2013-07-30 00:15:07
【问题描述】:

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

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

然后您希望能够单击 Apple 并编写一些任意文本 - 然后您可以绑定 Enter 键,例如,根据新文本触发一个函数。

【问题讨论】:

    标签: python tkinter listbox tkinter-entry


    【解决方案1】:

    我知道这个问题已经有一段时间了,但我创建了一个名为“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())
    

    一些截图

    【讨论】:

    • 我知道已经有好几年了——但是,您将ListBoxEditable 设为object 的子类,而不是Frame。我以前见过这个,但很好奇为什么?如果我想使用这个类,如何使用packgrid 将其放置在主框架中?是否像将声明更改为 class ListboxEditable(Frame) 而不是 class ListboxEditable(object): 一样简单?如果这是一个愚蠢的问题,我深表歉意 - 我是新来的。
    • @lukehawk,在这种情况下,我创建的列表框有一个放置它的方法,称为“placeListBoxEditable()”。你只需要在你想放置它的时候调用它。它基于网格系统。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      你可以给用户一些条目然后从那个输入创建一个列表框

      但你不能像那样更改列表框文本

      也许可以尝试不同的 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 添加到第五位

      这是我能想到的唯一方法

      【讨论】:

      • 这是我的假设,但我会发痒,直到我明白为什么不 - 它在某处说这个吗?
      • 嗯,我不认为它真的是写在任何地方,它更像是 Tk 还不够先进,无法做那种事情......虽然环顾四周
      • 这就是我所拥有的(或多或少),但如果列表框离条目很远,则来回单击很慢,如果它就在它们旁边,它看起来很乱。没什么大不了的,这种风格是编写解决方案的好方法。谢谢!
      • 你可以编辑我刚刚输入的布局,我很高兴我能帮上忙:)
      【解决方案4】:
      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()
      

      【讨论】:

      • 如果您可以围绕您的代码添加一些解释以及它与接受的答案有何不同,那就太好了。仅仅转储代码并不总是最好的。
      猜你喜欢
      • 2011-11-22
      • 2021-07-19
      • 2017-10-19
      • 2013-11-07
      • 1970-01-01
      • 2011-07-14
      • 2013-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多