在tkinter中显示带有图像的多个独立窗口,并在所有窗口关闭时退出主循环

时间:2018-10-19 16:45:00

标签: python tkinter

我的绘图库需要能够同时显示多个绘图,每个绘图都表示为PIL图像,并且每个绘图都应显示为自己的窗口。窗口应该是独立的,因此关闭其中任何一个窗口都不会影响其他窗口,但是当所有窗口都关闭时,主循环应该退出。在qt和wx中,这种行为很容易实现,但到目前为止,在qt中,这很难证明。

这是我到目前为止最接近的一个

from six.moves import tkinter
from PIL import ImageTk

class Window:
  def __init__(self, img):
    self.window = tkinter.Toplevel()
    self.window.minsize(img.width, img.height)
    self.canvas = tkinter.Canvas(self.window, width=img.width, height=img.height)
    self.canvas.pack()
    self.canvas.configure(background="white")
    self.photo  = ImageTk.PhotoImage(img)
    self.sprite = self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
windows = []
for img in imgs:
  windows.append(Window(img))
if len(windows) > 0: windows[0].window.mainloop()

这将在每个窗口中显示图像,并且每个窗口都可以独立关闭。但是它还会显示一个空的根窗口,需要关闭该窗口才能退出主循环,并且将导致所有窗口在关闭时关闭,这不是我想要的行为。

如果我将tkinter.Toplevel()替换为tkinter.Tk(),则第二个窗口的create_image失败,出现模糊的“ pyimageX不存在”错误消息,其中X是递增的整数。

我将不得不创建一个不可见的根窗口,然后手动计算关闭了多少个子窗口,并在所有子窗口都关闭后触发销毁该不可见的根窗口,以获取所需的行为?还是有一种简单的方法来实现这一目标?

编辑:只是为了澄清:我的程序主要不是Tk应用程序。它几乎将所有时间都花在做其他事情上,并且仅在单个函数中临时使用Tk来显示一些图。这就是为什么在关闭绘图后退出主循环很重要,以便程序可以恢复其正常操作。考虑一下matplotlib中的show()是如何工作的,以这种情况为例。

2 个答案:

答案 0 :(得分:1)

这是您可能要执行此操作的示例。此示例使用根窗口来容纳一个按钮,该按钮将在顶层打开所有图像。

确保将self.path更改为图像文件夹。

import tkinter as tk
import os

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        tk.Button(self, text="Open Images", command=self.open_images).pack()
        self.path = ".\RGB"
    def open_images(self):
        directory = os.fsencode(self.path)

        for file in os.listdir(directory):
            filename = os.fsdecode(file)
            if filename.endswith(".gif"): 
                print(filename)
                top = tk.Toplevel(self)
                img = tk.PhotoImage(file="{}\{}".format(self.path, filename))
                lbl = tk.Label(top, image=img)
                lbl.image = img
                lbl.pack()


if __name__ == '__main__':   
    app = App()
    app.mainloop()

这是我的第二个示例,您可以在其中隐藏根窗口,并且在关闭最后一个顶级窗口时,tkinter实例也被破坏。这是一个简单的跟踪变量。

import tkinter as tk
import os


class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.top_level_count = 0
        self.path = ".\RGB"
        self.open_images()
        self.withdraw()

    def open_images(self):
        directory = os.fsencode(self.path)
        for file in os.listdir(directory):
            filename = os.fsdecode(file)
            if filename.endswith(".gif"): 
                self.top_level_count += 1
                image_top(self, self.path, filename)


    def check_top_count(self):
        print(self.top_level_count)
        if self.top_level_count <= 0:
            self.destroy()


class image_top(tk.Toplevel):
    def __init__(self, controller, path, filename):
        tk.Toplevel.__init__(self, controller)  
        self.controller = controller 
        self.protocol("WM_DELETE_WINDOW", self.handle_close)             
        img = tk.PhotoImage(file="{}\{}".format(path, filename))
        lbl = tk.Label(self, image=img)
        lbl.image = img
        lbl.pack()

    def handle_close(self):
        self.controller.top_level_count -= 1
        self.destroy()
        self.controller.check_top_count()


if __name__ == '__main__':   
    app = App()
    app.mainloop()

答案 1 :(得分:0)

好吧,这是我想解决这个问题的几门课:

class ImgRoot(tkinter.Tk):
    def __init__(self, imgs):
        super(ImgRoot, self).__init__()
        for i in imgs:
            Window(self, i)
        self.withdraw()
        self.open=True
        self.tick()
    def tick(self):
        if not self.open:
            self.destroy()
        self.open=False
        self.after(100, self.tick)
    def checkin(self):
        self.open=True
class Window(tkinter.Toplevel):
    def __init__(self, root, img):
        super(Window, self).__init__()
        self.root=root
        self.tick()
        self.minsize(img.width, img.height)
        self.canvas = tkinter.Canvas(self, width=img.width, height=img.height)
        self.canvas.pack()
        self.canvas.configure(background="white")
        self.photo  = ImageTk.PhotoImage(img)
        self.sprite = self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
    def tick(self):
        self.root.checkin()
        self.after(100, self.tick)

这里的想法是创建一个处理整个事件的主类(ImgRoot)。然后,每隔0.1秒(100毫秒),它将检查是否有任何图像窗口告诉它它们仍在运行,如果没有,则关闭。图像窗口(Window s)通过每运行0.1秒将ImgRoot的{​​{1}}属性设置为open来实现。这是一个示例用法:

True