首页 > 解决方案 > TkInter - 无法让帧正常工作并调整大小

问题描述

TkInter 的框架让我发疯。我的目标是有一个选项框架,我可以在其中选择一些选项,然后按“存档”,TkInter 窗口将更改为显示脚本其余部分的输出。

我无法正确调整它的大小 - 窗口中似乎有一些额外的框架占用了空间。

import string
from tkinter import *
import tkinter as tk
import threading

def main(argv):
    print("In Main")
    for arg in argv:
        print(arg)

class TextOut(tk.Text):

    def write(self, s):
        self.insert(tk.CURRENT, s)
        self.see(tk.END)

    def flush(self):
        pass

class Mainframe(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self._frame = OptionsFrame(self)
        self._frame.pack(expand=True)

    def change(self, frameClass):
        # make new frame - for archive output
        self._frame = frameClass(self)
        self._frame.pack(fill="both", expand=True)
        return self._frame

class Mainframe(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self._frame = OptionsFrame(self)
        self._frame.pack(expand=True)

    def change(self, newFrameClass):
        # make new frame - for archive output
        self._frame = newFrameClass(self)
        self._frame.pack(fill="both", expand=True)
        return self._frame

class OptionsFrame(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)

        master.title("Test")
        master.geometry("325x180")
        self.selectedProject = None
        self.initUI(master)

    def initUI(self, master):

        frame1 = Frame(master)
        frame1.pack(fill=BOTH, expand=True)

        self.label1 = Label(frame1, text="Select Project to Archive, then click Archive")

        self.projectListbox = tk.Listbox(frame1, width=60, height=100)
        self.projectListbox.bind("<<ProjectSelected>>", self.changeProject)

        # create a vertical scrollbar for the listbox to the right of the listbox
        self.yscroll = tk.Scrollbar(self.projectListbox,command=self.projectListbox.yview,orient=tk.VERTICAL)
        self.projectListbox.configure(yscrollcommand=self.yscroll.set)

        # Archive button
        self.archiveBtn=tk.Button(frame1,text="Archive",command=self.ArchiveButtonClick)

        # Do layout
        self.label1.pack()
        self.projectListbox.pack(fill="both", expand=True)
        self.yscroll.pack(side="right", fill="y")
        self.archiveBtn.pack(side="bottom", pady=10, expand=False)

        choices = ["test 1", "test 2", "test 3", "test 4", "test 5", "test 6"]
        # load listbox with sorted data
        for item in choices:
            self.projectListbox.insert(tk.END, item)

    def getSelectedProject(self):
        # get selected line index
        index = self.projectListbox.curselection()[0]
        # get the line's text
        return self.projectListbox.get(index)

    # on change dropdown value
    def changeProject(self,*args):
        self.selectedProject = self.getSelectedProject()

    def ArchiveButtonClick(self):
        # Switch to second frame - for running the archive
        self.changeProject(None)
        # Hide existing controls
        self.label1.pack_forget()
        self.projectListbox.pack_forget()
        self.yscroll.pack_forget()
        self.archiveBtn.pack_forget()

        newFrame = self.master.change(ArchivingOutputFrame)
        newFrame.args = [ "-n", self.selectedProject]
        newFrame.start()

# Frame shown while archive task is running
class ArchivingOutputFrame(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)
        master.title("Test Frame 2")
        master.geometry("1000x600")
        # Set up for standard output in window
        self.var = tk.StringVar(self)
        lbl = tk.Label(self, textvariable=self.var)
        #lbl.grid(row=0, column=0)
        lbl.pack(anchor="nw")

    def start(self):
        t = threading.Thread(target=self.process)
        t.start()

    def process(self):
        main(self.args)

if __name__=="__main__":
    # If command line options passed - skip the UI
    if len(sys.argv) > 1:
        main(sys.argv[1:])
    else:
        app=Mainframe()
        text = TextOut(app)
        sys.stdout = text
        sys.stderr = text
        text.pack(expand=True, fill=tk.BOTH)
        app.mainloop()

这是我在 UI 中得到的;请注意,这显示了来自 Microsoft 的 Spy++ 的 UI 层次结构——在窗口底部有一个我没有创建的框架(至少我认为我没有创建),它占据了 UI 区域的一半;这是黄色的亮点。因此,我的选项窗格被挤进了上半部分。
用户界面区域

调整大小也不起作用 - 如果我调整窗口大小,我会得到:

调整大小的窗口仅在底部增长

当我单击按钮和代码以删除选项框架并放入从主脚本运行中捕获 stdout/stderr 的框架时,我得到了这个:

来自标准输出的 UI

现在额外的空间似乎在顶部!

感谢您的任何想法 - 我知道我可以切换到使用“网格”UI 布局引擎,但这看起来很简单 - 我在这里没有做任何不应该与 pack 一起使用的复杂事情。

标签: python-3.xtkinter

解决方案


那是很多复杂的代码。如果您提供一个Minimal、Complete 和 Verifiable example将更容易调试。

然而; 底部的 Frame 是您在 Mainframe() 之后打包的 TextOut() 小部件:

if __name__=="__main__":
    app = Mainframe()
    text = TextOut(app)  # This one
    sys.stdout = text
    sys.stderr = text
    text.pack(expand=True, fill=tk.BOTH)
    app.mainloop()

如果您给每个小部件一个 bg 颜色,然后给它们所有的填充,您将有一个更轻松的调试时间,这样您就可以更容易地识别哪个小部件在哪个小部件内。


推荐阅读