如何在 tkinter 中跨不同框架对齐元素?

How can I align elements across different frames in tkinter?

我正在重组 GUI 的结构,但遇到了一些对齐问题。我在左边有一个 LabelFrame 包含一个 matplotlib canvas,我在右边有一个 Notebook 在选项卡中包含几个 LabelFrames,每个包含另一个 matplotlib canvas。我想在视觉上将 Notebook 中的内部 LabelFrames 对齐到最左边的 LabelFrame,这样在下图中,单词 "Left Frame" 和下方的 "Tab 1"垂直对齐。那怎么办?下面包含一个(大部分)最小的工作示例。

import tkinter as tk
from tkinter import ttk
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk

class App(tk.Frame):
    def __init__(self,parent):
        tk.Frame.__init__(self, parent)
        parent.deiconify()
        self.parent = parent

        #define and position the main panel on the left
        self.left_frame = tk.LabelFrame(self.parent, text='Left Panel')
        self.left_frame.grid(row=0,column=0)

        #define and position the tab-enabled secondary panel on the right
        self.ntbk = ttk.Notebook(self.parent)
        self.ntbk.grid(row=0,column=1)

        #define internal frames for the tabs
        self.tab1_frame = tk.LabelFrame(self.ntbk, text='Tab 1')
        self.tab2_frame = tk.LabelFrame(self.ntbk, text='Tab 2')

        self.ntbk.add(self.tab1_frame, text='Tab 1')
        self.ntbk.add(self.tab2_frame, text='Tab 2')


        #Left panel children
        #define a figure canvas to go in the left panel
        self.left_f = Figure(figsize=(7,5), dpi=100)
        self.left_canvas = FigureCanvasTkAgg(self.left_f, master=self.left_frame)
        self.left_toolbar_frame = tk.Frame(self.left_frame)
        self.left_toolbar = NavigationToolbar2Tk(self.left_canvas, self.left_toolbar_frame)
        self.left_toolbar.update()
        self.left_canvas.get_tk_widget().grid(row=0,column=0)
        self.left_toolbar_frame.grid(row=1,column=0)

        #define a control panel for the left panel to go below the figure
        self.left_control_frame = tk.LabelFrame(self.left_frame, text='Left Control Panel')
        self.left_control_frame.grid(row=2,column=0, sticky=tk.E+tk.W)

        self.left_button = tk.Button(self.left_control_frame, text='Button')
        self.left_button.grid(row=0,column=0, sticky=tk.E+tk.W)


        #notebook children
        #define a figure canvas to go in tab 1
        self.tab1_f = Figure(figsize=(7,5), dpi=100)
        self.tab1_canvas = FigureCanvasTkAgg(self.tab1_f, master=self.tab1_frame)
        self.tab1_toolbar_frame = tk.Frame(self.tab1_frame)
        self.tab1_toolbar = NavigationToolbar2Tk(self.tab1_canvas, self.tab1_toolbar_frame)
        self.tab1_toolbar.update()
        self.tab1_canvas.get_tk_widget().grid(row=0,column=0)
        self.tab1_toolbar_frame.grid(row=1,column=0)

        self.tab1_control_frame = tk.LabelFrame(self.tab1_frame, text='Tab 1 Control Panel')
        self.tab1_control_frame.grid(row=2,column=0, sticky=tk.E+tk.W)

        self.tab1_button = tk.Button(self.tab1_control_frame, text='Button')
        self.tab1_button.grid(row=0,column=0, sticky=tk.E+tk.W)

        #define a figure canvas to go in tab 2
        self.tab2_f = Figure(figsize=(7,5), dpi=100)
        self.tab2_canvas = FigureCanvasTkAgg(self.tab2_f, master=self.tab2_frame)
        self.tab2_toolbar_frame = tk.Frame(self.tab2_frame)
        self.tab2_toolbar = NavigationToolbar2Tk(self.tab2_canvas, self.tab2_toolbar_frame)
        self.tab2_toolbar.update()
        self.tab2_canvas.get_tk_widget().grid(row=0,column=0)
        self.tab2_toolbar_frame.grid(row=1,column=0)

        self.tab2_control_frame = tk.LabelFrame(self.tab2_frame, text='Tab 2 Control Panel')
        self.tab2_control_frame.grid(row=2,column=0, sticky=tk.E+tk.W)

        self.tab2_button = tk.Button(self.tab2_control_frame, text='Button')
        self.tab2_button.grid(row=0,column=0, sticky=tk.E+tk.W)



def main():
    root=tk.Tk()
    root.withdraw()
    App(root).grid(row=0,column=0)
    root.mainloop()

if __name__=="__main__":
    main()

如果只想让它在视觉上对齐,只需在代码中添加一些填充即可。

在您的 LeftFrame .grid() 中,添加 pady=xx 并手动查看外观。

self.left_frame.grid(row=0,column=0,pady=(22,0)) # This will be bad when you use DPI awareness.

PS:我尝试用self.ntbk.winfo_height()-self.tab1_frame.winfo_height()得到tab的高度,但实际上不准确。(得到28,但看起来不准确非常好。)

编辑:我找到了直接设置Tab1y的方法,现在你的代码可能是:

import tkinter as tk
from tkinter import ttk
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
# import ctypes,sys 
#
# if sys.getwindowsversion().major == 10:
#     ctypes.windll.shcore.SetProcessDpiAwareness(2) # DPI AWARENESS


class App(tk.Frame):
    def getTabHeight(self):
        self.left_frame.grid(row=0,column=0,pady=(self.tab1_frame.winfo_y()-2,0))  # Get its height,I don't know why it need to minus 2,But in this way,no matter you use DPI awareness,It looked good.

    def __init__(self,parent):
        tk.Frame.__init__(self, parent)
        parent.deiconify()
        self.parent = parent

        #define and position the main panel on the left
        self.left_frame = tk.LabelFrame(self.parent, text='Left Panel')

        #define and position the tab-enabled secondary panel on the right
        self.ntbk = ttk.Notebook(self.parent)
        self.ntbk.grid(row=0,column=1)

        #define internal frames for the tabs
        self.tab1_frame = tk.LabelFrame(self.ntbk, text='Tab 1')
        self.tab2_frame = tk.LabelFrame(self.ntbk, text='Tab 2')

        self.ntbk.add(self.tab1_frame, text='Tab 1')
        self.ntbk.add(self.tab2_frame, text='Tab 2')


        #Left panel children
        #define a figure canvas to go in the left panel
        self.left_f = Figure(figsize=(7,5), dpi=100)
        self.left_canvas = FigureCanvasTkAgg(self.left_f, master=self.left_frame)
        self.left_toolbar_frame = tk.Frame(self.left_frame)
        self.left_toolbar = NavigationToolbar2Tk(self.left_canvas, self.left_toolbar_frame)
        self.left_toolbar.update()
        self.left_canvas.get_tk_widget().grid(row=0,column=0)
        self.left_toolbar_frame.grid(row=1,column=0)

        #define a control panel for the left panel to go below the figure
        self.left_control_frame = tk.LabelFrame(self.left_frame, text='Left Control Panel')
        self.left_control_frame.grid(row=2,column=0, sticky=tk.E+tk.W)

        self.left_button = tk.Button(self.left_control_frame, text='Button')
        self.left_button.grid(row=0,column=0, sticky=tk.E+tk.W)


        #notebook children
        #define a figure canvas to go in tab 1
        self.tab1_f = Figure(figsize=(7,5), dpi=100)
        self.tab1_canvas = FigureCanvasTkAgg(self.tab1_f, master=self.tab1_frame)
        self.tab1_toolbar_frame = tk.Frame(self.tab1_frame)
        self.tab1_toolbar = NavigationToolbar2Tk(self.tab1_canvas, self.tab1_toolbar_frame)
        self.tab1_toolbar.update()
        self.tab1_canvas.get_tk_widget().grid(row=0,column=0)
        self.tab1_toolbar_frame.grid(row=1,column=0)

        self.tab1_control_frame = tk.LabelFrame(self.tab1_frame, text='Tab 1 Control Panel')
        self.tab1_control_frame.grid(row=2,column=0, sticky=tk.E+tk.W)

        self.tab1_button = tk.Button(self.tab1_control_frame, text='Button')
        self.tab1_button.grid(row=0,column=0, sticky=tk.E+tk.W)

        #define a figure canvas to go in tab 2
        self.tab2_f = Figure(figsize=(7,5), dpi=100)
        self.tab2_canvas = FigureCanvasTkAgg(self.tab2_f, master=self.tab2_frame)
        self.tab2_toolbar_frame = tk.Frame(self.tab2_frame)
        self.tab2_toolbar = NavigationToolbar2Tk(self.tab2_canvas, self.tab2_toolbar_frame)
        self.tab2_toolbar.update()
        self.tab2_canvas.get_tk_widget().grid(row=0,column=0)
        self.tab2_toolbar_frame.grid(row=1,column=0)

        self.tab2_control_frame = tk.LabelFrame(self.tab2_frame, text='Tab 2 Control Panel')
        self.tab2_control_frame.grid(row=2,column=0, sticky=tk.E+tk.W)

        self.tab2_button = tk.Button(self.tab2_control_frame, text='Button')
        self.tab2_button.grid(row=0,column=0, sticky=tk.E+tk.W)

        self.after(100,self.getTabHeight)



def main():
    root=tk.Tk()
    root.withdraw()
    App(root).grid(row=0,column=0)
    root.mainloop()

if __name__=="__main__":
    main()

在 DPI 意识中: