tkinter frame:更改内容时固定框架高度

tkinter frame: fix frame height when changing its content

我正在开发一个带有适当 GUI 的自写模拟工具。通常有两种主要的用户模式。在第一个中,用户可以为所需的计算输入多个值。所有计算完成后,用户可以通过点击右下角的"Show Results"按钮切换到"results mode"。通过这样做,GUI 上部带有图像的主要 window 不会改变。但是,之前在 GUI 下部的输入条目应该更改为消息区域,其中显示错误、警告或其他信息(持续时间、收敛性、...)。

我可以让两种GUI模式都可以切换,但是下面的框架(称为lowerframe的框架)的框架高度发生了变化,看起来很难看。

我认为所描述的行为是框架内容的不同高度造成的。在第一种模式中有几个 entry 小部件,而在第二种模式中只有一个小 label 小部件包含 foo.

如何防止框架的高度动态变化?换句话说:有没有办法固定 lowerframe?

的高度

我指定了 height=200(以及 GUI 右下角的 btnframe),这似乎没有任何效果,因为包含 foo 的框架-label widget 绝对小于那些 200 px.

--- MWE ---

#!/usr/bin/env python3
# coding: utf-8

from tkinter import *
from tkinter import ttk
from PIL import Image, ImageTk

class Window(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.parameternames = []

        # create master window
        self.master = master

        # create master window
        self.create_masterwindow()

    def create_masterwindow(self):
        """
        creates master window with menubar and sets GUI to input mode
        """
        self.master.title('GUI')
        self.master.geometry('1280x850')
        self.master.resizable(width=FALSE, height=FALSE)

        self.gui_inputmode(firstcall=True)
        self.handle_resultsbtn(firstcall=True, showbtn=False)

    def gui_inputmode(self, firstcall=False):
        """
        turn GUI inputmode on
        """
        if firstcall:
            self.master.grid_columnconfigure(0, weight=1)
            self.master.grid_rowconfigure(0, weight=1)

            self.create_upperframe()
            self.upperframe.grid(row=0, column=0, pady=20, padx=20, columnspan=2, sticky='NEW')

            self.create_lowerframe()
            self.lowerframe.grid(row=1, column=0, pady=10, padx=20, sticky='SEW')

            self.create_btnframe()
            self.btnframe.grid(row=1, column=1, pady=5, padx=20, sticky='NSEW')
            self.put_inputfields()
            self.handle_inputbtn(firstcall=True, showbtn=True)

            self.pid = ImageTk.PhotoImage(file='test_1230_510.png')

            self.pidlabel = Label(self.upperframe, image=self.pid)
            self.pidlabel.pid = self.pid  # store a reference to the image as an attribute of the widget
            self.pidlabel.grid(row=0, column=0)

        else:
            self.handle_resultsbtn(showbtn=False)
            self.handle_inputbtn()
            self.error_label.grid_remove()
            for i, subdict in enumerate(self.parameternames):
                if type(subdict) == dict:
                    self.master.nametowidget(subdict['pname_entry_table']).grid()

    def gui_resultsmode(self):
        """
        turn GUI resultsmode on
        """
        self.handle_inputbtn(showbtn=False)
        self.handle_resultsbtn()

        for i, subdict in enumerate(self.parameternames):
            if type(subdict) == dict:
                self.master.nametowidget(subdict['pname_entry_table']).grid_remove()

        self.error_label = ttk.Label(self.lowerframe, text='foo')
        self.error_label.grid(row=0, column=0, sticky='E')

    def create_upperframe(self):
        """
        creates frame for PID and loads appropriate PID (GUI input mode)
        """
        if hasattr(self, 'upperframe'):
            del self.upperframe
        self.upperframe = Frame(self.master, width=1235, height=510, relief=RIDGE, bd=2)

    def create_lowerframe(self):
        """
        creates lower frame
        """
        if hasattr(self, 'lowerframe'):
            del self.lowerframe
        self.lowerframe = Frame(self.master, relief=RIDGE, bd=2, height=200)

    def put_inputfields(self):
        """
        creates entry widgets in lowerframe to read parameter as user input (GUI input mode)
        """
        for i in range(0, 18):
            r = (i // 4)
            c = (i % 4)*3

            self.lowerframe.grid_columnconfigure(c, weight=1)
            self.lowerframe.grid_columnconfigure(c+2, weight=1)

            entry_table = Entry(self.lowerframe, width=10, justify='right')
            entry_table.insert(0, format(float(42), ".3f"))
            entry_table.grid(row=r, column=c+1, pady=5, padx=0, sticky='EW')
            subdict = {}
            subdict['pname_entry_table'] = str(entry_table)
            self.parameternames.append(subdict)

    def handle_inputbtn(self, firstcall=False, showbtn=True):
        """
        creates buttons in btnframe when GUI in input mode
        """
        if firstcall:
            self.startanalysis_btn = Button(self.btnframe, width=15, text='Start Analysis')
            self.abortanalysis_btn = Button(self.btnframe, width=15, text='Abort Analysis')
            self.resetanalysis_btn = Button(self.btnframe, width=15, text='Reset Analysis')
            self.showresults_btn = Button(self.btnframe, width=15, text='Show Results', command=self.gui_resultsmode)
        if showbtn:
            self.startanalysis_btn.grid(pady=5)
            self.abortanalysis_btn.grid(pady=5)
            self.resetanalysis_btn.grid(pady=5)
            self.showresults_btn.grid(pady=5, sticky='S')
        if not showbtn:
            self.startanalysis_btn.grid_remove()
            self.abortanalysis_btn.grid_remove()
            self.resetanalysis_btn.grid_remove()
            self.showresults_btn.grid_remove()

    def handle_resultsbtn(self, firstcall=False, showbtn=True):
        """
        creates buttons in btnframe when GUI in result mode
        """
        if firstcall:
            self.exportdata_btn = Button(self.btnframe, width=15, text='Export Data')
            self.exportgraphs_btn = Button(self.btnframe, width=15, text='Export Graphs')
            self.createreport_btn = Button(self.btnframe, width=15, text='Create Report')
            self.showinput_btn = Button(self.btnframe, width=15, text='Show Input', command=self.gui_inputmode)
        if showbtn:
            self.exportdata_btn.grid(pady=5)
            self.exportgraphs_btn.grid(pady=5)
            self.createreport_btn.grid(pady=5)
            self.showinput_btn.grid(pady=5, sticky='S')
        if not showbtn:
            self.exportdata_btn.grid_remove()
            self.exportgraphs_btn.grid_remove()
            self.createreport_btn.grid_remove()
            self.showinput_btn.grid_remove()

    def create_btnframe(self):
        """
        creates frame for buttons in lower right corner
        """
        if hasattr(self, 'btnframe'):
            del self.btnframe
        self.btnframe = Frame(self.master, width=130, height=200)
        self.btnframe.grid_rowconfigure(3, weight=1)


master = Tk()
app = Window(master)
master.mainloop()

--- 测试图像 ---

.grid_propagate(flag) 就是您要找的。

设置框架的高度和宽度后,如果禁用传播,无论您有多少个小部件,它的大小都将相同。

举个例子,我写的代码很少。尽管它看起来很丑陋,但显示了传播是如何工作的。

import tkinter as tk

root = tk.Tk()
root.geometry("500x300")

def add():
    tk.Entry(frame).grid()

def disable():
    frame.configure(height=frame["height"],width=frame["width"])
    frame.grid_propagate(0)

def enable():
    frame.grid_propagate(1)

frame = tk.Frame(root, height=100,width=150,bg="black")
frame.grid(row=1,column=0)

tk.Button(root, text="add widget", command=add).grid(row=0,column=0)
tk.Button(root, text="disable propagation", command=disable).grid(row=0,column=1)
tk.Button(root, text="enable propagation", command=enable).grid(row=0,column=2)