【问题标题】:Assigning items from dictionary's set to a Combobox将字典集中的项目分配给组合框
【发布时间】:2021-04-12 17:31:03
【问题描述】:

我的计划是创建一个包含个人姓名的组合框,以便用户可以从集合中选择一个名称并将其点数输入到输入框中。我尝试这样做,但我的下拉列表没有显示个人集合中的名称列表,而是显示所有 5 个集合:Team1、Team2、Team3、Team4 和 Individuals。

最初,一切都安排好,以便用户输入个人的姓名,该姓名保存在字典teams = {... 'Individuals': set()} 中,之后姓名将显示在列表框中。在下一个窗口中,应该有一个下拉列表,其中包含个人集合中的所有名称,但是,正如我上面所说,我无法创建它。

我想知道如何解决这个问题。

我的代码:

from tkinter import *
from tkinter import messagebox
import tkinter.ttk as ttk

# This code is a simplified version of a full program code. In the original program, there is not only a list
# of individuals, but also lists of team1, team2 ... team4.
# However, now I am only interested in the problems associated with individual list,
# and I cut out all the part of the code related to team.


class CollegeApp(Tk):
    def __init__(self):
        Tk.__init__(self)
        container = ttk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        self.frames = {}
        for F in (IndividPage, listCheckPage, counterPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame(IndividPage)
        self.lift()

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()


# In this  class I have created a window with entry widget to input name of individual and save it in
# eponymous set "Individual"

class IndividPage(ttk.Frame):

    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        self.userEntry()

    def userEntry(self):
        headingTest = Label(self, text="Enter your User Name:", font="Arial 20")
        headingTest.grid(row=0, column=0, pady=5, padx=5)

        self.usernameEnter = Entry(self, width=40)
        self.usernameEnter.grid(row=0, column=1, padx=5, pady=5)

        self.TeamName = StringVar(self)
        self.TeamName.set("Individual")

        confirmBtn = Button(self, text="Confirm User", font="Arial 16",
                            command=self.confirm)

        confirmBtn.config(height=4, width=12)
        confirmBtn.grid(row=2, column=2, sticky=E, padx=45, pady=360)

# Checking the "add_to_team" function has been executed and moving to the next page.
    def confirm(self):
        if self.add_to_team():
            self.controller.show_frame(listCheckPage)

# Function to check the presence of input

    def add_to_team(self):
        user = self.usernameEnter.get()
        if len(user) == 0:
            messagebox.showwarning(title='No user', message='Please enter a username!')
            return
        if self.usernameEnter.get():
            self.controller.show_frame(listCheckPage)

        team_name = self.TeamName.get()
        team = teams[team_name]

        team.add(user)
        self.controller.frames[listCheckPage].team_listboxes[team_name].insert(END, user)
        print(teams)

# Class that creates page with lists of four teams and individuals (Focusing on individuals right now)
# Also there is two buttons "Add User" and "Start Counter" to start points calculator

class listCheckPage(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        self.userEntry()

    def userEntry(self):
        self.team_listboxes = {}
        for col_num, teamname in enumerate(teams):
            teamMembers = Listbox(self)
            teamMembers.config(height=13, width=15)
            teamMembers.grid(row=0, column=col_num, padx=5, pady=50, sticky=S)
            for i, user in enumerate(teams[teamname]):
                teamMembers.insert(i, user)
            self.team_listboxes[teamname] = teamMembers

        INDHeading = Label(self, text="Individuals", font="Arial 16")
        INDHeading.grid(row=0, column=4, pady=0, padx=15, sticky=N)

        addUserBtn = Button(self, text="Add User", font="Arial 16",
                            command=lambda: self.controller.show_frame(IndividPage))
        addUserBtn.config(height=3, width=80)
        addUserBtn.grid(row=1, column=0, columnspan=5, pady=0, sticky=N)

        CounterBtn = Button(self, text="Start Counter", font="Arial 16",
                            command=lambda: self.controller.show_frame(counterPage))
        CounterBtn.config(height=3, width=80)
        CounterBtn.grid(row=2, column=0, columnspan=5, pady=0, sticky=N)

# Main problem  start here
# This class creating dropdown menu (or combobox) with sets "teamX" and "Individual" but it was unplanned
# I want this combobox to show not all possible sets (team1, team2 etc.).
# Instead of that I want the combobox will show all the names that were entered in the "Individuals" set.
# I would also like to point out that the same process will be used for the sets of team1, team2 etc.


class counterPage(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        self.userEntry()

    def userEntry(self):

        indivLabel = Label(self, text="Please select an individual: ", font="Arial 20")
        indivLabel.grid(row=0, column=0, pady=10, padx=10)

        IndivName = StringVar(self)
        IndivName.set(teams['Individual'])

        indivMenu = OptionMenu(self, IndivName, *teams)
        indivMenu.grid(row=0, column=1, pady=10, padx=10)

        backBtn = Button(self, text="BACK", font="Arial 16", height=2, width=6,
                         command=lambda: self.controller.show_frame(IndividPage))
        backBtn.grid(row=7, column=0, sticky=W, pady=245, padx=10)


if __name__ == '__main__':
    teams = {}
    for team in range(1, 5):
        teams[f'Team{team}'] = set()
    teams = {'Team1': set(), 'Team2': set(), 'Team3': set(), 'Team4': set(), 'Individual': set()}
    pointsInd = []
    app = CollegeApp()
    app.geometry("800x500")
    app.resizable(False, False)
    app.title('Points Counter')
    app.mainloop()

【问题讨论】:

  • 您能否缩短您的代码以仅包含显示问题的代码?查看here 了解如何创建一个最小的可重现示例。
  • 顺便说一句,我建议至少在您弄清楚时删除.geometry(),但问题是在第二页上,个人不适合该页面,这就是他们没有被看到的原因。

标签: python python-3.x user-interface tkinter


【解决方案1】:

好的,所以我想我想通了(我用 cmets 标记了代码中的更改,它们有很多“-”,所以这就是您知道的方式(总共更改了 3 个位置(5 个 cmets很多“-”))):

from tkinter import *
from tkinter import messagebox
import tkinter.ttk as ttk

# This code is a simplified version of a full program code. In the original program, there is not only a list
# of individuals, but also lists of team1, team2 ... team4.
# However, now I am only interested in the problems associated with individual list,
# and I cut out all the part of the code related to team.


class CollegeApp(Tk):
    def __init__(self):
        Tk.__init__(self)
        container = ttk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        self.frames = {}
        for F in (IndividPage, listCheckPage, counterPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame(IndividPage)
        self.lift()

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()
        if cont == counterPage:  # changes here ----------------------------------------------------------------------------------------------------------
            frame.userEntry()  # ------------------------------------------------------------------------------


# In this  class I have created a window with entry widget to input name of individual and save it in
# eponymous set "Individual"

class IndividPage(ttk.Frame):

    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        self.userEntry()

    def userEntry(self):
        headingTest = Label(self, text="Enter your User Name:", font="Arial 20")
        headingTest.grid(row=0, column=0, pady=5, padx=5)

        self.usernameEnter = Entry(self, width=40)
        self.usernameEnter.grid(row=0, column=1, padx=5, pady=5)

        self.TeamName = StringVar(self)
        self.TeamName.set("Individual")

        confirmBtn = Button(self, text="Confirm User", font="Arial 16",
                            command=self.confirm)

        confirmBtn.config(height=4, width=12)
        confirmBtn.grid(row=2, column=2, sticky=E, padx=45, pady=360)

# Checking the "add_to_team" function has been executed and moving to the next page.
    def confirm(self):
        if self.add_to_team():
            self.controller.show_frame(listCheckPage)

# Function to check the presence of input

    def add_to_team(self):
        user = self.usernameEnter.get()
        if len(user) == 0:
            messagebox.showwarning(title='No user', message='Please enter a username!')
            return
        if self.usernameEnter.get():
            self.controller.show_frame(listCheckPage)

        team_name = self.TeamName.get()
        team = teams[team_name]

        team.add(user)
        self.controller.frames[listCheckPage].team_listboxes[team_name].insert(END, user)
        print(teams)

# Class that creates page with lists of four teams and individuals (Focusing on individuals right now)
# Also there is two buttons "Add User" and "Start Counter" to start points calculator

class listCheckPage(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        self.userEntry()

    def userEntry(self):
        self.team_listboxes = {}
        for col_num, teamname in enumerate(teams):
            teamMembers = Listbox(self)
            teamMembers.config(height=13, width=15)
            teamMembers.grid(row=0, column=col_num, padx=5, pady=50, sticky=S)
            for i, user in enumerate(teams[teamname]):
                teamMembers.insert(i, user)
            self.team_listboxes[teamname] = teamMembers

        INDHeading = Label(self, text="Individuals", font="Arial 16")
        INDHeading.grid(row=0, column=4, pady=0, padx=15, sticky=N)

        addUserBtn = Button(self, text="Add User", font="Arial 16",
                            command=lambda: self.controller.show_frame(IndividPage))
        addUserBtn.config(height=3, width=80)
        addUserBtn.grid(row=1, column=0, columnspan=5, pady=0, sticky=N)

        CounterBtn = Button(self, text="Start Counter", font="Arial 16",
                            command=lambda: self.controller.show_frame(counterPage))
        CounterBtn.config(height=3, width=80)
        CounterBtn.grid(row=2, column=0, columnspan=5, pady=0, sticky=N)

# Main problem  start here
# This class creating dropdown menu (or combobox) with sets "teamX" and "Individual" but it was unplanned
# I want this combobox to show not all possible sets (team1, team2 etc.).
# Instead of that I want the combobox will show all the names that were entered in the "Individuals" set.
# I would also like to point out that the same process will be used for the sets of team1, team2 etc.


class counterPage(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        # self.userEntry() this method call seems to be useless -------------------------------------------------

    def userEntry(self):

        indivLabel = Label(self, text="Please select an individual: ", font="Arial 20")
        indivLabel.grid(row=0, column=0, pady=10, padx=10)

        list_ = []  # changes here ----------------------------------------------------------------------------------------------------
        for set_ in teams.values():
            for name in set_:
                list_.append(name)

        IndivName = StringVar(self)
        IndivName.set(list_[0] if len(list_) else None)
        indivMenu = OptionMenu(self, IndivName, list_)
        indivMenu.grid(row=0, column=1, pady=10, padx=10)  # --------------------------------------------------------------------------------

        backBtn = Button(self, text="BACK", font="Arial 16", height=2, width=6,
                         command=lambda: self.controller.show_frame(IndividPage))
        backBtn.grid(row=7, column=0, sticky=W, pady=245, padx=10)


if __name__ == '__main__':
    # teams = {}
    # for team in range(1, 5):
    #     teams[f'Team{team}'] = set()
    teams = {'Team1': set(), 'Team2': set(), 'Team3': set(), 'Team4': set(), 'Individual': set()}
    pointsInd = []
    app = CollegeApp()
    # app.geometry("x500")
    # app.resizable(False, False)
    app.title('Points Counter')
    app.mainloop()

基本上,我这样做是为了在用户到达该框架时调用 userEntry 函数,而不是在启动类时调用,这意味着每次有人切换到该框架时它都会更新,也使它单独显示每个名称。

我还建议遵循 PEP 8 并使用 snake_case 作为函数、变量和方法名称,并使用 CapitalCase 作为类名。我建议也遵循其他 PEP 8 规则(它们不是强制性的)

【讨论】:

  • 将代码放在像 frame.userEntry() 这样不明显的方法中通常不是一个好主意。CollegeApp.show_frame。它使您的代码无法调试,因为调用者永远不会知道正在调用什么。您是否还注意到所有userEntry 函数在技术上与set_up_frame 相同。这就是为什么 OP 在counterPage.__init__ 中使用self.userEntry()。如果我是你,我会恢复你对 CollegeApp.show_framecounterPage.__init__ 所做的更改。
  • @TheLizzard 嗯,我的评论去哪儿了,无论如何,我确定我已经写过一篇了。关于这个显而易见性,在这种情况下真的那么糟糕,因为在调用该方法之前必须满足一个非常具体的条件。也不,我没有注意到它们是相同的,我只是注意到将框架放在屏幕上的按钮并按照它。会尝试恢复那些东西
  • @TheLizzard set_up_frame 是什么?我在代码中没有看到这样的功能
猜你喜欢
  • 2014-10-16
  • 2021-01-23
  • 1970-01-01
  • 2018-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-20
  • 1970-01-01
相关资源
最近更新 更多