[Python] 미니 포토샵과 그림판 프로그램 만들기

김나연·2022년 7월 8일
0

Python

목록 보기
1/2
post-thumbnail


코드


from tkinter import *
from tkinter.messagebox import *
from tkinter.filedialog import *
from tkinter.simpledialog import *
from tkinter.colorchooser import *
from tkinter import filedialog
from wand.image import *

window, canvas, paper = None, None, None
photo, photo2 = None, None
oriX, oriY, newX, newY = 0, 0, 0, 0
x1, y1, x2, y2 = None, None, None, None
openNum, newNum = 0, 0
penColor = 'black'
penWidth = 3


# 함수 정의부
# 함수 정의 부분
def displayImage(img, width, height) :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    
    # window.geometry(str(width)+"x"+str(height))
    if canvas != None :
        canvas.destroy()

    canvas = Canvas(window, width=width, height=height, highlightthickness=0)
    paper = PhotoImage(width=width, height=height)
    canvas.create_image((width/2, height/2), image=paper, state="normal")
    blob = img.make_blob(format='png')
    paper.put(blob)

    canvas.place(x=(1050-width)/2, y=(940-height)/2)

def mouseClick(event) :
    global x1, y1, x2, y2, penColor, penWidth
    x1, y1 = (event.x), (event.y)

def mouseDrop(event) :
    global x1, y1, x2, y2, penColor, penWidth
    x2, y2 = (event.x), (event.y)
    canvas.create_line(x1, y1, x2, y2, width=penWidth, fill=penColor)

def circleClick(event) :
    global x1, y1, x2, y2, penColor, penWidth
    x1, y1 = (event.x-1), (event.y-1)

def circleDrop(event) :
    global x1, y1, x2, y2, penColor, penWidth
    x2, y2 = (event.x+1), (event.y+1)
    canvas.create_oval(x1, y1, x2, y2, width=penWidth, fill=None, outline=penColor)

def recDrop(event) :
    global x1, y1, x2, y2, penColor, penWidth
    x2, y2 = (event.x+1), (event.y+1)
    canvas.create_rectangle(x1, y1, x2, y2, width=penWidth, fill=None, outline=penColor)

def paint(event) :
    global x1, y1, x2, y2, penColor, penWidth
    x1, y1 = (event.x-1), (event.y-1)
    x2, y2 = (event.x+1), (event.y+1)
    canvas.create_oval(x1, y1, x2, y2, width=penWidth, fill=penColor, outline=penColor)

def getColor() :
    global x1, y1, x2, y2, penColor, penWidth
    color = askcolor()
    penColor = color[1]

def getWidth() :
    global x1, y1, x2, y2, penColor, penWidth
    penWidth = askinteger("펜 두께", "펜 두께(1~10)를 입력하세요.", minvalue=1, maxvalue=10)

def func_pen() :
    global x1, y1, x2, y2, penColor, penWidth
    canvas.bind("<B1-Motion>", paint)

def func_line() :
    global x1, y1, x2, y2, penColor, penWidth
    canvas.bind("<Button-1>", mouseClick)
    canvas.bind("<ButtonRelease-1>", mouseDrop)

def func_circle() :
    global x1, y1, x2, y2, penColor, penWidth
    canvas.bind("<Button-1>", circleClick)
    canvas.bind("<ButtonRelease-1>", circleDrop)

def func_rectangle() :
    global x1, y1, x2, y2, penColor, penWidth
    canvas.bind("<Button-1>", circleClick)
    canvas.bind("<ButtonRelease-1>", recDrop)
    
def func_new() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY, openNum, newNum, fileName, frameDraw, penColor, penWidth
    ask_save()
    
    if canvas != None :
        canvas.destroy()
        
    width = askinteger("가로 사이즈", "캔버스의 <가로> 사이즈(1~800)", minvalue=1, maxvalue=800)
    height = askinteger("세로 사이즈", "캔버스의 <세로> 사이즈(1~750)", minvalue=1, maxvalue=750)
    canvas = Canvas(window, width=width, height=height, bg="white")
    canvas.place(x=(1080-width)/2, y=(920-height)/2)

    openNum += 1
    newNum += 1
    
    if newNum > 1 :
        frameDraw.destroy()
    frameDraw = Frame()
    frameDraw.configure(width=40, height=850, bg='Gray36', bd=1, relief='solid')
    frameDraw.pack(side='left', anchor='n')
    frameDraw.propagate(0)

    buttonPen0 = Button(frameDraw, text="Draw", state='disabled')
    buttonPen0.configure(font=('Arial', 9), disabledforeground='white',
                         bg='Gray26', width=5)
    buttonPen1 = Button(frameDraw, text="Color", command=getColor)
    buttonPen1.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                         bd=1, relief='ridge', overrelief='sunken',
                         activebackground='Gray28', activeforeground='white')
    buttonPen2 = Button(frameDraw, text="Width", command=getWidth)
    buttonPen2.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                         bd=1, relief='ridge', overrelief='sunken',
                         activebackground='Gray28', activeforeground='white')
    buttonPen3 = Button(frameDraw, text="Pen", command=func_pen)
    buttonPen3.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                         bd=1, relief='ridge', overrelief='sunken',
                         activebackground='Gray28', activeforeground='white')

    buttonPen0.pack()
    buttonPen1.pack()
    buttonPen2.pack()
    buttonPen3.pack()
    
    # 그리기 - 도형툴
    buttonShape0 = Button(frameDraw, text="Shape", state='disabled')
    buttonShape0.configure(font=('Arial', 9), disabledforeground='white',
                           bg='Gray26', width=5)
    buttonShape1 = Button(frameDraw, text="Line", command=func_line)
    buttonShape1.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                           bd=1, relief='ridge', overrelief='sunken',
                           activebackground='Gray28', activeforeground='white')
    buttonShape2 = Button(frameDraw, text="Circle", command=func_circle)
    buttonShape2.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                           bd=1, relief='ridge', overrelief='sunken',
                           activebackground='Gray28', activeforeground='white')
    buttonShape3 = Button(frameDraw, text="Rentangle", command=func_rectangle)
    buttonShape3.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                           bd=1, relief='ridge', overrelief='sunken',
                           activebackground='Gray28', activeforeground='white')

    buttonShape0.pack()
    buttonShape1.pack()
    buttonShape2.pack()
    buttonShape3.pack()

    if openNum > 1 :
        fileName.destroy()
    fileName = Label(window, text="/New file.png")
    fileName.configure(font=('Arial', 10), anchor='nw', fg='white',
                       width=1350, height=1, padx=10,
                       bd=1, bg='Gray32', relief='solid')
    fileName.pack()

def func_open() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY, openNum, fileName
    ask_save()
    
    myFile = askopenfilename(parent=window, filetype=(("JPEG", "*.JPG;*.JPEG;*.JPE"), ("PNG", "*.PNG;*.PNG"), ("CompuServe GIF", "*.GIF"), ("모든 파일", "*.*")))

    openNum += 1
    if openNum > 1 :
        fileName.destroy()
    fileName = Label(window, text=myFile)
    fileName.configure(font=('Arial', 10), anchor='nw', fg='white',
                       width=1350, height=1, padx=10,
                       bd=1, bg='Gray32', relief='solid')
    fileName.pack()
    
    photo = Image(filename=myFile)
    oriX = photo.width
    oriY = photo.height

    photo2 = photo.clone()
    newX = photo2.width
    newY = photo2.height
    displayImage(photo, oriX, oriY)

def func_save() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY

    if photo2 == None :
        messagebox.showinfo("오류", "이미지가 없습니다.")
        return

    saveFile = asksaveasfile(parent=window, mode="w", defaultextension=".png", filetypes=(("JPEG", "*.JPG;*.JPEG;*.JPE"), ("PNG", "*.PNG;*.PNG"), ("CompuServe GIF", "*.GIF"), ("모든 파일", "*.*")))
    savePhoto = photo2.convert("png")
    savePhoto.save(filename=saveFile.name)

def exit() :
        window.quit()
        window.destroy()

def func_exit() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    if canvas != None :
        response= messagebox.askyesnocancel(title="안내", message="파일을 저장하시겠습니까?")
        
        if response == 1 : # 예
            func_save()
            exit()
        elif response == 0 : # 아니오
            exit()
        else : # 취소
            return
        
    else : # 캔버스가 비어있다면 프로그램 종료
        exit()

def error_message() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    if photo2 == None :
        messagebox.showinfo("오류", "이미지가 없습니다.")
        func_open()

def ask_save() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    if canvas != None :
        response= messagebox.askyesnocancel(title="안내", message="아직 저장하지 않은 파일이 존재합니다.\n저장하시겠습니까?")
        
        if response == 1 : # 예
            func_save()
            return
        elif response == 0 : # 아니오
            return
        else : # 취소
            return
        
    else :
        return

def func_zoomIn() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    scale = askinteger("확대", "확대할 배수를 입력하세요(2~4)", minvalue=2, maxvalue=4)
    photo2.resize(int(newX*scale), int(newY*scale))
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_zoomOut() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    scale = askinteger("축소", "축소할 배수를 입력하세요(2~4)", minvalue=2, maxvalue=4)
    photo2.resize(int(newX/scale), int(newY/scale))
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_mirrorW() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()
    
    photo2.flip()
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_mirrorH() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()
    
    photo2.flop()
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_rotate() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    degree = askinteger("회전", "회전할 각도를 입력하세요", minvalue=0, maxvalue=360)
    photo2.rotate(degree)
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_bright() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    value = askinteger("밝게", "값을 입력하세요(0~100)", minvalue=0, maxvalue=100)
    # modulate(명도, 채도, 색상) 함수로 value값 만큼 밝기 수정
    photo2.modulate((value+100), 100, 100)
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_dark() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    value = askinteger("어둡게", "값을 입력하세요(0~100)", minvalue=0, maxvalue=100)
    photo2.modulate((100-value), 100, 100)
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_saturationH() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    value = askinteger("채도", "값을 입력하세요(0~100)", minvalue=0, maxvalue=100)
    photo2.modulate(100, (value+100), 100)
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_saturationL() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    value = askinteger("채도", "값을 입력하세요(0~100)", minvalue=0, maxvalue=100)
    photo2.modulate(100, (100-value), 100)
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_bw() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    photo2.type="grayscale"
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_autoZoomIn() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    photo2.resize(int(newX*1.5), int(newY*1.5))
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_autoZoomOut() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    photo2.resize(int(newX/1.5), int(newY/1.5))
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_autoRotate() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()
    
    photo2.rotate(90)
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_resetImage() :
    global window, canvas, paper, photo, photo2, oriX, oriY, newX, newY
    error_message()

    photo2 = photo.clone()
    newX = photo2.width
    newY = photo2.height
    displayImage(photo2, newX, newY)

def func_() :
    global x1, y1, x2, y2


# 메인 코드 부분
window = Tk()
window.geometry("1349x880")
window.resizable(False, False)
window.title("Mini Photoshop(Ver2.0)")


# 메뉴 구현
# 메뉴 자체 생성
mainMenu = Menu(window)
window.config(menu=mainMenu)
window.configure(bg='Gray38')


# 상단 라벨
topImage = PhotoImage(file='./bar.png')
topLabel = Label(window, image=topImage)
topLabel.configure(bd=0)
topLabel.pack(side='top', anchor='w')

# 우측 라벨
rightImage = PhotoImage(file='./box.png')
rightLabel = Label(window, image=rightImage)
rightLabel.configure(bd=0)
rightLabel.pack(side='right', anchor='n')


# 좌측 프레임
frame = Frame(master=window)
frame.configure(width=40, height=850, bg='Gray36', bd=1, relief='solid')
frame.pack(side='left', anchor='n')
frame.propagate(0)


# 버튼 구현
icon1 = PhotoImage(file='./icon/zoomin.png')
icon2 = PhotoImage(file='./icon/zoomout.png')
icon3 = PhotoImage(file='./icon/rotate.png')

button0 = Button(frame, text="Tool", state='disabled')
button0.configure(font=('Arial', 9), disabledforeground='white', bg='Gray26',
                  width=5)

button1 = Button(frame, text="Zoom in", image=icon1, command=func_autoZoomIn)
button1.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                  bd=1, relief='ridge', overrelief='sunken', 
                  activebackground='Gray28', activeforeground='white')

button2 = Button(frame, text="Zoom out", image=icon2, command=func_autoZoomOut)
button2.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                  bd=1, relief='ridge', overrelief='sunken', 
                  activebackground='Gray28', activeforeground='white')

button3 = Button(frame, text="Rotate", image=icon3, command=func_autoRotate)
button3.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                  bd=1, relief='ridge', overrelief='sunken', 
                  activebackground='Gray28', activeforeground='white')

button4 = Button(frame, text="임시", command=func_)
button4.configure(font=('Arial', 9), fg='white', bg='Gray30', cursor='hand2',
                  bd=1, relief='ridge', overrelief='sunken', 
                  activebackground='Gray28', activeforeground='white')

button0.pack()
button1.pack(padx=2, pady=2)
button2.pack(padx=2, pady=2)
button3.pack(padx=2, pady=2)
button4.pack(padx=2, pady=2)

# File 상위메뉴 생성
fileMenu = Menu(mainMenu, tearoff=0, bg='Gray32', fg='white', font='Arial',
                activebackground='Gray42')
mainMenu.add_cascade(label="File", menu = fileMenu)

# File 하위메뉴 생성
fileMenu.add_separator() # 구분선 삽입
fileMenu.add_command(label="New file", command=func_new)
fileMenu.add_command(label="Open file", command=func_open)
fileMenu.add_command(label="Save file", command=func_save)
fileMenu.add_separator() # 구분선 삽입
fileMenu.add_command(label="Exit", command=func_exit)
fileMenu.add_separator() # 구분선 삽입


# Edit 상위메뉴 생성
image1Menu = Menu(mainMenu, tearoff=0, bg='Gray32', fg='white', font='Arial',
                  activebackground='Gray42')
mainMenu.add_cascade(label="Edit", menu = image1Menu)
    
# Edit 하위메뉴 생성
image1Menu.add_separator() # 구분선 삽입
image1Menu.add_command(label="Zoom in", command=func_zoomIn)
image1Menu.add_command(label="Zoom out", command=func_zoomOut)
image1Menu.add_separator() # 구분선 삽입
image1Menu.add_command(label="Mirror width", command=func_mirrorW)
image1Menu.add_command(label="Mirror height", command=func_mirrorH)
image1Menu.add_command(label="Rotate", command=func_rotate)
image1Menu.add_separator() # 구분선 삽입


# Image 상위메뉴 생성
image2Menu = Menu(mainMenu, tearoff=0, bg='Gray32', fg='white', font='Arial',
                  activebackground='Gray42')
mainMenu.add_cascade(label="Image", menu = image2Menu)

# Image 하위메뉴 생성
image2Menu.add_separator() # 구분선 삽입
image2Menu.add_command(label="Bright", command=func_bright)
image2Menu.add_command(label="Dark", command=func_dark)
image2Menu.add_separator() # 구분선 삽입
image2Menu.add_command(label="Saturation high", command=func_saturationH)
image2Menu.add_command(label="Saturation low", command=func_saturationL)
image2Menu.add_separator() # 구분선 삽입
image2Menu.add_command(label="BW", command=func_bw)
image2Menu.add_separator() # 구분선 삽입
image2Menu.add_command(label="Reset image", command=func_resetImage)
image2Menu.add_separator() # 구분선 삽입


window.mainloop()
profile
비전공자의 개발 공부 과정과 기록

0개의 댓글