Отображение нескольких независимых окон с изображениями в tkinter и выход из основного цикла, когда все они были закрыты - PullRequest
0 голосов
/ 19 октября 2018

Моя библиотека графиков должна иметь возможность отображать несколько графиков одновременно, каждый из которых представлен в виде изображения 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 в одной функции для отображения некоторых графиков.Вот почему важно, чтобы основной цикл завершался после закрытия графиков, чтобы программа могла возобновить свою нормальную работу.Подумайте о том, как show() в matplotlib работает на примере этого сценария.

Ответы [ 2 ]

0 голосов
/ 19 октября 2018

Хорошо, вот пара классов, которые я придумал, чтобы решить эту проблему:

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) делают это, устанавливая для атрибута ImgRoot s open значение True каждые 0,1 секунды, пока они живы.Вот пример использования:

import tkinter
#above classes go here
ImgRoot(imgs) #imgs is a list as defined in your question
tkinter.mainloop()
print('done') #or whatever you want to do next
0 голосов
/ 19 октября 2018

Вот пример того, как вы можете сделать это.В этом примере используется корневое окно для размещения кнопки, которая откроет все изображения на верхнем уровне.

Убедитесь, что вы изменили 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...