布局布线和时序分析
笔记本


2025-08-13 16:12:51PYTHON写的用来查看0qor.rpt的查看器走来走去116.236.47.26

import tkinter as tk
from tkinter import Menu, filedialog, messagebox
import os
import fnmatch  # ? 用于通配符匹配

class FixedSizeViewer:
    def __init__(self, root):
        self.root = root
        self.root.title("\u56FA\u5B9A\u5C3A\u5BF8\u67E5\u770B\u5668")
        self.root.geometry("1220x950")
        self.root.resizable(False, False)

        # 固定内容区尺寸
        self.content_width = 1200
        self.content_height = 840
        self.current_font_size = 10

        # 状态
        self.file_paths = []
        self.current_index = -1
        self.modified = []

        # 创建菜单
        self.menu_bar = Menu(root)
        self.root.config(menu=self.menu_bar)

        self.file_menu = Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="\u6587\u4EF6", menu=self.file_menu)
        self.file_menu.add_command(label="\u5237\u65B0", command=self.refresh_files)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="\u4FDD\u5B58", command=self.save_current)
        self.file_menu.add_command(label="\u53E6\u5B58\u4E3A", command=self.save_as_current)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="\u9000\u51FA", command=self.root.destroy)

        self.recent_menu = Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="\u6253\u5F00\u6587\u4EF6", menu=self.recent_menu)

        self.help_menu = Menu(self.menu_bar, tearoff=0)
        self.menu_bar.add_cascade(label="\u5E2E\u52A9", menu=self.help_menu)
        self.help_menu.add_command(label="\u5173\u4E8E", command=self.show_about)

        # --- 工具栏:单行布局 ---
        self.toolbar = tk.Frame(root, height=30, bg="white")
        self.toolbar.grid(row=0, column=0, sticky='ew', padx=10, pady=5)
        self.toolbar.grid_propagate(False)
        self.toolbar.grid_columnconfigure(1, weight=2)
        self.toolbar.grid_columnconfigure(2, weight=2)

        # 刷新按钮
        self.refresh_btn = tk.Button(self.toolbar, text="\u5237\u65B0", command=self.refresh_files, width=10)
        self.refresh_btn.grid(row=0, column=0, padx=(0, 10), pady=0)

        # 路径过滤
        filter_frame = tk.Frame(self.toolbar)
        filter_frame.grid(row=0, column=1, sticky='ew', padx=(0, 10))
        tk.Label(filter_frame, text="\u8DEF\u5F84\u8FC3\u6EE4", font=("Arial", 9), width=10, anchor="e").pack(side='left')
        self.filter_var = tk.StringVar()
        self.filter_entry = tk.Entry(filter_frame, textvariable=self.filter_var, font=("Arial", 9))
        self.filter_entry.pack(side='left', fill='x', expand=True)
        self.filter_entry.bind("", lambda e: self.refresh_files())

        # 定位内容
        goto_frame = tk.Frame(self.toolbar)
        goto_frame.grid(row=0, column=2, sticky='ew')
        tk.Label(goto_frame, text="\u5B9A\u4F4D\uFF1A", font=("Arial", 9), width=8, anchor="e").pack(side='left')
        self.goto_content_var = tk.StringVar()
        self.goto_content_entry = tk.Entry(goto_frame, textvariable=self.goto_content_var, font=("Arial", 9))
        self.goto_content_entry.pack(side='left', fill='x', expand=True)
        self.goto_content_entry.bind("", lambda e: self.relocate_current())

        # --- 内容区 ---
        self.root.grid_rowconfigure(2, weight=0)
        self.root.grid_rowconfigure(3, weight=1)
        self.root.grid_columnconfigure(0, weight=1)

        self.content_container = tk.Frame(root, bg="white")
        self.content_container.grid(row=2, column=0, pady=(0, 10), sticky="n")

        self.content_frame = tk.Frame(
            self.content_container,
            width=self.content_width,
            height=self.content_height,
            relief="sunken",
            bd=1,
            bg="white"
        )
        self.content_frame.grid(row=0, column=0)
        self.content_frame.grid_propagate(False)
        self.content_frame.grid_rowconfigure(0, weight=1)
        self.content_frame.grid_columnconfigure(0, weight=1)

        self.text_widget = tk.Text(
            self.content_frame,
            wrap='word',
            font=("Consolas", self.current_font_size),
            undo=True,
            bd=0,
            highlightthickness=0,
            padx=6,
            pady=6
        )

        v_scroll = tk.Scrollbar(self.content_frame, orient='vertical', command=self.text_widget.yview)
        h_scroll = tk.Scrollbar(self.content_frame, orient='horizontal', command=self.text_widget.xview)
        self.text_widget.configure(yscrollcommand=v_scroll.set, xscrollcommand=h_scroll.set)

        self.text_widget.grid(row=0, column=0, sticky='nsew', padx=(1, 0), pady=(1, 0))
        v_scroll.grid(row=0, column=1, sticky='ns')
        h_scroll.grid(row=1, column=0, sticky='ew')

        # ? 定义高亮标签
        self.text_widget.tag_configure("goto_line", background="#d0e6ff", foreground="black")
        self.text_widget.tag_configure("match_line", background="#fffacd", foreground="black")
        self.text_widget.tag_configure("neg_lt_minus02", foreground="#8B0000")   # 暗红
        self.text_widget.tag_configure("neg_02_to_01", foreground="red")         # 红
        self.text_widget.tag_configure("neg_01_to_005", foreground="blue")       # 蓝
        self.text_widget.tag_configure("neg_005_to_0", foreground="green")       # 绿

        self.bind_font_zoom(self.text_widget)

        # --- 状态栏 ---
        self.status_frame = tk.Frame(root, height=25, bg="#f0f0f0", relief="sunken", bd=1)
        self.status_frame.grid(row=3, column=0, sticky='ew', padx=10, pady=(0, 10))
        self.status_frame.grid_propagate(False)
        self.status_frame.grid_columnconfigure(1, weight=1)

        self.status_path = tk.Label(self.status_frame, text="\u5C31\u7EEA", bg="#f0f0f0", anchor="w", font=("Arial", 9))
        self.status_size = tk.Label(self.status_frame, text=f"1200 \u00D7 {self.content_height}", bg="#f0f0f0", anchor="center", font=("Arial", 9))
        self.status_cursor = tk.Label(self.status_frame, text="", bg="#f0f0f0", anchor="e", font=("Arial", 9))

        self.status_path.grid(row=0, column=0, padx=(10, 5), pady=2, sticky='w')
        self.status_size.grid(row=0, column=1, padx=5, pady=2, sticky='ew')
        self.status_cursor.grid(row=0, column=2, padx=(5, 10), pady=2, sticky='e')

        # 初始化
        self.refresh_files()
        self.root.protocol("WM_DELETE_WINDOW", self.root.destroy)

    def bind_font_zoom(self, text_widget):
        """Ctrl + \u9F20\u6807\u6EDA\u8F6E \u7F29\u653E\u5B57\u4F53"""
        def on_mousewheel(event):
            if event.state & 0x4:
                if event.delta > 0 and self.current_font_size < 24:
                    self.current_font_size += 1
                elif event.delta < 0 and self.current_font_size > 8:
                    self.current_font_size -= 1
                else:
                    return
                font_family = text_widget.cget("font").split()[0] if " " not in text_widget.cget("font") else "Consolas"
                new_font = (font_family, self.current_font_size)
                text_widget.config(font=new_font)
        text_widget.bind("", on_mousewheel)

    def show_about(self):
        """\u663E\u793A\u5173\u4E8E\u4FE1\u606F\uFF0C\u4F7F\u7528\u81EA\u5B9A\u4E49\u7A97\u53E3\u4EE5\u52A0\u5BBD"""
        about_text = (
            "\u8FD9\u662F\u4E00\u4E2A0qor.rpt\u67E5\u770B\u5668\n\n"
            "\u25CF \u5237\u65B0\u6309\u94AE\uFF1A\u91CD\u65B0\u626B\u63CF\u5F53\u524D\u76EE\u5F55\u53CA\u5B50\u76EE\u5F55\u4E2D\u7684\u6240\u6709 0qor.rpt \u6587\u4EF6\n"
            "\u25CF \u8DEF\u5F84\u8FC3\u6EE4\uFF1A\u53EA\u663E\u793A\u8DEF\u5F84\u4E2D\u5339\u914D\u901A\u914D\u7B26\u7684\u6587\u4EF6\uFF08*\u5339\u914D\u4EFB\u610F\u5B57\u7B26\uFF0C?\u5339\u914D\u5355\u4E2A\u5B57\u7B26\uFF09\n"
            "\u25CF \u5B9A\u4F4D\u6587\u672C\uFF1A\u5207\u6362\u6587\u4EF6\u65F6\uFF0C\u5C06\u7B2C\u4E00\u4E2A\u5305\u542B\u8BE5\u5185\u5BB9\u7684\u884C\u6EDA\u5230\u7A97\u53E3\u7B2C\u4E00\u884C\u5E76\u9AD8\u4EAE\n\n"
            "\u652F\u6301\u529F\u80FD\uFF1A\n"
            "\u2022 \u56FA\u5B9A\u5C3A\u5BF8\u663E\u793A\uFF081200x840\uFF09\n"
            "\u2022 \u6587\u672C\u9AD8\u4EAE\uFF08\u6240\u6709\u5339\u914D\u884C\u6D45\u9EC4\uFF0C\u5B9A\u4F4D\u884C\u6D45\u84DD\uFF09\n"
            "\u2022 \u6570\u5B57\u7740\u8272\uFF1A\n"
            "    \u2022 < -0.2 \u2192 \u6697\u7EA2\u8272\n"
            "    \u2022 -0.2 ~ -0.1 \u2192 \u7EA2\u8272\n"
            "    \u2022 -0.1 ~ -0.05 \u2192 \u84DD\u8272\n"
            "    \u2022 -0.05 ~ 0 \u2192 \u7EFF\u8272\n"
            "\u2022 \u5B57\u4F53\u7F29\u653E\uFF08Ctrl + \u9F20\u6807\u6EDA\u8F6E\uFF09\n"
            "\u2022 \u7A97\u53E3\u6807\u9898\u663E\u793A\u5F53\u524D\u6587\u4EF6\u8DEF\u5F84"
        )

        about_window = tk.Toplevel(self.root)
        about_window.title("\u5173\u4E8E")
        about_window.resizable(False, False)
        about_window.geometry("580x420")
        about_window.transient(self.root)
        about_window.grab_set()

        tk.Label(
            about_window,
            text="\u5173\u4E8E 0qor.rpt \u67E5\u770B\u5668",
            font=("Microsoft YaHei", 12, "bold")
        ).pack(pady=(15, 5))

        text_widget = tk.Text(
            about_window,
            wrap='word',
            font=("Microsoft YaHei", 9),
            padx=15,
            pady=10,
            bg="white",
            relief="flat"
        )
        text_widget.insert("1.0", about_text)
        text_widget.configure(state='disabled')
        text_widget.pack(expand=True, fill='both', padx=20, pady=10)

        tk.Button(about_window, text="\u786E\u5B9A", command=about_window.destroy, width=10).pack(pady=10)

    def find_files(self):
        matches = []
        pattern = self.filter_var.get().strip()
        if not pattern:
            # 无过滤,返回所有
            for root_dir, _, files in os.walk("."):
                if "0qor.rpt" in files:
                    matches.append(os.path.abspath(os.path.join(root_dir, "0qor.rpt")))
        else:
            # 通配符匹配
            for root_dir, _, files in os.walk("."):
                if "0qor.rpt" in files:
                    full_path = os.path.abspath(os.path.join(root_dir, "0qor.rpt"))
                    # 使用 fnmatch 进行通配符匹配(不区分大小写)
                    if fnmatch.fnmatch(full_path.lower(), pattern.lower()):
                        matches.append(full_path)
        return sorted(matches)

    def load_file_content(self, file_path):
        encodings = ['utf-8', 'gbk', 'cp936']
        content = None
        for encoding in encodings:
            try:
                with open(file_path, 'r', encoding=encoding) as f:
                    content = f.read()
                break
            except UnicodeDecodeError:
                continue
            except Exception as e:
                return f"\u9519\u8BEF\uFF1A{str(e)}"
        if content is None:
            return "\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6"
        if content.startswith('\ufeff'):
            content = content[1:]
        return content

    def refresh_files(self):
        self.recent_menu.delete(0, "end")
        self.file_paths.clear()
        self.modified.clear()

        self.file_paths = self.find_files()
        base_dir = os.path.abspath(".")

        if not self.file_paths:
            self.text_widget.delete(1.0, tk.END)
            self.text_widget.insert(tk.END, "# \u672A\u627E\u5230\u7B26\u5408\u6761\u4EF6\u7684 0qor.rpt \u6587\u4EF6")
            self.status_path.config(text="\u65E0\u5339\u914D\u6587\u4EF6")
            self.update_window_title("\u65E0\u5339\u914D\u6587\u4EF6")
            return

        for i, file_path in enumerate(self.file_paths):
            try:
                rel_path = os.path.relpath(file_path, start=base_dir)
            except:
                rel_path = file_path

            self.recent_menu.add_command(
                label=rel_path,
                command=lambda idx=i: self.switch_to(idx)
            )
            self.modified.append(False)

        self.switch_to(0)

    def colorize_numbers(self):
        """\u4E3A\u6587\u672C\u4E2D\u7684\u8D1F\u5C0F\u6570\u7740\u8272"""
        content = self.text_widget.get("1.0", tk.END)
        pattern = r'-0\.\d+'
        import re
        matches = re.finditer(pattern, content)

        for match in matches:
            num_str = match.group()
            try:
                num = float(num_str)
            except:
                continue

            start_pos = match.start()
            end_pos = match.end()

            start_index = self.text_widget.index(f"1.0 + {start_pos} chars")
            end_index = self.text_widget.index(f"1.0 + {end_pos} chars")

            if num < -0.2:
                tag = "neg_lt_minus02"
            elif num < -0.1:
                tag = "neg_02_to_01"
            elif num < -0.05:
                tag = "neg_01_to_005"
            elif num < 0:
                tag = "neg_005_to_0"
            else:
                continue

            self.text_widget.tag_add(tag, start_index, end_index)

    def switch_to(self, index):
        if index < 0 or index >= len(self.file_paths):
            return

        file_path = self.file_paths[index]
        content = self.load_file_content(file_path)

        self.text_widget.delete(1.0, tk.END)
        self.text_widget.insert(tk.END, content)
        self.text_widget.edit_modified(False)

        try:
            rel_path = os.path.relpath(file_path, start=os.path.abspath("."))
        except:
            rel_path = file_path
        self.status_path.config(text=rel_path)
        self.current_index = index

        for tag in ["match_line", "goto_line", "neg_lt_minus02", "neg_02_to_01", "neg_01_to_005", "neg_005_to_0"]:
            self.text_widget.tag_remove(tag, "1.0", tk.END)

        goto_text = self.goto_content_var.get().strip()
        if goto_text:
            pos = '1.0'
            first_match = True
            while True:
                pos = self.text_widget.search(goto_text, pos, stopindex='end', nocase=True)
                if not pos:
                    break
                line_start = pos.split('.')[0] + '.0'
                line_end = line_start + ' lineend'

                if first_match:
                    self.text_widget.mark_set(tk.INSERT, line_start)
                    self.text_widget.see(line_start)
                    self.text_widget.yview(line_start)
                    self.text_widget.tag_add("goto_line", line_start, line_end)
                    first_match = False
                else:
                    self.text_widget.tag_add("match_line", line_start, line_end)

                pos = line_end

        self.colorize_numbers()

        self.update_window_title(file_path)

        self.text_widget.bind("", lambda e: self.on_cursor_move())
        self.text_widget.bind("", lambda e: self.on_cursor_move())
        self.on_cursor_move()

    def relocate_current(self):
        self.switch_to(self.current_index)

    def update_window_title(self, file_path):
        self.root.title(f"\u5F53\u524D\u6587\u4EF6\uFF1A {file_path}")

    def on_cursor_move(self):
        cursor = self.text_widget.index(tk.INSERT)
        row, col = cursor.split('.')
        self.status_cursor.config(text=f"\u884C {row}, \u5217 {col}")

    def save_current(self):
        if self.current_index >= 0:
            self.save_file(self.current_index, force_save_as=False)

    def save_as_current(self):
        if self.current_index >= 0:
            self.save_file(self.current_index, force_save_as=True)

    def save_file(self, index, force_save_as=False):
        if index < 0 or index >= len(self.file_paths):
            return True
        file_path = self.file_paths[index]

        if force_save_as or not os.path.exists(file_path):
            initial_dir = os.path.dirname(file_path) if os.path.exists(file_path) else "."
            new_path = filedialog.asksaveasfilename(
                title="\u4FDD\u5B58",
                initialfile="0qor.rpt",
                initialdir=initial_dir,
                defaultextension=".rpt",
                filetypes=[("\u62A5\u544A\u6587\u4EF6", "*.rpt"), ("*.*", "*.*")]
            )
            if not new_path:
                return False
            self.file_paths[index] = new_path
            self.refresh_files()

        try:
            content = self.text_widget.get(1.0, tk.END).rstrip('\r\n') + '\n'
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(content)
            self.modified[index] = False
            messagebox.showinfo("\u4FDD\u5B58\u6210\u529F", "\u6587\u4EF6\u5DF2\u4FDD\u5B58")
            return True
        except Exception as e:
            messagebox.showerror("\u5931\u8D25", str(e))
            return False


if __name__ == "__main__":
    root = tk.Tk()
    app = FixedSizeViewer(root)
    root.mainloop()


回到首页时钟 , 联系信箱:yzbox#163.com(把#换成@) 粤ICP备18155639号