You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib图表嵌入Tkinter框架缩放时比例失调、内容截断求助

解决CTkFrame中Matplotlib图表缩放时内容被截断的问题

问题描述

当根窗口调整大小时,嵌入到CTkFrame中的Matplotlib图表会非等比例放大或缩小,导致X轴标签等图表内容被窗口边缘截断,需要让图表更好地保持自身比例与边界完整性。

原始代码

主程序代码

import CTkGraph
import tkinter as tk

if __name__ == '__main__':
    root = tk.Tk()
    root.geometry("800x600")
    root.rowconfigure(0, weight=1, uniform='u')
    root.columnconfigure(0, weight=1, uniform='u')
    graph = CTkGraph.CTkGraph(root)
    graph.grid(row=0, column=0, sticky="nsew")
    root.mainloop()

Graph类代码

import tkinter as tk
import customtkinter as ctk

from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.ticker import StrMethodFormatter
import matplotlib.pyplot as plt

import numpy as np
import pandas as pd

class CTkGraph(ctk.CTkFrame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        allowed_keys = ['dataframe', 'xlabel', 'colors']
        x = np.arange(0, 3, .01)
        y = 2 * np.sin(2 * np.pi * x)
        self.dataframe = pd.DataFrame({'x': x, 'y': y})
        self.xlabel = 'x'
        self.ylabel = 'y'
        self.default_color = 'black'
        self.colors = {}

        for key, value in kwargs.items():
            if key in allowed_keys:
                setattr(self, key, value)

        for column in self.dataframe.columns:
            if column != self.xlabel:
                self.colors.setdefault(column, self.default_color)

        self.fig, self.ax = plt.subplots(layout='tight')

        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        self.plot()

    def plot(self):
        self.ax.cla()
        self.ax.yaxis.set_major_formatter(StrMethodFormatter('{x:,.1f}'))
        self.ax.set_xlabel(self.xlabel)
        self.ax.set_ylabel(self.ylabel)

        for column in self.dataframe.columns:
            if column != self.xlabel:
                self.ax.plot(self.dataframe[self.xlabel], self.dataframe[column], self.colors[column], linewidth=1)

        self.fig.tight_layout()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        self.canvas.draw()

视图对比

  • 正常视图:
    正常视图

  • 异常表现(X轴标签被截断):
    异常表现(X轴标签被截断)

解决方案

核心思路是让Matplotlib自动管理布局,并在窗口尺寸变化时触发图表重新调整,避免内容被裁剪。具体修改如下:

1. 启用constrained_layout自动布局

创建Figure时替换原有的layout='tight'为constrained_layout=True,它会动态计算图表元素的位置,确保标签、标题等不会被窗口边缘截断:

# 替换原创建fig的代码
self.fig, self.ax = plt.subplots(constrained_layout=True)

2. 绑定窗口Resize事件

在__init__方法中,给Canvas的TK组件绑定<Configure>事件,每次尺寸变化时触发图表重绘:

# 在self.plot()之后添加
self.canvas_widget = self.canvas.get_tk_widget()
self.canvas_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.canvas_widget.bind('<Configure>', self.on_resize)

3. 添加Resize处理方法

在CTkGraph类中新增on_resize方法,确保尺寸变化时图表布局自适应更新:

def on_resize(self, event):
    # 触发重绘,让constrained_layout重新计算布局
    self.canvas.draw()

4. 优化plot方法

移除plot方法中重复的pack调用和tight_layout,避免布局冲突:

def plot(self):
    self.ax.cla()
    self.ax.yaxis.set_major_formatter(StrMethodFormatter('{x:,.1f}'))
    self.ax.set_xlabel(self.xlabel)
    self.ax.set_ylabel(self.ylabel)

    for column in self.dataframe.columns:
        if column != self.xlabel:
            self.ax.plot(self.dataframe[self.xlabel], self.dataframe[column], self.colors[column], linewidth=1)

    self.canvas.draw()

修改后的完整Graph类代码

import tkinter as tk
import customtkinter as ctk

from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.ticker import StrMethodFormatter
import matplotlib.pyplot as plt

import numpy as np
import pandas as pd

class CTkGraph(ctk.CTkFrame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        allowed_keys = ['dataframe', 'xlabel', 'colors']
        x = np.arange(0, 3, .01)
        y = 2 * np.sin(2 * np.pi * x)
        self.dataframe = pd.DataFrame({'x': x, 'y': y})
        self.xlabel = 'x'
        self.ylabel = 'y'
        self.default_color = 'black'
        self.colors = {}

        for key, value in kwargs.items():
            if key in allowed_keys:
                setattr(self, key, value)

        for column in self.dataframe.columns:
            if column != self.xlabel:
                self.colors.setdefault(column, self.default_color)

        # 使用constrained_layout自动管理布局
        self.fig, self.ax = plt.subplots(constrained_layout=True)

        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        self.plot()

        # 获取canvas的tk组件并绑定resize事件
        self.canvas_widget = self.canvas.get_tk_widget()
        self.canvas_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        self.canvas_widget.bind('<Configure>', self.on_resize)

    def plot(self):
        self.ax.cla()
        self.ax.yaxis.set_major_formatter(StrMethodFormatter('{x:,.1f}'))
        self.ax.set_xlabel(self.xlabel)
        self.ax.set_ylabel(self.ylabel)

        for column in self.dataframe.columns:
            if column != self.xlabel:
                self.ax.plot(self.dataframe[self.xlabel], self.dataframe[column], self.colors[column], linewidth=1)

        self.canvas.draw()

    def on_resize(self, event):
        # 尺寸变化时重绘图表,触发constrained_layout调整
        self.canvas.draw()

额外说明

如果需要固定图表的宽高比例,可以在on_resize方法中添加aspect设置:

def on_resize(self, event):
    # 设置宽高比为1.5,可根据需求调整数值
    self.ax.set_aspect(1.5, adjustable='box')
    self.canvas.draw()

内容的提问来源于stack exchange,提问作者Dak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 00:44:54