吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 827|回复: 3
上一主题 下一主题
收起左侧

[Python 转载] 一键对话 WEB 对话型 AI 便利工具。

[复制链接]
跳转到指定楼层
楼主
m_h 发表于 2026-6-7 15:01 回帖奖励

我们在web 对话页面 需要上传文件的时候很麻烦 并不能保证 文件都上传成功了  有的 web 对话 限制上传数量   就有了这个工具。
把 你要上传的文件 复制到 一条超长文本内。
同时支持导出的后缀名 双击加载  以及 1.后缀名  自动加载
import os
import sys
import json
import fnmatch
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk
import threading
import platform

if platform.system() == "Windows":
    import winreg

def register_makelove_association(script_path):
    try:
        pythonw_path = sys.executable
        if not pythonw_path.lower().endswith("pythonw.exe"):
            pythonw_path = pythonw_path[:-10] + "pythonw.exe" if pythonw_path.endswith("python.exe") else pythonw_path
        cmd = f'"{pythonw_path}" "{script_path}" "%1"'
        with winreg.CreateKey(winreg.HKEY_CURRENT_USER, r"Software\Classes\.makelove") as key:
            winreg.SetValue(key, "", winreg.REG_SZ, "CodeCollector.makelove")
        with winreg.CreateKey(winreg.HKEY_CURRENT_USER, r"Software\Classes\CodeCollector.makelove\shell\open\command") as key:
            winreg.SetValue(key, "", winreg.REG_SZ, cmd)
        return True
    except Exception as e:
        print(f"注册文件关联失败: {e}")
        return False

def is_makelove_registered():
    try:
        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Classes\.makelove") as key:
            val = winreg.QueryValue(key, "")
            if val != "CodeCollector.makelove":
                return False
        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Classes\CodeCollector.makelove\shell\open\command") as key:
            cmd = winreg.QueryValue(key, "")
            return True
    except FileNotFoundError:
        return False

class DirSelector(tk.Toplevel):
    def __init__(self, master, app, all_dirs, exclude_var):
        super().__init__(master)
        self.app = app
        self.title("选择排除目录")
        self.geometry("500x450")
        self.resizable(True, True)

        self.all_dirs = all_dirs
        self.exclude_var = exclude_var
        self.selected_dirs = set()

        search_frame = ttk.Frame(self)
        search_frame.pack(fill=tk.X, padx=10, pady=5)
        ttk.Label(search_frame, text="搜索目录:").pack(side=tk.LEFT)
        self.search_var = tk.StringVar()
        self.search_var.trace_add("write", lambda *args: self.update_list())
        ttk.Entry(search_frame, textvariable=self.search_var, width=30).pack(side=tk.LEFT, padx=5)
        ttk.Button(search_frame, text="清除", command=lambda: self.search_var.set("")).pack(side=tk.LEFT)

        list_frame = ttk.Frame(self)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        if self.all_dirs:
            self.listbox = tk.Listbox(list_frame, selectmode='multiple', exportselection=False)
            scrollbar = ttk.Scrollbar(list_frame, orient="vertical", command=self.listbox.yview)
            self.listbox.configure(yscrollcommand=scrollbar.set)
            self.listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
            scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
            self.listbox.bind('<<ListboxSelect>>', self.on_select)
        else:
            ttk.Label(list_frame, text="未发现任何子目录", anchor="center").pack(expand=True)

        option_frame = ttk.Frame(self)
        option_frame.pack(fill=tk.X, padx=10, pady=2)
        self.use_dirname_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(option_frame, text="仅用目录名(*目录名*)", variable=self.use_dirname_var).pack(side=tk.LEFT)
        ttk.Checkbutton(option_frame, text="或用完整相对路径", variable=tk.BooleanVar(value=False),
                        command=lambda: self.use_dirname_var.set(not self.use_dirname_var.get())).pack(side=tk.LEFT)

        btn_frame = ttk.Frame(self)
        btn_frame.pack(fill=tk.X, padx=10, pady=5)
        if self.all_dirs:
            ttk.Button(btn_frame, text="全选", command=self.select_all).pack(side=tk.LEFT, padx=2)
            ttk.Button(btn_frame, text="取消全选", command=self.deselect_all).pack(side=tk.LEFT, padx=2)
            ttk.Button(btn_frame, text="添加到排除列表", command=self.add_to_exclude).pack(side=tk.RIGHT, padx=5)
        ttk.Button(btn_frame, text="关闭", command=self.destroy).pack(side=tk.RIGHT, padx=2)

        if self.all_dirs:
            self.update_list()

    def update_list(self):
        search_text = self.search_var.get().lower()
        self.listbox.delete(0, tk.END)
        for d in self.all_dirs:
            if search_text in d.lower():
                self.listbox.insert(tk.END, d)
        for i in range(self.listbox.size()):
            if self.listbox.get(i) in self.selected_dirs:
                self.listbox.selection_set(i)

    def on_select(self, event=None):
        self.selected_dirs = {self.listbox.get(i) for i in self.listbox.curselection()}

    def select_all(self):
        self.listbox.selection_set(0, tk.END)
        self.on_select()

    def deselect_all(self):
        self.listbox.selection_clear(0, tk.END)
        self.selected_dirs.clear()

    def add_to_exclude(self):
        if not self.selected_dirs:
            messagebox.showinfo("提示", "请先选择目录")
            return
        patterns = []
        for d in self.selected_dirs:
            if self.use_dirname_var.get():
                dirname = os.path.basename(d)
                patterns.append(f"*{dirname}*")
            else:
                patterns.append(d)
        current = self.exclude_var.get().strip()
        existing = [p.strip() for p in current.split(',') if p.strip()] if current else []
        for pat in patterns:
            if pat not in existing:
                existing.append(pat)
        self.exclude_var.set(", ".join(existing))
        self.app.log_message(f"已添加排除模式: {', '.join(patterns)},请点击「开始扫描文件」应用更改。")
        self.destroy()

class SuffixSelector(tk.Toplevel):
    def __init__(self, master, app):
        super().__init__(master)
        self.app = app
        self.title("选择后缀")
        self.geometry("500x450")
        self.resizable(True, True)

        search_frame = ttk.Frame(self)
        search_frame.pack(fill=tk.X, padx=10, pady=5)
        ttk.Label(search_frame, text="搜索后缀:").pack(side=tk.LEFT)
        self.search_var = tk.StringVar()
        self.search_var.trace_add("write", lambda *args: self.update_checkboxes())
        ttk.Entry(search_frame, textvariable=self.search_var, width=30).pack(side=tk.LEFT, padx=5)
        ttk.Button(search_frame, text="清除", command=lambda: self.search_var.set("")).pack(side=tk.LEFT)

        self.canvas = tk.Canvas(self, borderwidth=0, highlightthickness=0)
        scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=scrollbar.set)
        self.inner = ttk.Frame(self.canvas)
        self.canvas.create_window((0,0), window=self.inner, anchor="nw")
        self.inner.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))

        self.canvas.pack(side="left", fill="both", expand=True, padx=10, pady=5)
        scrollbar.pack(side="right", fill="y")

        self.canvas.bind("<Enter>", lambda e: self.canvas.focus_set())
        self.canvas.bind("<MouseWheel>", lambda e: self.canvas.yview_scroll(int(-1*(e.delta/120)), "units"))

        btn_frame = ttk.Frame(self)
        btn_frame.pack(fill=tk.X, padx=10, pady=5)
        ttk.Button(btn_frame, text="全选", command=self.select_all).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="取消全选", command=self.deselect_all).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="应用并关闭", command=self.apply_and_close).pack(side=tk.RIGHT, padx=5)
        ttk.Button(btn_frame, text="关闭", command=self.destroy).pack(side=tk.RIGHT, padx=2)

        self.checkbuttons = []
        self.update_checkboxes()

    def update_checkboxes(self):
        for cb, _ in self.checkbuttons:
            cb.destroy()
        self.checkbuttons.clear()

        filter_text = self.search_var.get().lower()
        if filter_text:
            display = [s for s in self.app.all_suffixes if filter_text in s.lower()]
        else:
            display = self.app.all_suffixes.copy()

        if not display:
            return

        self.inner.update_idletasks()
        frame_width = self.inner.winfo_width()
        if frame_width < 100:
            frame_width = 400
        max_len = max(len(s) for s in display) if display else 5
        btn_width = max(100, max_len * 10 + 40)
        cols = max(1, frame_width // btn_width)

        for i, ext in enumerate(display):
            if ext not in self.app.suffix_vars:
                self.app.suffix_vars[ext] = tk.BooleanVar(value=True)
            var = self.app.suffix_vars[ext]
            cb = ttk.Checkbutton(self.inner, text=ext, variable=var,
                                 command=self.app.on_suffix_changed)
            row = i // cols
            col = i % cols
            cb.grid(row=row, column=col, padx=3, pady=2, sticky='w')
            self.checkbuttons.append((cb, ext))

        self.inner.update_idletasks()
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

    def select_all(self):
        self.app._batch_updating = True
        for var in self.app.suffix_vars.values():
            var.set(True)
        self.app._batch_updating = False
        self.app.on_suffix_changed()

    def deselect_all(self):
        self.app._batch_updating = True
        for var in self.app.suffix_vars.values():
            var.set(False)
        self.app._batch_updating = False
        self.app.on_suffix_changed()

    def apply_and_close(self):
        self.app.update_filter_display()
        self.destroy()

class HeaderFooterEditor(tk.Toplevel):
    def __init__(self, master, app):
        super().__init__(master)
        self.app = app
        self.title("编辑头部/尾部文本")
        self.geometry("700x600")
        self.resizable(True, True)

        self.protocol("WM_DELETE_WINDOW", self.save_and_close)

        header_frame = ttk.LabelFrame(self, text="头部文本(在文件内容之前)")
        header_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        self.header_text = scrolledtext.ScrolledText(header_frame, wrap=tk.WORD)
        self.header_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.header_text.insert("1.0", self.app.header_content)

        footer_frame = ttk.LabelFrame(self, text="尾部文本(在文件内容之后)")
        footer_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        self.footer_text = scrolledtext.ScrolledText(footer_frame, wrap=tk.WORD)
        self.footer_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.footer_text.insert("1.0", self.app.footer_content)

        btn_frame = ttk.Frame(self)
        btn_frame.pack(fill=tk.X, padx=10, pady=5)
        ttk.Button(btn_frame, text="保存并关闭", command=self.save_and_close).pack(side=tk.RIGHT, padx=5)

    def save_and_close(self):
        self.app.header_content = self.header_text.get("1.0", tk.END).strip()
        self.app.footer_content = self.footer_text.get("1.0", tk.END).strip()
        self.destroy()

class CodeCollector:
    def __init__(self, root):
        self.root = root
        self.root.title("C++ 源码收集器 v13.1")
        self.root.geometry("900x750")
        self.root.resizable(True, True)

        self.all_files = []
        self.all_suffixes = sorted([])
        self.selected_files = set()
        self.root_dir = ""

        self.all_dirs = []

        self.suffix_vars = {}
        self._batch_updating = False

        self.pending_selected_files = None
        self.pending_active_suffixes = None

        self.header_content = ""
        self.footer_content = ""

        # ---------- 注册 .makelove 关联 ----------
        if platform.system() == "Windows":
            script_path = os.path.abspath(__file__)
            if not is_makelove_registered():
                if register_makelove_association(script_path):
                    self.root.after(100, lambda: self.log_message("已自动注册 .makelove 文件关联,现可双击 .makelove 文件打开程序。"))
            else:
                try:
                    pythonw_path = sys.executable
                    if not pythonw_path.lower().endswith("pythonw.exe"):
                        pythonw_path = pythonw_path[:-10] + "pythonw.exe" if pythonw_path.endswith("python.exe") else pythonw_path
                    cmd = f'"{pythonw_path}" "{script_path}" "%1"'
                    with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Classes\CodeCollector.makelove\shell\open\command", 0, winreg.KEY_SET_VALUE) as key:
                        winreg.SetValue(key, "", winreg.REG_SZ, cmd)
                except:
                    pass

        # ---------- 解析命令行参数 / 自动加载 1.makelove ----------
        config_to_load = None
        if len(sys.argv) > 1:
            file_arg = sys.argv[1]
            if os.path.isfile(file_arg) and file_arg.lower().endswith(".makelove"):
                config_to_load = file_arg
        if config_to_load is None:
            default_config = os.path.join(os.path.dirname(os.path.abspath(__file__)), "1.makelove")
            if os.path.isfile(default_config):
                config_to_load = default_config

        self.setup_ui()

        if config_to_load:
            self._load_config_file(config_to_load)
            self.log_message(f"已自动加载配置: {os.path.basename(config_to_load)}")

        self.root.bind_all("<MouseWheel>", self._on_mousewheel_global)

    def setup_ui(self):
        # ========== 目录选择 ==========
        frame_top = ttk.Frame(self.root)
        frame_top.pack(fill=tk.X, padx=10, pady=5)
        ttk.Label(frame_top, text="根目录:").pack(side=tk.LEFT)
        self.path_var = tk.StringVar()
        ttk.Entry(frame_top, textvariable=self.path_var, width=50).pack(side=tk.LEFT, padx=5, expand=True, fill=tk.X)
        ttk.Button(frame_top, text="浏览...", command=self.browse_folder).pack(side=tk.LEFT)

        # ========== 扫描选项 + 排除目录 ==========
        frame_scan = ttk.Frame(self.root)
        frame_scan.pack(fill=tk.X, padx=10, pady=5)
        self.recursive_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(frame_scan, text="包含子文件夹", variable=self.recursive_var).pack(side=tk.LEFT, padx=5)

        ttk.Label(frame_scan, text="排除目录:").pack(side=tk.LEFT, padx=(15,2))
        self.exclude_var = tk.StringVar(value="")
        self.exclude_entry = ttk.Entry(frame_scan, textvariable=self.exclude_var, width=30)
        self.exclude_entry.pack(side=tk.LEFT, padx=5)
        ttk.Button(frame_scan, text="选择目录", command=self.open_dir_selector).pack(side=tk.LEFT, padx=5)

        self.scan_btn = ttk.Button(frame_scan, text="开始扫描文件", command=self.start_scan)
        self.scan_btn.pack(side=tk.LEFT, padx=10)
        self.scan_progress = ttk.Label(frame_scan, text="")
        self.scan_progress.pack(side=tk.LEFT, padx=5)

        ttk.Button(frame_scan, text="保存配置", command=self.save_config).pack(side=tk.RIGHT, padx=2)
        ttk.Button(frame_scan, text="加载配置", command=self.load_config).pack(side=tk.RIGHT, padx=2)

        # ========== 后缀过滤摘要 ==========
        suffix_summary = ttk.Frame(self.root)
        suffix_summary.pack(fill=tk.X, padx=10, pady=5)
        ttk.Label(suffix_summary, text="后缀过滤:").pack(side=tk.LEFT)
        self.suffix_display_var = tk.StringVar(value="")
        ttk.Entry(suffix_summary, textvariable=self.suffix_display_var, state="readonly", width=60).pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        ttk.Button(suffix_summary, text="选择后缀", command=self.open_suffix_selector).pack(side=tk.LEFT, padx=5)
        ttk.Button(suffix_summary, text="编辑头部/尾部", command=self.open_header_footer_editor).pack(side=tk.LEFT, padx=5)

        # ========== 文件列表 ==========
        frame_list = ttk.LabelFrame(self.root, text="文件列表(勾选要合并的文件)")
        frame_list.pack(fill=tk.BOTH, padx=10, pady=5, expand=True)

        search_frame = ttk.Frame(frame_list)
        search_frame.pack(fill=tk.X, padx=5, pady=2)
        ttk.Label(search_frame, text="搜索:").pack(side=tk.LEFT)
        self.search_var = tk.StringVar()
        self.search_var.trace_add("write", lambda *args: self.update_listbox())
        ttk.Entry(search_frame, textvariable=self.search_var, width=30).pack(side=tk.LEFT, padx=5)
        ttk.Button(search_frame, text="清除", command=lambda: self.search_var.set("")).pack(side=tk.LEFT)

        list_container = ttk.Frame(frame_list)
        list_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.listbox = tk.Listbox(list_container, selectmode='multiple', exportselection=False)
        scrollbar_list = ttk.Scrollbar(list_container, orient="vertical", command=self.listbox.yview)
        self.listbox.configure(yscrollcommand=scrollbar_list.set)
        self.listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar_list.pack(side=tk.RIGHT, fill=tk.Y)
        self.listbox.bind('<<ListboxSelect>>', self.on_listbox_select)

        frame_list_btns = ttk.Frame(frame_list)
        frame_list_btns.pack(fill=tk.X, padx=5, pady=2)
        ttk.Button(frame_list_btns, text="全选全部", command=self.select_all).pack(side=tk.LEFT, padx=2)
        ttk.Button(frame_list_btns, text="取消全部", command=self.deselect_all).pack(side=tk.LEFT, padx=2)
        ttk.Button(frame_list_btns, text="选中可见", command=self.select_visible).pack(side=tk.LEFT, padx=2)
        ttk.Button(frame_list_btns, text="取消可见", command=self.deselect_visible).pack(side=tk.LEFT, padx=2)

        # ========== 操作按钮 ==========
        frame_actions = ttk.Frame(self.root)
        frame_actions.pack(fill=tk.X, padx=10, pady=5)
        self.save_btn = ttk.Button(frame_actions, text="保存到文件", command=self.save_to_file, state='disabled')
        self.save_btn.pack(side=tk.LEFT, padx=5)
        self.copy_btn = ttk.Button(frame_actions, text="复制到剪贴板", command=self.copy_to_clipboard, state='disabled')
        self.copy_btn.pack(side=tk.LEFT, padx=5)
        ttk.Button(frame_actions, text="退出", command=self.root.destroy).pack(side=tk.RIGHT, padx=5)

        # ========== 日志 ==========
        self.log = scrolledtext.ScrolledText(self.root, height=6, state='disabled')
        self.log.pack(fill=tk.BOTH, padx=10, pady=5, expand=False)

    def browse_folder(self):
        folder = filedialog.askdirectory()
        if folder:
            self.path_var.set(folder)

    def log_message(self, msg):
        self.log.configure(state='normal')
        self.log.insert(tk.END, msg + "\n")
        self.log.see(tk.END)
        self.log.configure(state='disabled')

    def update_button_states(self):
        if self.selected_files:
            self.save_btn.config(state='normal')
            self.copy_btn.config(state='normal')
        else:
            self.save_btn.config(state='disabled')
            self.copy_btn.config(state='disabled')

    def open_dir_selector(self):
        DirSelector(self.root, self, self.all_dirs, self.exclude_var)

    def open_suffix_selector(self):
        SuffixSelector(self.root, self)

    def open_header_footer_editor(self):
        HeaderFooterEditor(self.root, self)

    def _on_mousewheel_global(self, event):
        widget = self.root.winfo_containing(event.x_root, event.y_root)
        if widget is None:
            return
        if self._is_descendant(widget, self.listbox):
            self._on_mousewheel_list(event)
        elif self._is_descendant(widget, self.log):
            self._on_mousewheel_log(event)

    def _is_descendant(self, widget, parent):
        while widget is not None:
            if widget is parent:
                return True
            widget = widget.master
        return False

    def _on_mousewheel_list(self, event):
        self.listbox.yview_scroll(int(-1*(event.delta/120)), "units")

    def _on_mousewheel_log(self, event):
        self.log.yview_scroll(int(-1*(event.delta/120)), "units")

    def get_exclude_patterns(self):
        raw = self.exclude_var.get().strip()
        if not raw:
            return []
        return [p.strip() for p in raw.split(',') if p.strip()]

    def update_filter_display(self):
        active = self.get_active_suffixes()
        self.suffix_display_var.set(", ".join(active))

    def get_active_suffixes(self):
        return [ext for ext, var in self.suffix_vars.items() if var.get()]

    def on_suffix_changed(self):
        if self._batch_updating:
            return
        self.update_filter_display()
        self.apply_filter()

    def start_scan(self):
        root_dir = self.path_var.get().strip()
        if not root_dir or not os.path.isdir(root_dir):
            messagebox.showerror("错误", "请选择有效的根目录")
            return

        self.scan_btn.config(state='disabled')
        self.save_btn.config(state='disabled')
        self.copy_btn.config(state='disabled')
        self.listbox.delete(0, tk.END)
        self.all_files.clear()
        self.selected_files.clear()
        self.root_dir = root_dir
        self.scan_progress.config(text="扫描中...")
        self.log_message("开始扫描所有文件...")

        recursive = self.recursive_var.get()
        exclude_patterns = self.get_exclude_patterns()
        thread = threading.Thread(target=self.scan_files, args=(root_dir, recursive, exclude_patterns))
        thread.daemon = True
        thread.start()

    def scan_files(self, root_dir, recursive, exclude_patterns):
        files = []
        suffixes = set()
        dirs = []
        try:
            for dirpath, dirnames, filenames in os.walk(root_dir):
                for d in dirnames:
                    rel_dir = os.path.relpath(os.path.join(dirpath, d), root_dir)
                    dirs.append(rel_dir)
                if exclude_patterns:
                    dirnames[:] = [d for d in dirnames if not any(
                        fnmatch.fnmatch(d, pat) for pat in exclude_patterns
                    )]
                rel = os.path.relpath(dirpath, root_dir)
                if rel != '.':
                    dirs.append(rel)
                for fname in filenames:
                    full = os.path.join(dirpath, fname)
                    rel_file = os.path.relpath(full, root_dir)
                    files.append(rel_file)
                    ext = os.path.splitext(fname)[1].lower()
                    if ext:
                        suffixes.add(ext)
                if not recursive:
                    break
        except Exception as e:
            self.root.after(0, self.on_scan_error, str(e))
            return
        dirs = sorted(set(dirs))
        self.root.after(0, self.on_scan_complete, files, sorted(suffixes), dirs)

    def on_scan_error(self, err_msg):
        self.log_message(f"扫描错误: {err_msg}")
        self.scan_btn.config(state='normal')
        self.scan_progress.config(text="")

    def on_scan_complete(self, files, suffixes, dirs):
        self.all_files = files
        self.all_suffixes = suffixes
        self.all_dirs = dirs

        for ext in self.all_suffixes:
            if ext not in self.suffix_vars:
                self.suffix_vars[ext] = tk.BooleanVar(value=True)

        if self.pending_active_suffixes is not None:
            for ext, var in self.suffix_vars.items():
                var.set(ext in self.pending_active_suffixes)
            self.pending_active_suffixes = None

        if self.pending_selected_files is not None:
            restored = set(self.pending_selected_files).intersection(self.all_files)
            self.selected_files = restored
            self.pending_selected_files = None
        else:
            self.selected_files = set(files)

        self.update_filter_display()
        self.apply_filter()
        self.scan_btn.config(state='normal')
        self.update_button_states()
        self.scan_progress.config(text=f"找到 {len(files)} 个文件,{len(suffixes)} 种后缀,{len(dirs)} 个子目录")
        self.log_message(f"扫描完成,共找到 {len(files)} 个文件,{len(dirs)} 个子目录。")

    def get_filtered_files(self):
        active_exts = self.get_active_suffixes()
        if not active_exts:
            return []
        return [f for f in self.all_files if os.path.splitext(f)[1].lower() in active_exts]

    def apply_filter(self):
        filtered = self.get_filtered_files()
        new_selected = {f for f in filtered if f in self.selected_files}
        if not new_selected and filtered:
            new_selected = set(filtered)
        self.selected_files = new_selected
        self.update_listbox()
        self.update_button_states()
        self.log_message(f"过滤后显示 {len(filtered)} 个文件。")

    def update_listbox(self):
        search_text = self.search_var.get().lower()
        filtered_files = self.get_filtered_files()
        self.listbox.delete(0, tk.END)
        for rel in filtered_files:
            if search_text in rel.lower():
                self.listbox.insert(tk.END, rel)
        for idx in range(self.listbox.size()):
            rel = self.listbox.get(idx)
            if rel in self.selected_files:
                self.listbox.selection_set(idx)

    def on_listbox_select(self, event=None):
        visible = [self.listbox.get(i) for i in range(self.listbox.size())]
        selected_indices = self.listbox.curselection()
        visible_selected = {self.listbox.get(i) for i in selected_indices}
        for rel in visible:
            if rel in visible_selected:
                self.selected_files.add(rel)
            else:
                self.selected_files.discard(rel)
        self.update_button_states()

    def select_all(self):
        filtered = self.get_filtered_files()
        self.selected_files = set(filtered)
        self.update_listbox()
        self.update_button_states()

    def deselect_all(self):
        self.selected_files.clear()
        self.update_listbox()
        self.update_button_states()

    def select_visible(self):
        for idx in range(self.listbox.size()):
            self.selected_files.add(self.listbox.get(idx))
        self.update_listbox()
        self.update_button_states()

    def deselect_visible(self):
        for idx in range(self.listbox.size()):
            self.selected_files.discard(self.listbox.get(idx))
        self.update_listbox()
        self.update_button_states()

    def save_config(self):
        if not self.root_dir:
            messagebox.showerror("错误", "请先扫描一个目录")
            return
        config = {
            "root_dir": self.root_dir,
            "recursive": self.recursive_var.get(),
            "exclude_patterns": self.get_exclude_patterns(),
            "active_suffixes": self.get_active_suffixes(),
            "selected_files": list(self.selected_files),
            "header": self.header_content,
            "footer": self.footer_content
        }
        filepath = filedialog.asksaveasfilename(
            defaultextension=".makelove",
            filetypes=[("配置文件", "*.makelove"), ("所有文件", "*.*")],
            title="保存配置文件"
        )
        if filepath:
            with open(filepath, 'w', encoding='utf-8') as f:
                json.dump(config, f, indent=2, ensure_ascii=False)
            self.log_message(f"配置已保存到: {filepath}")

    def _load_config_file(self, filepath):
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                config = json.load(f)
        except Exception as e:
            self.log_message(f"加载配置失败: {e}")
            return

        root_dir = config.get("root_dir", "")
        if not root_dir or not os.path.isdir(root_dir):
            self.log_message("配置中的根目录无效,无法加载")
            return

        self.path_var.set(root_dir)
        self.recursive_var.set(config.get("recursive", True))
        self.exclude_var.set(", ".join(config.get("exclude_patterns", [])))

        self.header_content = config.get("header", "")
        self.footer_content = config.get("footer", "")

        self.pending_active_suffixes = config.get("active_suffixes", None)
        self.pending_selected_files = config.get("selected_files", [])

        self.start_scan()

    def load_config(self):
        filepath = filedialog.askopenfilename(
            filetypes=[("配置文件", "*.makelove"), ("所有文件", "*.*")],
            title="加载配置文件"
        )
        if not filepath:
            return
        self._load_config_file(filepath)
        self.log_message(f"已手动加载配置: {filepath}")

    def read_selected_files(self):
        merged = []
        count = 0
        for rel in sorted(self.selected_files):
            full = os.path.join(self.root_dir, rel)
            if not os.path.isfile(full):
                continue
            with open(full, 'r', encoding='utf-8', errors='replace') as f:
                content = f.read()
            merged.append(f"// ====== 文件: {rel} ======\n")
            merged.append(content)
            if not content.endswith('\n'):
                merged.append('\n')
            merged.append('\n')
            count += 1
        return ''.join(merged), count

    def build_final_text(self):
        core, count = self.read_selected_files()
        final = ""
        if self.header_content:
            final += self.header_content + "\n\n"
        final += core
        if self.footer_content:
            final += "\n\n" + self.footer_content
        return final, count

    def save_to_file(self):
        if not self.selected_files:
            messagebox.showerror("错误", "没有选中任何文件")
            return
        self.save_btn.config(state='disabled')
        self.copy_btn.config(state='disabled')
        thread = threading.Thread(target=self._save_thread)
        thread.daemon = True
        thread.start()

    def _save_thread(self):
        text, count = self.build_final_text()
        self.root.after(0, self._save_finish, text, count)

    def _save_finish(self, text, count):
        if count == 0:
            self.log_message("没有成功读取任何文件,取消保存。")
        else:
            path = filedialog.asksaveasfilename(defaultextension=".txt",
                                                filetypes=[("文本文件", "*.txt")],
                                                title="保存合并文档")
            if path:
                with open(path, 'w', encoding='utf-8') as f:
                    f.write(text)
                self.log_message(f"已保存 {count} 个文件到: {path}")
        self.update_button_states()

    def copy_to_clipboard(self):
        if not self.selected_files:
            messagebox.showerror("错误", "没有选中任何文件")
            return
        self.save_btn.config(state='disabled')
        self.copy_btn.config(state='disabled')
        thread = threading.Thread(target=self._copy_thread)
        thread.daemon = True
        thread.start()

    def _copy_thread(self):
        text, count = self.build_final_text()
        self.root.after(0, self._copy_finish, text, count)

    def _copy_finish(self, text, count):
        if count == 0:
            self.log_message("没有成功读取任何文件,剪贴板未更新。")
        else:
            self.root.clipboard_clear()
            self.root.clipboard_append(text)
            self.log_message(f"已将 {count} 个文件的最新内容复制到剪贴板。")
        self.update_button_states()

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

52pojie.PNG (164.68 KB, 下载次数: 0)

52pojie.PNG

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
忘情的城市 发表于 2026-6-7 18:55
啥意思,没搞懂,超长文本不也被切割或不支持吗,能详细说一下吗
沙发
jty123000321 发表于 2026-6-7 17:36
4#
pinghost 发表于 2026-6-7 19:44
看起来很厉害的样子,不明觉厉,这是干啥用的啊
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-8 01:16

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表