810 lines
34 KiB
Python
810 lines
34 KiB
Python
|
from functools import partial
|
||
|
import tkinter as tk
|
||
|
import tkinter.filedialog as file
|
||
|
import tkinter.messagebox as messagebox
|
||
|
import os
|
||
|
|
||
|
InfoText = """Character Generator v.b.3
|
||
|
build on May 16th, 2023
|
||
|
by Gabriel Weingardt
|
||
|
|
||
|
Feel free to modify ;)
|
||
|
github.com/0xMAC8205
|
||
|
|
||
|
Known issues:
|
||
|
> Draw Box Applying when
|
||
|
selecting a Character box"""
|
||
|
|
||
|
ToDo = """
|
||
|
- Correct 8x16 Char Export
|
||
|
"""
|
||
|
|
||
|
|
||
|
class Main(tk.Tk):
|
||
|
def __init__(self, mode, carry_file, *args, **kwargs):
|
||
|
super().__init__(*args, **kwargs)
|
||
|
self.protocol("WM_DELETE_WINDOW", self.exit_protocol)
|
||
|
self.Path = os.path.abspath(os.path.dirname(__file__)).replace("\\", "/") + "/assets"
|
||
|
|
||
|
self.Mode = mode
|
||
|
self.X_Select = 0
|
||
|
self.Y_Select = 0
|
||
|
|
||
|
self.BrushVar = tk.IntVar()
|
||
|
self.Size = tk.IntVar()
|
||
|
self.Grid = tk.IntVar()
|
||
|
self.Drag = tk.IntVar()
|
||
|
self.Wrap = tk.IntVar()
|
||
|
self.Hint = tk.IntVar()
|
||
|
self.Tooltip = tk.IntVar()
|
||
|
self.ProjectSize = tk.IntVar()
|
||
|
self.BrushVar.set(2)
|
||
|
self.ProjectSize.set(0)
|
||
|
|
||
|
self.background = "#FFFFFF"
|
||
|
self.foreground = "#000000"
|
||
|
self.draw_off = "#FFFFFF"
|
||
|
self.draw_on = "#000000"
|
||
|
self.font = "Arial 12 bold"
|
||
|
|
||
|
self.Copy = [0 for _ in range(16)]
|
||
|
|
||
|
self.X_Text = []
|
||
|
self.Y_Text = []
|
||
|
|
||
|
self.File_Formats = []
|
||
|
|
||
|
self.CurrentFile = ""
|
||
|
|
||
|
try:
|
||
|
self.Preset = open(self.Path + "/settings/settings", "rb")
|
||
|
|
||
|
self.Size.set(int.from_bytes(self.Preset.read(1), "big"))
|
||
|
self.Grid.set(int.from_bytes(self.Preset.read(1), "big"))
|
||
|
self.Drag.set(int.from_bytes(self.Preset.read(1), "big"))
|
||
|
self.Wrap.set(int.from_bytes(self.Preset.read(1), "big"))
|
||
|
self.Hint.set(int.from_bytes(self.Preset.read(1), "big"))
|
||
|
|
||
|
self.Preset.close()
|
||
|
|
||
|
for i in os.listdir(self.Path + "/custom_formats"):
|
||
|
if len(i) > 3 and not i[0] == ".":
|
||
|
self.File_Formats.append(i)
|
||
|
|
||
|
self.Format = open(self.Path + "/settings/theme.txt", "r")
|
||
|
self.import_code(self.Format.read().splitlines())
|
||
|
self.Format.close()
|
||
|
|
||
|
except Exception as e:
|
||
|
print("Error in Reading Settings:\n", e)
|
||
|
open(self.Path + "/settings/settings", "w").close()
|
||
|
|
||
|
self.BrushVar.set(2)
|
||
|
self.Size.set(16)
|
||
|
self.Grid.set(1)
|
||
|
self.Drag.set(1)
|
||
|
self.Wrap.set(0)
|
||
|
self.Hint.set(0)
|
||
|
|
||
|
open(self.Path + "/settings/theme.txt", "w").close()
|
||
|
|
||
|
self.config(bg=self.background)
|
||
|
|
||
|
self.Matrix = []
|
||
|
self.Characters = {}
|
||
|
|
||
|
self.UpperFrame = tk.Frame(master=self, bg=self.background)
|
||
|
self.UpperFrame.pack(side="top", fill="both", expand=0)
|
||
|
self.Frame = tk.Frame(master=self, bg=self.background)
|
||
|
self.Frame.pack(side="bottom", fill="y", expand=0)
|
||
|
self.LowerFrame = tk.Frame(master=self.Frame, bg=self.background)
|
||
|
self.LowerFrame.pack(side="bottom")
|
||
|
self.InfoBox = tk.Frame(master=self.Frame, bg=self.background)
|
||
|
self.InfoBox.pack(side="bottom")
|
||
|
|
||
|
self.Options = tk.LabelFrame(master=self.LowerFrame, bg=self.background, fg=self.foreground,
|
||
|
text="Options:", font=self.font,)
|
||
|
self.Options.pack(side="right", anchor="s", expand=0, fill="y")
|
||
|
self.DrawFrame = tk.Frame(master=self.LowerFrame, bg=self.background)
|
||
|
self.DrawFrame.pack(side="right", anchor="s", expand=0, fill="y")
|
||
|
self.Toolbox = tk.LabelFrame(master=self.LowerFrame, bg=self.background, fg=self.foreground,
|
||
|
text="Brush:", font=self.font)
|
||
|
self.Toolbox.pack(side="right", anchor="s", expand=0, fill="y")
|
||
|
|
||
|
self.CurrentCoord = tk.Label(master=self.InfoBox, text="Char: 0 | 0x00", bg=self.background,
|
||
|
foreground=self.foreground, font=self.font)
|
||
|
self.CurrentCoord.pack()
|
||
|
|
||
|
self.Draw = DrawBox(master=self.DrawFrame, bg=self.background, size=self.Size.get(), mode=self.Mode,
|
||
|
background=self.draw_off, foreground=self.draw_on)
|
||
|
self.Draw.pack()
|
||
|
|
||
|
# Brush Options
|
||
|
|
||
|
self.Brush_White = tk.Radiobutton(master=self.Toolbox, variable=self.BrushVar, value=0, text="White",
|
||
|
background=self.background, foreground=self.foreground,
|
||
|
indicatoron=False, command=self.update_brush, font=self.font)
|
||
|
self.Brush_Black = tk.Radiobutton(master=self.Toolbox, variable=self.BrushVar, value=1, text="Black",
|
||
|
background=self.background, foreground=self.foreground,
|
||
|
indicatoron=False, command=self.update_brush, font=self.font)
|
||
|
self.Brush_Invert = tk.Radiobutton(master=self.Toolbox, variable=self.BrushVar, value=2, text="Invert",
|
||
|
background=self.background, foreground=self.foreground,
|
||
|
indicatoron=False, command=self.update_brush, font=self.font)
|
||
|
self.Brush_Invert.pack(side="top", expand=1, fill="both")
|
||
|
self.Brush_Black.pack(side="top", expand=1, fill="both")
|
||
|
self.Brush_White.pack(side="top", expand=1, fill="both")
|
||
|
|
||
|
# Options
|
||
|
|
||
|
self.Invert_Button = tk.Button(master=self.Options, text="Invert", command=self.Draw.invert_screen,
|
||
|
background=self.background, foreground=self.foreground, font=self.font)
|
||
|
self.Clear_Button = tk.Button(master=self.Options, text="Clear", command=self.Draw.clear_screen,
|
||
|
background=self.background, foreground=self.foreground, font=self.font)
|
||
|
self.Apply_Button = tk.Button(master=self.Options, text="Apply", command=self.apply,
|
||
|
background=self.background, foreground=self.foreground, font=self.font)
|
||
|
self.Clear_Button.pack(side="top", expand=1, fill="both")
|
||
|
self.Invert_Button.pack(side="top", expand=1, fill="both")
|
||
|
self.Apply_Button.pack(side="bottom", expand=1, fill="both")
|
||
|
|
||
|
self.Menu = tk.Menu(self)
|
||
|
self.Menu_File = tk.Menu(self.Menu, tearoff=False)
|
||
|
self.Menu_Options = tk.Menu(self.Menu, tearoff=False)
|
||
|
self.Menu_Settings = tk.Menu(self.Menu, tearoff=False)
|
||
|
self.Menu_Export = tk.Menu(self.Menu_File, tearoff=False)
|
||
|
self.Menu_Help = tk.Menu(self.Menu, tearoff=False)
|
||
|
|
||
|
self.Menu_File.add_command(label="New Window", accelerator="Control+N",
|
||
|
command=lambda: self.start_project(False))
|
||
|
self.Menu_File.add_command(label="New Project", accelerator="Shift+N",
|
||
|
command=lambda: self.start_project(True))
|
||
|
self.Menu_File.add_separator()
|
||
|
self.Menu_File.add_command(label="Open Project", accelerator="Control+O", command=self.open)
|
||
|
self.Menu_File.add_command(label="Save Project", accelerator="Control+S", command=self.save)
|
||
|
self.Menu_File.add_command(label="Save Project As", accelerator="Control+Shift+S", command=self.save_as)
|
||
|
self.Menu_File.add_separator()
|
||
|
self.Menu_File.add_cascade(label="Export", menu=self.Menu_Export)
|
||
|
self.Menu_File.add_separator()
|
||
|
self.Menu_File.add_command(label="Close", accelerator="Control+Q", command=self.exit_protocol)
|
||
|
|
||
|
self.Menu_Export.add_command(label="Assembler", accelerator="Control+E", command=lambda: self.export("asm"))
|
||
|
self.Menu_Export.add_command(label="C Include", accelerator="Control+Shift+E", command=lambda: self.export("c"))
|
||
|
self.Menu_Export.add_command(label="Raw Bytes", command=lambda: self.export("out"))
|
||
|
|
||
|
"""
|
||
|
if len(self.File_Formats) > 0:
|
||
|
self.Menu_Export.add_separator()
|
||
|
for i in self.File_Formats:
|
||
|
if len(i) > 3:
|
||
|
self.Menu_Export.add_command(label=i.split(".")[0],
|
||
|
command=partial(self.export, "custom", type_carry=i))
|
||
|
"""
|
||
|
|
||
|
self.Menu_Options.add_command(label="Copy", accelerator="Control+C", command=self.copy)
|
||
|
self.Menu_Options.add_command(label="Paste", accelerator="Control+V", command=self.paste)
|
||
|
self.Menu_Options.add_separator()
|
||
|
self.Menu_Options.add_command(label="Clear", command=self.Draw.clear_screen, accelerator="Control+X")
|
||
|
self.Menu_Options.add_command(label="Invert", command=self.Draw.invert_screen, accelerator="Control+Y")
|
||
|
self.Menu_Options.add_command(label="Apply", command=self.apply, accelerator="Control+A")
|
||
|
|
||
|
self.Menu_Settings.add_checkbutton(label="Big Canvas", variable=self.Size, onvalue=32, offvalue=16,
|
||
|
command=self.update_draw, accelerator="Control+Plus")
|
||
|
self.Menu_Settings.add_checkbutton(label="Grid", variable=self.Grid, onvalue=1, offvalue=0,
|
||
|
command=self.update_draw, accelerator="Control+G")
|
||
|
self.Menu_Settings.add_checkbutton(label="Cursor Dragging", variable=self.Drag, onvalue=1, offvalue=0,
|
||
|
command=self.update_draw)
|
||
|
self.Menu_Settings.add_separator()
|
||
|
self.Menu_Settings.add_checkbutton(label="Coordinate Hints", variable=self.Hint,
|
||
|
onvalue=1, offvalue=0, command=self.update_draw)
|
||
|
self.Menu_Settings.add_checkbutton(label="Cursor Wrapping", variable=self.Wrap,
|
||
|
onvalue=1, offvalue=0)
|
||
|
|
||
|
self.Menu_Help.add_command(label="Creating a Theme",
|
||
|
command=lambda: FileViewer(self.Path + "/create_theme.txt"))
|
||
|
# self.Menu_Help.add_command(label="Creating Custom Formats",
|
||
|
# command=lambda: FileViewer(self.Path + "/format_help.txt"))
|
||
|
# self.Menu_Help.add_command(label="Importing Custom Formats",
|
||
|
# command=lambda: FileViewer(self.Path + "/create_format.txt"))
|
||
|
self.Menu_Help.add_separator()
|
||
|
self.Menu_Help.add_checkbutton(label="What is this?", variable=self.Tooltip)
|
||
|
self.Menu_Help.add_command(label="About", command=self.about_menu)
|
||
|
|
||
|
self.Menu.add_cascade(label="File", menu=self.Menu_File)
|
||
|
self.Menu.add_cascade(label="Options", menu=self.Menu_Options)
|
||
|
self.Menu.add_cascade(label="Settings", menu=self.Menu_Settings)
|
||
|
self.Menu.add_cascade(label="Help", menu=self.Menu_Help)
|
||
|
|
||
|
self["menu"] = self.Menu
|
||
|
|
||
|
self.bind("<Left>", self.cursor)
|
||
|
self.bind("<Right>", self.cursor)
|
||
|
self.bind("<Up>", self.cursor)
|
||
|
self.bind("<Down>", self.cursor)
|
||
|
self.bind("<a>", self.cursor)
|
||
|
self.bind("<d>", self.cursor)
|
||
|
self.bind("<w>", self.cursor)
|
||
|
self.bind("<s>", self.cursor)
|
||
|
|
||
|
self.bind("<Return>", lambda _: self.apply())
|
||
|
self.bind("<Control-a>", lambda _: self.apply())
|
||
|
self.bind("<Control-y>", self.Draw.invert_screen)
|
||
|
self.bind("<Control-x>", self.Draw.clear_screen)
|
||
|
self.bind("<Control-g>", self.show_grid)
|
||
|
|
||
|
self.bind("<Control-q>", self.exit_protocol)
|
||
|
self.bind("<Control-o>", self.open)
|
||
|
self.bind("<Control-s>", self.save)
|
||
|
self.bind("<Control-Shift-s>", self.save_as)
|
||
|
self.bind("<Control-n>", lambda _: self.start_project(False))
|
||
|
self.bind("<Shift-n>", lambda _: self.start_project(True))
|
||
|
self.bind("<Control-e>", lambda _: self.export("asm"))
|
||
|
self.bind("<Control-Shift-E>", lambda _: self.export("c"))
|
||
|
|
||
|
self.bind("<Control-plus>", self.extend_draw)
|
||
|
self.bind("<Control-c>", self.copy)
|
||
|
self.bind("<Control-v>", self.paste)
|
||
|
|
||
|
ToolTip(self.Brush_White, "White Brush.\nDraws white on selected field")
|
||
|
ToolTip(self.Brush_Black, "Black Brush.\nDraws black on selected field")
|
||
|
ToolTip(self.Brush_Invert, "Inverted Brush.\nInverts the selected field")
|
||
|
ToolTip(self.Invert_Button, "Invert Button.\nInverts the entire draw field")
|
||
|
ToolTip(self.Clear_Button, "Clear Button.\nClears the entire draw field")
|
||
|
ToolTip(self.Apply_Button, "Apply Button.\nSets the selected character \nto the edited character")
|
||
|
ToolTip(self.CurrentCoord, "Coordinate Label.\nShows the index of selected \ncharacter in Decimal and Hex")
|
||
|
ToolTip(self.DrawFrame, "Draw field.\nHere you can edit\nthe selected character")
|
||
|
ToolTip(self.UpperFrame, "Character list.\nHere you select one of\n256 characters to edit")
|
||
|
|
||
|
self.build_matrix()
|
||
|
self.Draw.update_screen()
|
||
|
self.update_draw()
|
||
|
self.update_brush()
|
||
|
|
||
|
if carry_file:
|
||
|
self.open(in_file=carry_file)
|
||
|
|
||
|
def exit_protocol(self, event=None):
|
||
|
try:
|
||
|
self.check_save_status()
|
||
|
self.Preset = open(self.Path + "/settings/settings", "wb")
|
||
|
self.Preset.write(self.Size.get().to_bytes(1, "big"))
|
||
|
self.Preset.write(self.Grid.get().to_bytes(1, "big"))
|
||
|
self.Preset.write(self.Drag.get().to_bytes(1, "big"))
|
||
|
self.Preset.write(self.Wrap.get().to_bytes(1, "big"))
|
||
|
self.Preset.write(self.Hint.get().to_bytes(1, "big"))
|
||
|
|
||
|
self.Preset.close()
|
||
|
except Exception as e:
|
||
|
messagebox.showerror("Error", "Error while saving local settings:\n{0}".format(e))
|
||
|
|
||
|
self.destroy()
|
||
|
|
||
|
def save(self, event=None):
|
||
|
if self.CurrentFile == "":
|
||
|
name_raw = file.asksaveasfilename(confirmoverwrite=True, defaultextension=".bmf", title="Save As",
|
||
|
filetypes=(("BitMap File", '*.bmf'), ("Text File", "*.txt"),
|
||
|
("All Files", "*.*")))
|
||
|
name = open(name_raw, "wb")
|
||
|
else:
|
||
|
name = open(self.CurrentFile, "wb")
|
||
|
|
||
|
if name:
|
||
|
name.write(int(self.Mode).to_bytes(1, "big"))
|
||
|
|
||
|
for y in range(16):
|
||
|
for x in range(16):
|
||
|
for y_ in range(16):
|
||
|
bin_value = ""
|
||
|
for x_ in range(8):
|
||
|
bin_value += str(self.Matrix[y][x].Pixels[x_][y_])
|
||
|
name.write(int(bin_value, 2).to_bytes(1, "big"))
|
||
|
|
||
|
self.CurrentFile = name.name
|
||
|
self.modified(False)
|
||
|
name.close()
|
||
|
|
||
|
def save_as(self, event=None):
|
||
|
self.CurrentFile = ""
|
||
|
self.save()
|
||
|
|
||
|
def open(self, event=None, in_file=None):
|
||
|
self.check_save_status()
|
||
|
|
||
|
if in_file:
|
||
|
read = open(in_file, "rb")
|
||
|
else:
|
||
|
read_raw = file.askopenfilename(defaultextension=".bmf",
|
||
|
filetypes=(("BitMap File", '*.bmf'),
|
||
|
("Text File", "*.txt"),
|
||
|
("All Files", "*.*")))
|
||
|
read = open(read_raw, "rb")
|
||
|
|
||
|
if read:
|
||
|
read.read(1)
|
||
|
for y in range(16):
|
||
|
for x in range(16):
|
||
|
for y_ in range(16):
|
||
|
value = int.from_bytes(read.read(1), "big")
|
||
|
for x_ in range(8):
|
||
|
shift_val = bin(value)[2:].zfill(8)[x_:x_+1]
|
||
|
self.Matrix[y][x].Pixels[x_][y_] = int(shift_val)
|
||
|
self.select_grid(x, y)
|
||
|
|
||
|
self.CurrentFile = read.name
|
||
|
self.select_grid(0, 0)
|
||
|
self.Draw.update_screen()
|
||
|
self.modified(False)
|
||
|
read.close()
|
||
|
|
||
|
def export(self, mode, event=None):
|
||
|
write_raw = file.asksaveasfilename(confirmoverwrite=False, defaultextension="." + mode, title="Export As",
|
||
|
filetypes=((mode.upper(), "." + mode), ("All Files", "*.*")))
|
||
|
if write_raw:
|
||
|
write = open(write_raw, "w")
|
||
|
if mode == "asm":
|
||
|
write.write("{0}".format(os.path.basename(self.CurrentFile).split(".")[0]))
|
||
|
# write.write("\n\t; 8x{0} Character Size | 256 Characters Total".format(8 * (self.Mode + 1)))
|
||
|
for y in range(16):
|
||
|
for x in range(16):
|
||
|
write.write("\n \t.byte ")
|
||
|
buffer = ""
|
||
|
for y_ in range(8 * (self.Mode + 1)):
|
||
|
bin_value = ""
|
||
|
for x_ in range(8):
|
||
|
bin_value += str(self.Matrix[y][x].Pixels[x_][y_])
|
||
|
buffer += "$" + str(hex(int(bin_value, 2)))[2:].zfill(2).upper() + ", "
|
||
|
write.write(buffer[:len(buffer)-2])
|
||
|
write.write(" ;char 0x{0}, {1}".format(hex(x + y * 16)[2:].zfill(2).upper(), x + y * 16))
|
||
|
write.write("{0}_end".format(os.path.basename(self.CurrentFile).split(".")[0]))
|
||
|
|
||
|
elif mode == "c":
|
||
|
write.write("// {0}".format(os.path.abspath(self.CurrentFile)))
|
||
|
write.write("\n// 8x{0} Character Size | 256 Characters Total".format(8 * (self.Mode + 1)))
|
||
|
write.write("\n{")
|
||
|
for y in range(16):
|
||
|
for x in range(16):
|
||
|
write.write("\n \t")
|
||
|
for y_ in range(8 * (self.Mode + 1)):
|
||
|
bin_value = ""
|
||
|
for x_ in range(8):
|
||
|
bin_value += str(self.Matrix[y][x].Pixels[x_][y_])
|
||
|
write.write("0x" + str(hex(int(bin_value, 2)))[2:].zfill(2).upper() + ", ")
|
||
|
write.write(" //Char {0} ; {1}".format(hex(x + y * 16)[2:].zfill(2).upper(), x + y * 16))
|
||
|
write.write("\n}")
|
||
|
|
||
|
elif mode == "out":
|
||
|
write = open(write_raw, "wb")
|
||
|
for y in range(16):
|
||
|
for x in range(16):
|
||
|
for y_ in range(8 * (self.Mode + 1)):
|
||
|
bin_value = ""
|
||
|
for x_ in range(8):
|
||
|
bin_value += str(self.Matrix[y][x].Pixels[x_][y_])
|
||
|
write.write(int(bin_value, 2).to_bytes(1, "big"))
|
||
|
|
||
|
write.close()
|
||
|
|
||
|
def copy(self, event=None):
|
||
|
self.Copy = []
|
||
|
for y_ in range(16):
|
||
|
bin_value = ""
|
||
|
for x_ in range(8):
|
||
|
bin_value += str(self.Matrix[self.Y_Select][self.X_Select].Pixels[x_][y_])
|
||
|
self.Copy.append(int(bin_value, 2))
|
||
|
|
||
|
def paste(self, event=None):
|
||
|
for y in range(16):
|
||
|
txt = str(bin(self.Copy[y]))[2:].zfill(8)
|
||
|
for x in range(8):
|
||
|
self.Matrix[self.Y_Select][self.X_Select].Pixels[x][y] = int(txt[x])
|
||
|
self.select_grid(self.X_Select, self.Y_Select)
|
||
|
|
||
|
def build_matrix(self):
|
||
|
hex_count_y = 0
|
||
|
for y in range(18):
|
||
|
row = []
|
||
|
hex_count_x = 0
|
||
|
for x in range(18):
|
||
|
if x == 0 or x == 17:
|
||
|
if y != 0 and y != 17:
|
||
|
txt = tk.Label(master=self.UpperFrame, text=str(hex(hex_count_y)).upper()[2:],
|
||
|
bg=self.background, fg=self.foreground, font=self.font)
|
||
|
txt.grid(row=y, column=x)
|
||
|
if x != 17:
|
||
|
self.Y_Text.append(txt)
|
||
|
if x == 17:
|
||
|
hex_count_y += 1
|
||
|
|
||
|
ToolTip(txt, "Y Coordinate.\nThis is the first Hex digit\n>> 0xY0")
|
||
|
|
||
|
elif y == 0 or y == 17:
|
||
|
if x != 0 and x != 17:
|
||
|
txt = tk.Label(master=self.UpperFrame, text=str(hex(hex_count_x)).upper()[2:],
|
||
|
bg=self.background, fg=self.foreground, font=self.font)
|
||
|
txt.grid(row=y, column=x)
|
||
|
if x != 17:
|
||
|
self.X_Text.append(txt)
|
||
|
hex_count_x += 1
|
||
|
|
||
|
ToolTip(txt, "X Coordinate.\nThis is the second Hex digit\n>> 0x0X")
|
||
|
else:
|
||
|
matrix = ImageBox(master=self.UpperFrame, bg=self.draw_off, height=24, width=24,
|
||
|
relief="flat", bd=1, background=self.draw_off, foreground=self.draw_on)
|
||
|
matrix.grid(row=y, column=x)
|
||
|
matrix.bind("<Button-1>", partial(self.select_grid, x - 1, y - 1))
|
||
|
matrix.bind("<Button-2>", partial(self.select_grid, x - 1, y - 1))
|
||
|
matrix.bind("<Button-3>", partial(self.select_grid, x - 1, y - 1))
|
||
|
row.append(matrix)
|
||
|
if y != 0:
|
||
|
self.Matrix.append(row)
|
||
|
|
||
|
def update_draw(self):
|
||
|
for i in range(16):
|
||
|
self.X_Text[i].config(bg=self.background, fg=self.foreground)
|
||
|
self.Y_Text[i].config(bg=self.background, fg=self.foreground)
|
||
|
|
||
|
self.Draw.Size = self.Size.get()
|
||
|
self.Draw.config(height=self.Size.get() * 8, width=self.Size.get() * 8)
|
||
|
self.Draw.Outline = self.Grid.get()
|
||
|
self.Draw.Drag = self.Drag.get()
|
||
|
self.select_grid(self.X_Select, self.Y_Select)
|
||
|
|
||
|
def update_brush(self):
|
||
|
self.Draw.Brush = self.BrushVar.get()
|
||
|
|
||
|
def start_project(self, mode=None, event=None):
|
||
|
if mode:
|
||
|
self.check_save_status()
|
||
|
self.destroy()
|
||
|
os.system("python3 main.py")
|
||
|
|
||
|
def modified(self, status):
|
||
|
self.Draw.Modified = status
|
||
|
self.title("Bitmap Editor - {0}".format(os.path.basename(self.CurrentFile)))
|
||
|
if status:
|
||
|
self.title("Bitmap Editor - {0} *".format(os.path.basename(self.CurrentFile)))
|
||
|
|
||
|
def check_save_status(self):
|
||
|
if self.Draw.Modified:
|
||
|
if tk.messagebox.askyesno(message="Do you want to Save?"):
|
||
|
self.save()
|
||
|
|
||
|
def select_grid(self, x, y, event=None):
|
||
|
if self.Hint.get():
|
||
|
self.X_Text[self.X_Select].config(bg=self.background, fg=self.foreground)
|
||
|
self.Y_Text[self.Y_Select].config(bg=self.background, fg=self.foreground)
|
||
|
self.X_Text[x].config(bg=self.foreground, fg=self.background)
|
||
|
self.Y_Text[y].config(bg=self.foreground, fg=self.background)
|
||
|
|
||
|
if self.Draw.Applied:
|
||
|
pass
|
||
|
|
||
|
self.Matrix[self.Y_Select][self.X_Select].load(self.Draw.PixelGrid, mode=self.Mode)
|
||
|
self.Draw.load(self.Matrix[y][x].Pixels)
|
||
|
|
||
|
self.Matrix[self.Y_Select][self.X_Select].config(relief="flat")
|
||
|
self.Matrix[y][x].config(relief="solid")
|
||
|
self.X_Select, self.Y_Select = x, y
|
||
|
|
||
|
hex_number = str(hex(y)[2:]) + str(hex(x)[2:])
|
||
|
self.CurrentCoord.config(text="{0} | 0x{1}".format(int(hex_number, 16), hex_number.upper()))
|
||
|
if event and event.num == 2 or event and event.num == 3:
|
||
|
extern = tk.Toplevel(master=self, height=20, width=20)
|
||
|
extern.resizable(False, False)
|
||
|
extern.title("{0} | 0x{1}".format(int(hex_number, 16), hex_number.upper()))
|
||
|
extern_canvas = tk.Canvas(master=extern, bg=self.draw_off, height=255, width=255, relief="solid", bd=1)
|
||
|
extern_canvas.pack(expand=1, fill="both")
|
||
|
|
||
|
for y in range(8 * (self.Mode + 1)):
|
||
|
for x in range(8):
|
||
|
fill = self.draw_off
|
||
|
out = self.draw_on
|
||
|
if self.Matrix[self.Y_Select][self.X_Select].Pixels[x][y]:
|
||
|
fill = self.draw_on
|
||
|
out = self.draw_off
|
||
|
x_cord = x * 32
|
||
|
y_cord = y * 32 / (self.Mode + 1)
|
||
|
extern_canvas.create_rectangle(x_cord, y_cord, x_cord + 32, y_cord + 32 / (self.Mode + 1),
|
||
|
fill=fill, outline=out, width=0)
|
||
|
|
||
|
self.Draw.update_screen()
|
||
|
|
||
|
def apply(self):
|
||
|
self.Draw.Applied = True
|
||
|
self.Matrix[self.Y_Select][self.X_Select].load(self.Draw.PixelGrid, mode=self.Mode)
|
||
|
self.modified(True)
|
||
|
|
||
|
def show_grid(self, event):
|
||
|
if self.Grid.get():
|
||
|
self.Grid.set(0)
|
||
|
else:
|
||
|
self.Grid.set(1)
|
||
|
self.update_draw()
|
||
|
|
||
|
def extend_draw(self, event):
|
||
|
if self.Size.get() == 32:
|
||
|
self.Size.set(16)
|
||
|
else:
|
||
|
self.Size.set(32)
|
||
|
self.update_draw()
|
||
|
|
||
|
def cursor(self, event):
|
||
|
x, y = self.X_Select, self.Y_Select
|
||
|
if event.keysym == "Left" or event.keysym == "a":
|
||
|
x -= 1
|
||
|
elif event.keysym == "Right" or event.keysym == "d":
|
||
|
x += 1
|
||
|
elif event.keysym == "Up" or event.keysym == "w":
|
||
|
y -= 1
|
||
|
elif event.keysym == "Down" or event.keysym == "s":
|
||
|
y += 1
|
||
|
|
||
|
if x < 0:
|
||
|
x = 15
|
||
|
if self.Wrap.get():
|
||
|
y -= 1
|
||
|
if x > 15:
|
||
|
x = 0
|
||
|
if self.Wrap.get():
|
||
|
y += 1
|
||
|
if y > 15:
|
||
|
y = 0
|
||
|
if y < 0:
|
||
|
y = 15
|
||
|
self.select_grid(x, y)
|
||
|
|
||
|
def about_menu(self):
|
||
|
menu = tk.Toplevel(self)
|
||
|
menu.resizable(False, False)
|
||
|
menu.title("About")
|
||
|
|
||
|
textbox = tk.Text(master=menu, height=10, width=30)
|
||
|
textbox.insert("end", InfoText)
|
||
|
textbox.config(state="disabled")
|
||
|
textbox.pack(side="top")
|
||
|
exit_button = tk.Button(master=menu, text="Exit", command=menu.destroy)
|
||
|
exit_button.pack(side="bottom", expand=0, fill="x")
|
||
|
|
||
|
def import_code(self, code):
|
||
|
for i in code:
|
||
|
if len(i) > 1:
|
||
|
if i == "[end]":
|
||
|
break
|
||
|
elif not i[0] == "#":
|
||
|
try:
|
||
|
exec(i)
|
||
|
except Exception as e:
|
||
|
print("Error while Importing Code: \n", e)
|
||
|
|
||
|
|
||
|
class FileViewer(tk.Tk):
|
||
|
def __init__(self, path, *args, **kwargs):
|
||
|
tk.Tk.__init__(self, *args, **kwargs)
|
||
|
self.title(os.path.basename(path))
|
||
|
|
||
|
self.ShowText = tk.Text(master=self, bg="#FFFFFF", height=32, width=160)
|
||
|
self.ShowText.pack(side="top", expand=1, fill="both")
|
||
|
|
||
|
tk.Button(master=self, text="Close", bg="#FFFFFF",
|
||
|
command=lambda: self.destroy()).pack(side="bottom", expand=0, fill="x")
|
||
|
|
||
|
file_read = open(path, "r")
|
||
|
for i in file_read.read():
|
||
|
self.ShowText.insert("end", i)
|
||
|
file_read.close()
|
||
|
|
||
|
self.ShowText.config(state="disabled")
|
||
|
|
||
|
|
||
|
class ToolTip(object):
|
||
|
def __init__(self, widget, text):
|
||
|
widget.bind("<Enter>", self.showtip)
|
||
|
widget.bind("<Leave>", self.hidetip)
|
||
|
|
||
|
self.widget = widget
|
||
|
self.text = text
|
||
|
self.window = None
|
||
|
self.id = None
|
||
|
self.x = 0
|
||
|
self.y = 0
|
||
|
|
||
|
def showtip(self, event):
|
||
|
if self.window or not self.text or not main.Tooltip.get():
|
||
|
return
|
||
|
x, y, cx, cy = self.widget.bbox("insert")
|
||
|
x += self.widget.winfo_rootx() + 57
|
||
|
y += cy + self.widget.winfo_rooty() + 27
|
||
|
|
||
|
self.window = tk.Toplevel(self.widget)
|
||
|
self.window.overrideredirect(True)
|
||
|
self.window.wm_geometry("+%d+%d" % (x, y))
|
||
|
|
||
|
label = tk.Label(self.window, text=self.text, background="#FFFFE0", relief="solid", bd=1, justify="left")
|
||
|
label.pack(ipadx=1)
|
||
|
|
||
|
def hidetip(self, event):
|
||
|
win = self.window
|
||
|
self.window = None
|
||
|
if win:
|
||
|
win.destroy()
|
||
|
|
||
|
|
||
|
class DrawBox(tk.Canvas):
|
||
|
def __init__(self, size, mode, background, foreground, *args, **kwargs):
|
||
|
tk.Canvas.__init__(self, *args, **kwargs)
|
||
|
self.background = background
|
||
|
self.foreground = foreground
|
||
|
self.Size = size
|
||
|
self.Mode = mode
|
||
|
self.X_coord = []
|
||
|
self.Y_coord = []
|
||
|
self.Brush = 0
|
||
|
self.Outline = 1
|
||
|
self.Drag = 1
|
||
|
self.Modified = False
|
||
|
self.Applied = False
|
||
|
self.Previous_X = -1
|
||
|
self.Previous_Y = -1
|
||
|
self.PixelGrid = [[0 for _ in range(16)] for _ in range(8)]
|
||
|
self.update_coord(size)
|
||
|
|
||
|
self.config(height=size * 8 + 1, width=size * 8 + 1)
|
||
|
self.bind("<B1-Motion>", lambda e: self.draw("motion", e))
|
||
|
self.bind("<Button-1>", lambda e: self.draw("click", e))
|
||
|
|
||
|
def update_coord(self, size):
|
||
|
self.X_coord.clear()
|
||
|
self.Y_coord.clear()
|
||
|
for i in range(8):
|
||
|
self.X_coord.append(i * size)
|
||
|
for i in range((self.Mode + 1) * 8):
|
||
|
self.Y_coord.append(int(i * (size / (self.Mode + 1))))
|
||
|
|
||
|
def draw(self, click, event):
|
||
|
ex = event.x - (self.Size / 2) # Brush Cursor fine tune
|
||
|
ey = event.y - (self.Size / (2 * (self.Mode + 1)))
|
||
|
x = int(min(self.X_coord, key=lambda x_: abs(x_ - (ex - int(self.Size / 16)))) / self.Size)
|
||
|
y = int((min(self.Y_coord, key=lambda y_: abs(y_ - (ey - int(self.Size / 16)))) /
|
||
|
(self.Size / 128) / (8 / (self.Size / 16)) / (self.Size / (self.Mode + 1))))
|
||
|
if click == "click" or click == "motion" and self.Drag != 0:
|
||
|
if self.Previous_X != x or self.Previous_Y != y or click == "click":
|
||
|
if self.Brush == 2:
|
||
|
if self.PixelGrid[x][y]:
|
||
|
self.PixelGrid[x][y] = 0
|
||
|
else:
|
||
|
self.PixelGrid[x][y] = 1
|
||
|
elif self.Brush:
|
||
|
self.PixelGrid[x][y] = 1
|
||
|
else:
|
||
|
self.PixelGrid[x][y] = 0
|
||
|
self.update_screen()
|
||
|
self.Previous_X, self.Previous_Y = x, y
|
||
|
|
||
|
def update_screen(self):
|
||
|
self.delete("all")
|
||
|
for y in range(8 * (self.Mode + 1)):
|
||
|
for x in range(8):
|
||
|
fill = self.background
|
||
|
out = self.foreground
|
||
|
if self.PixelGrid[x][y]:
|
||
|
fill = self.foreground
|
||
|
out = self.background
|
||
|
x_cord = x * self.Size
|
||
|
y_cord = y * self.Size / (self.Mode + 1)
|
||
|
self.create_rectangle(x_cord, y_cord, x_cord + self.Size, y_cord + self.Size / (self.Mode + 1),
|
||
|
fill=fill, outline=out, width=self.Outline)
|
||
|
|
||
|
def clear_screen(self, event=None):
|
||
|
for y in range(8 * (self.Mode + 1)):
|
||
|
for x in range(8):
|
||
|
self.PixelGrid[x][y] = 0
|
||
|
self.update_screen()
|
||
|
|
||
|
def invert_screen(self, event=None):
|
||
|
for y in range(8 * (self.Mode + 1)):
|
||
|
for x in range(8):
|
||
|
if self.PixelGrid[x][y]:
|
||
|
self.PixelGrid[x][y] = 0
|
||
|
else:
|
||
|
self.PixelGrid[x][y] = 1
|
||
|
self.update_screen()
|
||
|
|
||
|
def load(self, carry):
|
||
|
self.PixelGrid = carry
|
||
|
|
||
|
|
||
|
class ImageBox(tk.Canvas):
|
||
|
def __init__(self, background, foreground, *args, **kwargs):
|
||
|
tk.Canvas.__init__(self, *args, **kwargs)
|
||
|
self.background = background
|
||
|
self.foreground = foreground
|
||
|
self.Pixels = [[0 for _ in range(16)] for _ in range(8)]
|
||
|
|
||
|
def load(self, bitmap, mode):
|
||
|
# self.Pixels = bitmap
|
||
|
self.delete("all")
|
||
|
for y in range((mode + 1) * 8):
|
||
|
for x in range(8):
|
||
|
fill_color = self.background
|
||
|
if bitmap[x][y]:
|
||
|
fill_color = self.foreground
|
||
|
self.create_rectangle(x * 3 + 2, (y * 3 + 2) / (mode + 1) + mode,
|
||
|
x * 3 + 5, (y * 3 + 5) / (mode + 1) + mode,
|
||
|
fill=fill_color, width=0)
|
||
|
|
||
|
|
||
|
class StartupDialog(tk.Tk):
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
super().__init__(*args, **kwargs)
|
||
|
self.protocol("WM_DELETE_WINDOW", self.exit_protocol)
|
||
|
|
||
|
self.closed = False
|
||
|
self.file = None
|
||
|
self.ProjectSize = tk.IntVar()
|
||
|
|
||
|
self.Button_Frame = tk.LabelFrame(master=self, text="Character Size:", bg="#FFFFFF")
|
||
|
self.Button_Frame.pack(side="left", fill="both", expand=0)
|
||
|
|
||
|
self.Action_Frame = tk.Frame(master=self, bg="#FFFFFF")
|
||
|
self.Action_Frame.pack(side="right", fill="both", expand=0)
|
||
|
|
||
|
self.Button_8 = tk.Radiobutton(master=self.Button_Frame, variable=self.ProjectSize, value=0,
|
||
|
text="8x8 Pixels", bg="#FFFFFF", justify="center")
|
||
|
self.Button_16 = tk.Radiobutton(master=self.Button_Frame, variable=self.ProjectSize, value=1,
|
||
|
text="8x16 Pixels", bg="#FFFFFF", justify="center")
|
||
|
|
||
|
self.Continue_Button = tk.Button(master=self, text="Open", bg="#FFFFFF", command=self.destroy, width=15)
|
||
|
self.Cancel_Button = tk.Button(master=self, text="Cancel", bg="#FFFFFF", command=self.exit_protocol)
|
||
|
self.Open_Button = tk.Button(master=self, text="Open File", bg="#FFFFFF", command=self.fileopen)
|
||
|
|
||
|
self.File_Label = tk.Label(master=self, text="File: None\nNew Blank File", bg="#FFFFFF", justify="left")
|
||
|
|
||
|
self.Button_8.pack(side="top", expand=1, fill="both")
|
||
|
self.Button_16.pack(side="bottom", expand=1, fill="both")
|
||
|
|
||
|
self.Cancel_Button.pack(side="bottom", expand=1, fill="x", padx=2, pady=2)
|
||
|
self.Continue_Button.pack(side="bottom", expand=1, fill="x", padx=2, pady=2)
|
||
|
self.Open_Button.pack(side="top", expand=1, fill="x", padx=2, pady=2)
|
||
|
self.File_Label.pack(side="top", anchor="w", padx=2, pady=2)
|
||
|
|
||
|
self.Continue_Button.focus()
|
||
|
|
||
|
self.bind("<Return>", lambda _: self.destroy())
|
||
|
self.bind("<Control-o>", self.fileopen)
|
||
|
self.bind("<Escape>", self.exit_protocol)
|
||
|
|
||
|
def exit_protocol(self, event=None):
|
||
|
self.closed = True
|
||
|
self.destroy()
|
||
|
|
||
|
def fileopen(self, event=None):
|
||
|
self.file = file.askopenfilename(defaultextension=".bmf", filetypes=(("BitMap File", '*.bmf'),
|
||
|
("Text File", "*.txt"),
|
||
|
("All Files", "*.*")))
|
||
|
if self.file:
|
||
|
read = open(self.file, "rb")
|
||
|
if int.from_bytes(read.read(1), "big") == 1:
|
||
|
self.ProjectSize.set(1)
|
||
|
else:
|
||
|
self.ProjectSize.set(0)
|
||
|
|
||
|
self.File_Label.config(text="File: {0}\nDetected Size: {1}x{2}".
|
||
|
format(os.path.basename(self.file), 8, (self.ProjectSize.get() + 1) * 8))
|
||
|
|
||
|
read.close()
|
||
|
else:
|
||
|
self.File_Label.config(text="File: None\nNew Blank File")
|
||
|
self.file = None
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
selector = StartupDialog()
|
||
|
selector.config(bg="#FFFFFF")
|
||
|
selector.title("Project Selector")
|
||
|
selector.resizable(False, False)
|
||
|
selector.mainloop()
|
||
|
|
||
|
if not selector.closed:
|
||
|
main = Main(mode=selector.ProjectSize.get(), carry_file=selector.file) # Mode: 0 = 8x8 | 1 = 8x16
|
||
|
main.resizable(False, False)
|
||
|
main.title("Bitmap Editor")
|
||
|
main.mainloop()
|