Requirements Make sure you have the following before we start:
- Python is installed on your computer.
- A code editor or integrated development environment (IDE) like Visual Studio Code, PyCharm, or IDLE.
- The Tkinter library is usually included with Python installations.
- Install Pillow:
pip install Pillow
Setting up the Project Let’s create a new directory and set up the files:
- Create a new directory named “DrawingApp”.
- Inside the “DrawingApp” directory, create a new Python script file named “drawing_app.py”.
Importing Required Libraries Our drawing app relies on Tkinter for the GUI. In “drawing_app.py”, import the necessary libraries:
import time
from tkinter import *
import tkinter as tk
from tkinter import filedialog
import PIL.ImageGrab as ImageGrab
from tkinter import colorchooser, messagebox
In the above code, we import the tkinter module and specifically import the colorchooser and filedialog submodules for color selection and file dialog functionality. We also import the ImageGrab module from the Pillow library for grabbing screenshots of the selected portion, which in our case is the canvas area.
Creating the Drawing Application
Next, let’s create the main application window. Add the following code snippet to the “drawing_app.py” file:
window = Tk()
window.title("Drawing App")
def select_brush():
pass
def change_brush_size(value):
pass
def select_brush_size():
pass
def select_eraser():
pass
def select_eraser_size():
pass
def select_brush_color():
pass
def select_background_color():
pass
def clear_canvas():
pass
def save_image():
pass
# Create the menu bar
menu_bar = Menu(window)
# Create the brush menu
brush_menu = Menu(menu_bar, tearoff=0)
brush_menu.add_command(label="Select Brush", command=select_brush)
brush_menu.add_command(label="Select Brush Size", command=select_brush_size)
# Create the eraser menu
eraser_menu = Menu(menu_bar, tearoff=0)
eraser_menu.add_command(label="Select Eraser", command=select_eraser)
eraser_menu.add_command(label="Select Eraser Size", command=select_eraser_size)
# Create the color menu
color_menu = Menu(menu_bar, tearoff=0)
color_menu.add_command(label="Select Drawing Color", command=select_brush_color)
color_menu.add_command(label="Select Background Color", command=select_background_color)
# Create the clear menu
clear_menu = Menu(menu_bar, tearoff=0)
clear_menu.add_command(label="Clear Canvas", command=clear_canvas)
# Create the save menu
save_menu = Menu(menu_bar, tearoff=0)
save_menu.add_command(label="Save Drawing", command=save_image)
# Add the menus to the menu bar
menu_bar.add_cascade(label="Brush", menu=brush_menu)
menu_bar.add_cascade(label="Eraser", menu=eraser_menu)
menu_bar.add_cascade(label="Color", menu=color_menu)
menu_bar.add_cascade(label="Clear", menu=clear_menu)
menu_bar.add_cascade(label="Save", menu=save_menu)
# Configure the menu bar
window.config(menu=menu_bar)
canvas = Canvas(window, width=800, height=480, bg="white")
canvas.pack()
window.mainloop()
Explanation of above code:
In the provided code, we start by creating a new Tkinter window using tk.Tk() and set its title to “Drawing App” with window.title().
Next, we define several empty functions (select_brush(), select_brush_size(), select_eraser(), select_eraser_size(), select_brush_color(), select_background_color(), clear_canvas(), save_image(), change_brush_size(), etc.) that will handle the functionality of each menu option. These functions will be implemented later to add the desired features.
Then, we create a menu bar using tk.Menu() and define separate menus for brush size, color, polygon, and save options using tk.Menu() as well. Each menu is filled with options using menu.add_command(). The command parameter specifies the function to execute when the option is selected.
Finally, we add the menus to the menu bar using menu_bar.add_cascade() and configure the window to display the menu bar using window.config(menu=menu_bar).
The application enters the main event loop with window.mainloop() to manage user interactions and keep the application running.
Menu Option Setup:
Now that we have set up the menu bar, let’s implement the functionality for each menu option. Modify the previously empty functions as follows:
Select Brush To allow users to select the brush, we’ve introduced a feature called “Select Brush” in the “Brush” menu. Additionally, we have implemented an empty function to handle this functionality, in the previous section.
By default, the brush will remain selected, but it will be necessary to choose the brush again after performing an erasing operation. To incorporate this functionality, please include the following code within the select_brush() function.
def select_brush():
global selected_color
selected_color = previous_color
Code to Select Brush Size:
def change_brush_size(value):
global brush_size
brush_size = int(value)
def select_brush_size():
new_window = Toplevel(window)
new_window.title("Select Brush Size")
new_window.geometry("400x100")
brush_size_label = Label(new_window, text="Brush Size")
brush_size_label.pack()
brush_size_slider = Scale(new_window, from_=1, to=25,
orient=HORIZONTAL, command=change_brush_size)
brush_size_slider.set(brush_size)
brush_size_slider.pack()
Explanation of the above code:
def change_brush_size(value):: This defines a functionchange_brush_sizethat takes avalueas an argument. This function is used to update thebrush_sizevariable based on the value selected by the user on the brush size slider.global brush_size: This line declares that thebrush_sizevariable being used inside the function is a global variable. This allows the function to modify the value ofbrush_sizefrom outside its scope.brush_size = int(value): This line sets thebrush_sizevariable to the integer value of thevalueargument. Thevalueargument is the value selected by the user on the brush size slider.def select_brush_size():: This defines a functionselect_brush_sizewith no arguments. This function is used to create a new window (Toplevel) where the user can select the brush size using a slider.new_window = Toplevel(window): This creates a new window (Toplevel) that is a child of the main window (window).new_window.title("Select Brush Size"): This sets the title of the new window to “Select Brush Size”.new_window.geometry("400x100"): This sets the size of the new window to 400 pixels wide and 100 pixels high.brush_size_label = Label(new_window, text="Brush Size"): This creates a label (Label) widget in the new window with the text “Brush Size”.brush_size_label.pack(): This displays the brush size label in the new window.brush_size_slider = Scale(new_window, from_=1, to=25, orient=HORIZONTAL, command=change_brush_size): This creates a slider (Scale) widget in the new window with values ranging from 1 to 25. Thecommandparameter specifies the function (change_brush_size) to call when the slider value changes.brush_size_slider.set(brush_size): This sets the initial value of the brush size slider to the current value of thebrush_sizevariable.brush_size_slider.pack(): This displays the brush size slider in the new window.
Now, let’s write code to select Eraser and eraser size
To implement the functionality of the “Select Eraser” in the “Eraser” menu, insert the following code within the `select_eraser()` function.
def select_eraser():
global selected_color, previous_color
previous_color = selected_color
selected_color = background_color
def select_eraser_size():
new_window = Toplevel(window)
new_window.title("Select Eraser Size")
new_window.geometry("400x100")
eraser_size_label = Label(new_window, text="Eraser Size")
eraser_size_label.pack()
eraser_size_slider = Scale(new_window, from_=1,
to=20, orient=HORIZONTAL, command=change_brush_size)
eraser_size_slider.set(brush_size)
eraser_size_slider.pack()
Adding Multiple Color Options
To enable users to choose different colors for drawing, include the following code within the select_brush_color() function. This enhancement lets users select from a variety of colors using the “Select Drawing Color” option in the “Color” menu
def select_brush_color():
global selected_color, previous_color
color = colorchooser.askcolor(title="Select Color")[1]
if color:
selected_color = color
previous_color = color
Code to change background color:
def select_background_color():
global background_color, canvas
color = colorchooser.askcolor(title="Select Color")[1]
if color:
background_color = color
canvas.config(bg=background_color)
Clear the Canvas
There is an option for “Clear Canvas” in the “Clear” menu. To enhance this feature add the following code within `clear_canvas()` function.
def clear_canvas():
canvas.delete("all")
Save the image you have drawn:
def save_image():
file_path = filedialog.asksaveasfilename(defaultextension=".png")
time.sleep(0.1)
x1 = window.winfo_rootx() + canvas.winfo_x()
y1 = window.winfo_rooty() + canvas.winfo_x()
x2 = x1 + canvas.winfo_width()
y2 = y1 + canvas.winfo_height()
if file_path:
canvas.postscript(file=file_path, colormode='color')
# Format: crop(left, top, right, bottom)
ImageGrab.grab().crop((x1, y1, x2, y2)).save(file_path)
Explanation of the above code:
file_path = filedialog.asksaveasfilename(defaultextension=".png"): Opens a dialog box for the user to choose a file path to save the image with a default extension of.pngand assigns the chosen path to thefile_pathvariable.time.sleep(0.1): Pauses the execution for 0.1 seconds. This is used to ensure that the window positions are correctly captured.x1 = window.winfo_rootx() + canvas.winfo_x(): Gets the x-coordinate of the top-left corner of the canvas relative to the screen by adding the window’s root x-coordinate to the canvas’s x-coordinate within the window.y1 = window.winfo_rooty() + canvas.winfo_x(): Gets the y-coordinate of the top-left corner of the canvas relative to the screen by adding the window’s root y-coordinate to the canvas’s y-coordinate within the window.x2 = x1 + canvas.winfo_width(): Calculates the x-coordinate of the bottom-right corner of the canvas by adding the canvas’s width to the x1 coordinate.y2 = y1 + canvas.winfo_height(): Calculates the y-coordinate of the bottom-right corner of the canvas by adding the canvas’s height to the y1 coordinate.if file_path:: Checks if a file path was chosen by the user.canvas.postscript(file=file_path, colormode='color'): Saves the canvas content as a PostScript file at the specifiedfile_pathwith the specifiedcolormode.ImageGrab.grab().crop((x1, y1, x2, y2)).save(file_path): Captures a screenshot of the specified area defined by(x1, y1, x2, y2)usingImageGrab, crops the image to that area, and then saves it at thefile_path.
This function essentially saves the canvas content as an image file at the specified file path, including only the area covered by the canvas in the screenshot.
Implementing Drawing Functionality
# Default Values
is_drawing = False
last_x = 0
last_y = 0
brush_size = 2
selected_color = "black"
previous_color = "black"
background_color = "white"
def start_drawing(event):
global is_drawing, last_x, last_y
is_drawing = True
last_x = event.x
last_y = event.y
def draw(event):
global is_drawing, last_x, last_y
if is_drawing:
x, y = event.x, event.y
canvas.create_line(last_x, last_y, x, y,
width=brush_size, fill=selected_color)
last_x = x
last_y = y
def stop_drawing(event):
global is_drawing
is_drawing = False
def erase_drawing():
global selected_color
selected_color = "white"
Explanation of the above code:
is_drawing = False: Initializes a variableis_drawingtoFalseto indicate that the user is not currently drawing.last_x = 0: Initializes a variablelast_xto0to store the last x-coordinate of the mouse cursor.last_y = 0: Initializes a variablelast_yto0to store the last y-coordinate of the mouse cursor.brush_size = 2: Initializes a variablebrush_sizeto2as the default brush size.selected_color = "black": Initializes a variableselected_colorto"black"as the default drawing color.previous_color = "black": Initializes a variableprevious_colorto"black"to store the previous drawing color.background_color = "white": Initializes a variablebackground_colorto"white"as the default background color.def start_drawing(event):: Defines a functionstart_drawingthat takes an event as input when the user starts drawing.global is_drawing, last_x, last_y: Declares that the variablesis_drawing,last_x, andlast_yinside the function refer to the global variables with the same names.is_drawing = True: Setsis_drawingtoTrueto indicate that the user has started drawing.last_x = event.x: Stores the current x-coordinate of the mouse cursor inlast_x.last_y = event.y: Stores the current y-coordinate of the mouse cursor inlast_y.def draw(event):: Defines a functiondrawthat takes an event as input to draw lines when the user moves the mouse while drawing.if is_drawing:: Checks ifis_drawingisTrueto determine if the user is currently drawing.x, y = event.x, event.y: Gets the current x and y coordinates of the mouse cursor.canvas.create_line(last_x, last_y, x, y, width=brush_size, fill=selected_color): Draws a line on the canvas from the last coordinates (last_x,last_y) to the current coordinates (x,y) with the specifiedbrush_sizeandselected_color.last_x = x: Updateslast_xto the current x-coordinate.last_y = y: Updateslast_yto the current y-coordinate.def stop_drawing(event):: Defines a functionstop_drawingthat takes an event as input when the user stops drawing.global is_drawing: Declares that the variableis_drawinginside the function refers to the global variable with the same name.is_drawing = False: Setsis_drawingtoFalseto indicate that the user has stopped drawing.def erase_drawing():: Defines a functionerase_drawingto change the drawing color to white for erasing.global selected_color: Declares that the variableselected_colorinside the function refers to the global variable with the same name.selected_color = "white": Setsselected_colorto"white"to change the drawing color to white for erasing.
Event Handlers:
canvas.bind('<Button-1>', start_drawing)
canvas.bind('<B1-Motion>', draw)
canvas.bind('<ButtonRelease-1>', stop_drawing)
Whole Code:
import time
from tkinter import *
import tkinter as tk
from tkinter import filedialog, colorchooser, messagebox
import PIL.ImageGrab as ImageGrab
window = Tk()
window.title("Drawing App")
# Default Values
is_drawing = False
last_x = 0
last_y = 0
brush_size = 2
selected_color = "black"
previous_color = "black"
background_color = "white"
def select_brush():
global selected_color
selected_color = previous_color
def change_brush_size(value):
global brush_size
brush_size = int(value)
def select_brush_size():
new_window = Toplevel(window)
new_window.title("Select Brush Size")
new_window.geometry("400x100")
brush_size_label = Label(new_window, text="Brush Size")
brush_size_label.pack()
brush_size_slider = Scale(new_window, from_=1, to=25,
orient=HORIZONTAL, command=change_brush_size)
brush_size_slider.set(brush_size)
brush_size_slider.pack()
def select_eraser():
global selected_color, previous_color
previous_color = selected_color
selected_color = background_color
def select_eraser_size():
new_window = Toplevel(window)
new_window.title("Select Eraser Size")
new_window.geometry("400x100")
eraser_size_label = Label(new_window, text="Eraser Size")
eraser_size_label.pack()
eraser_size_slider = Scale(new_window, from_=1,
to=20, orient=HORIZONTAL, command=change_brush_size)
eraser_size_slider.set(brush_size)
eraser_size_slider.pack()
def select_brush_color():
global selected_color, previous_color
color = colorchooser.askcolor(title="Select Color")[1]
if color:
selected_color = color
previous_color = color
def select_background_color():
global background_color, canvas
color = colorchooser.askcolor(title="Select Color")[1]
if color:
background_color = color
canvas.config(bg=background_color)
def clear_canvas():
canvas.delete("all")
def save_image():
file_path = filedialog.asksaveasfilename(defaultextension=".png")
time.sleep(0.1)
x1 = window.winfo_rootx() + canvas.winfo_x()
y1 = window.winfo_rooty() + canvas.winfo_x()
x2 = x1 + canvas.winfo_width()
y2 = y1 + canvas.winfo_height()
if file_path:
canvas.postscript(file=file_path, colormode='color')
ImageGrab.grab().crop((x1, y1, x2, y2)).save(file_path)
# Create the menu bar
menu_bar = Menu(window)
# Create the brush menu
brush_menu = Menu(menu_bar, tearoff=0)
brush_menu.add_command(label="Select Brush", command=select_brush)
brush_menu.add_command(label="Select Brush Size", command=select_brush_size)
# Create the eraser menu
eraser_menu = Menu(menu_bar, tearoff=0)
eraser_menu.add_command(label="Select Eraser", command=select_eraser)
eraser_menu.add_command(label="Select Eraser Size", command=select_eraser_size)
# Create the color menu
color_menu = Menu(menu_bar, tearoff=0)
color_menu.add_command(label="Select Drawing Color", command=select_brush_color)
color_menu.add_command(label="Select Background Color", command=select_background_color)
# Create the clear menu
clear_menu = Menu(menu_bar, tearoff=0)
clear_menu.add_command(label="Clear Canvas", command=clear_canvas)
# Create the save menu
save_menu = Menu(menu_bar, tearoff=0)
save_menu.add_command(label="Save Drawing", command=save_image)
# Add the menus to the menu bar
menu_bar.add_cascade(label="Brush", menu=brush_menu)
menu_bar.add_cascade(label="Eraser", menu=eraser_menu)
menu_bar.add_cascade(label="Color", menu=color_menu)
menu_bar.add_cascade(label="Clear", menu=clear_menu)
menu_bar.add_cascade(label="Save", menu=save_menu)
# Configure the menu bar
window.config(menu=menu_bar)
def start_drawing(event):
global is_drawing, last_x, last_y
is_drawing = True
last_x = event.x
last_y = event.y
def draw(event):
global is_drawing, last_x, last_y
if is_drawing:
x, y = event.x, event.y
canvas.create_line(last_x, last_y, x, y, width=brush_size, fill=selected_color)
last_x = x
last_y = y
def stop_drawing(event):
global is_drawing
is_drawing = False
canvas = Canvas(window, width=800, height=480, bg="white")
canvas.pack()
# Event handlers
canvas.bind('<Button-1>', start_drawing)
canvas.bind('<B1-Motion>', draw)
canvas.bind('<ButtonRelease-1>', stop_drawing)
window.mainloop()
Output:
Conclusion:
In conclusion, using Tkinter to create a drawing application offers a versatile and beginner-friendly approach to developing graphical user interfaces (GUIs) in Python. Tkinter’s simplicity and robustness make it a suitable choice for projects that require interactive elements such as drawing tools.
Throughout this article, we explored various features and functionalities of Tkinter, including creating menus, handling user inputs, and managing colors and brush sizes. We learned how to implement these features to build a basic drawing application with essential drawing and erasing capabilities.
Tkinter’s ease of use allows developers to quickly prototype and develop applications with graphical interfaces. However, it may lack some advanced features and customization options found in other GUI libraries. For more complex applications requiring advanced graphics or animations, other libraries like Pygame or Kivy might be more suitable.
Overall, Tkinter provides a solid foundation for creating simple drawing applications and is an excellent tool for beginners to get started with GUI development in Python. With its rich set of widgets and straightforward approach, Tkinter enables developers to create functional and visually appealing applications with relative ease.





Leave a Reply