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

Tkinter Treeview转DataFrame绘Matplotlib图表无折线问题排查

问题:Tkinter Treeview数据转Pandas DataFrame后,Matplotlib折线图无显示

我编写了Python代码,试图将Tkinter Treeview中插入的数据转换为Pandas DataFrame,筛选出年份为2023、2024且dc_options为Pondok Ungu的数据后绘制Matplotlib折线图表,但点击图表按钮后图表中无折线显示。

原代码

#create a function for the insert data button
def insert_data():
    from tkinter import messagebox
    year = year_button.get()
    month = month_button.get()
    product_categories = product_categories_button.get()
    dc_options = dc_options_button.get()
    actual_data = int(actual_spinbox.get())
    forecast_data = int(forecast_spinbox.get())
    accuracy = (((forecast_data - actual_data) / actual_data) * 100 )
    messagebox.askyesno("Confirmation", "Do You Want to Input this Data?")
    value = year, month, product_categories, dc_options, forecast_data, actual_data, accuracy
    forecast_table.insert("", index='end', values=value)
    return accuracy

#create a function to option of show forecast accuracy chart for 2023
row_list = []
column = ('year', 'month', 'product_categories', 'dc_options', 'forecast_data', 'actual_data', 'accuracy')
for child in forecast_table.get_children():
    row_list.append([forecast_table.item(child)['values']])
forecast_table_dataframe = pd.DataFrame(row_list, columns=column)

#create a multi-condition filter to show the forecast accuracy chart for year 2023, dc place = pondok ungu
filtered_forecast_table_dataframe_2023_pondok_ungu = forecast_table_dataframe.loc[(forecast_table_dataframe['year'] == 2023) & (forecast_table_dataframe['dc_options']=='Pondok Ungu')]

#create a graph for forecasta accuracy chart year 2023, dc place pondok ungu: Forecast Accuracy in Each Month for Each Product Categories in 2023
fig1, ax1 = plt.subplots()
ax1.set_xlabel('Month')
ax1.set_ylabel('Forecast Accuracy')
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%b'))
ax1.set_title("Forecast Accuracy in Each Month for Each Product Categories in 2023")
ax1.plot(filtered_forecast_table_dataframe_2023_pondok_ungu['month'], filtered_forecast_table_dataframe_2023_pondok_ungu['accuracy'], label='Accuracy')
ax1.plot(filtered_forecast_table_dataframe_2023_pondok_ungu['month'], filtered_forecast_table_dataframe_2023_pondok_ungu['product_categories'], label= 'Product Categories')
ax1.legend()

#create a graph for forecast accuracy chart year 2023, dc place pondok ungu: Actual vs Forecast Data for Each Month in 2023
fig2, ax2 = plt.subplots()
ax2.set_xlabel('Month')
ax2.set_ylabel('Actual Data')
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%b'))
ax2.plot(filtered_forecast_table_dataframe_2023_pondok_ungu['month'], filtered_forecast_table_dataframe_2023_pondok_ungu['actual_data'], label='Actual Data')
ax2.plot(filtered_forecast_table_dataframe_2023_pondok_ungu['month'], filtered_forecast_table_dataframe_2023_pondok_ungu['forecast_data'], label='Forecast Data')
ax2.legend()
ax2.set_title('Actual vs Forecast Data for Each Month in 2023')

#create a multi-condition filter to show the forecast accuracy chart for year 2024, dc place = pondok ungu
filtered_forecast_table_dataframe_2024_pondok_ungu = forecast_table_dataframe.loc[(forecast_table_dataframe['year'] == 2024) & (forecast_table_dataframe['dc_options'] == 'Pondok Ungu')]

#create a graph for forecast accuracy chart year 2023, dc place pondok ungu: Actual vs Forecast Data for Each Month in 2024
fig3, ax3 = plt.subplots()
ax3.set_xlabel('Month')
ax3.set_ylabel('Actual Data')
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%b'))
ax3.plot(filtered_forecast_table_dataframe_2024_pondok_ungu['month'], filtered_forecast_table_dataframe_2024_pondok_ungu['actual_data'], label='Actual Data')
ax3.plot(filtered_forecast_table_dataframe_2024_pondok_ungu['month'], filtered_forecast_table_dataframe_2024_pondok_ungu['forecast_data'], label='Forecast Data')
ax3.legend()
ax3.set_title('Actual vs Forecast Data for Each Month in 2024')

#create a graph for forecasta accuracy chart year 2024, dc place pondok ungu: Forecast Accuracy in Each Month for Each Product Categories in 2023
fig4, ax4 = plt.subplots()
ax4.xaxis.set_major_formatter(mdates.DateFormatter('%b'))
ax4.set_xlabel('Month')
ax4.set_ylabel('Forecast Accuracy')
ax4.set_title("Forecast Accuracy in Each Month for Each Product Categories in 2024")
ax4.plot(filtered_forecast_table_dataframe_2024_pondok_ungu['month'], filtered_forecast_table_dataframe_2024_pondok_ungu['forecast_data'], label='Forecast Data')
ax4.plot(filtered_forecast_table_dataframe_2024_pondok_ungu['month'], filtered_forecast_table_dataframe_2024_pondok_ungu['product_categories'], label='Product Categories')
ax4.legend()

问题分析与修复方案

1. Treeview数据转换错误

原代码中row_list.append([forecast_table.item(child)['values']])添加了嵌套列表,导致DataFrame的每一行都是列表对象,而非扁平的数值,后续筛选逻辑完全失效。

修复:直接appendforecast_table.item(child)['values'],去掉外层列表:

row_list.append(forecast_table.item(child)['values'])

2. 数据类型不匹配

year_button.get()获取的是字符串类型,而筛选条件用的是整数2023/2024,两者比较结果为空,导致筛选后的数据框无内容,自然画不出折线。

修复:插入数据时将year转为整数:

year = int(year_button.get())

3. 日期格式化与无效绘图逻辑错误

  • month是字符串(如"01"),不是日期类型,使用mdates.DateFormatter('%b')会报错;
  • product_categories是分类文本,无法用折线图绘制(折线图要求数值型数据)。

修复:将month转为月份缩写,移除对product_categories的折线绘制:

month_map = {1:'Jan',2:'Feb',3:'Mar',4:'Apr',5:'May',6:'Jun',7:'Jul',8:'Aug',9:'Sep',10:'Oct',11:'Nov',12:'Dec'}
forecast_table_dataframe['month_name'] = forecast_table_dataframe['month'].astype(int).map(month_map)

4. 图表逻辑未绑定到按钮回调

原代码中数据读取和绘图逻辑是直接执行的,不是绑定到图表按钮的点击事件,导致点击按钮时不会重新读取Treeview的最新数据,始终使用初始空数据。

修复:将所有数据读取、筛选、绘图逻辑封装到一个函数,绑定到图表按钮的command参数:

def show_charts():
    row_list = []
    columns = ('year', 'month', 'product_categories', 'dc_options', 'forecast_data', 'actual_data', 'accuracy')
    for child in forecast_table.get_children():
        row_list.append(forecast_table.item(child)['values'])
    
    if not row_list:
        messagebox.showwarning("Warning", "No data available to plot")
        return
    
    # 后续筛选和绘图逻辑放在这里...

5. 消息框确认未生效

原代码中messagebox.askyesno的返回值未处理,不管用户点击"是"还是"否"都会插入数据。

修复:根据返回值判断是否插入:

if messagebox.askyesno("Confirmation", "Do You Want to Input this Data?"):
    value = year, month, product_categories, dc_options, forecast_data, actual_data, accuracy
    forecast_table.insert("", index='end', values=value)

修正后的完整代码示例

import tkinter as tk
from tkinter import ttk, messagebox
import pandas as pd
import matplotlib.pyplot as plt

# 初始化主窗口
root = tk.Tk()
root.title("Forecast Data Manager")

# 基础控件定义
year_button = ttk.Entry(root)
month_button = ttk.Entry(root)
product_categories_button = ttk.Entry(root)
dc_options_button = ttk.Entry(root)
actual_spinbox = ttk.Spinbox(root, from_=0, to=10000)
forecast_spinbox = ttk.Spinbox(root, from_=0, to=10000)
forecast_table = ttk.Treeview(root, columns=('year', 'month', 'product_categories', 'dc_options', 'forecast_data', 'actual_data', 'accuracy'), show='headings')

# 设置Treeview表头
for col in ('year', 'month', 'product_categories', 'dc_options', 'forecast_data', 'actual_data', 'accuracy'):
    forecast_table.heading(col, text=col)
    forecast_table.column(col, width=100)

# 插入数据函数
def insert_data():
    try:
        year = int(year_button.get())
        month = int(month_button.get())
        product_categories = product_categories_button.get()
        dc_options = dc_options_button.get()
        actual_data = int(actual_spinbox.get())
        forecast_data = int(forecast_spinbox.get())
        accuracy = (((forecast_data - actual_data) / actual_data) * 100 )
        if messagebox.askyesno("Confirmation", "Do You Want to Input this Data?"):
            value = (year, month, product_categories, dc_options, forecast_data, actual_data, accuracy)
            forecast_table.insert("", index='end', values=value)
        return accuracy
    except ValueError:
        messagebox.showerror("Error", "Please enter valid numeric values for year, month, actual and forecast data")

# 显示图表函数
def show_charts():
    row_list = []
    columns = ('year', 'month', 'product_categories', 'dc_options', 'forecast_data', 'actual_data', 'accuracy')
    for child in forecast_table.get_children():
        row_list.append(forecast_table.item(child)['values'])
    
    if not row_list:
        messagebox.showwarning("Warning", "No data available to plot")
        return
    
    forecast_table_dataframe = pd.DataFrame(row_list, columns=columns)
    # 转换数据类型
    forecast_table_dataframe['year'] = forecast_table_dataframe['year'].astype(int)
    forecast_table_dataframe['month'] = forecast_table_dataframe['month'].astype(int)
    # 映射月份为缩写
    month_map = {1:'Jan',2:'Feb',3:'Mar',4:'Apr',5:'May',6:'Jun',7:'Jul',8:'Aug',9:'Sep',10:'Oct',11:'Nov',12:'Dec'}
    forecast_table_dataframe['month_name'] = forecast_table_dataframe['month'].map(month_map)

    # 2023年Pondok Ungu数据筛选
    filtered_2023 = forecast_table_dataframe.loc[(forecast_table_dataframe['year'] == 2023) & (forecast_table_dataframe['dc_options']=='Pondok Ungu')]
    # 2024年Pondok Ungu数据筛选
    filtered_2024 = forecast_table_dataframe.loc[(forecast_table_dataframe['year'] == 2024) & (forecast_table_dataframe['dc_options']=='Pondok Ungu')]

    # 绘制2023年准确率图表
    if not filtered_2023.empty:
        fig1, ax1 = plt.subplots()
        ax1.set_xlabel('Month')
        ax1.set_ylabel('Forecast Accuracy')
        ax1.set_title("Forecast Accuracy in Each Month (2023, Pondok Ungu)")
        ax1.plot(filtered_2023['month_name'], filtered_2023['accuracy'], marker='o', label='Accuracy')
        ax1.legend()
        plt.xticks(rotation=45)
        plt.tight_layout()
        plt.show()
    else:
        messagebox.showinfo("Info", "No data for 2023, Pondok Ungu")

    # 绘制2023年实际vs预测图表
    if not filtered_2023.empty:
        fig2, ax2 = plt.subplots()
        ax2.set_xlabel('Month')
        ax2.set_ylabel('Data Value')
        ax2.set_title('Actual vs Forecast Data (2023, Pondok Ungu)')
        ax2.plot(filtered_2023['month_name'], filtered_2023['actual_data'], marker='s', label='Actual Data')
        ax2.plot(filtered_2023['month_name'], filtered_2023['forecast_data'], marker='^', label='Forecast Data')
        ax2.legend()
        plt.xticks(rotation=45)
        plt.tight_layout()
        plt.show()

    # 绘制2024年实际vs预测图表
    if not filtered_2024.empty:
        fig3, ax3 = plt.subplots()
        ax3.set_xlabel('Month')
        ax3.set_ylabel('Data Value')
        ax3.set_title('Actual vs Forecast Data (2024, Pondok Ungu)')
        ax3.plot(filtered_2024['month_name'], filtered_2024['actual_data'], marker='s', label='Actual Data')
        ax3.plot(filtered_2024['month_name'], filtered_2024['forecast_data'], marker='^', label='Forecast Data')
        ax3.legend()
        plt.xticks(rotation=45)
        plt.tight_layout()
        plt.show()
    else:
        messagebox.showinfo("Info", "No data for 2024, Pondok Ungu")

    # 绘制2024年准确率图表
    if not filtered_2024.empty:
        fig4, ax4 = plt.subplots()
        ax4.set_xlabel('Month')
        ax4.set_ylabel('Forecast Accuracy')
        ax4.set_title("Forecast Accuracy in Each Month (2024, Pondok Ungu)")
        ax4.plot(filtered_2024['month_name'], filtered_2024['accuracy'], marker='o', label='Accuracy')
        ax4.legend()
        plt.xticks(rotation=45)
        plt.tight_layout()
        plt.show()

# 按钮布局
insert_btn = ttk.Button(root, text="Insert Data", command=insert_data)
chart_btn = ttk.Button(root, text="Show Charts", command=show_charts)

# 放置控件
year_button.pack(pady=2)
month_button.pack(pady=2)
product_categories_button.pack(pady=2)
dc_options_button.pack(pady=2)
actual_spinbox.pack(pady=2)
forecast_spinbox.pack(pady=2)
insert_btn.pack(pady=5)
forecast_table.pack(pady=5, fill=tk.BOTH, expand=True)
chart_btn.pack(pady=5)

root.mainloop()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 09:22:36