lint_filter_gui

import tkinter as tk

from tkinter import filedialog, messagebox

import re, os

# 解析 log,提取数据结构

def parse_log(filepath):

    lines = []

    with open(filepath, encoding='utf-8', errors='ignore') as f:

        lines = f.readlines()

    # file_path -> set(error_codes), set(warning_codes), 行号列表

    file2errs, file2warns, file2idxs = {}, {}, {}

    for idx, l in enumerate(lines):

        mfile = re.search(r'([A-Za-z]:[\\/][^:]+\.c)', l)

        if mfile:

            fpath = mfile.group(1)

            file2errs.setdefault(fpath, set())

            file2warns.setdefault(fpath, set())

            file2idxs.setdefault(fpath, [])

            for em in re.finditer(r'Error (\d+):', l):

                file2errs[fpath].add(em.group(1))

            for wm in re.finditer(r'Warning (\d+):', l):

                file2warns[fpath].add(wm.group(1))

            file2idxs[fpath].append(idx)

    return lines, file2errs, file2warns, file2idxs

class LintLogFilterApp:

    def __init__(self, root):

        self.root = root

        self.root.title("PCLint日志多维筛选工具(升级版)")

        self.root.geometry('1200x750')

        # 数据

        self.lines = []

        self.file2errs = {}  # c文件->set(err编号)

        self.file2warns = {} # c文件->set(warn编号)

        self.file2idxs = {}  # c文件->log行号索引

        self.files = []

        # 界面

        self.build_gui()

    def build_gui(self):

        # 顶部

        top_frame = tk.Frame(self.root)

        top_frame.pack(fill=tk.X, pady=4)

        tk.Button(top_frame, text="导入Log", command=self.load_log).pack(side=tk.LEFT, padx=8)

        tk.Button(top_frame, text="导出筛选结果", command=self.export_result).pack(side=tk.LEFT, padx=8)

        self.status_label = tk.Label(top_frame, text="请先导入 lint.log", fg="blue")

        self.status_label.pack(side=tk.LEFT, padx=16)

        # 主体区域

        main_frame = tk.Frame(self.root)

        main_frame.pack(fill=tk.BOTH, expand=1)

        # 左:c文件区

        left = tk.Frame(main_frame)

        left.pack(side=tk.LEFT, fill=tk.Y, padx=8)

        tk.Label(left, text="c文件筛选(多选)", fg="blue").pack()

        self.file_listbox = tk.Listbox(left, selectmode=tk.MULTIPLE, width=50, height=33, exportselection=0)

        self.file_listbox.pack(side=tk.LEFT, fill=tk.Y)

        file_scroll = tk.Scrollbar(left, command=self.file_listbox.yview)

        file_scroll.pack(side=tk.LEFT, fill=tk.Y)

        self.file_listbox.config(yscrollcommand=file_scroll.set)

        self.file_listbox.bind("<<ListboxSelect>>", self.update_code_list)

        # 文件名悬浮提示

        self.file_tip = tk.Label(left, text="", fg="gray")

        self.file_tip.pack()

        self.file_listbox.bind('<Motion>', self.show_file_tip)

        # 中:Error区

        center = tk.Frame(main_frame)

        center.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)

        tk.Label(center, text="Error编号(联动筛选)", fg="red").pack()

        self.err_listbox = tk.Listbox(center, selectmode=tk.MULTIPLE, width=20, height=33, exportselection=0)

        self.err_listbox.pack(side=tk.LEFT, fill=tk.Y)

        err_scroll = tk.Scrollbar(center, command=self.err_listbox.yview)

        err_scroll.pack(side=tk.LEFT, fill=tk.Y)

        self.err_listbox.config(yscrollcommand=err_scroll.set)

        # 右:Warning区

        right = tk.Frame(main_frame)

        right.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)

        tk.Label(right, text="Warning编号(联动筛选)", fg="darkorange").pack()

        self.warn_listbox = tk.Listbox(right, selectmode=tk.MULTIPLE, width=20, height=33, exportselection=0)

        self.warn_listbox.pack(side=tk.LEFT, fill=tk.Y)

        warn_scroll = tk.Scrollbar(right, command=self.warn_listbox.yview)

        warn_scroll.pack(side=tk.LEFT, fill=tk.Y)

        self.warn_listbox.config(yscrollcommand=warn_scroll.set)

        # 右下:全选/反选

        opt_frame = tk.Frame(self.root)

        opt_frame.pack(fill=tk.X, pady=2)

        tk.Button(opt_frame, text="c文件全选", command=self.select_all_files).pack(side=tk.LEFT, padx=8)

        tk.Button(opt_frame, text="Error全选", command=self.select_all_errs).pack(side=tk.LEFT, padx=8)

        tk.Button(opt_frame, text="Warning全选", command=self.select_all_warns).pack(side=tk.LEFT, padx=8)

        # 补全n行

        self.nline_var = tk.IntVar(value=1)

        tk.Label(opt_frame, text="每条导出补全后续行数:").pack(side=tk.LEFT, padx=8)

        tk.Entry(opt_frame, textvariable=self.nline_var, width=4).pack(side=tk.LEFT)

        tk.Label(opt_frame, text="(建议1或2)").pack(side=tk.LEFT)

    def show_file_tip(self, event):

        idx = self.file_listbox.nearest(event.y)

        if 0 <= idx < len(self.files):

            f = self.files[idx]

            self.file_tip.config(text=f)

        else:

            self.file_tip.config(text="")

    def load_log(self):

        log_path = filedialog.askopenfilename(filetypes=[("Lint Log", "*.log"), ("All files", "*.*")])

        if not log_path: return

        try:

            self.lines, self.file2errs, self.file2warns, self.file2idxs = parse_log(log_path)

            self.files = sorted(self.file2errs)

            self.status_label.config(text=f"已载入: {os.path.basename(log_path)} 共{len(self.lines)}行")

            # 填充c文件列表

            self.file_listbox.delete(0, tk.END)

            for f in self.files:

                self.file_listbox.insert(tk.END, os.path.basename(f))

        except Exception as e:

            messagebox.showerror("读取失败", f"读取log出错:{e}")

            return

        self.err_listbox.delete(0, tk.END)

        self.warn_listbox.delete(0, tk.END)

    def update_code_list(self, event=None):

        # 动态刷新右侧 error/warning 编号,仅展示当前选中文件包含的

        sel_idxs = self.file_listbox.curselection()

        sel_files = [self.files[i] for i in sel_idxs]

        errset, warnset = set(), set()

        for f in sel_files:

            errset.update(self.file2errs.get(f, set()))

            warnset.update(self.file2warns.get(f, set()))

        errlist = sorted(errset, key=lambda x: int(x))

        warnlist = sorted(warnset, key=lambda x: int(x))

        self.err_listbox.delete(0, tk.END)

        for e in errlist:

            self.err_listbox.insert(tk.END, e)

        self.warn_listbox.delete(0, tk.END)

        for w in warnlist:

            self.warn_listbox.insert(tk.END, w)

    def select_all_files(self): self.file_listbox.select_set(0, tk.END)

    def select_all_errs(self): self.err_listbox.select_set(0, tk.END)

    def select_all_warns(self): self.warn_listbox.select_set(0, tk.END)

    def export_result(self):

        # 导出日志:满足文件名和error/warning编号的所有log行,并补全后续N行

        sel_files_idx = self.file_listbox.curselection()

        sel_files = [self.files[i] for i in sel_files_idx]

        sel_errs = [self.err_listbox.get(i) for i in self.err_listbox.curselection()]

        sel_warns = [self.warn_listbox.get(i) for i in self.warn_listbox.curselection()]

        n = self.nline_var.get()

        if not sel_files: messagebox.showwarning("未选c文件", "请选择要筛选的c文件!"); return

        if not sel_errs and not sel_warns: messagebox.showwarning("未选编号", "请至少选择Error或Warning编号!"); return

        out_path = filedialog.asksaveasfilename(defaultextension='.log', filetypes=[('Log', '*.log')])

        if not out_path: return

        re_files = re.compile('|'.join(re.escape(f) for f in sel_files))

        re_errs = re.compile(r'Error (%s):' % '|'.join(map(str, sel_errs))) if sel_errs else None

        re_warns = re.compile(r'Warning (%s):' % '|'.join(map(str, sel_warns))) if sel_warns else None

        exported_idx = set()

        with open(out_path, 'w', encoding='utf-8') as fout:

            for idx, l in enumerate(self.lines):

                if re_files.search(l) and (

                    (re_errs and re_errs.search(l)) or (re_warns and re_warns.search(l))

                ):

                    # 输出本行和后续n行

                    for i in range(n+1):

                        if idx+i not in exported_idx and (idx+i)<len(self.lines):

                            fout.write(self.lines[idx+i])

                            exported_idx.add(idx+i)

        messagebox.showinfo("完成", f"导出完成: {out_path}")

if __name__ == '__main__':

    root = tk.Tk()

    app = LintLogFilterApp(root)

    root.mainloop()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容