123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- import tkinter as tk
- from tkinter import ttk, filedialog, messagebox
- from PIL import Image
- import os
- import tempfile
- import sys
- from LVGLImage import LVGLImage, ColorFormat, CompressMethod
- HELP_TEXT = """LVGL图片转换工具使用说明:
- 1. 添加文件:点击“添加文件”按钮选择需要转换的图片,支持批量导入
- 2. 移除文件:在列表中选中文件前的复选框“[ ]”(选中后会变成“[√]”),点击“移除选中”可删除选定文件
- 3. 设置分辨率:选择需要的分辨率,如128x128
- 建议根据自己的设备的屏幕分辨率来选择。过大和过小都会影响显示效果。
- 4. 颜色格式:选择“自动识别”会根据图片是否透明自动选择,或手动指定
- 除非你了解这个选项,否则建议使用自动识别,不然可能会出现一些意想不到的问题……
- 5. 压缩方式:选择NONE或RLE压缩
- 除非你了解这个选项,否则建议保持默认NONE不压缩
- 6. 输出目录:设置转换后文件的保存路径
- 默认为程序所在目录下的output文件夹
- 7. 转换:点击“转换全部”或“转换选中”开始转换
- """
- class ImageConverterApp:
- def __init__(self, root):
- self.root = root
- self.root.title("LVGL图片转换工具")
- self.root.geometry("750x650")
-
- # 初始化变量
- self.output_dir = tk.StringVar(value=os.path.abspath("output"))
- self.resolution = tk.StringVar(value="128x128")
- self.color_format = tk.StringVar(value="自动识别")
- self.compress_method = tk.StringVar(value="NONE")
- # 创建UI组件
- self.create_widgets()
- self.redirect_output()
- def create_widgets(self):
- # 参数设置框架
- settings_frame = ttk.LabelFrame(self.root, text="转换设置")
- settings_frame.grid(row=0, column=0, padx=10, pady=5, sticky="ew")
- # 分辨率设置
- ttk.Label(settings_frame, text="分辨率:").grid(row=0, column=0, padx=2)
- ttk.Combobox(settings_frame, textvariable=self.resolution,
- values=["128x128", "64x64", "32x32"], width=8).grid(row=0, column=1, padx=2)
- # 颜色格式
- ttk.Label(settings_frame, text="颜色格式:").grid(row=0, column=2, padx=2)
- ttk.Combobox(settings_frame, textvariable=self.color_format,
- values=["自动识别", "RGB565", "RGB565A8"], width=10).grid(row=0, column=3, padx=2)
- # 压缩方式
- ttk.Label(settings_frame, text="压缩方式:").grid(row=0, column=4, padx=2)
- ttk.Combobox(settings_frame, textvariable=self.compress_method,
- values=["NONE", "RLE"], width=8).grid(row=0, column=5, padx=2)
- # 文件操作框架
- file_frame = ttk.LabelFrame(self.root, text="输入文件")
- file_frame.grid(row=1, column=0, padx=10, pady=5, sticky="nsew")
- # 文件操作按钮
- btn_frame = ttk.Frame(file_frame)
- btn_frame.pack(fill=tk.X, pady=2)
- ttk.Button(btn_frame, text="添加文件", command=self.select_files).pack(side=tk.LEFT, padx=2)
- ttk.Button(btn_frame, text="移除选中", command=self.remove_selected).pack(side=tk.LEFT, padx=2)
- ttk.Button(btn_frame, text="清空列表", command=self.clear_files).pack(side=tk.LEFT, padx=2)
- # 文件列表(Treeview)
- self.tree = ttk.Treeview(file_frame, columns=("selected", "filename"),
- show="headings", height=10)
- self.tree.heading("selected", text="选中", anchor=tk.W)
- self.tree.heading("filename", text="文件名", anchor=tk.W)
- self.tree.column("selected", width=60, anchor=tk.W)
- self.tree.column("filename", width=600, anchor=tk.W)
- self.tree.pack(fill=tk.BOTH, expand=True)
- self.tree.bind("<ButtonRelease-1>", self.on_tree_click)
- # 输出目录
- output_frame = ttk.LabelFrame(self.root, text="输出目录")
- output_frame.grid(row=2, column=0, padx=10, pady=5, sticky="ew")
- ttk.Entry(output_frame, textvariable=self.output_dir, width=60).pack(side=tk.LEFT, padx=5)
- ttk.Button(output_frame, text="浏览", command=self.select_output_dir).pack(side=tk.RIGHT, padx=5)
- # 转换按钮和帮助按钮
- convert_frame = ttk.Frame(self.root)
- convert_frame.grid(row=3, column=0, padx=10, pady=10)
- ttk.Button(convert_frame, text="转换全部文件", command=lambda: self.start_conversion(True)).pack(side=tk.LEFT, padx=5)
- ttk.Button(convert_frame, text="转换选中文件", command=lambda: self.start_conversion(False)).pack(side=tk.LEFT, padx=5)
- ttk.Button(convert_frame, text="帮助", command=self.show_help).pack(side=tk.RIGHT, padx=5)
- # 日志区域(新增清空按钮部分)
- log_frame = ttk.LabelFrame(self.root, text="日志")
- log_frame.grid(row=4, column=0, padx=10, pady=5, sticky="nsew")
-
- # 添加按钮框架
- log_btn_frame = ttk.Frame(log_frame)
- log_btn_frame.pack(fill=tk.X, side=tk.BOTTOM)
-
- # 清空日志按钮
- ttk.Button(log_btn_frame, text="清空日志", command=self.clear_log).pack(side=tk.RIGHT, padx=5, pady=2)
-
- self.log_text = tk.Text(log_frame, height=15)
- self.log_text.pack(fill=tk.BOTH, expand=True)
- # 布局配置
- self.root.columnconfigure(0, weight=1)
- self.root.rowconfigure(1, weight=1)
- self.root.rowconfigure(4, weight=1)
- def clear_log(self):
- """清空日志内容"""
- self.log_text.delete(1.0, tk.END)
- def show_help(self):
- messagebox.showinfo("帮助", HELP_TEXT)
- def redirect_output(self):
- class StdoutRedirector:
- def __init__(self, text_widget):
- self.text_widget = text_widget
- self.original_stdout = sys.stdout
- def write(self, message):
- self.text_widget.insert(tk.END, message)
- self.text_widget.see(tk.END)
- self.original_stdout.write(message)
- def flush(self):
- self.original_stdout.flush()
- sys.stdout = StdoutRedirector(self.log_text)
- def on_tree_click(self, event):
- region = self.tree.identify("region", event.x, event.y)
- if region == "cell":
- col = self.tree.identify_column(event.x)
- item = self.tree.identify_row(event.y)
- if col == "#1": # 点击的是选中列
- current_val = self.tree.item(item, "values")[0]
- new_val = "[√]" if current_val == "[ ]" else "[ ]"
- self.tree.item(item, values=(new_val, self.tree.item(item, "values")[1]))
- def select_output_dir(self):
- path = filedialog.askdirectory()
- if path:
- self.output_dir.set(path)
- def select_files(self):
- files = filedialog.askopenfilenames(filetypes=[("图片文件", "*.png;*.jpg;*.jpeg;*.bmp;*.gif")])
- for f in files:
- self.tree.insert("", tk.END, values=("[ ]", os.path.basename(f)), tags=(f,))
- def remove_selected(self):
- to_remove = []
- for item in self.tree.get_children():
- if self.tree.item(item, "values")[0] == "[√]":
- to_remove.append(item)
- for item in reversed(to_remove):
- self.tree.delete(item)
- def clear_files(self):
- for item in self.tree.get_children():
- self.tree.delete(item)
- def start_conversion(self, convert_all):
- input_files = [
- self.tree.item(item, "tags")[0]
- for item in self.tree.get_children()
- if convert_all or self.tree.item(item, "values")[0] == "[√]"
- ]
-
- if not input_files:
- msg = "没有找到可转换的文件" if convert_all else "没有选中任何文件"
- messagebox.showwarning("警告", msg)
- return
-
- os.makedirs(self.output_dir.get(), exist_ok=True)
-
- # 解析转换参数
- width, height = map(int, self.resolution.get().split('x'))
- compress = CompressMethod.RLE if self.compress_method.get() == "RLE" else CompressMethod.NONE
- # 执行转换
- self.convert_images(input_files, width, height, compress)
- def convert_images(self, input_files, width, height, compress):
- success_count = 0
- total_files = len(input_files)
-
- for idx, file_path in enumerate(input_files):
- try:
- print(f"正在处理: {os.path.basename(file_path)}")
-
- with Image.open(file_path) as img:
- # 调整图片大小
- img = img.resize((width, height), Image.Resampling.LANCZOS)
-
- # 处理颜色格式
- color_format_str = self.color_format.get()
- if color_format_str == "自动识别":
- # 检测透明通道
- has_alpha = img.mode in ('RGBA', 'LA') or (img.mode == 'P' and 'transparency' in img.info)
- if has_alpha:
- img = img.convert('RGBA')
- cf = ColorFormat.RGB565A8
- else:
- img = img.convert('RGB')
- cf = ColorFormat.RGB565
- else:
- if color_format_str == "RGB565A8":
- img = img.convert('RGBA')
- cf = ColorFormat.RGB565A8
- else:
- img = img.convert('RGB')
- cf = ColorFormat.RGB565
- # 保存调整后的图片
- base_name = os.path.splitext(os.path.basename(file_path))[0]
- output_image_path = os.path.join(self.output_dir.get(), f"{base_name}_{width}x{height}.png")
- img.save(output_image_path, 'PNG')
- # 创建临时文件
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmpfile:
- temp_path = tmpfile.name
- img.save(temp_path, 'PNG')
- # 转换为LVGL C数组
- lvgl_img = LVGLImage().from_png(temp_path, cf=cf)
- output_c_path = os.path.join(self.output_dir.get(), f"{base_name}.c")
- lvgl_img.to_c_array(output_c_path, compress=compress)
- success_count += 1
- os.unlink(temp_path)
- print(f"成功转换: {base_name}.c\n")
- except Exception as e:
- print(f"转换失败: {str(e)}\n")
- print(f"转换完成! 成功 {success_count}/{total_files} 个文件\n")
- if __name__ == "__main__":
- root = tk.Tk()
- app = ImageConverterApp(root)
- root.mainloop()
|