1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
| import tkinter as tk from tkinter import filedialog, colorchooser, simpledialog import ttkbootstrap as ttk from ttkbootstrap.constants import * from PIL import ImageGrab, Image, ImageTk import threading
class DrawingApp: def __init__(self, master): self.master = master self.canvas = tk.Canvas(master, bg='white') self.canvas.pack(expand=True, fill=tk.BOTH)
self.shape = tk.StringVar(value="Line") self.color = tk.StringVar(value="black") self.width = tk.IntVar(value=1)
self.canvas.bind("<Button-1>", self.start_draw) self.canvas.bind("<B1-Motion>", self.draw) self.canvas.bind("<ButtonRelease-1>", self.stop_draw) self.canvas.bind("<Button-3>", self.select_shape) self.canvas.bind("<B3-Motion>", self.move_shape)
self.start = None self.drawn = None self.current_drawn = None self.history = [] self.selected = None
@staticmethod def thread_it(func, *args): t = threading.Thread(target=func, args=args) t.setDaemon(True) t.start()
def choose_color(self): self.color.set(colorchooser.askcolor()[1])
def start_draw(self, event): self.start = event if self.shape.get() == "Text": self.canvas.unbind("<B1-Motion>") else: self.canvas.bind("<B1-Motion>", self.draw)
def draw(self, event): if self.drawn not in self.history: self.canvas.delete(self.drawn) if self.shape.get() == "Line": self.drawn = self.canvas.create_line(self.start.x, self.start.y, event.x, event.y, fill=self.color.get(), width=self.width.get()) elif self.shape.get() == "Rectangle": self.drawn = self.canvas.create_rectangle(self.start.x, self.start.y, event.x, event.y, outline=self.color.get(), width=self.width.get()) elif self.shape.get() == "Oval": self.drawn = self.canvas.create_oval(self.start.x, self.start.y, event.x, event.y, outline=self.color.get(), width=self.width.get()) elif self.shape.get() == "Arrow": self.drawn = self.canvas.create_line(self.start.x, self.start.y, event.x, event.y, arrow=tk.LAST, fill=self.color.get(), width=self.width.get())
def stop_draw(self, event): if self.shape.get() == "Text": text = simpledialog.askstring("Input", "Enter text:") self.drawn = self.canvas.create_text(self.start.x, self.start.y, text=text, fill=self.color.get(), font=('Arial', 12, 'bold')) self.canvas.bind("<B1-Motion>", self.draw) elif self.shape.get() == "Image": file_path = filedialog.askopenfilename() img = Image.open(file_path) img = img.resize((self.canvas.winfo_width(), self.canvas.winfo_height())) self.image = ImageTk.PhotoImage(img) self.drawn = self.canvas.create_image(0, 0, image=self.image, anchor=tk.NW) self.history.append(self.drawn)
def select_shape(self, event): try: self.selected = self.canvas.find_closest(event.x, event.y)[0] except IndexError: pass
def move_shape(self, event): if self.selected: self.canvas.move(self.selected, event.x - self.canvas.coords(self.selected)[0], event.y - self.canvas.coords(self.selected)[1])
def export_to_png(self): x = self.master.winfo_rootx() + self.canvas.winfo_x() y = self.master.winfo_rooty() + self.canvas.winfo_y() x1 = x + self.canvas.winfo_width() y1 = y + self.canvas.winfo_height() ImageGrab.grab().crop((x, y, x1, y1)).save("output.png")
def undo(self): if self.history: item = self.history.pop() self.canvas.delete(item)
if __name__ == '__main__': root = ttk.Window(themename="sandstone") root.title('my window') root.geometry('450x600') app = DrawingApp(root)
menubar = tk.Menu(root) root.config(menu=menubar)
filemenu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="File", menu=filemenu) filemenu.add_command(label="Save as PNG", command=app.export_to_png)
editmenu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Edit", menu=editmenu) editmenu.add_command(label="Undo", command=app.undo)
colormenu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Color", menu=colormenu) colormenu.add_command(label="Choose Color", command=app.choose_color)
widthmenu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Width", menu=widthmenu) for i in range(1, 11): widthmenu.add_radiobutton(label=str(i), variable=app.width, value=i)
shapemenu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Shape", menu=shapemenu) for shape in ["Line", "Rectangle", "Oval", "Arrow", "Text", "Image"]: shapemenu.add_radiobutton(label=shape, variable=app.shape, value=shape)
root.mainloop()
|