Page 1 of 1

Factorio Locale File Comparison Tool

Posted: Sat Nov 02, 2024 9:19 am
by plexpt

Code: Select all

import tkinter as tk
from tkinter import ttk
import re
from tkinterdnd2 import DND_FILES, TkinterDnD
import os


class FactorioConfigComparer(TkinterDnD.Tk):
    def __init__(self):
        super().__init__()

        self.title("Factorio Config Comparer")
        self.geometry("1000x600")

        # 创建主框架
        self.main_frame = ttk.Frame(self)
        self.main_frame.pack(expand=True, fill='both', padx=10, pady=10)

        # 创建左右两栏
        self.create_panels()

        # 创建对比结果区域
        self.create_result_area()

        # 存储当前加载的配置
        self.left_config = None
        self.right_config = None

    def create_panels(self):
        # 创建左右面板容器
        panels_frame = ttk.Frame(self.main_frame)
        panels_frame.pack(expand=True, fill='both')

        # 左面板
        left_frame = ttk.LabelFrame(panels_frame, text="Left Config File")
        left_frame.pack(side='left', expand=True, fill='both', padx=5)

        self.left_text = tk.Text(left_frame, height=10, wrap=tk.WORD)
        self.left_text.pack(expand=True, fill='both', padx=5, pady=5)
        self.left_text.insert('1.0', "Drop .cfg file here")

        # 配置拖放
        self.left_text.drop_target_register(DND_FILES)
        self.left_text.dnd_bind('<<Drop>>', lambda e: self.on_drop(e, 'left'))

        # 右面板
        right_frame = ttk.LabelFrame(panels_frame, text="Right Config File")
        right_frame.pack(side='left', expand=True, fill='both', padx=5)

        self.right_text = tk.Text(right_frame, height=10, wrap=tk.WORD)
        self.right_text.pack(expand=True, fill='both', padx=5, pady=5)
        self.right_text.insert('1.0', "Drop .cfg file here")

        # 配置拖放
        self.right_text.drop_target_register(DND_FILES)
        self.right_text.dnd_bind('<<Drop>>', lambda e: self.on_drop(e, 'right'))

    def create_result_area(self):
        # 创建结果显示区域
        result_frame = ttk.LabelFrame(self.main_frame, text="Comparison Results")
        result_frame.pack(expand=True, fill='both', pady=10)

        # 创建两个子区域用于显示缺失的键
        results_container = ttk.Frame(result_frame)
        results_container.pack(expand=True, fill='both')

        # 左侧缺失的键
        left_missing_frame = ttk.LabelFrame(results_container, text="Keys missing in Left file")
        left_missing_frame.pack(side='left', expand=True, fill='both', padx=5, pady=5)

        self.left_missing_text = tk.Text(left_missing_frame, height=10, wrap=tk.WORD)
        self.left_missing_text.pack(expand=True, fill='both', padx=5, pady=5)

        # 右侧缺失的键
        right_missing_frame = ttk.LabelFrame(results_container, text="Keys missing in Right file")
        right_missing_frame.pack(side='left', expand=True, fill='both', padx=5, pady=5)

        self.right_missing_text = tk.Text(right_missing_frame, height=10, wrap=tk.WORD)
        self.right_missing_text.pack(expand=True, fill='both', padx=5, pady=5)

    def parse_factorio_cfg(self, file_path):
        """解析 Factorio 配置文件,返回键值对字典"""
        config_dict = {}
        current_key = None

        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                line = line.strip()

                # 跳过空行和注释
                if not line or line.startswith('#') or line.startswith('_'):
                    continue

                # 检查是否是键值对
                if '=' in line:
                    key, value = line.split('=', 1)
                    key = key.strip()
                    value = value.strip()

                    # 去掉值的引号(如果存在)
                    value = value.strip('"').strip("'")

                    config_dict[key] = value

        return config_dict

    def on_drop(self, event, side):
        # 获取拖放的文件路径
        file_path = event.data
        # 移除可能的大括号和引号
        file_path = file_path.strip('{}').strip('"')

        # 检查文件是否存在且是.cfg文件
        if not os.path.exists(file_path) or not file_path.lower().endswith('.cfg'):
            self.show_error("Please drop a valid .cfg file")
            return

        # 读取配置文件
        try:
            config_dict = self.parse_factorio_cfg(file_path)

            # 更新相应的文本框和存储的配置
            if side == 'left':
                self.left_text.delete('1.0', tk.END)
                self.left_text.insert('1.0', f"Loaded: {os.path.basename(file_path)}\n")
                self.left_text.insert(tk.END, f"Found {len(config_dict)} keys")
                self.left_config = config_dict
            else:
                self.right_text.delete('1.0', tk.END)
                self.right_text.insert('1.0', f"Loaded: {os.path.basename(file_path)}\n")
                self.right_text.insert(tk.END, f"Found {len(config_dict)} keys")
                self.right_config = config_dict

            # 如果两个配置文件都已加载,则进行比较
            if self.left_config and self.right_config:
                self.compare_configs()

        except Exception as e:
            self.show_error(f"Error reading config file: {str(e)}")

    def compare_configs(self):
        # 清空结果区域
        self.left_missing_text.delete('1.0', tk.END)
        self.right_missing_text.delete('1.0', tk.END)

        # 获取所有键
        left_keys = set(self.left_config.keys())
        right_keys = set(self.right_config.keys())

        # 找出缺失的键
        missing_in_left = right_keys - left_keys
        missing_in_right = left_keys - right_keys

        # 显示结果
        if missing_in_left:
            for key in sorted(missing_in_left):
                self.left_missing_text.insert(tk.END, f"{key}={self.right_config[key]}\n")
        else:
            self.left_missing_text.insert('1.0', "No missing keys")

        if missing_in_right:
            for key in sorted(missing_in_right):
                self.right_missing_text.insert(tk.END, f"{key}={self.left_config[key]}\n")
        else:
            self.right_missing_text.insert('1.0', "No missing keys")

    def show_error(self, message):
        """显示错误消息"""
        import tkinter.messagebox as messagebox
        messagebox.showerror("Error", message)


if __name__ == "__main__":
    app = FactorioConfigComparer()
    app.mainloop()