如何在Tkinter中使用面向对象编程将小部件放入不同的框架中

2 投票
2 回答
14071 浏览
提问于 2025-04-17 15:49

背景

大家好!我现在正在做一个简单的图形界面文本编辑器,可以加载和保存文本文件。我想用多个框架来放置工具栏和文本框,就像我在这里学到的那样。我在使用面向对象编程(OOP),并在__init__方法中设置了我的框架,在widget方法中设置了小部件。但不知为什么,这些小部件无法放到它们各自的框架里。

代码

from Tkinter import *
class Application:
    def __init__(self,parent):  #initialize the grid and widgets
        self.myParent = parent

        #Init the toolbar
        self.toolbar = Frame(parent)
        self.toolbar.grid(row = 0)

        #Init frame for the text box

        self.mainframe = Frame(parent)
        self.toolbar.grid(row = 1)
    def widget(self):#Place widgets here

        #Save Button
        self.saveButton = Button (self, self.toolbar,
                                  text = "Save", command = self.saveMe)
        self.saveButton.grid(column = 0, row = 0, sticky = W)

        #Open Button
        self.openButton = Button (self, self.toolbar,
                                 text = "Open", command = self.openMe)
        self.openButton.grid(column = 0, row = 1, sticky = W)
        #Area where you write 
        self.text = Text (self, self.mainframe,
                          width = (root.winfo_screenwidth() - 20),
                          height = (root.winfo_screenheight() - 10))
       self.text.grid(row = 2)

问题

  1. 在使用不同方法的情况下,我该如何确保每个小部件都放在正确的框架里呢?

    • 如果这不可能,请告诉我如何使用面向对象编程来做到这一点——我在这种情况下对Tkinter最为熟悉,并且我承诺要提高自己的水平。
  2. 解释你的答案。我需要理解,而不是只是对着电脑点头。

  3. 额外加分:如果我想用Tkinter在面向对象编程中初始化多个窗口(每个窗口是不同的类),该怎么做?比如,如果我的代码是:

    class MainWindow(Frame):
        ---init stuff---
        def widget(self):
            newWindow = Button(self, text = "click for a new window",
                               command = self.window)
            newWindow.grid()
       def window(self):
             #What would I put in here to initialize the new window??
    
    class theNextWindow(Frame):
    

    我该在window.self方法中放什么,才能让theNextWindow窗口可见呢?

感谢大家的帮助!

编辑 1

我在__init__方法中添加了self.widget()这一行,结果得到了一个“精彩”的错误:

Traceback (most recent call last):
File "D:\Python Programs\Text Editor\MyTextv2.py", line 67, in <module>
 app = Application(root)
File "D:\Python Programs\Text Editor\MyTextv2.py", line 14, in __init__
 self.widget()
File "D:\Python Programs\Text Editor\MyTextv2.py", line 24, in widget
 text = "Save", command = self.saveMe)
File "C:\Python27\lib\lib-tk\Tkinter.py", line 2044, in __init__
 Widget.__init__(self, master, 'button', cnf, kw)
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1965, in __init__
 BaseWidget._setup(self, master, cnf)
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1943, in _setup
 self.tk = master.tk
AttributeError: Application instance has no attribute 'tk'

因为错误日志清楚地提到了我的主循环:File "D:\Python Programs\Text Editor\MyTextv2.py", line 67, in <module> app = Application(root),所以我决定添加它:

root = Tk()
root.title("My Text Editor")

#This is wierd - it gets the computer windows dimensions
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(0)

#And then applies them here
root.geometry("%dx%d+0+0" % (w, h))

app = Application(root)

root.mainloop()

2 个回答

1

问题 1:

一个小部件只能有一个直接的父级。也就是说,不能同时指定两个父级。比如,你似乎在给 self.saveButton 传入了 selfself.toolbar 作为父级,这样是不对的。

myButton = Button(self.toolbar, text="Blah", command=self.someCommand)

这是你应该使用的格式。

问题 2:

假设你想让 Application(也就是在 Button(self, self.toolbar...) 中的 self)成为 myButton 的父级。这样也不行,因为要成为 Tk 小部件的层级父级,一个类必须是 Widget 的实例。通常,如果你想这样做,你需要在 Application 中继承 tk.Tk(),格式如下:

class Application(Tk):

    def __init__(self, *args, **kwargs):


        Tk.__init__(self, *args, **kwargs) #It's important that you call the parent class's __init__ method first

        self.createWidgets()

    def createWidgets(self):

        self.myButton = Button(self, text="Blah", command=lambda x: print "x")
        #this is ok, because now self (AKA Application) is a valid instance of Tk
3

终于找到了答案。根据我了解到的(如果有错的地方请随意修改),在Tkinter中,Frame的继承方式只有两种:一种是从它自己这个类继承,另一种是从当前小部件所在的方法继承。为了解决这个问题,我把Application类设置成一个框架,然后在里面放置其他框架。下面是我所做的一个基本示例:

#Import Tkinter
from Tkinter import *

#Main Frame
class Application(Frame):
    def __init__(self, master):  #initialize the grid and widgets
        Frame.__init__(self,master)
        self.grid()
        self.redFUN() #initialize the red frame's Function
        self.greenFUN() #initialize the green frame's Function
        self.widgets() #To show that you can still place non-Frame widgets 
    def widgets(self):
        self.mylabel = Label (self, text = "Hello World!")
        self.mylabel.grid()
    def redFUN(self): #The 'self' means that it is an instance of the main frame
        #Init the red frame
        self.redFrame = Frame(root, width = 100, height = 50,pady = 5,
                              bg = "red")
        self.redFrame.grid()



    def greenFUN(self): #Child of the mainframe
        self.greenFrame = Frame(root, width = 100, height = 50,pady = 5,
                          bg = "green") #it is green!
        self.greenFrame.grid()








#These lines of code are used for the grid
root = Tk()
root.title("Frame Example")
root.geometry("300x300")
app = Application(root)

root.mainloop()

希望这对大家有帮助 - 如果你们有任何问题,欢迎留言!

撰写回答