python语言像素画编辑器代码ZXQZQ-2026-6-3
·
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, colorchooser
import random
import json
import os
from PIL import Image, ImageDraw
import time
class XiaobaWangPainter:
def __init__(self, root):
self.root = root
self.root.title("小霸王风格像素画编辑器 - 增强版")
self.root.geometry("1350x950")
self.root.configure(bg='#2c3e50')
# ========== 核心参数 ==========
self.grid_width = 32
self.grid_height = 32
self.pixel_size = 20
self.current_color = '1'
self.current_tool = 'pen'
self.current_frame = 0
self.frames = [] # 多帧动画
self.history = []
self.history_index = -1
self.is_playing = False
self.anim_index = 0
self.last_save_time = 0
self.code_text_update_timer = None
self.loop_animation = tk.BooleanVar(value=True)
self.frame_delay = 200
self.current_animation_id = None
self._updating_code = False # 防止循环更新
# 调色板
self.palette = {
'0': '#000000', '1': '#FF0000', '2': '#00FF00', '3': '#FFFF00',
'4': '#0000FF', '5': '#FF00FF', '6': '#00FFFF', '7': '#FFFFFF',
'8': '#FF8800', '9': '#8800FF', 'A': '#888888', 'B': '#FF8888',
'C': '#88FF88', 'D': '#8888FF', 'E': '#FFFF88', 'F': '#FF88FF',
}
self.tools = {
'pen': '✏️ 画笔', 'fill': '🪣 填充', 'line': '📏 直线',
'circle': '⚪ 圆形', 'rect': '⬛ 矩形', 'eraser': '🧽 橡皮',
'picker': '🎨 取色'
}
# 初始化数据
self.grid_data = [['7' for _ in range(self.grid_width)] for _ in range(self.grid_height)]
self.save_to_history()
self.setup_ui()
self.draw_grid()
self.load_demo_pattern()
print("=" * 50)
print("小霸王像素画编辑器已启动")
print("使用说明:")
print("1. 点击「➕ 添加帧」添加当前画布为第一帧")
print("2. 在文本框中编辑颜色代码,会自动更新当前帧和画布")
print("3. 点击「下一帧」切换到其他帧编辑")
print("4. 点击「播放」观看动画")
print("=" * 50)
def setup_ui(self):
toolbar = tk.Frame(self.root, bg='#34495e', height=40)
toolbar.pack(fill=tk.X, padx=5, pady=5)
file_frame = tk.Frame(toolbar, bg='#34495e')
file_frame.pack(side=tk.LEFT, padx=10)
tk.Button(file_frame, text="📁 新建", command=self.new_file,
bg='#3498db', fg='white', padx=10).pack(side=tk.LEFT, padx=2)
tk.Button(file_frame, text="💾 保存", command=self.save_file,
bg='#2ecc71', fg='white', padx=10).pack(side=tk.LEFT, padx=2)
tk.Button(file_frame, text="📂 打开", command=self.open_file,
bg='#3498db', fg='white', padx=10).pack(side=tk.LEFT, padx=2)
tk.Button(file_frame, text="📸 导出图片", command=self.export_image,
bg='#e67e22', fg='white', padx=10).pack(side=tk.LEFT, padx=2)
tk.Button(file_frame, text="🎬 导出GIF", command=self.export_gif,
bg='#e74c3c', fg='white', padx=10).pack(side=tk.LEFT, padx=2)
edit_frame = tk.Frame(toolbar, bg='#34495e')
edit_frame.pack(side=tk.LEFT, padx=20)
tk.Button(edit_frame, text="↩️ 撤销", command=self.undo,
bg='#f39c12', fg='white', padx=8).pack(side=tk.LEFT, padx=2)
tk.Button(edit_frame, text="🔄 重做", command=self.redo,
bg='#f39c12', fg='white', padx=8).pack(side=tk.LEFT, padx=2)
tk.Button(edit_frame, text="🗑️ 清空", command=self.clear_grid,
bg='#e74c3c', fg='white', padx=8).pack(side=tk.LEFT, padx=2)
tool_frame = tk.LabelFrame(self.root, text="绘图工具", bg='#ecf0f1', fg='#2c3e50')
tool_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5)
self.tool_buttons = {}
for tool_id, tool_name in self.tools.items():
btn = tk.Button(tool_frame, text=tool_name, width=10,
command=lambda t=tool_id: self.set_tool(t),
relief=tk.RAISED, bg='#bdc3c7')
btn.pack(padx=10, pady=5)
self.tool_buttons[tool_id] = btn
self.tool_buttons['pen'].config(relief=tk.SUNKEN, bg='#3498db', fg='white')
self.palette_frame = tk.LabelFrame(self.root, text="调色板", bg='#ecf0f1', fg='#2c3e50')
self.palette_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5)
colors = list(self.palette.items())
for i, (code, color) in enumerate(colors):
row = i // 4
col = i % 4
btn = tk.Button(self.palette_frame, bg=color, width=8, height=2,
command=lambda c=code: self.set_color(c))
btn.grid(row=row, column=col, padx=2, pady=2)
if code == '1':
btn.config(relief=tk.SUNKEN, bd=3)
self.current_color_btn = btn
right_frame = tk.Frame(self.root, bg='#ecf0f1')
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5)
canvas_frame = tk.LabelFrame(right_frame, text="绘图画布", bg='#ecf0f1')
canvas_frame.pack(fill=tk.BOTH, expand=True, pady=5)
self.canvas_container = tk.Canvas(canvas_frame, bg='#95a5a6')
self.canvas_container.pack(fill=tk.BOTH, expand=True)
self.canvas = tk.Canvas(self.canvas_container, bg='white',
width=self.grid_width * self.pixel_size,
height=self.grid_height * self.pixel_size)
self.canvas.place(x=0, y=0)
self.canvas.bind('<Button-1>', self.on_mouse_click)
self.canvas.bind('<B1-Motion>', self.on_mouse_drag)
self.canvas.bind('<Button-3>', self.on_right_click)
h_scroll = tk.Scrollbar(canvas_frame, orient=tk.HORIZONTAL, command=self.canvas_container.xview)
v_scroll = tk.Scrollbar(canvas_frame, orient=tk.VERTICAL, command=self.canvas_container.yview)
self.canvas_container.configure(xscrollcommand=h_scroll.set, yscrollcommand=v_scroll.set)
h_scroll.pack(side=tk.BOTTOM, fill=tk.X)
v_scroll.pack(side=tk.RIGHT, fill=tk.Y)
anim_frame = tk.LabelFrame(right_frame, text="动画控制", bg='#ecf0f1')
anim_frame.pack(fill=tk.X, pady=5)
anim_controls = tk.Frame(anim_frame, bg='#ecf0f1')
anim_controls.pack(pady=5)
button_row1 = tk.Frame(anim_controls, bg='#ecf0f1')
button_row1.pack()
tk.Button(button_row1, text="◀ 上一帧", command=self.prev_frame,
bg='#3498db', fg='white', width=8).pack(side=tk.LEFT, padx=2)
tk.Button(button_row1, text="▶ 播放", command=self.play_animation,
bg='#2ecc71', fg='white', width=8).pack(side=tk.LEFT, padx=2)
tk.Button(button_row1, text="⏸ 暂停", command=self.pause_animation,
bg='#f39c12', fg='white', width=8).pack(side=tk.LEFT, padx=2)
tk.Button(button_row1, text="⏹ 停止", command=self.stop_animation,
bg='#e74c3c', fg='white', width=8).pack(side=tk.LEFT, padx=2)
tk.Button(button_row1, text="▶ 下一帧", command=self.next_frame,
bg='#3498db', fg='white', width=8).pack(side=tk.LEFT, padx=2)
tk.Button(button_row1, text="➕ 添加帧", command=self.add_frame,
bg='#9b59b6', fg='white', width=8).pack(side=tk.LEFT, padx=2)
tk.Button(button_row1, text="➖ 删除帧", command=self.delete_frame,
bg='#e74c3c', fg='white', width=8).pack(side=tk.LEFT, padx=2)
button_row2 = tk.Frame(anim_controls, bg='#ecf0f1')
button_row2.pack(pady=5)
self.loop_checkbox = tk.Checkbutton(button_row2, text="🔁 循环播放",
variable=self.loop_animation,
bg='#ecf0f1', font=('Arial', 10))
self.loop_checkbox.pack(side=tk.LEFT, padx=10)
tk.Label(button_row2, text="播放速度:", bg='#ecf0f1', font=('Arial', 10)).pack(side=tk.LEFT, padx=5)
self.speed_var = tk.IntVar(value=200)
self.speed_scale = tk.Scale(button_row2, from_=0, to=5000, orient=tk.HORIZONTAL,
variable=self.speed_var, length=200,
command=self.on_speed_change,
bg='#ecf0f1', highlightthickness=0)
self.speed_scale.pack(side=tk.LEFT, padx=5)
tk.Label(button_row2, text="毫秒/帧", bg='#ecf0f1', font=('Arial', 10)).pack(side=tk.LEFT)
self.speed_label = tk.Label(button_row2, text="(200ms)", bg='#ecf0f1', font=('Arial', 9))
self.speed_label.pack(side=tk.LEFT, padx=5)
# 在动画控制面板添加帧跳转功能(button_row2 后面添加)
frame_jump_frame = tk.Frame(anim_controls, bg='#ecf0f1')
frame_jump_frame.pack(pady=2)
tk.Label(frame_jump_frame, text="跳转到帧:", bg='#ecf0f1').pack(side=tk.LEFT, padx=5)
self.jump_entry = tk.Entry(frame_jump_frame, width=6)
self.jump_entry.pack(side=tk.LEFT, padx=5)
tk.Button(frame_jump_frame, text="GO", command=self.jump_to_frame,
bg='#3498db', fg='white', width=4).pack(side=tk.LEFT)
self.frame_label = tk.Label(anim_controls, text="当前帧: 0/0", bg='#ecf0f1', font=('Arial', 12, 'bold'))
self.frame_label.pack(pady=2)
effect_frame = tk.LabelFrame(right_frame, text="特效工具", bg='#ecf0f1')
effect_frame.pack(fill=tk.X, pady=5)
effects = [
('🔍 放大', self.zoom_in), ('🔍 缩小', self.zoom_out),
('🔄 水平翻转', self.flip_horizontal), ('🔄 垂直翻转', self.flip_vertical),
('🎲 随机填充', self.random_fill), ('🌈 渐变填充', self.gradient_fill),
]
for text, cmd in effects:
tk.Button(effect_frame, text=text, command=cmd,
bg='#95a5a6', fg='white', width=12).pack(side=tk.LEFT, padx=2, pady=2)
code_frame = tk.LabelFrame(right_frame, text="数字序列编辑器 (编辑后自动更新当前帧)", bg='#ecf0f1')
code_frame.pack(fill=tk.BOTH, expand=True, pady=5)
code_text_frame = tk.Frame(code_frame)
code_text_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.code_text = tk.Text(code_text_frame, font=('Courier', 10), wrap=tk.NONE, height=8)
self.code_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
code_scrollbar = tk.Scrollbar(code_text_frame, orient=tk.VERTICAL, command=self.code_text.yview)
code_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.code_text.config(yscrollcommand=code_scrollbar.set)
code_h_scroll = tk.Scrollbar(code_frame, orient=tk.HORIZONTAL, command=self.code_text.xview)
code_h_scroll.pack(side=tk.BOTTOM, fill=tk.X)
self.code_text.config(xscrollcommand=code_h_scroll.set)
# 绑定文本框变化事件
self.code_text.bind("<<Modified>>", self.on_code_text_edit)
self.code_text.bind("<KeyRelease>", self.on_code_text_edit)
code_btn_frame = tk.Frame(code_frame, bg='#ecf0f1')
code_btn_frame.pack(fill=tk.X, pady=5)
tk.Button(code_btn_frame, text="🔄 刷新显示", command=self.update_code_from_grid,
bg='#3498db', fg='white').pack(side=tk.LEFT, padx=2)
tk.Button(code_btn_frame, text="💾 保存到当前帧", command=self.save_current_frame_from_code,
bg='#2ecc71', fg='white').pack(side=tk.LEFT, padx=2)
self.status_bar = tk.Label(self.root, text="就绪 | 工具: 画笔 | 颜色: 红色",
bg='#34495e', fg='white', anchor=tk.W)
self.status_bar.pack(fill=tk.X, side=tk.BOTTOM)
def jump_to_frame(self):
try:
frame_num = int(self.jump_entry.get()) - 1
if 0 <= frame_num < len(self.frames):
self.current_frame = frame_num
self.grid_data = [row[:] for row in self.frames[self.current_frame]]
self.draw_grid()
self.update_code_from_grid()
self.update_frame_indicator()
self.status_bar.config(text=f"跳转到第 {frame_num + 1} 帧")
except:
pass
def on_code_text_edit(self, event=None):
"""文本框编辑时自动更新当前帧和画布"""
if event == '<<Modified>>':
self.code_text.edit_modified(False)
# 防抖处理
if self.code_text_update_timer:
self.root.after_cancel(self.code_text_update_timer)
self.code_text_update_timer = self.root.after(300, self.update_current_frame_from_code)
def update_current_frame_from_code(self):
"""从文本框内容更新当前帧和画布"""
if self._updating_code:
return
self._updating_code = True
try:
text = self.code_text.get("1.0", tk.END).strip()
# 解析文本框内容
lines = []
current_line = ""
for char in text:
if char == '\n':
if current_line:
lines.append(current_line)
current_line = ""
elif char == ' ':
continue
elif char == '/':
if current_line:
lines.append(current_line)
current_line = ""
elif char.upper() in '0123456789ABCDEF':
current_line += char.upper()
if current_line:
lines.append(current_line)
# 创建新网格
new_grid = [['7' for _ in range(self.grid_width)] for _ in range(self.grid_height)]
for y, line in enumerate(lines):
if y >= self.grid_height:
break
for x, char in enumerate(line):
if x >= self.grid_width:
break
if char in self.palette:
new_grid[y][x] = char
# 更新当前网格
self.grid_data = new_grid
self.draw_grid()
# 如果有帧列表,更新当前帧
if self.frames and self.current_frame < len(self.frames):
self.frames[self.current_frame] = [row[:] for row in self.grid_data]
self.update_frame_indicator()
self.status_bar.config(text=f"✅ 已更新当前帧 (第{self.current_frame + 1}帧)")
else:
self.status_bar.config(text="✅ 画布已更新 (尚未添加帧)")
self.save_to_history()
except Exception as e:
print(f"更新出错: {e}")
finally:
self._updating_code = False
def save_current_frame_from_code(self):
"""手动保存当前文本框内容到当前帧"""
self.update_current_frame_from_code()
messagebox.showinfo("成功", f"已保存到第 {self.current_frame + 1} 帧")
def update_code_from_grid(self):
"""从当前网格更新文本框显示"""
if self._updating_code:
return
self._updating_code = True
try:
code = []
for y in range(self.grid_height):
row = ''.join(str(self.grid_data[y][x]) for x in range(self.grid_width))
code.append(row)
self.code_text.delete("1.0", tk.END)
self.code_text.insert(tk.END, '\n'.join(code))
finally:
self._updating_code = False
def add_frame(self):
"""添加动画帧"""
new_frame = [row[:] for row in self.grid_data]
self.frames.append(new_frame)
self.current_frame = len(self.frames) - 1
self.update_frame_indicator()
self.status_bar.config(text=f"✅ 已添加第 {len(self.frames)} 帧")
print(f"添加帧: 当前共有 {len(self.frames)} 帧")
def next_frame(self):
"""下一帧"""
if not self.frames:
self.status_bar.config(text="没有动画帧,请先添加帧")
return
if self.current_frame < len(self.frames) - 1:
self.current_frame += 1
# 加载帧数据到当前网格
self.grid_data = [row[:] for row in self.frames[self.current_frame]]
self.draw_grid()
self.update_code_from_grid() # 更新文本框显示
self.status_bar.config(text=f"切换到第 {self.current_frame + 1} 帧")
self.update_frame_indicator()
def prev_frame(self):
"""上一帧"""
if not self.frames:
self.status_bar.config(text="没有动画帧,请先添加帧")
return
if self.current_frame > 0:
self.current_frame -= 1
# 加载帧数据到当前网格
self.grid_data = [row[:] for row in self.frames[self.current_frame]]
self.draw_grid()
self.update_code_from_grid() # 更新文本框显示
self.status_bar.config(text=f"切换到第 {self.current_frame + 1} 帧")
self.update_frame_indicator()
def delete_frame(self):
"""删除当前帧"""
if not self.frames:
return
if len(self.frames) == 1:
self.frames = []
self.current_frame = 0
self.grid_data = [['7' for _ in range(self.grid_width)] for _ in range(self.grid_height)]
self.draw_grid()
self.update_code_from_grid()
self.status_bar.config(text="已删除最后一帧")
self.update_frame_indicator()
return
self.frames.pop(self.current_frame)
if self.current_frame >= len(self.frames):
self.current_frame = len(self.frames) - 1
self.grid_data = [row[:] for row in self.frames[self.current_frame]]
self.draw_grid()
self.update_code_from_grid()
self.status_bar.config(text=f"已删除帧,现在显示第 {self.current_frame + 1} 帧")
self.update_frame_indicator()
def play_animation(self):
"""播放动画"""
if self.is_playing:
return
if not self.frames:
self.status_bar.config(text="❌ 没有动画帧,请先添加帧")
messagebox.showwarning("警告", "没有动画帧!请先点击「➕ 添加帧」")
return
if self.current_animation_id:
self.root.after_cancel(self.current_animation_id)
self.is_playing = True
self.anim_index = 0
speed_text = "最快" if self.frame_delay == 0 else f"{self.frame_delay}ms"
self.status_bar.config(text=f"▶ 播放中 ({len(self.frames)}帧, {speed_text}/帧)")
self.animate()
def animate(self):
"""动画循环"""
if not self.is_playing:
return
if not self.frames:
self.is_playing = False
return
if self.anim_index < len(self.frames):
# 显示当前帧
frame_data = self.frames[self.anim_index]
for y in range(self.grid_height):
for x in range(self.grid_width):
self.grid_data[y][x] = frame_data[y][x]
self.draw_grid()
self.current_frame = self.anim_index
self.update_frame_indicator()
self.anim_index += 1
delay = max(1, self.frame_delay)
self.current_animation_id = self.root.after(delay, self.animate)
else:
if self.loop_animation.get():
self.anim_index = 0
delay = max(1, self.frame_delay)
self.current_animation_id = self.root.after(delay, self.animate)
else:
self.is_playing = False
self.current_animation_id = None
self.status_bar.config(text="⏹ 播放完毕")
def pause_animation(self):
if self.is_playing:
self.is_playing = False
if self.current_animation_id:
self.root.after_cancel(self.current_animation_id)
self.status_bar.config(text="⏸ 已暂停")
def stop_animation(self):
self.is_playing = False
if self.current_animation_id:
self.root.after_cancel(self.current_animation_id)
self.current_animation_id = None
if self.frames and self.current_frame < len(self.frames):
self.grid_data = [row[:] for row in self.frames[self.current_frame]]
self.draw_grid()
self.update_code_from_grid()
self.status_bar.config(text="⏹ 已停止")
def update_frame_indicator(self):
if self.frames and len(self.frames) > 0:
self.frame_label.config(text=f"当前帧: {self.current_frame + 1}/{len(self.frames)}", fg='black')
else:
self.frame_label.config(text="当前帧: 0/0 (点击➕添加帧)", fg='red')
def on_speed_change(self, value):
self.frame_delay = int(value)
if self.frame_delay == 0:
self.speed_label.config(text="(最快)")
else:
self.speed_label.config(text=f"({self.frame_delay}ms)")
# ========== 基础绘图方法 ==========
def set_tool(self, tool_id):
self.current_tool = tool_id
for tid, btn in self.tool_buttons.items():
if tid == tool_id:
btn.config(relief=tk.SUNKEN, bg='#3498db', fg='white')
else:
btn.config(relief=tk.RAISED, bg='#bdc3c7', fg='black')
self.status_bar.config(text=f"工具: {self.tools[tool_id]} | 颜色: {self.get_color_name()}")
def set_color(self, color_code):
self.current_color = color_code
if hasattr(self, 'current_color_btn'):
self.current_color_btn.config(relief=tk.RAISED)
for widget in self.palette_frame.winfo_children():
if isinstance(widget, tk.Button) and widget.cget('bg') == self.palette.get(color_code, '#000000'):
widget.config(relief=tk.SUNKEN, bd=3)
self.current_color_btn = widget
break
self.status_bar.config(text=f"工具: {self.tools[self.current_tool]} | 颜色: {self.get_color_name()}")
def get_color_name(self):
color_names = {'0': '黑', '1': '红', '2': '绿', '3': '黄', '4': '蓝',
'5': '紫', '6': '青', '7': '白', '8': '橙', '9': '靛',
'A': '灰', 'B': '粉', 'C': '浅绿', 'D': '浅蓝', 'E': '浅黄', 'F': '浅紫'}
return color_names.get(self.current_color, '未知')
def draw_pixel(self, x, y, color_code):
color = self.palette.get(color_code, '#FFFFFF')
x1 = x * self.pixel_size
y1 = y * self.pixel_size
x2 = x1 + self.pixel_size
y2 = y1 + self.pixel_size
self.canvas.create_rectangle(x1, y1, x2, y2, fill=color, outline='#DDD', width=0.5)
def draw_grid(self):
self.canvas.delete('all')
for y in range(self.grid_height):
for x in range(self.grid_width):
self.draw_pixel(x, y, self.grid_data[y][x])
def on_mouse_click(self, event):
x = event.x // self.pixel_size
y = event.y // self.pixel_size
if 0 <= x < self.grid_width and 0 <= y < self.grid_height:
if self.current_tool == 'pen':
self.set_pixel(x, y, self.current_color)
elif self.current_tool == 'eraser':
self.set_pixel(x, y, '7')
elif self.current_tool == 'picker':
self.current_color = self.grid_data[y][x]
self.set_color(self.current_color)
elif self.current_tool == 'fill':
self.flood_fill(x, y, self.current_color)
def on_mouse_drag(self, event):
x = event.x // self.pixel_size
y = event.y // self.pixel_size
if 0 <= x < self.grid_width and 0 <= y < self.grid_height:
if self.current_tool == 'pen':
self.set_pixel(x, y, self.current_color)
elif self.current_tool == 'eraser':
self.set_pixel(x, y, '7')
def on_right_click(self, event):
x = event.x // self.pixel_size
y = event.y // self.pixel_size
if 0 <= x < self.grid_width and 0 <= y < self.grid_height:
self.current_color = self.grid_data[y][x]
self.set_color(self.current_color)
def set_pixel(self, x, y, color):
if self.grid_data[y][x] != color:
self.grid_data[y][x] = color
self.draw_pixel(x, y, color)
self.save_to_history()
# 同时更新当前帧
if self.frames and self.current_frame < len(self.frames):
self.frames[self.current_frame] = [row[:] for row in self.grid_data]
self.update_code_from_grid()
def flood_fill(self, x, y, new_color):
old_color = self.grid_data[y][x]
if old_color == new_color:
return
stack = [(x, y)]
visited = set()
while stack:
cx, cy = stack.pop()
if (cx, cy) in visited:
continue
if cx < 0 or cx >= self.grid_width or cy < 0 or cy >= self.grid_height:
continue
if self.grid_data[cy][cx] != old_color:
continue
visited.add((cx, cy))
self.set_pixel(cx, cy, new_color)
stack.extend([(cx + 1, cy), (cx - 1, cy), (cx, cy + 1), (cx, cy - 1)])
self.draw_grid()
def save_to_history(self):
import copy
self.history = self.history[:self.history_index + 1]
self.history.append(copy.deepcopy(self.grid_data))
if len(self.history) > 50:
self.history.pop(0)
self.history_index = len(self.history) - 1
def undo(self):
if self.history_index > 0:
self.history_index -= 1
self.grid_data = [row[:] for row in self.history[self.history_index]]
self.draw_grid()
self.update_code_from_grid()
if self.frames and self.current_frame < len(self.frames):
self.frames[self.current_frame] = [row[:] for row in self.grid_data]
def redo(self):
if self.history_index < len(self.history) - 1:
self.history_index += 1
self.grid_data = [row[:] for row in self.history[self.history_index]]
self.draw_grid()
self.update_code_from_grid()
if self.frames and self.current_frame < len(self.frames):
self.frames[self.current_frame] = [row[:] for row in self.grid_data]
def clear_grid(self):
for y in range(self.grid_height):
for x in range(self.grid_width):
self.grid_data[y][x] = '7'
self.draw_grid()
self.save_to_history()
self.update_code_from_grid()
if self.frames and self.current_frame < len(self.frames):
self.frames[self.current_frame] = [row[:] for row in self.grid_data]
def random_fill(self):
colors = list(self.palette.keys())
for y in range(self.grid_height):
for x in range(self.grid_width):
self.grid_data[y][x] = random.choice(colors)
self.draw_grid()
self.save_to_history()
self.update_code_from_grid()
if self.frames and self.current_frame < len(self.frames):
self.frames[self.current_frame] = [row[:] for row in self.grid_data]
def gradient_fill(self):
colors = list(self.palette.keys())
for y in range(self.grid_height):
for x in range(self.grid_width):
idx = (x + y) % len(colors)
self.grid_data[y][x] = colors[idx]
self.draw_grid()
self.save_to_history()
self.update_code_from_grid()
if self.frames and self.current_frame < len(self.frames):
self.frames[self.current_frame] = [row[:] for row in self.grid_data]
def flip_horizontal(self):
for y in range(self.grid_height):
self.grid_data[y].reverse()
self.draw_grid()
self.save_to_history()
self.update_code_from_grid()
if self.frames and self.current_frame < len(self.frames):
self.frames[self.current_frame] = [row[:] for row in self.grid_data]
def flip_vertical(self):
self.grid_data.reverse()
self.draw_grid()
self.save_to_history()
self.update_code_from_grid()
if self.frames and self.current_frame < len(self.frames):
self.frames[self.current_frame] = [row[:] for row in self.grid_data]
def zoom_in(self):
if self.pixel_size < 40:
self.pixel_size += 5
self.canvas.config(width=self.grid_width * self.pixel_size,
height=self.grid_height * self.pixel_size)
self.draw_grid()
def zoom_out(self):
if self.pixel_size > 10:
self.pixel_size -= 5
self.canvas.config(width=self.grid_width * self.pixel_size,
height=self.grid_height * self.pixel_size)
self.draw_grid()
def new_file(self):
self.grid_data = [['7' for _ in range(self.grid_width)] for _ in range(self.grid_height)]
self.draw_grid()
self.save_to_history()
self.update_code_from_grid()
self.frames = []
self.current_frame = 0
self.update_frame_indicator()
def save_file(self):
filename = filedialog.asksaveasfilename(defaultextension=".draw",
filetypes=[("画谱文件", "*.draw")])
if filename:
data = {
'grid_data': self.grid_data,
'frames': self.frames,
'frame_delay': self.frame_delay,
'grid_width': self.grid_width,
'grid_height': self.grid_height
}
with open(filename, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2)
messagebox.showinfo("成功", f"已保存到 {filename}")
def open_file(self):
filename = filedialog.askopenfilename(filetypes=[("画谱文件", "*.draw")])
if filename:
with open(filename, 'r', encoding='utf-8') as f:
data = json.load(f)
self.grid_data = data['grid_data']
self.frames = data.get('frames', [])
self.frame_delay = data.get('frame_delay', 200)
self.speed_var.set(self.frame_delay)
self.on_speed_change(self.frame_delay)
self.draw_grid()
self.save_to_history()
self.update_code_from_grid()
if self.frames:
self.current_frame = 0
self.update_frame_indicator()
def export_image(self):
filename = filedialog.asksaveasfilename(defaultextension=".png",
filetypes=[("PNG图片", "*.png")])
if filename:
img = Image.new('RGB', (self.grid_width * 10, self.grid_height * 10), (255, 255, 255))
draw = ImageDraw.Draw(img)
for y in range(self.grid_height):
for x in range(self.grid_width):
color = self.palette.get(self.grid_data[y][x], '#FFFFFF')
r = int(color[1:3], 16)
g = int(color[3:5], 16)
b = int(color[5:7], 16)
draw.rectangle([x * 10, y * 10, (x + 1) * 10, (y + 1) * 10], fill=(r, g, b))
img.save(filename)
messagebox.showinfo("成功", "图片已保存")
def export_gif(self):
if not self.frames:
messagebox.showwarning("警告", "没有动画帧可导出!")
return
filename = filedialog.asksaveasfilename(defaultextension=".gif",
filetypes=[("GIF动画", "*.gif")])
if filename:
images = []
for frame_data in self.frames:
img = Image.new('RGB', (self.grid_width * 10, self.grid_height * 10), (255, 255, 255))
draw = ImageDraw.Draw(img)
for y in range(self.grid_height):
for x in range(self.grid_width):
color = self.palette.get(frame_data[y][x], '#FFFFFF')
r = int(color[1:3], 16)
g = int(color[3:5], 16)
b = int(color[5:7], 16)
draw.rectangle([x * 10, y * 10, (x + 1) * 10, (y + 1) * 10], fill=(r, g, b))
images.append(img)
gif_delay = max(20, self.frame_delay) if self.frame_delay > 0 else 20
images[0].save(filename, save_all=True, append_images=images[1:], duration=gif_delay, loop=0)
messagebox.showinfo("成功", f"GIF已保存")
def custom_color(self):
color = colorchooser.askcolor(title="选择颜色")
if color:
new_code = str(len(self.palette))
if new_code in '0123456789ABCDEF':
new_code = chr(ord('A') + len(self.palette) - 10)
self.palette[new_code] = color[1]
def load_demo_pattern(self):
demo = [
"00000000000000000000000000000000",
"0FFFFFFFFFFFFFFFFFFFFFFFFFFFFF0",
"0F111111111111111111111111111F0",
"0F1FFFFFFFFFFFFFFFFFFFFFFFF1F0",
"0F1F0000000000000000000000F1F0",
"0F1F0FFFFFFFFFFFFFFFFFF0F1F0",
"0F1F0F1111111111111111F0F1F0",
"0F1F0F1FFFFFFFFFFFF1F0F1F0",
"0F1F0F1F0000000000F1F0F1F0",
"0F1F0F1F0FFFFFFF0F1F0F1F0",
"0F1F0F1F0F111111F0F1F0F1F0",
"0F1F0F1F0F1FFFF1F0F1F0F1F0",
"0F1F0F1F0F1F111F1F0F1F0F1F0",
"0F1F0F1F0F1F111F1F0F1F0F1F0",
"0F1F0F1F0F1F111F1F0F1F0F1F0",
"0F1F0F1F0F1FFFF1F0F1F0F1F0",
"0F1F0F1F0F111111F0F1F0F1F0",
"0F1F0F1F0FFFFFFF0F1F0F1F0",
"0F1F0F1F0000000000F1F0F1F0",
"0F1F0F1FFFFFFFFFFFF1F0F1F0",
"0F1F0F1111111111111111F0F1F0",
"0F1F0FFFFFFFFFFFFFFFFFF0F1F0",
"0F1F00000000000000000000F1F0",
"0F1FFFFFFFFFFFFFFFFFFFFFFFF1F0",
"0F111111111111111111111111111F0",
"0FFFFFFFFFFFFFFFFFFFFFFFFFFFFF0",
"00000000000000000000000000000000",
]
for y, line in enumerate(demo):
if y >= self.grid_height:
break
for x, char in enumerate(line):
if x >= self.grid_width:
break
if char in self.palette:
self.grid_data[y][x] = char
self.draw_grid()
self.save_to_history()
self.update_code_from_grid()
if __name__ == "__main__":
root = tk.Tk()
app = XiaobaWangPainter(root)
root.mainloop()
更多推荐
所有评论(0)