简体   繁体   English

Python-Tkinter-在继承自Toplevel()的类中创建的窗口小部件显示在该类之外的其他框架中,Toplevel()类为空

[英]Python - Tkinter - Widgets created inside a class inherited from Toplevel() appear in a different frame OUTSIDE the class, Toplevel() class is empty

I'm attempting to create a class and inherit from Toplevel() so that the GUI elements of the class can be contained within a separate window. 我试图创建一个类并从Toplevel()继承,以便该类的GUI元素可以包含在单独的窗口中。 Usually I would just inherit from Frame() but for my purpose I need everything to be in a window. 通常我只是继承自Frame()但出于我的目的,我需要将所有内容都放在一个窗口中。 I'm writing the code in my GUI template script just so I can figure out how to get it working before I stick it in the actual script I want to use it in. Here is the code: 我只是在GUI模板脚本中编写代码,因此我可以在将其粘贴到要使用的实际脚本中之前弄清楚如何使其工作。这是代码:

from Tkinter import *
import socket

myplayername = ''

class Application(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()

        class InfoLabel(Frame):
            def __init__(self, name, value, bgc, nfgc, vfgc, master=None):
                Frame.__init__(self, master)
                self.pack()
                Label(master=self, text=name, bg=bgc, fg=nfgc).pack({"side": "left"})
                Label(master=self, text=value, bg=bgc, fg=vfgc).pack({"side": "left"})

        class LabelEntry(Frame):
            def __init__(self, name, variable, bgc, fgc, entrysize, master=None):
                Frame.__init__(self, master)
                self.pack()
                Label(master=self, text=name, bg=bgc, fg=fgc).pack({"side": "left"})
                Entry(master=self, textvariable=variable, bg=bgc).pack({"side": "left"})

        class HostGameWindow(Toplevel):
            def __init__(self):
                global myplayername
                Toplevel.__init__(self)
                self.title('Host a Game')
                hostname = socket.gethostname()
                hostipaddr = socket.gethostbyname(hostname)
                hostport = 11489
                players = 0

                portsv = StringVar(value=str(hostport))
                numofplayers = StringVar(value=str(players))
                myname = StringVar(value=myplayername)

                hostgameframe = Frame(master=self, bg='#999', bd=3, relief=RIDGE, padx=5, pady=5).pack({"side": "left"})
                hoststatusframe = Frame(master=self, bg='white', bd=3, relief=RIDGE).pack({"side": "left"})
                hostbuttonframe = Frame(master=hostgameframe, bd=2, relief=RAISED, padx=5, pady=5).pack({"side": "bottom"})
                InfoLabel(master=hostgameframe, name='Hostname:', value=hostname, bgc='#999', nfgc='blue', vfgc='red').pack({"side": "top"})
                InfoLabel(master=hostgameframe, name='IP Address:', value=hostipaddr, bgc='#999', nfgc='blue', vfgc='red').pack({"side": "top"})
                LabelEntry(master=hostgameframe, name='Host on port:', variable=portsv, bgc='#999', fgc='blue', entrysize=len(str(hostport))).pack({"side": "top"})
                LabelEntry(master=hostgameframe, name='Players Joining:', variable=numofplayers, bgc='#999', fgc='blue', entrysize=2).pack({"side": "top"})
                LabelEntry(master=hostgameframe, name='Player Name:', variable=myname, bgc='#999', fgc='blue', entrysize=16).pack({"side": "top"})
                Button(master=hostbuttonframe, text='Host Game', width=10).pack({"side": "left"})
                Button(master=hostbuttonframe, text='Start Game', width=10).pack({"side": "left"})

            def close():
                self.destroy()

        def HostGameDialog():
            HostGameWindow()

        Button(master=self, text='Host a Game', command=HostGameDialog).pack()




root = Tk()
app = Application(master=root)
#root.wm_iconbitmap(default='INSERT ICON HERE')
#root.wm_title("TITLE OF PROGRAM")
#app.master.maxsize(640, 480)
app.master.minsize(640, 480)
app.master.resizable(0, 0)
app.mainloop()
app.quit()

Now for some reason when I click the 'Host a Game' Button, it calls the HostGameDialog() function and it creates the HostGameWindow() , BUT the window that gets created is sized down as small as it can be and all the GUI elements that should be contained in the HostGameWindow() class instead appear in the main Application() frame. 现在由于某种原因,当我单击“主持游戏”按钮时,它将调用HostGameDialog()函数并创建HostGameWindow() ,但是将创建的窗口缩小到最小,并且所有GUI元素应该包含在HostGameWindow()类中的内容,而不是出现在主Application()框架中。 And the thing which is really wierd to me is that it doesn't give any errors, it just puts all the widgets inside the main application frame instead of the Toplevel() that gets created. 而且让我真正感到困扰的是,它没有给出任何错误,它只是将所有小部件放置在主应用程序框架内,而不是创建的Toplevel()

What the heck am I doing wrong? 我到底在做什么错? Why aren't any the widgets placed inside the Toplevel() ? 为什么没有在Toplevel()内放置任何小部件? I've been at it for hours now and nothing is making sense. 我已经花了几个小时了,没有任何意义。 Please if you know anything that could be of some help to me let me know. 如果您对我有所帮助,请告诉我。

Wow, I've never had to wait so long for reply here before, this must be a pretty nifty problem, I still don't know what to do. 哇,我从来没有等这么久才在这里回复,这肯定是一个很漂亮的问题,我仍然不知道该怎么办。 Any ideas are greatly appreciated! 任何想法都将不胜感激!

I guess no one know what to make of this... I'll keep checking here though! 我猜没人知道该怎么做...不过我会继续检查这里!

SOLVED! 解决了! turns out its not a good idea to create AND pack a widget on the same line if you create a reference to it. 如果创建对窗口小部件的引用,那么在同一行上创建并打包窗口小部件并不是一个好主意。 Creating and packing a widget on the same line only works if you just call the Widget(args*).pack(args*) without creating a reference to it. 仅在仅调用Widget(args*).pack(args*)而不创建引用的情况下,才能在同一行上创建和打包窗口小部件。

When you do x=a().b() , what is stored in x is the result of b() . 当您执行x=a().b() ,x中存储的是b()的结果。

Consider the following line of code: 考虑以下代码行:

hostgameframe = Frame(self, bg='#999', bd=3, 
    relief=RIDGE, padx=5, pady=5).pack({"side": "left"})

If we collapse all of the options (for clarity) we're left with this: 如果我们折叠所有选项(为清楚起见),我们将得到以下结果:

hostgameframe = Frame(...).pack(...)

Can you see what's happening? 你知道发生了什么吗? hostgameframe is being set to the result of pack(...) . hostgameframe被设置为pack(...)的结果。 pack always returns None , so hostgameframe is None . pack始终返回None ,因此hostgameframeNone When you later create another widget and set this as it's master, that widget ends up going in the main window. 当您以后创建另一个窗口小部件并将其设置为主窗口时,该窗口小部件最终将进入主窗口。

So, to solve your problem you need to separate the creation of the widget from the layout. 因此,要解决您的问题,您需要将窗口小部件的创建与布局分开。 Personally I think this is a best practice that you should always adhere to. 我个人认为这是您应始终坚持的最佳做法。 For cases where you don't need to keep a reference to the widget, combining them into one statement is harmless. 对于不需要保留对窗口小部件的引用的情况,将它们组合为一个语句是无害的。 Even so, I think your code will be easier to manage if you get in the habit of always separating widget creation from widget layout. 即使这样,如果您习惯于始终将小部件的创建与小部件的布局分开,那么我认为您的代码将更易于管理。

hostgameframe = Frame(self, bg='#999', bd=3, relief=RIDGE, padx=5, pady=5)
hostgameframe.pack({"side": "left"})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM