【问题标题】:How Do I Make Python Tkinter Widgets Created in a Loop Independant?如何使在循环中创建的 Python Tkinter 小部件独立?
【发布时间】:2020-11-19 16:27:41
【问题描述】:

我尝试通过附加将小部件放入列表中,但没有成功。我在 for 循环中创建了所有内容,但我希望每个月的列表框都有自己的滚动条。目前只有最后一个列表框可以正常工作,移动其他月份的滚动条会移动 12 月的列表框。

最终每天都可以点击并打开另一个窗口来添加每日交易。

    mainFrame = tk.Frame(tkRoot)
    mainFrame.config(bd=2, relief='groove')
    mainFrame.grid(row=0, column=0, padx=60)

    trxLabel = tk.Label(mainFrame, text='Transactions ' + str(year), font='courierNew 20 bold')
    trxLabel.grid(row=0, columnspan=12, pady=(20, 0))

    # Hold each month frame
    self.monthFrame = []
    self.dayList = []
    self.endBalList = []
    self.vScroll = []

    for i in range(12):
        monthLabel = tk.Label(mainFrame, text=month[i][0], font='courierNew 12 bold')
        monthLabel.grid(row=1, column=i, pady=(20, 0))

        self.monthFrame.append(tk.Frame(mainFrame))
        self.monthFrame[i].config(bd=2, relief='groove')
        self.monthFrame[i].grid(row=2, column=i, sticky=tk.W)

        self.dayList.append(tk.Listbox(self.monthFrame[i], width=3))
        self.dayList[i].config(bd=2, relief='sunken')
        self.dayList[i].grid(row=0, column=0, sticky=tk.W)

        self.endBalList.append(tk.Listbox(self.monthFrame[i], width=16))
        self.endBalList[i].config(bd=2, relief='raised', background='LIGHT GRAY')
        self.endBalList[i].grid(row=0, column=1, sticky=tk.W)

        self.vScroll.append(tk.Scrollbar(self.monthFrame[i], orient='vertical'))
        self.vScroll[i].grid(row=0, column=2, sticky=(tk.N, tk.S, tk.E))

        # Insert data
        for j in range(month[i][1]):
            self.dayList[i].insert(tk.END, 1 + j)
            self.endBalList[i].insert(tk.END, '999,999,999.99')

            self.getFrame(idx=i)

def getFrame(self, idx):
    self.dayList[idx].config(yscrollcommand=self.vScroll[idx].set)
    self.endBalList[idx].config(yscrollcommand=self.vScroll[idx].set)
    self.scrollAll(idx)

def scrollAll(self, idx):
    self.dayList[idx].yview(idx)
    self.endBalList[idx].yview(idx)

【问题讨论】:

  • stackoverflow 上有很多关于在循环中创建小部件的问题。您是否通读过其中任何一个以了解基本原则?
  • 我已经阅读了很多这样的问题,但是对于将小部件存储在列表中后我应该做什么感到困惑。我认为它会像 dayLabelID[i].config、dayLabelID[i].grid 那样工作。
  • 问问自己:如果scrollBoth 调用self.daysList.yview,你认为它应该如何知道它应该关注循环的哪个迭代? self.daysList 在任何时候只能引用一个小部件。
  • 我明白了,并为那些添加了索引,但没有骰子。我尝试将 command=functools.partial(self.scrollBoth, idx=i) 添加到滚动条并将 idx 传递给 scrollBoth,但随后出现错误提示“scrollBoth() 为参数 'idx' 获取了多个值”
  • 我用我最近尝试的方法更新了上面的代码。

标签: python for-loop user-interface tkinter listbox


【解决方案1】:

由于会重复创建同一组小部件,因此最好将它们嵌入到一个类中:

import tkinter as tk

class BalanceLog(tk.Frame):
    months = [
        ['January', 31],
        ['February', 28],  # Check for leap year
        ['March', 31],
        ['April', 30],
        ['May', 31],
        ['June', 30],
        ['July', 31],
        ['August', 31],
        ['September', 30],
        ['October', 31],
        ['November', 30],
        ['December', 31]
    ]
    def __init__(self, parent, month):
        super().__init__(parent)

        tk.Label(self, text=self.months[month-1][0], font='CourierNew 12 bold').grid(row=0, column=0, columnspan=3)
        tk.Label(self, text='Day', bd=1, relief='raised').grid(row=1, column=0, sticky='ew')
        tk.Label(self, text='Ending Balance', bd=1, relief='raised', anchor='e').grid(row=1, column=1, sticky='ew')

        sb = tk.Scrollbar(self, orient='vertical', command=self.on_scroll)
        sb.grid(row=2, column=2, sticky='ns')

        self.day_list = tk.Listbox(self, width=3, activestyle='none', exportselection=0, yscrollcommand=sb.set)
        self.day_list.grid(row=2, column=0, sticky='ew')
        self.day_list.bind('<<ListboxSelect>>', self.on_select)

        self.balance_list = tk.Listbox(self, width=16, activestyle='none', exportselection=0, justify='right', yscrollcommand=sb.set)
        self.balance_list.grid(row=2, column=1, sticky='ew')
        self.balance_list.bind('<<ListboxSelect>>', self.on_select)

        # fill the listboxes
        for day in range(1, self.months[month-1][1]+1):
            self.day_list.insert('end', day)
            self.balance_list.insert('end', str(day)+',999,999.99')

    def on_scroll(self, *args):
        self.day_list.yview(*args)
        self.balance_list.yview(*args)

    def on_select(self, event):
        selected = event.widget.curselection()[0]
        # update selection of another listbox
        another = self.balance_list if event.widget is self.day_list else self.day_list
        another.selection_clear(0, 'end')
        another.selection_set(selected)
        another.see(selected)

然后使用类创建所需的项目:

root = tk.Tk()
for month in range(1, 13):
    BalanceLog(root, month).pack(side='left')
root.mainloop()

【讨论】:

  • 感谢您的帮助,我只是想从总体上理解 Tkitner,因为除了简单的小部件之外,文档非常简单。我使用列表更新了上面的代码,但我无法让两个列表框都滚动。
  • 您是否使用了我的代码,因为它在我的 Python 环境中运行良好。
  • 还没有,因为我不完全理解嵌入,也不想编写我不理解的东西。我想尝试这两种方式,一种是使用您的嵌入方法,另一种是我在上面使用列表更新的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多