打开对话框或创建窗口时,tkinter会打开第二个窗口

时间:2018-06-13 09:18:57

标签: python user-interface tkinter

我有以下问题。我用Tkinter创建了一个gui,当我在我的IDE(Spyder)中运行它时,一切都运行得很好,但当我将文件保存为并希望通过执行.py,everytime a window is created or a dialog opens, a second Tkinter window is poping up来启动它。当我将代码保存为.pyw时,会出现同样的问题。 我发布了一个持续存在相同问题的简短示例。

import tkinter as tk
from tkinter import messagebox


class test_GUI(tk.Frame):
    def __init__(self,master=None):
        super().__init__(master)
        self._initializeWindow()
        self._window.protocol("WM_DELETE_WINDOW", self.__on_closing)
        self._window.mainloop()

    def _initializeWindow(self):
        self._window=tk.Tk()
        self._window.title("The window I initzialized")

    def __on_closing(self):
        if(messagebox.askokcancel("Quit", "Quit program?")):
            self._window.destroy()
            self._window.quit()        

app=test_GUI()

3 个答案:

答案 0 :(得分:1)

您将班级定义为

class test_GUI(tk.Frame):

所以你的班级继承自tk.Frame,这意味着你的班级基本上是一个具有额外功能的框架 当你这样做

super().__init__(master)

您初始化要继承的类,即tk.Frame。目前,没有tk.Tk个对象(和master=None)。因为没有tk.Tk的实例,Frame(或任何其他tkinter小部件)不能存在,所以tkinter会默默地为你创建一个。{1}}这是你的第一个窗口 之后你打电话给

self._window = tk.Tk()

自己制作tk.Tk个实例。这是你的第二个窗口。除此之外你不想要两个窗口,你不应该同时运行多个tk.Tk(或更准确地说是关联的Tcl解释器)的实例,因为这会导致意外的行为。

那么你怎么能解决这个问题呢? 您基本上有两个选择:删除继承或在启动课程之前启动tk.Tk

如果没有继承,您的应用可以像

一样构建
import tkinter as tk

class test_GUI():
    def __init__(self):
        self._window=tk.Tk()
        self._window.title("The window I initzialized")

        self.button = tk.Button(self._window, text='Test button')
        self.button.pack()

        ...
        self._window.mainloop()

继承你可以这样做

import tkinter as tk

class test_GUI(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)

        self.master = master
        self.master.title("The window I initzialized")

        self.button = tk.Button(self, text='Test button')
        self.button.pack()


root = tk.Tk()
app=test_GUI(root)
app.pack(fill='both', expand=True)
root.mainloop()

两种方式都很好。我个人喜欢带继承的版本。另请参阅Bryan Oakley关于构建tkinter应用程序here的帖子。

答案 1 :(得分:0)

def _initializeWindow(self):
        self._window=tk.Tk()
        self._window.title("The window I initzialized")
        self._window.withdraw()

self._window.withdraw()会删除第二个窗口。

super().__ init __(master)实际上负责第一个窗口。评论出来。在这种情况下,您不需要撤销在_initializeWindow中创建的窗口。

import tkinter as tk
from tkinter import messagebox


class test_GUI(tk.Frame):
    def __init__(self,master=None):
        #super().__init__(master)
        self._initializeWindow()
        self._window.protocol("WM_DELETE_WINDOW", self.__on_closing)
        self._window.mainloop()

    def _initializeWindow(self):
        self._window=tk.Tk()
        self._window.title("The window I initzialized")
        #self._window.withdraw()

    def __on_closing(self):
        if(messagebox.askokcancel("Quit", "Quit program?")):
            self._window.destroy()
            self._window.quit()        

app=test_GUI()

答案 2 :(得分:0)

对于那些试图对您的 GUI 进行单元测试并试图通过数据类插入根 tk 依赖项的人,您可以通过将 tk 初始化放在 __post_init__ 方法中来解决多窗口问题:

from dataclasses import dataclass
import tkinter as tk


@dataclass
class App():
    tk_root: tk.Tk = None

    def __post_init__(self):
        if self.tk_root is None:
            self.tk_root = tk.Tk()
# ...

然后,如果您使用需要初始化根 Tk 的 tkinter 类(如 StringVars),则需要在 pytest 夹具中修补 tk:

import pytest
from unittest.mock import patch, MagicMock

from GUI import App


@pytest.fixture
def app():
    with patch('GUI.tk'):
        return GUI(tk_root=MagicMock())