Python Tkinter和OOP

2024-10-01 15:42:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试将我的过程编程Python项目转换为面向对象编程。 在我的项目中,我使用Tkinter

过程版本运行得很好,但在OOP中我得到了

line 2493, in grid_configure self.tk.call( _tkinter.TclError: can't invoke "grid" command: application has been destroyed

尝试对第一个标签进行网格化时出错

我的代码:

    from tkinter import *
       class Document:
       root = Tk()
       root.geometry("1000x500")
    
       file_name = Label(text="File Name")
       document_title = Label(text="Document Title")

       def gridding(self):
          self.file_name.grid(row=1,column=2)
          self.document_title.grid(row=2,column=2)

       root.mainloop()

   doc1 = Document()
   doc1.gridding()

这个错误消息一点帮助都没有,所以希望这篇文章也能帮助其他人

非常感谢你事先的帮助


Tags: 项目textnameselftitletkinter过程root
2条回答
<>你的代码有很多事情要考虑,我会把它们都处理好。

  1. 您正在导入所有tkinter而不使用任何别名
  2. 将您的root称为“doc1”意味着您认为您将制作一个“doc2”、“doc3”等等。。离开那门课,那是行不通的。您不能有太多的root实例。充其量应该是Toplevel,但前提是您要为每个新文档打开一个新窗口
  3. 您根本不应该在类内调用mainloop。您应该使用模块约定。这样,您就可以将此脚本的元素导入到另一个脚本中,而不用担心此脚本会自动运行。至少,为mainloop创建包装器是愚蠢的。您已经可以调用mainloop,将它粘贴到另一个方法中的目的是什么?(doc1.root.mainloop()
  4. 创建一个方法来进行非常特定的网格放置是不可重用的。我认为mixin是一种更好的方法,它可以使某些功能更可用,但保持它们的动态性。没有必要为小部件创建自定义包装器,但是如果要在所有内容上都使用gridding方法,那么为什么不创建一个更好的gridding方法,并将其自动“混合”到所有适用的内容中呢?只要你能做到这一点,你还可以提供一些其他的便利
  5. Document这样的东西(即,你可能需要很多或者经常改变的东西)可能不应该是root。它应该在root中。它当然不应该把root作为财产埋在里面

下面给出了我上面提到的许多例子。这些例子可以而且应该加以美化。例如,BaseWidget可以包括xrootxyrooty等的属性。。。按照ParentWidget的设计方式,您可以使用range作为rowcfgcolcfg的第一个参数,在一次调用中进行“批量配置”。它们都返回self,因此可以内联使用

import tkinter as tk
from typing import Iterable


class BaseWidget:
    @property
    def width(self) -> int:
        self.update_idletasks()
        return self.winfo_width()
        
    @property
    def height(self) -> int:
        self.update_idletasks()
        return self.winfo_height()
    
    def grid_(self, r=None, c=None, rs=1, cs=1, s='nswe', **kwargs):
        #this allows keyword shorthand, as well as original keywords
        #while also allowing you to rearrange the above arguments 
        #so you know the exact order they appear and don't have to use the keyword, at all
        self.grid(**{'row':r, 'column':c, 'rowspan':rs, 'columnspan':cs, 'sticky':s, **kwargs})
        #return self so this can be used inline
        return self
        
        
class ParentWidget:
    @property
    def descendants(self):
        return self.winfo_children()
    
    #inline and ranged grid_rowconfigure
    def rowcfg(self, index, **options):
        index = index if isinstance(index, Iterable) else [index]
        for i in index:
            self.grid_rowconfigure(i, **options)
        #so this can be used inline
        return self
        
    #inline and ranged grid_columnconfigure
    def colcfg(self, index, **options):
        index = index if isinstance(index, Iterable) else [index]
        for i in index:
            self.grid_columnconfigure(i, **options)
        #so this can be used inline
        return self


class Custom_Label(tk.Label, BaseWidget):
    @property
    def text(self) -> str:
        return self['text']
        
    @text.setter
    def text(self, value:str):
        self['text'] = value
    
    def __init__(self, master, **kwargs):
        tk.Label.__init__(self, master, **kwargs)
        

class Document(tk.Frame, ParentWidget, BaseWidget):
    def __init__(self, master, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        
        #the rest of this class is an example based on what little code you posted
        #the results are not meant to be ideal. It's a demo of usage ... a gist
        self.file_name = Custom_Label(self, text='File Name').grid_(1,2)
        self.doc_title = Custom_Label(self, text='Document Title').grid_(2,2)
        
        #possible
        #r = range(len(self.descendants))
        #self.colcfg(r, weight=1).rowcfg(r, weight=1)

        self.colcfg(2, weight=1).rowcfg([1,2], weight=1)
        

class Root(tk.Tk, ParentWidget):
   def __init__(self, title, width, height, x, y, **kwargs):
       tk.Tk.__init__(self)
       self.configure(**kwargs)
       self.title(title)
       self.geometry(f'{width}x{height}+{x}+{y}')
       
       self.rowcfg(0, weight=1).colcfg(0, weight=1)
       
       doc1 = Document(self).grid_()

if __name__ == '__main__':
    Root('MyApplication', 800, 600, 200, 200, bg='#000000').mainloop()

您就快到了:您应该在__init__方法中构建类,并且只在设置结束时调用mainloop

from tkinter import Tk
from tkinter import Label


class Document:
    def __init__(self):
        self.root = Tk()
        self.root.geometry("1000x500")

        self.file_name = Label(text="File Name")
        self.document_title = Label(text="Document Title")

        self.gridding()

    def gridding(self):
        self.file_name.grid(row=1, column=2)
        self.document_title.grid(row=2, column=2)

    def start(self):
        self.root.mainloop()


doc1 = Document()
doc1.start()

这将创建一个带有两个标签的窗口,如预期的那样

干杯

相关问题 更多 >

    热门问题