Skip to content
This repository was archived by the owner on May 18, 2025. It is now read-only.

Commit c116ec1

Browse files
authored
Add files via upload
1 parent ab50d36 commit c116ec1

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed

paint.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import tkinter as tk
2+
import tkinter.colorchooser as colorchooser
3+
import tkinter.filedialog as filedialog
4+
from PIL import Image, ImageTk, ImageDraw
5+
import json
6+
7+
def hex_to_rgb(hex_color):
8+
"""Converts a hex color string to an RGB tuple.
9+
10+
Args:
11+
hex_color: The color string in hex format (e.g., "#FF0000") or a valid color name.
12+
13+
Returns:
14+
An RGB tuple (e.g., (255, 0, 0)) or None if the color is invalid.
15+
"""
16+
try:
17+
hex_color = hex_color.strip('#')
18+
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
19+
except ValueError:
20+
# Handle non-hex color strings gracefully (e.g., return default color)
21+
return (0, 0, 0) # Default black
22+
23+
class PaintApp:
24+
def __init__(self, master):
25+
self.master = master
26+
self.master.title("BirdBrush (BBF version 1)")
27+
self.master.geometry("700x700")
28+
29+
self.canvas = tk.Canvas(master, bg="white")
30+
self.canvas.pack(fill=tk.BOTH, expand=True)
31+
32+
self.color_choice = "black"
33+
self.line_width = 2
34+
self.eraser_on = False
35+
self.fill_on = False
36+
37+
self.setup_colors()
38+
self.setup_options()
39+
40+
self.draw_x, self.draw_y = 0, 0
41+
self.drawing = False
42+
43+
self.canvas.bind("<Button-1>", self.start_draw)
44+
self.canvas.bind("<B1-Motion>", self.draw)
45+
self.canvas.bind("<ButtonRelease-1>", self.stop_draw)
46+
47+
def setup_colors(self):
48+
self.colors = ["black", "red", "green", "blue", "yellow"]
49+
color_frame = tk.Frame(self.master)
50+
color_frame.pack(side=tk.LEFT, fill=tk.Y)
51+
52+
for color in self.colors:
53+
button = tk.Button(color_frame, bg=color, width=2, command=lambda c=color: self.set_color(c))
54+
button.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
55+
56+
def setup_options(self):
57+
option_frame = tk.Frame(self.master)
58+
option_frame.pack(side=tk.RIGHT)
59+
60+
self.line_width_label = tk.Label(option_frame, text="Line Width:")
61+
self.line_width_label.pack()
62+
63+
self.line_width_scale = tk.Scale(option_frame, from_=1, to=50, orient=tk.HORIZONTAL, command=self.set_line_width)
64+
self.line_width_scale.set(self.line_width)
65+
self.line_width_scale.pack()
66+
67+
eraser_button = tk.Button(option_frame, text="Eraser", command=self.toggle_eraser)
68+
eraser_button.pack()
69+
70+
color_picker_button = tk.Button(option_frame, text="Color Picker", command=self.pick_color)
71+
color_picker_button.pack(side=tk.LEFT, expand=True, fill='both')
72+
73+
save_button = tk.Button(option_frame, text="Save", command=self.save_image)
74+
save_button.pack(side=tk.LEFT, expand=True, fill='both')
75+
76+
load_button = tk.Button(option_frame, text="Load", command=self.load_image)
77+
load_button.pack(side=tk.LEFT, expand=True, fill='both')
78+
79+
def set_color(self, color):
80+
self.color_choice = color
81+
self.eraser_on = False
82+
self.fill_on = False
83+
84+
def set_line_width(self, value):
85+
self.line_width = int(value)
86+
87+
def toggle_eraser(self):
88+
self.eraser_on = not self.eraser_on
89+
self.fill_on = False
90+
91+
def toggle_fill(self):
92+
self.fill_on = not self.fill_on
93+
self.eraser_on = False
94+
95+
def start_draw(self, event):
96+
self.draw_x, self.draw_y = event.x, event.y
97+
self.drawing = True
98+
99+
def draw(self, event):
100+
if self.drawing:
101+
x, y = event.x, event.y
102+
if self.eraser_on:
103+
color = "white"
104+
else:
105+
color = self.color_choice
106+
self.bresenham_line(self.draw_x, self.draw_y, x, y, color)
107+
self.draw_x, self.draw_y = x, y
108+
109+
def stop_draw(self, event):
110+
self.drawing = False
111+
112+
def bresenham_line(self, x1, y1, x2, y2, color):
113+
dx = abs(x2 - x1)
114+
dy = abs(y2 - y1)
115+
sx = 1 if x1 < x2 else -1
116+
sy = 1 if y1 < y2 else -1
117+
err = dx - dy
118+
119+
while True:
120+
self.canvas.create_line(x1, y1, x1 + 1, y1 + 1, fill=color, width=self.line_width)
121+
if x1 == x2 and y1 == y2:
122+
break
123+
e2 = 2 * err
124+
if e2 > -dy:
125+
err -= dy
126+
x1 += sx
127+
if e2 < dx:
128+
err += dx
129+
y1 += sy
130+
131+
def save_image(self):
132+
file_path = filedialog.asksaveasfilename(filetypes=[("BirdBrush file", "*.bbf"), ("All files", "*.*")], defaultextension=".bbf")
133+
if file_path:
134+
lines = []
135+
for item in self.canvas.find_all():
136+
item_type = self.canvas.type(item)
137+
if item_type == "line":
138+
coords = self.canvas.coords(item)
139+
fill = self.canvas.itemcget(item, "fill")
140+
width = self.canvas.itemcget(item, "width")
141+
lines.append({"type": "line", "coords": coords, "fill": fill, "width": width})
142+
143+
with open(file_path, 'w') as f:
144+
json.dump(lines, f)
145+
146+
def load_image(self):
147+
file_path = filedialog.askopenfilename(filetypes=[("BirdBrush file", "*.bbf"), ("All files", "*.*")])
148+
if file_path:
149+
with open(file_path, 'r') as f:
150+
lines = json.load(f)
151+
152+
self.canvas.delete("all")
153+
for line in lines:
154+
if line["type"] == "line":
155+
self.canvas.create_line(line["coords"], fill=line["fill"], width=line["width"])
156+
157+
def pick_color(self):
158+
color = colorchooser.askcolor()[1]
159+
if color:
160+
self.color_choice = color
161+
162+
root = tk.Tk()
163+
app = PaintApp(root)
164+
root.mainloop()

0 commit comments

Comments
 (0)