Skip to content

Instantly share code, notes, and snippets.

@nikhilkumarsingh
Created November 3, 2017 16:26
Show Gist options
  • Star 72 You must be signed in to star a gist
  • Fork 28 You must be signed in to fork a gist
  • Save nikhilkumarsingh/85501ee2c3d8c0cfa9d1a27be5781f06 to your computer and use it in GitHub Desktop.
Save nikhilkumarsingh/85501ee2c3d8c0cfa9d1a27be5781f06 to your computer and use it in GitHub Desktop.
A simple paint application using tkinter in Python 3
from tkinter import *
from tkinter.colorchooser import askcolor
class Paint(object):
DEFAULT_PEN_SIZE = 5.0
DEFAULT_COLOR = 'black'
def __init__(self):
self.root = Tk()
self.pen_button = Button(self.root, text='pen', command=self.use_pen)
self.pen_button.grid(row=0, column=0)
self.brush_button = Button(self.root, text='brush', command=self.use_brush)
self.brush_button.grid(row=0, column=1)
self.color_button = Button(self.root, text='color', command=self.choose_color)
self.color_button.grid(row=0, column=2)
self.eraser_button = Button(self.root, text='eraser', command=self.use_eraser)
self.eraser_button.grid(row=0, column=3)
self.choose_size_button = Scale(self.root, from_=1, to=10, orient=HORIZONTAL)
self.choose_size_button.grid(row=0, column=4)
self.c = Canvas(self.root, bg='white', width=600, height=600)
self.c.grid(row=1, columnspan=5)
self.setup()
self.root.mainloop()
def setup(self):
self.old_x = None
self.old_y = None
self.line_width = self.choose_size_button.get()
self.color = self.DEFAULT_COLOR
self.eraser_on = False
self.active_button = self.pen_button
self.c.bind('<B1-Motion>', self.paint)
self.c.bind('<ButtonRelease-1>', self.reset)
def use_pen(self):
self.activate_button(self.pen_button)
def use_brush(self):
self.activate_button(self.brush_button)
def choose_color(self):
self.eraser_on = False
self.color = askcolor(color=self.color)[1]
def use_eraser(self):
self.activate_button(self.eraser_button, eraser_mode=True)
def activate_button(self, some_button, eraser_mode=False):
self.active_button.config(relief=RAISED)
some_button.config(relief=SUNKEN)
self.active_button = some_button
self.eraser_on = eraser_mode
def paint(self, event):
self.line_width = self.choose_size_button.get()
paint_color = 'white' if self.eraser_on else self.color
if self.old_x and self.old_y:
self.c.create_line(self.old_x, self.old_y, event.x, event.y,
width=self.line_width, fill=paint_color,
capstyle=ROUND, smooth=TRUE, splinesteps=36)
self.old_x = event.x
self.old_y = event.y
def reset(self, event):
self.old_x, self.old_y = None, None
if __name__ == '__main__':
Paint()
@andreys42
Copy link

andreys42 commented Mar 6, 2019

how to implement eraser tool in this app? I mean not just painting white...

@SubhoBasak
Copy link

Line no 52, in choose_color function

self.color = askcolor(color=self.color)[1]

it is better to write it in this way,

color = askcolor(color = self.color)[1]
if color != None:
    self.color = color
else:
    return None

@RVSNS
Copy link

RVSNS commented Nov 19, 2019

code to completely clear the canvas ?
thank you in advance
and also is there any way to save my "Masterpiece"

@SubhoBasak
Copy link

Tkinter canvas keep track of every elements is added to it and return an id of each element. You can store the id, to specify which element to delete.
id1= canvas.create_line(0, 0, 100, 100)
And then to delete the element use the following code,
canvas.delete(id1)

And, to delete all the elements just use 'all' as the parameter to delete method,
canvas.delete('all')

@niralivasoya
Copy link

niralivasoya commented Mar 10, 2020

Hey @nikhilkumarsingh, how can i save masterpiece so that i can directly use it in another application? is there any way to directly connect this paint with another python file?

@LosGnidoS
Copy link

Super!

@dubeyji10
Copy link

Hey @nikhilkumarsingh, how can i save masterpiece so that i can directly use it in another application? is there any way to directly connect this paint with another python file?

if you plan on saving it as an image grab the rectangle ( could use win32gui.GetWindowRect) and then save it using 'filedialog.asksaveasfilename'

@hq9000
Copy link

hq9000 commented Nov 16, 2020

if anyone, like me, is wondering if we can make it anti aliased somehow, here is an interesting approach:

vlachoudis/bCNC#986

@PVkolos
Copy link

PVkolos commented Apr 24, 2021

код, чтобы полностью очистить холст?
заранее спасибо,
а также есть ли способ спасти мой "Шедевр"
Found the best repository, everything you need is there, here it is: https://github.com/PVkolos/Paint_Python
You can save the drawing and paint over with white, in general, everything you need

@PVkolos
Copy link

PVkolos commented Apr 24, 2021

Привет, @nikhilkumarsingh , как я могу сохранить шедевр, чтобы напрямую использовать его в другом приложении? есть ли способ напрямую связать эту краску с другим файлом python?
Found the best repository, everything you need is there, here it is: https://github.com/PVkolos/Paint_Python
You can save the drawing and paint over with white, in general, everything you need

@Kaabasane
Copy link

Thank you @nikhilkumarsingh. I have updated the code a bit. May I upload that code on my git, crediting you for base code?

@Kaabasane
Copy link

code to completely clear the canvas ?
thank you in advance
and also is there any way to save my "Masterpiece"

&

how to implement eraser tool in this app? I mean not just painting white...

@andreys42 & @RVSNS try this fork by me - https://gist.github.com/Kaabasane/a4f1fb10d27611bdd6b4ccf6b9206a9b

@tlantzakis
Copy link

Thank you.

@taoclav
Copy link

taoclav commented Feb 2, 2022

can you do a save option? i have a similar program but i don't know how to make a file save option

@Kaabasane
Copy link

@alidnrc200
Copy link

hello
how can i paint in turtle in python with(alt)kebind?

@meenuturan30
Copy link

meenuturan30 commented Apr 19, 2023

Can someone review my code . I am not getting final expected output - import tkinter as tk
import zipfile
import os

create a directory to store the extracted images

if not os.path.exists('images'):
os.mkdir('images')

extract the contents of the images.zip file to the images directory

with zipfile.ZipFile('images.zip', 'r') as zip_ref:
zip_ref.extractall('images')

#Step 1: Import necessary libraries
from tkinter import *
from tkinter import ttk
from PIL import Image, ImageTk

#Step 2: Create a class for the Pizza Builder app and extend it from the tk.Frame class
class PizzaBuilderApp(Frame):
    def __init__(self, parent):
        super().__init__(parent)

#Step 3: Create the overall layout of the app using grid geometry manager
        self.grid(column=0, row=0, sticky=(N, W, E, S))
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

#Step 4: Create a separator widget to separate each section of the app
        ttk.Separator(self, orient="horizontal").grid(row=0, column=0, sticky="ew")


#Step 5: Create radio buttons for pizza size options
        size_label = Label(self, text="Pizza Size")
        size_label.grid(row=1, column=0, sticky=W, padx=5)
        self.size_var = StringVar(value="Small")
        size_options = ["Small", "Medium", "Large"]
        for i, size in enumerate(size_options):
            Radiobutton(self, text=size, variable=self.size_var, value=size, command=self.update_pizza_image).grid(row=2, column=i, padx=5)

#Step 6: Create radio buttons for crust options
        crust_label = Label(self, text="Crust")
        crust_label.grid(row=3, column=0, sticky=W, padx=5)
        self.crust_var = StringVar(value="New York Thin Crust")
        crust_options = ["New York Thin Crust", "Chicago Deep Dish"]
        for i, crust in enumerate(crust_options):
            Radiobutton(self, text=crust, variable=self.crust_var, value=crust, command=self.update_pizza_image).grid(row=4, column=i, padx=5)


#Step 7: Create an OptionMenu for sauce options
        sauce_label = Label(self, text="Sauce")
        sauce_label.grid(row=5, column=0, sticky=W, padx=5)
        self.sauce_var = StringVar(value="Tomato")
        sauce_options = ["Tomato", "Pesto", "Barbecue", "Alfredo"]
        sauce_menu = OptionMenu(self, self.sauce_var, *sauce_options, command=self.update_pizza_image)
        sauce_menu.grid(row=6, column=0, padx=5, pady=5)

#Step 8: Create a ListBox for cheese options
        cheese_label = Label(self, text="Cheese")
        cheese_label.grid(row=7, column=0, sticky=W, padx=5)
        self.cheese_var = StringVar(value="Normal")
        cheese_options = ["None", "Normal", "Extra"]
        cheese_listbox = Listbox(self, listvariable=self.cheese_var, selectmode=SINGLE, height=len(cheese_options))
        for i, cheese in enumerate(cheese_options):
            cheese_listbox.insert(i, cheese)
        cheese_listbox.grid(row=8, column=0, padx=5, pady=5)
        cheese_scrollbar = Scrollbar(self, orient="vertical", command=cheese_listbox.yview)
        cheese_scrollbar.grid(row=8, column=1, sticky=N+S)
        cheese_listbox.configure(yscrollcommand=cheese_scrollbar.set)

#Step 9: Implement the "Build Pizza" Button

class PizzaBuilder(Frame):
def init(self, parent):
super().init(parent)

    # create "Build Pizza" button
    self.build_button = Button(self, text="Build Pizza", command=self.build_pizza_handler)
    self.build_button.grid(row=9, column=0, padx=5, pady=5)

#build_button = Button(self, text="Build Pizza", command=self.build_pizza_handler)
#build_button.grid(row=9, column=0, padx=5, pady=5)
def build_pizza_handler(self):
# Collect user's selected options
pizza_size = self.pizza_size.get()
crust = self.crust.get()
sauce = self.sauce.get()
cheeses = self.cheeses.curselection()
toppings = [topping.get() for topping in self.toppings]
seasoning = self.seasoning_var.get()

# Create a list of images to stack on top of each other
images = []
images.append(Image.open(f'images/crust_{crust.lower().replace(" ", "_")}.png'))
images.append(Image.open(f'images/sauce_{sauce.lower().replace(" ", "_")}.png'))
if "0" not in cheeses:
    images.append(Image.open(f'images/cheese_normal.png'))
if "1" in cheeses:
    images.append(Image.open(f'images/cheese_extra.png'))
for i, topping_selected in enumerate(toppings):
    if topping_selected:
        images.append(Image.open(f'images/topping_{i+1}.png'))
if seasoning:
    images.append(Image.open('images/seasoning.png'))

# Resize images to 600x600
images_resized = []
for image in images:
    image_resized = image.resize((600, 600))
    images_resized.append(image_resized)

# Create a composite image
pizza_image = Image.new('RGBA', (600, 600), (255, 255, 255, 0))
for image in images_resized:
    pizza_image = Image.alpha_composite(pizza_image, image)

# Display the composite image on the GUI
pizza_image_tk = ImageTk.PhotoImage(pizza_image)
self.pizza_display_label.config(image=pizza_image_tk)
self.pizza_display_label.image = pizza_image_tk



    # Create a new blank image to paste the pizza layers onto
    pizza_image = Image.new('RGBA', (600, 600), (255, 255, 255, 255))

    # Paste each layer onto the pizza image
    for img in images:
        pizza_image.paste(img, (0, 0), img)

    # Display the final pizza image
    pizza_image.show()

############################################################################################
def update_pizza_image(self):
# Collect user's selected options
pizza_size = self.size_var.get()
crust = self.crust_var.get()
sauce = self.sauce_var.get()

# Load the crust and sauce images
crust_image = Image.open(f'images/crust_{crust.lower().replace(" ", "_")}.png')
sauce_image = Image.open(f'images/sauce_{sauce.lower().replace(" ", "_")}.png')

# Resize the crust and sauce images based on the pizza size
if pizza_size == 'Small':
    crust_image = crust_image.resize((200, 200))
    sauce_image = sauce_image.resize((200, 200))
elif pizza_size == 'Medium':
    crust_image = crust_image.resize((300, 300))
    sauce_image = sauce_image.resize((300, 300))
else:
    crust_image = crust_image.resize((400, 400))
    sauce_image = sauce_image.resize((400, 400))

# Combine the crust and sauce images into one
pizza_image = Image.alpha_composite(crust_image, sauce_image)

# Display the pizza image
self.pizza_image_tk = ImageTk.PhotoImage(pizza_image)
self.pizza_image_label.config(image=self.pizza_image_tk)


def build_pizza_handler(self):
    # Collect user's selected options
    pizza_size = self.size_var.get()
    crust = self.crust_var.get()
    sauce = self.sauce_var.get()
    cheeses = self.cheese_var.get()
    toppings = [topping_var.get() for topping_var in self.topping_vars]
    seasoning = self.seasoning_var.get()

    # Create a list of images to stack on top of each other
    images = []
    crust_image = Image.open(f'images/crust_{crust.lower().replace(" ", "_")}.png')
    images.append(crust_image)
    sauce_image = Image.open(f'images/sauce_{sauce.lower().replace(" ", "_")}.png')
    images.append(sauce_image)
    if cheeses == "Normal":
        cheese_image = Image.open(f'images/cheese_normal.png')
        images.append(cheese_image)
    elif cheeses == "Extra":
        cheese_image = Image.open(f'images/cheese_extra.png')
        images.append(cheese_image)
    for i, topping_selected in enumerate(toppings):
        if topping_selected:
            topping_image = Image.open(f'images/topping_{i+1}.png')
            images.append(topping_image)
    if seasoning:
        seasoning_image = Image.open('images/seasoning.png')
        images.append(seasoning_image)

############################################################################################

#Step 10: Initialize the GUI
class PizzaBuilder(tk.Frame):
def init(self, parent):
super().init(parent)
if name == 'main':
root = tk.Tk()
root.title("Procedural Object-Oriented Pizza Company")
root.geometry("800x600")

pizza_builder = PizzaBuilder(root)
pizza_builder.pack(expand=True, fill='both')

root.mainloop()

################################################################################################

import os
import uuid
import base64
from PIL import Image
from io import BytesIO
from typing import Dict, List
from fastapi import FastAPI, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel

app = FastAPI()

Allow Cross-Origin Resource Sharing (CORS)

app.add_middleware(
CORSMiddleware,
allow_origins=[""],
allow_credentials=True,
allow_methods=["
"],
allow_headers=["*"],
)

class Pizza(BaseModel):
crust: str
cheese: List[str]
sauce: str
toppings: List[str]

def generate_pizza_image(pizza: Pizza) -> str:
# Load the pizza image
pizza_image_path = os.path.join("images", "pizza.jpg")
pizza_image = Image.open(pizza_image_path)

# Resize the pizza image
pizza_image = pizza_image.resize((500, 500))

# Add the selected toppings to the pizza image
toppings_path = os.path.join("images", "toppings")
for topping in pizza.toppings:
    topping_path = os.path.join(toppings_path, f"{topping}.png")
    topping_image = Image.open(topping_path)
    pizza_image.paste(topping_image, (0, 0), topping_image)

# Convert the pizza image to base64 string
buffered = BytesIO()
pizza_image.save(buffered, format="PNG")
return base64.b64encode(buffered.getvalue()).decode()

@app.post("/build_pizza")
async def build_pizza_handler(pizza: Pizza):
try:
# Generate the pizza image
pizza_image = generate_pizza_image(pizza)

    # Return the pizza image as a base64 string
    return {"pizza_image": pizza_image}
except Exception as e:
    raise HTTPException(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        detail=str(e)
    )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment