tkinter Toplevel window 无法从系统托盘菜单打开

tkinter Toplevel window won't open from system tray menu

我正在尝试使用 tkinter 从系统托盘菜单打开 Toplevel window。

from cmath import phase
from tkinter import *
from tkinter import messagebox, messagebox
from tracemalloc import start
from pystray import MenuItem as item
import pystray
from PIL import ImageTk,Image
import pickle

def quit_window(icon, item):
    icon.stop()
    root.destroy()
    exit()

def hidden():
    global my_img1
    top=Toplevel()
    top.title("Secret menu, shhh :^)")
    top.overrideredirect(True)
    top.attributes('-alpha', 0.9)
    w = 1100
    h = 450
    ws = top.winfo_screenwidth()
    hs = top.winfo_screenheight()
    x = (ws/2) - (w/2)
    y = (hs/3) - (h/2)
    top.geometry('%dx%d+%d+%d' % (w, h, x, y))
    top.iconbitmap('screen.ico')
    my_img1 = ImageTk.PhotoImage(Image.open("ITEXTRA.png"))
    label1=Label(top,image=my_img1).place(relx=0.01,rely=0.01)
    button2=Button(top,text="Close window",bg='#ff4a65',command=top.destroy, relief=GROOVE).place(relx=0.9,rely=0.9)
    # Marks window as used
    hiddenwindow=1
    pickle.dump(hiddenwindow, open("window.dat", "wb"))
    Button(root, text="Developer Options", padx=57, bg="#86b3b3",fg="black", command = hidden).grid(row=3,column=0)

def hide_window():
    root.withdraw()
    image=Image.open("screen.ico")
    menu=(item('Dev window', hidden),item('show window', show_window),item('Exit app', quit_window))
    icon=pystray.Icon("ITExtra", image, "Program", menu)
    icon.run()

def show_window(icon, item):
    icon.stop()
    root.after(0,root.deiconify())
    root.after(0,root.focus_force)


root = Tk()
root.title("ITextra")
root.geometry("400x400")
root.protocol('WM_DELETE_WINDOW', hide_window)
hidden()

root.mainloop()

但不幸的是,这不会起作用,它不会拉起顶层 window,也不会拉起主层。 如果我自己打开根 window,顶层 window 将打开,但没有响应。

编辑 好的,所以我尝试将 topwindow 添加为 class,但我一直收到错误 'Top' object has no attribute 'tk'。 我在下面粘贴了更新的代码。非常感谢任何帮助!

from cmath import phase
from tkinter import *
from tkinter import messagebox, messagebox
from tracemalloc import start
from pystray import MenuItem as item
import pystray
from PIL import ImageTk,Image
import pickle


class Top():
    def __init__(self,master=None):
        self.hide = True

    def hidden(self):
      if self.hide:
        global my_img1
        self.top=Toplevel(root)
        self.top.title("Secret menu, shhh :^)")
        self.top.attributes('-alpha', 0.9)
            
        w = 1100
        h = 450
        ws = self.top.winfo_screenwidth()
        hs = self.top.winfo_screenheight()
        x = (ws/2) - (w/2)
        y = (hs/3) - (h/2)
        self.top.geometry('%dx%d+%d+%d' % (w, h, x, y))
        self.top.iconbitmap('screen.ico')
        my_img1 = ImageTk.PhotoImage(Image.open("ITEXTRA.png"))
        label1=Label(self.top,image=my_img1).place(relx=0.01,rely=0.01)
        button2=Button(self.top,text="Close window",bg='#ff4a65',command=self.top.destroy, relief=GROOVE).place(relx=0.9,rely=0.9)
        # Marks window as used
        hiddenwindow=1
        pickle.dump(hiddenwindow, open("window.dat", "wb"))
        self.top.mainloop()


def somewhereelse():
    top.hide = True
    top.hidden()

def quit_window(icon, item):
    icon.stop()
    root.destroy()
    exit()

def show_window(icon, item):
    icon.stop()
    root.after(0,root.deiconify())
    root.after(0,root.focus_force)

def hide_window():
    root.withdraw()
    image=Image.open("screen.ico")
    try:
        if pickle.load(open("window.dat","rb")) ==1:
            menu=(item('Dev window', top.hidden),
                    item('show window', show_window),
                    item('Exit app', quit_window))
        else:
            menu=(item('Exit app', quit_window))
    except:
        menu=(item('Exit app', quit_window))
    icon=pystray.Icon("ITextra", image, "Program", menu)
    icon.run()
    
    
root = Tk()
root.title("ITextra")
root.geometry("400x400")
top = Top(root) #in main part
root.protocol('WM_DELETE_WINDOW', hide_window)

Button(root, text="Developer Options", padx=57, bg="#86b3b3",fg="black", command =top.hidden).grid(row=3,column=0)



root.mainloop()


置顶window仍然没有反应 不是当 root 打开时,而是当 top 自己打开时,它再次保持无响应。但是,当我单击按钮并拖动鼠标时,它会响应。我尝试在顶部添加一个主循环,但是 self.top.mainlooproot.mainloop 都不起作用。 我尝试使用绑定,但它们也表现出相同的行为。

我创建的东西行不通吗?

我正在创建的应用程序是多线程的,我的问题是;这会使其他 classes 变得复杂吗?我对编码很陌生,坦率地说不知道。 我将整个项目放在一个 pastebin here 中,供任何感兴趣的人使用。我觉得这很乱,但对于初学者来说我还是很自豪的。

Toplevel() 仍然没有响应,因为它没有附加事件循环 (mainloop()),因为在这段代码中 Toplevel 充当独立的主要 window。

需要将此 Toplevel 附加到根 - top = Toplevel(root) 其中根作为参数传递给 hidden(root)。这样,根事件循环适用于所有小部件 children,例如 Toplevel。这将有助于解决问题的主要部分。

(#added...) 所以不需要 top.mainloop() 因为现在根是 master/parent toproot.mainloop().

事件循环用于检查您的小部件上发生的任何事件,您通常会使用 bind() 进行编程。例如 top.bind('<Button>',dosomething) 其中 dosomething 是定义的函数。 (...#added)

如果您想要 top 的标题,那么如果您使用 overrideredirect(True),则需要创建自己的标题栏或标签,因为这会删除平台 window 管理器。

(#已添加...) 平台 window 管理器并没有被删除,因为它在使用 overrideredirect(True) 时没有被使用。这可能是您的 window 似乎对这一阶段的代码没有反应的另一个原因。需要自己为附加到小部件的事件编写代码 - 正如您对要关闭的 Button 小部件所做的那样。 (...#added)

对于问题的主要部分: 此代码中 show_window 中没有任何内容涉及 top 小部件。

(#已添加...) 可以看看使 top 成为 class 并在根目录中实例化它。 tophidden 的默认状态可能是此 class 的属性。然后,您可以更改 class 属性以在其他地方的代码 body 中隐藏或显示功能。

例如骨架草图:

class Top():
    def __init__(self,master=None):
        ... 
        self.hide = True
        ...

    def hidden(self):
      if self.hide:
          ...

def somewhereelse():
    top.hide = true
    top.hidden()

top = Top(root) #in main part

!!!显然非常简短的总体想法需要在这里工作以维护您的设计,这对我来说似乎非常好。有几种方法可以将 Toplevel 小部件合并到 class 中,但这与原始问题有点不同。 (...#added)

1 月 28 日添加...

我建议更彻底地研究 class 而不是只放我的例子。但这里还有更多

class Top():
    def __init__(self,master=None):
        super().__init__()
        self.master = master 
        self.hide = True

    def hidden(self):
        ...
        self.top = Toplevel(self.master)
        ...

用我的话来说,但是请检查 Python 文档,super().__init__() 将调用继承的 object 的初始化函数,在这种情况下会返回到 self.masterroot,然后返回到 tk.__init__,它在 Tk().

中被调用

我建议查看 Python 下载中 Lib\tkinter\ 文件夹中的代码 __init__.py 文件,以更好地理解 tkinter 的工作原理。

我认为这绝对是可以实现的,但可能需要不同的 GUI - 同意这对初学者来说是一个很好的开始,因此并不是一团糟!

使用 class 并不是实现你想要做的事情所必需的,但是 classes 对于封装一个 object 非常有用,这样任何额外的属性和方法都可以与 object 可以为您的项目定制。这使得进一步或未来的开发更容易。

...添加于 1 月 28 日