commit d3f9fcc8fddd531f10b0ebc17aedf929b89ddaee Author: 0xMAC <55045978+0xMAC8205@users.noreply.github.com> Date: Tue May 16 11:34:50 2023 +0200 Add files via upload diff --git a/Charset.bmf b/Charset.bmf new file mode 100644 index 0000000..193eb7d Binary files /dev/null and b/Charset.bmf differ diff --git a/assets/create_format.txt b/assets/create_format.txt new file mode 100644 index 0000000..e3cafea --- /dev/null +++ b/assets/create_format.txt @@ -0,0 +1,27 @@ +How to create your own Format File: + +1. Create a File, with the ".frm" extention, but any other will work just fine + +2. Write the Syntax + + The Syntax is just one Line. You can mix Text and Insert Indicators. + +Example: + ' .byte ${$data}, ${$data}, ${$data}, ${$data} ; Character ${$char} | {char} \n', exclusing ' + +The Output will be for example: + ' .byte $45, $3E, $F0, $3C ; Character $08 | 8\n' + + Insert Indicators are: + + {data} => Data in Decimal format + {char} => Character number + \n => New Line + \t => Tab (4 spaces) + + You can put $ or b infront of the text to indicate a format change + {$data} will be converted to Hexadecimal and + {bdata}, to binary + + The File is finished, when the Data Index Counter reaches + 256 (in 8x8 mode) or 512 (8x16 mode) diff --git a/assets/create_theme.txt b/assets/create_theme.txt new file mode 100644 index 0000000..a27d99b --- /dev/null +++ b/assets/create_theme.txt @@ -0,0 +1,17 @@ +How to edit the Current Theme: + +1. Open "theme.txt" in (ApplicationRunPath)/assets/settings/ + +2. Insert any Python Code. + + Empty lines, lines starting with "#" and text written under [end], + will be irgnored and be treated as Comments. + The code is executed upon startup, before the UI build and stops, + after the [end] incicator + +3. Save and Restart your application + + That's it! + +Useful Variables: + self.background, self.foreground, self.draw_off, self.draw_on, self.font diff --git a/assets/custom_formats/custom_assembler.frm b/assets/custom_formats/custom_assembler.frm new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/assets/custom_formats/custom_assembler.frm @@ -0,0 +1 @@ + diff --git a/assets/custom_formats/rust_format.frm b/assets/custom_formats/rust_format.frm new file mode 100644 index 0000000..20ab95e --- /dev/null +++ b/assets/custom_formats/rust_format.frm @@ -0,0 +1,10 @@ +self.write_mode = "w" +self.file_extension = ".rs" +self.start_symbol = "{\n" +self.end_symbol = "}" +self.value = hex(value) +self.index = hex(index) +self.text_body = " .byte ${value}, ${value}, ${value}, ${value} ; Char: ${index}\n" + + +[end] diff --git a/assets/format_help.txt b/assets/format_help.txt new file mode 100644 index 0000000..d31865b --- /dev/null +++ b/assets/format_help.txt @@ -0,0 +1,36 @@ +How to create your own formatted output: + +1. Create a new file, with the '.frm' extention + + +2. Insert your custom format syntax + +Example: ' .byte ${$data}, ${$data}, ${$data}, ${$data} ; Character ${$char} | {char} \n', excluding ' +Output: ' .byte $45, $3E, $F0, $3C ; Character $08 | 8\n' (of course '\n' will be a new line indicator) + + {$data} = Counting Data in Hex Format Example: 45, 3E, F0, 3C + {data} = Counting Data in Decimal Format Example: 69, 62, 240, 60 + + {$char} = Index as Hex format Example: 08, 09, 0A, 0B + {char} = Index as Decimal format Example: 8, 9, 10, 11 + + The Rest will be left unchanged, only replacing shown format markers. + You only need to write a single line, since the pattern repeats until Index reaches end. + + +3. Save at a custom location, or in the assets folder, found in the applications run path + + +4. Automatic Install: + Open format manager, under 'Export > Manage Custom Formats' + Add / Remove the format file. + Click "save", and thats it! + + Manual Install: + Copy the .frm file, to the assets folder, in the applications run path + edit "custom_format.txt" and add / delete the file. + Just insert the Filename without path >> "custom.frm" + + It's important, that every filename be written in it's own line! + + Restart the application, and you're good to go! diff --git a/assets/settings/settings b/assets/settings/settings new file mode 100644 index 0000000..de3337f --- /dev/null +++ b/assets/settings/settings @@ -0,0 +1 @@ +  \ No newline at end of file diff --git a/assets/settings/theme.txt b/assets/settings/theme.txt new file mode 100644 index 0000000..16d7507 --- /dev/null +++ b/assets/settings/theme.txt @@ -0,0 +1,13 @@ +# General Theme +self.foreground="black" +self.background="white" + +# Draw Field +self.draw_on="black" +self.draw_off="white" + + +# Font Style +self.font="Dejavu 10" + +[end] diff --git a/main.py b/main.py new file mode 100644 index 0000000..71224d2 --- /dev/null +++ b/main.py @@ -0,0 +1,809 @@ +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("", self.cursor) + self.bind("", self.cursor) + self.bind("", self.cursor) + self.bind("", self.cursor) + self.bind("", self.cursor) + self.bind("", self.cursor) + self.bind("", self.cursor) + self.bind("", self.cursor) + + self.bind("", lambda _: self.apply()) + self.bind("", lambda _: self.apply()) + self.bind("", self.Draw.invert_screen) + self.bind("", self.Draw.clear_screen) + self.bind("", self.show_grid) + + self.bind("", self.exit_protocol) + self.bind("", self.open) + self.bind("", self.save) + self.bind("", self.save_as) + self.bind("", lambda _: self.start_project(False)) + self.bind("", lambda _: self.start_project(True)) + self.bind("", lambda _: self.export("asm")) + self.bind("", lambda _: self.export("c")) + + self.bind("", self.extend_draw) + self.bind("", self.copy) + self.bind("", 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("", partial(self.select_grid, x - 1, y - 1)) + matrix.bind("", partial(self.select_grid, x - 1, y - 1)) + matrix.bind("", 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("", self.showtip) + widget.bind("", 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("", lambda e: self.draw("motion", e)) + self.bind("", 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("", lambda _: self.destroy()) + self.bind("", self.fileopen) + self.bind("", 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() diff --git a/main.spec b/main.spec new file mode 100644 index 0000000..9497724 --- /dev/null +++ b/main.spec @@ -0,0 +1,44 @@ +# -*- mode: python ; coding: utf-8 -*- + + +block_cipher = None + + +a = Analysis( + ['main.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='main', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +)