能否从一个框架创建一个窗口?

4
我有一个框架,我想从中创建一个顶级窗口。我想要创建一个类似于网络浏览器界面的系统。例如,在Google Chrome中,您可以将一个标签拖出来并创建一个新窗口。您还可以将其他标签添加到该新窗口中。这就是我试图“演示”这种行为的方式:
import tkinter as tk
from tkinter import ttk

root = tk.Tk()
moving_frame = tk.Frame()
moving_frame.pack()

notebook = ttk.Notebook(moving_frame, width=400, height=700)
notebook.pack()

movingFrame = tk.Frame(notebook, bg='green', width=400, height=700)
movingFrame2 = tk.Frame(notebook, bg='green', width=400, height=700)

lab = tk.Label(movingFrame, text='some text')
ent = tk.Entry(movingFrame)

ent2 = tk.Entry(movingFrame2, width=10, bg='grey30')

lab.pack()
ent.pack()
ent2.pack()

notebook.add(movingFrame, sticky='nesw', text='tab1')
notebook.add(movingFrame2, sticky='nesw', text='tab2')

def window_create(e):
    if notebook.identify(e.x, e.y):
        print('tab_pressed')
        frame_to_window = root.nametowidget(notebook.select())
        root.wm_manage(frame_to_window)

notebook.bind('<ButtonRelease-1>', window_create)
root.mainloop()

注意:此代码通过按下Tab键来工作,而不是拖动它。
我想知道是否可以通过使用wm_manage来调整创建的窗口。这个函数返回None,并且不像我想象的那样工作,但足够接近我所需要的。
如果不能通过使用wm_manage来实现,我应该怎么做呢?
我想我可以创建一个自定义类,例如从Frame开始,但是有一个问题:新窗口应该与创建它的框架相同。如果用户更改了某些内容,例如向Entry添加了文本;标记了一些checkbuttons;如果他使用了Treeview层次结构,就像这样:

新窗口应该记住哪些“文件夹”是打开的(在图片上标记为减号),当前选择了哪些项目等等。这只是关于Treeview的事情,对于每个小部件都应该考虑类似的事情。工作量相当大,所以也许有更好的方法来解决这个问题。

@Thingamabobs wm_manage不允许我调整那个新窗口。例如,我想在它上面使用overrideredirect或者使用geometry,但似乎都无法实现。 - Danya K
@Thingamabobs 应该是这样的,但tkinter以一种非常奇怪的方式创建了这个窗口,就像它既是一个窗口又是一个框架。我计划创建一个类似于浏览器工作方式的用户界面系统,您可以通过拖动当前标签来创建一个新窗口,向新窗口添加更多标签等等。 - Danya K
1
看,你可以使用root.call('wm', 'geometry', frame_to_window, "500x500")作为例子。所以tcl代码对于这个问题是有效的,而且写一个包装器应该不太难。此外,通过一些工作,将该框架的内容重新排列到新的窗口中的Notebook框架中也是可能的。但是,你仍然无法将一个选项卡从一个窗口移动到另一个窗口。 - Thingamabobs
如果你想知道为什么,这里有一个非常好的解释答案。 - Thingamabobs
@Thingamabobs 非常感谢你的 root.call。我从来没有想到这会起作用,尽管我知道 calleval 的存在。现在我开心了十倍。甚至 overrideredirect 现在也能正常工作!关于 你不能将一个标签从一个窗口移动到另一个窗口,我会先自己尝试测试一下,看看为什么你认为它不会起作用,因为我现在没有看到任何问题。 - Danya K
显示剩余3条评论
1个回答

1
总结一下,我想说我还没有能够制作出类似于网页浏览器的用户界面系统。在我的尝试过程中遇到了很多问题。也许将来我会做到这一点,并编辑这个答案。
但是我的主要问题是如何调整由wm_manage创建的新窗口?其中一个解决方案来自评论,但我找到了更好的方法,尽可能简单:
class ToplevelFrame(tk.Frame, tk.Toplevel):
    pass

这是一个类,通常表现得就像tk.frame一样,但在执行wm_manage命令之后,就可以使用标准的Toplevel方法,比如overrideredirectgeometryresizable等等。如果需要的话,还可以添加一个Menu。下面是一个快速示例,展示了可以做些什么:
import tkinter as tk


class ToplevelFrame(tk.Frame, tk.Toplevel):
    pass


if __name__ == "__main__":
    root = tk.Tk()

    top_frame = ToplevelFrame(root, bg='grey20')
    top_frame.pack()

    text_wid = tk.Text(top_frame, width=150, height=40)
    text_wid.pack(padx=40, pady=40)

    def transform():
        if button["text"] == 'to window':
            root.wm_manage(top_frame)
            button['text'] = 'to frame'

            # examples
            top_frame.wm_overrideredirect(True)
            top_frame.geometry('+600+100')
            top_frame.resizable()

            mainmenu = tk.Menu(top_frame)
            top_frame['menu'] = mainmenu

            filemenu = tk.Menu(mainmenu, tearoff=0)
            filemenu.add_command(label="Open...")
            filemenu.add_command(label="New")
            filemenu.add_command(label="Save as...")
            filemenu.add_command(label="Quit")

            mainmenu.add_cascade(label="File", menu=filemenu)

        else:
            root.wm_forget(top_frame)
            button['text'] = 'to window'
            top_frame.pack(side='bottom')


    button = tk.Button(text='to window', command=transform)
    button.pack(fill='both', expand=True, side='bottom')
    root.mainloop()

注意:在ToplevelFrame还不是一个独立窗口时,尝试使用Toplevel方法将导致错误:_tkinter.TclError: 窗口 ".!toplevelframe" 不是顶级窗口

这是你问题的答案吗?它是否按预期工作?另外,一个简单的方法是只获取text_wid的内容,并使用tk.Text创建一个新的tk.Toplevel,然后使用.insert("end", <contents>)将所有内容复制到新的文本小部件中。假设你没有很多小部件,这可能是最简单的方法。 - TheLizzard
@TheLizzard 不是真的。我没有完成我想要的主要任务。我知道可以复制小部件,但在我看来,这很困难且不太好(我在一个顶级问题中写了更多相关内容)。复制对于非常简单的任务有效。想象一下,一个 Text 小部件使用了 StringVar。它还被另一个小部件使用。这也会使一切变得更加复杂。 - Danya K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接