python-可视化提取表格-通用

表格内容如下图所示

绘图后的数据如下图所示:

代码

python 复制代码
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

'''
20260911 hao
简化版:提取指定行列,第一行表头直接作为图例
表格样例:第1行表头A B C D,第2行起数据,X轴取自指定列
'''

class SimpleTablePlotGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("简单表格绘图|表头作为图例")
        self.root.geometry("1000x720")

        self.file_path = tk.StringVar()
        self.sheet_list = []
        self.df_raw = None

        # ========= 行列配置:Excel原生行号从1开始 =========
        self.cfg_header_row = tk.StringVar(value="1")       # 表头(图例)行
        self.cfg_data_start_row = tk.StringVar(value="2")    # 数据起始行
        self.cfg_col_x = tk.StringVar(value="1")            # X轴所在列

        self.full_header = []

        # -------- 文件选择区 --------
        frame_file = ttk.LabelFrame(root, text="1.选择Excel文件 & Sheet")
        frame_file.pack(fill="x", padx=10, pady=4)
        ttk.Label(frame_file, text="文件路径:").grid(row=0, column=0, padx=5, pady=6)
        ttk.Entry(frame_file, textvariable=self.file_path, width=70).grid(row=0, column=1)
        ttk.Button(frame_file, text="浏览", command=self.select_file).grid(row=0, column=2, padx=5)

        ttk.Label(frame_file, text="Sheet名称:").grid(row=1, column=0, padx=5, pady=6)
        self.sheet_combo = ttk.Combobox(frame_file, state="readonly", width=66)
        self.sheet_combo.grid(row=1, column=1)

        # -------- 行列配置区 --------
        frame_cfg = ttk.LabelFrame(root, text="2.行列配置【Excel原生行号,从1开始】")
        frame_cfg.pack(fill="x", padx=10, pady=4)

        ttk.Label(frame_cfg, text="表头(图例)行号:").grid(row=0, column=0, padx=4, pady=4, sticky="e")
        ttk.Entry(frame_cfg, textvariable=self.cfg_header_row, width=8).grid(row=0, column=1, padx=4)

        ttk.Label(frame_cfg, text="数据起始行号:").grid(row=0, column=2, padx=4, pady=4, sticky="e")
        ttk.Entry(frame_cfg, textvariable=self.cfg_data_start_row, width=8).grid(row=0, column=3, padx=4)

        ttk.Label(frame_cfg, text="X轴数据列号:").grid(row=0, column=4, padx=4, pady=4, sticky="e")
        ttk.Entry(frame_cfg, textvariable=self.cfg_col_x, width=8).grid(row=0, column=5, padx=4)

        # -------- 勾选绘图数据列 --------
        frame_col = ttk.LabelFrame(root, text="3.勾选要绘制的数据列(表头自动作为图例名称)")
        frame_col.pack(fill="both", expand=True, padx=10, pady=4)
        self.col_check_frame = ttk.Frame(frame_col)
        self.col_check_frame.pack(fill="both", expand=True, padx=4, pady=3)
        self.col_vars = {}

        # -------- 按钮 --------
        frame_btn = ttk.Frame(root)
        frame_btn.pack(pady=6)
        ttk.Button(frame_btn, text="加载Sheet数据", command=self.load_sheet_data).grid(row=0, column=0, padx=12)
        ttk.Button(frame_btn, text="执行绘图", command=self.do_plot).grid(row=0, column=1, padx=12)

        # -------- 日志输出 --------
        self.info_text = tk.Text(root, height=10, wrap="word")
        self.info_text.pack(fill="x", padx=10, pady=3)
        self.log("就绪:选择Excel,设置行列,勾选数据列绘图,表格第一行直接作为图例")

    def log(self, msg):
        self.info_text.insert(tk.END, msg + "\n")
        self.info_text.see(tk.END)
        self.root.update_idletasks()

    @staticmethod
    def normalize_str(val):
        if pd.isna(val):
            return ""
        return str(val).strip()

    def _parse_int(self, text):
        try:
            return int(text.strip())
        except ValueError:
            return None

    def select_file(self):
        fp = filedialog.askopenfilename(filetypes=[("Excel xlsx", "*.xlsx")])
        if not fp:
            return
        self.file_path.set(fp)
        excel_file = pd.ExcelFile(fp)
        self.sheet_list = excel_file.sheet_names
        self.sheet_combo["values"] = self.sheet_list
        if self.sheet_list:
            self.sheet_combo.current(0)
        self.log(f"已选择文件:{fp}")

    def load_sheet_data(self):
        fp = self.file_path.get()
        sheet_name = self.sheet_combo.get()
        if not fp or not sheet_name:
            messagebox.showwarning("警告", "请先选择文件和sheet")
            return

        h_row = self._parse_int(self.cfg_header_row.get())
        data_start_excel = self._parse_int(self.cfg_data_start_row.get())
        x_col = self._parse_int(self.cfg_col_x.get())

        if None in [h_row, data_start_excel, x_col]:
            messagebox.showerror("输入错误", "行列号必须填写整数(Excel原生行/列号)")
            return

        try:
            self.df_raw = pd.read_excel(fp, sheet_name=sheet_name, header=None)
            # 读取表头行
            header_pd_idx = h_row - 1
            self.full_header = []
            for c in range(len(self.df_raw.columns)):
                self.full_header.append(self.normalize_str(self.df_raw.iloc[header_pd_idx, c]))

            self.log(f"✅加载成功|表头行={h_row}, 数据起始行={data_start_excel}")
            self.refresh_column_checkbox()
        except Exception as e:
            self.log(f"❌读取异常:{str(e)}")
            messagebox.showerror("错误", str(e))

    def refresh_column_checkbox(self):
        for widget in self.col_check_frame.winfo_children():
            widget.destroy()
        self.col_vars.clear()
        ui_row = 0
        for col_idx, col_name in enumerate(self.full_header):
            disp = f"列{col_idx+1}|{col_name}"
            var = tk.BooleanVar()
            cb = ttk.Checkbutton(self.col_check_frame, text=disp, variable=var)
            cb.grid(row=ui_row//4, column=ui_row%4, sticky="w", padx=4, pady=2)
            self.col_vars[col_idx] = var
            ui_row += 1

    @staticmethod
    def is_invalid(val):
        if pd.isna(val):
            return True
        s = str(val).strip()
        return s in ["#N/A", "#DIV/0!", "nan", ""]

    def do_plot(self):
        if self.df_raw is None:
            messagebox.showwarning("警告", "请先加载Sheet数据")
            return

        data_start_pd = self._parse_int(self.cfg_data_start_row.get()) - 1
        x_col_idx = self._parse_int(self.cfg_col_x.get()) - 1
        selected_cols = [c for c, v in self.col_vars.items() if v.get()]

        if len(selected_cols) == 0:
            messagebox.showwarning("警告", "请至少勾选一列绘图数据")
            return

        marker_list = ["o", "s", "^", "D", "v", "*", "p", "X"]
        color_list = plt.cm.tab10(np.linspace(0,1,10))

        fig, ax = plt.subplots(figsize=(13, 7))

        for plot_idx, c in enumerate(selected_cols):
            label_text = self.full_header[c]
            x_data = []
            y_data = []
            for ridx in range(data_start_pd, len(self.df_raw)):
                x_cell = self.df_raw.iloc[ridx, x_col_idx]
                y_cell = self.df_raw.iloc[ridx, c]
                if self.is_invalid(x_cell) or self.is_invalid(y_cell):
                    continue
                try:
                    xv = float(x_cell)
                    yv = float(y_cell)
                    x_data.append(xv)
                    y_data.append(yv)
                except ValueError:
                    continue
            if len(x_data) == 0:
                self.log(f"⚠️列{c+1}({label_text})无有效数值,跳过")
                continue
            mk = marker_list[plot_idx % len(marker_list)]
            clr = color_list[plot_idx % len(color_list)]
            ax.plot(x_data, y_data, marker=mk, linestyle="-", label=label_text, color=clr)

        ax.set_xlabel(f"X轴 - 列{x_col_idx+1}", fontsize=14)
        ax.set_ylabel("Y值", fontsize=14)
        ax.set_title("表格数据绘图(表头作为图例)", fontsize=15)
        ax.grid(alpha=0.35)
        ax.legend(fontsize=11)
        plt.tight_layout()
        fig.show()
        self.log(f"✅绘图完成,共绘制 {len(selected_cols)} 条曲线")


if __name__ == "__main__":
    root = tk.Tk()
    app = SimpleTablePlotGUI(root)
    root.mainloop()
相关推荐
零依赖极客1 小时前
《30 天手搓 ARM 架构零依赖纯 C 推理引擎》课程介绍与大纲
c语言·开发语言·arm开发·人工智能·嵌入式硬件·ai编程
qq_452396231 小时前
第三篇:《变量、类型与函数:Rust 的“基本盘”》
开发语言·后端·rust
5335ld1 小时前
app版本更新(vue3+unibest+静默更新+强制更新)
开发语言·javascript·ecmascript
chushiyunen2 小时前
mockito笔记
java·开发语言·笔记
悟天特斯2 小时前
AI驱动的楼宇节能:从粗放管控到精准降碳的实践路径
开发语言·人工智能·python·物联网
名字还没想好☜2 小时前
Python 用 tempfile 安全创建临时文件:NamedTemporaryFile、TemporaryDirectory 与别自己拼 /tmp 的坑
开发语言·后端·python·安全·编程语言
lvshuocool2 小时前
golang 多版本管理工具 -- g
开发语言·后端·golang
matlab代码2 小时前
基于matlab数字图像处理的指纹识别系统 点线特征(GUI界面)【源码69期】
开发语言·matlab