본문 바로가기
코딩(개발)/파이썬

파이썬 이미지 합치기 프로젝트

by 플랜데버 2020. 12. 30.

1. 레이아웃

# Project) 여러 이미지를 합치는 프로그램을 만드시오.

 

# [사용자 시나리오]

# 1. 사용자는 합치려는 이미지를 1개 이상 선택한다.

# 2. 합쳐진 이미지가 저장될 경로를 지정한다.

# 3. 가로넓이, 간격, 포맷 옵션을 지정한다.

# 4. 시작 버튼을 통해 이미지를 합친다.

# 5. 닫기 버튼을 통해 프로그램을 종료한다.

 

# [기능 명세]

# 1. 파일추가 : 리스트 박스에 파일 추가

# 2. 선택삭제 : 리스트 박스에서 선택된 항목 삭제

# 3. 찾아보기 : 저장 폴더를 선택하면 텍스트 위젯에 입력

# 4. 가로넓이 : 이미지 넓이 지정 (원본유지, 1024, 800, 640)

# 5. 간격 : 이미지 간의 간격지정 (없음, 좁게, 보통, 넓게)

# 6. 포맷 : 저장 이미지 포맷지정 (PNG, JPG, BMP)

# 7. 시작 : 이미지 합치기 작업 실행

# 8. 진행상황 : 현재 진행중인 파일 순서에 맞게 반영

# 9. 닫기 : 프로그램 종료

 

 

레이아웃 코드

import tkinter.ttk as ttk #콤보사용 위함
from tkinter import *

root = Tk()
root.title("nado gui")

#파일 프레임 ############################################
file_frame = Frame(root)
file_frame.pack(fill="x" , padx=5, pady=5) 

#버튼 생성
btn_add_file = Button(file_frame, padx=5, pady=5, width=12, text="파일추가")  # 어디에 만들지, 버튼텍스트
btn_add_file.pack(side="left")

btn_del_file = Button(file_frame, padx=5, pady=5, width=12, text="선택삭제")  # 어디에 만들지, 버튼텍스트
btn_del_file.pack(side="right")

#리스트 프레임###########################################
list_frame = Frame(root)
list_frame.pack(fill="both" , padx=5, pady=5) 

#스크롤바
scrollbar = Scrollbar(list_frame)
scrollbar.pack(side="right" , fill="y") #fill=y :  위아래로 꽉 채움
#리스트박스
list_file = Listbox(list_frame, selectmode = "extended" , height=15 , yscrollcommand=scrollbar.set)  # selectmode : single 은 하나만 선택가능 extended 는 다중선택가능 # 0이면 리스트에 있는 텍스트 만큼, 높이지정하면 그 갯수만큼 (스크롤바 필요)
list_file.pack(side="left" , fill="both" , expand=True)
scrollbar.config(command=list_file.yview) #스크롤바에 리스트박스 매핑

#저장경로 프레임#########################################
path_frame = LabelFrame(root, text="저장경로")
path_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

txt_dest_path = Entry(path_frame)
txt_dest_path.pack(side="left", fill="x", expand=True ,padx=5, pady=5 , ipady= 4) #ipad= innerpadding

btn_dest_path = Button(path_frame, text="찾아보기" , width=10)
btn_dest_path.pack(side="right" , padx=5, pady=5)

#옵션 프레임 ################################################################################
option_frame = LabelFrame(root, text="옵션")
option_frame.pack(padx=5, pady=5 ,ipadx=5, ipady= 10) 

#가로 넓이 옵션
#가로 넓이 레이블
lbl_width = Label(option_frame, text="가로넓이" , width=8)
lbl_width.pack(side="left")

#가로 넓이 콤보
opt_width = ["원본유지","1024","800","640"]
cmb_width = ttk.Combobox(option_frame, state="readonly" , values=opt_width , width=10)
cmb_width.current(0)
cmb_width.pack(side="left")

#간격 옵션
#간격 옵션 레이블
lbl_space = Label(option_frame, text="간격" , width=8)
lbl_space.pack(side="left")

#간격 옵션 콤보
opt_space = ["없음","좁게","보통","넓게"]
cmb_space= ttk.Combobox(option_frame, state="readonly" , values=opt_space , width=10)
cmb_space.current(0)
cmb_space.pack(side="left")
 
#파일 포맷 옵션
#파일 포맷 옵션 레이블
lbl_format = Label(option_frame, text="포맷" , width=8)
lbl_format.pack(side="left")

#파일 포맷 옵션 콤보
opt_format = ["PNG","JPG","BMP"]
cmb_format= ttk.Combobox(option_frame, state="readonly" , values=opt_format , width=10)
cmb_format.current(0)
cmb_format.pack(side="left")

#진행 상황 Progress Bar ################################################################
progress_frame = LabelFrame(root, text="진행상황")
progress_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

p_var = DoubleVar() 
progress_bar = ttk.Progressbar(progress_frame, maximum = 100 , variable=p_var) 
progress_bar.pack(fill="x" ,padx=5, pady=5)

#실행 프레임 ############################################################################
run_frame = Frame(root)
run_frame.pack(fill="x"  , padx=5, pady=5)

#버튼 생성

btn_close = Button(run_frame, padx=5, pady=5, width=12, text="닫기" , command=root.quit)  # 어디에 만들지, 버튼텍스트
btn_close.pack(side="right" , padx=5, pady=5)

btn_start = Button(run_frame, padx=5, pady=5, width=12, text="시작")  # 어디에 만들지, 버튼텍스트
btn_start.pack(side="right" , padx=5, pady=5)

root.resizable(False, False) # x 너비 , y 높이 값 변경 불가 (창크기 변경 못하게)
root.mainloop() # 창이 닫히지 않도록

 


2. 파일 추가

import tkinter.ttk as ttk 
from tkinter import *
from tkinter import filedialog  # 서브모듈이라 임포트 해줘야 한다. 파일 찾기

root = Tk()
root.title("nado gui")

file_frame = Frame(root)
file_frame.pack(fill="x" , padx=5, pady=5) 

# 함수정의

# 파일추가 #선택한 목록 저장
def add_file():
    files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요.", \
        filetypes=(("PNG파일","*.png"),("모든파일","*")),  \
        initialdir=r"E:\PythonWorkspace\pygame_project\images") #최초에 C:/경로를 보여줌 r을 스트링이 그대로 쓰여진다.
        
    #사용자가 선택한 파일 목록 파일 리스트에 뿌리기 
    for file in files:
        list_file.insert(END,file)

# 선택삭제
def del_file():
    #print(list_file.curselection()) #index 값 반환

    for index in reversed(list_file.curselection()): #revered 를 써서 index 값 뒷번호 지워준다. 앞(0)에서 부터 지우면 지운뒤 1번이 0번으로 바뀌기 때문에 엉뚱한 파일이 삭제된다.
        list_file.delete(index)   
        
btn_add_file = Button(file_frame, padx=5, pady=5, width=12, text="파일추가", command=add_file)  
btn_add_file.pack(side="left")

btn_del_file = Button(file_frame, padx=5, pady=5, width=12, text="선택삭제", command=del_file)  
btn_del_file.pack(side="right")

list_frame = Frame(root)
list_frame.pack(fill="both" , padx=5, pady=5) 

scrollbar = Scrollbar(list_frame)
scrollbar.pack(side="right" , fill="y") 

list_file = Listbox(list_frame, selectmode = "extended" , height=15 , yscrollcommand=scrollbar.set) 
list_file.pack(side="left" , fill="both" , expand=True)
scrollbar.config(command=list_file.yview) 

path_frame = LabelFrame(root, text="저장경로")
path_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

txt_dest_path = Entry(path_frame)
txt_dest_path.pack(side="left", fill="x", expand=True ,padx=5, pady=5 , ipady= 4) 

btn_dest_path = Button(path_frame, text="찾아보기" , width=10)
btn_dest_path.pack(side="right" , padx=5, pady=5)

option_frame = LabelFrame(root, text="옵션")
option_frame.pack(padx=5, pady=5 ,ipadx=5, ipady= 10) 

lbl_width = Label(option_frame, text="가로넓이" , width=8)
lbl_width.pack(side="left")

opt_width = ["원본유지","1024","800","640"]
cmb_width = ttk.Combobox(option_frame, state="readonly" , values=opt_width , width=10)
cmb_width.current(0)
cmb_width.pack(side="left")

lbl_space = Label(option_frame, text="간격" , width=8)
lbl_space.pack(side="left")

opt_space = ["없음","좁게","보통","넓게"]
cmb_space= ttk.Combobox(option_frame, state="readonly" , values=opt_space , width=10)
cmb_space.current(0)
cmb_space.pack(side="left")
 
lbl_format = Label(option_frame, text="포맷" , width=8)
lbl_format.pack(side="left")

opt_format = ["PNG","JPG","BMP"]
cmb_format= ttk.Combobox(option_frame, state="readonly" , values=opt_format , width=10)
cmb_format.current(0)
cmb_format.pack(side="left")

progress_frame = LabelFrame(root, text="진행상황")
progress_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

p_var = DoubleVar() 
progress_bar = ttk.Progressbar(progress_frame, maximum = 100 , variable=p_var) 
progress_bar.pack(fill="x" ,padx=5, pady=5)

run_frame = Frame(root)
run_frame.pack(fill="x"  , padx=5, pady=5)

btn_close = Button(run_frame, padx=5, pady=5, width=12, text="닫기" , command=root.quit) 
btn_close.pack(side="right" , padx=5, pady=5)

btn_start = Button(run_frame, padx=5, pady=5, width=12, text="시작") 
btn_start.pack(side="right" , padx=5, pady=5)

root.resizable(False, False)
root.mainloop() 


#reverse() : 실제 순서를 뒤바꿈 revered(): 바뀐 새로운 값을 반환함 , 실제 값에는 영향을 미치지 않음

 

 


2. 함수정의 ( 폴더경로 , 시작 함수)

import tkinter.ttk as ttk 
import tkinter.messagebox as msgbox # 메세지 박스 띄우기 위해 추가
from tkinter import *
from tkinter import filedialog  # 서브모듈이라 임포트 해줘야 한다. 파일 찾기

root = Tk()
root.title("nado gui")

file_frame = Frame(root)
file_frame.pack(fill="x" , padx=5, pady=5) 

# 함수정의

# 파일추가 #선택한 목록 저장
def add_file():
    files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요.", \
        filetypes=(("PNG파일","*.png"),("모든파일","*")),  \
        initialdir=r"E:\PythonWorkspace\pygame_project\images") #최초에 C:/경로를 보여줌 r을 스트링이 그대로 쓰여진다.
        
    #사용자가 선택한 파일 목록 파일 리스트에 뿌리기 
    for file in files:
        list_file.insert(END,file)

# 선택삭제
def del_file():
    #print(list_file.curselection()) #index 값 반환

    for index in reversed(list_file.curselection()):
        list_file.delete(index)   
        
        
# 저장 경로 (폴더)
def browse_dest_path():
    folder_selected = filedialog.askdirectory()
    if folder_selected == '': #사용자가 취소를 누를때
        return
    #print(folder_selected)
    txt_dest_path.delete(0,END)
    txt_dest_path.insert(0, folder_selected)

#시작
def start():
    # 각 옵션들 값을 확인
    print("가로넓이 : ", cmb_width.get())
    print("간격 : ", cmb_space.get())
    print("포맷 : ", cmb_format.get())

    #파일 목록 확인
    if list_file.size() == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","이미지 파일을 추가하세요")
        return # 빠져나간다.

    #저장 경로 확인
    if len(txt_dest_path.get()) == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","저장 경로를 선택하세요.")
        return # 빠져나간다.
   

btn_add_file = Button(file_frame, padx=5, pady=5, width=12, text="파일추가", command=add_file)  
btn_add_file.pack(side="left")

btn_del_file = Button(file_frame, padx=5, pady=5, width=12, text="선택삭제" ,command=del_file)  
btn_del_file.pack(side="right")

list_frame = Frame(root)
list_frame.pack(fill="both" , padx=5, pady=5) 

scrollbar = Scrollbar(list_frame)
scrollbar.pack(side="right" , fill="y") 

list_file = Listbox(list_frame, selectmode = "extended" , height=15 , yscrollcommand=scrollbar.set) 
list_file.pack(side="left" , fill="both" , expand=True)
scrollbar.config(command=list_file.yview) 

path_frame = LabelFrame(root, text="저장경로")
path_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

txt_dest_path = Entry(path_frame)
txt_dest_path.pack(side="left", fill="x", expand=True ,padx=5, pady=5 , ipady= 4) 

btn_dest_path = Button(path_frame, text="찾아보기" , width=10 , command=browse_dest_path)
btn_dest_path.pack(side="right" , padx=5, pady=5)

option_frame = LabelFrame(root, text="옵션")
option_frame.pack(padx=5, pady=5 ,ipadx=5, ipady= 10) 

lbl_width = Label(option_frame, text="가로넓이" , width=8)
lbl_width.pack(side="left")

opt_width = ["원본유지","1024","800","640"]
cmb_width = ttk.Combobox(option_frame, state="readonly" , values=opt_width , width=10)
cmb_width.current(0)
cmb_width.pack(side="left")

lbl_space = Label(option_frame, text="간격" , width=8)
lbl_space.pack(side="left")

opt_space = ["없음","좁게","보통","넓게"]
cmb_space= ttk.Combobox(option_frame, state="readonly" , values=opt_space , width=10)
cmb_space.current(0)
cmb_space.pack(side="left")
 
lbl_format = Label(option_frame, text="포맷" , width=8)
lbl_format.pack(side="left")

opt_format = ["PNG","JPG","BMP"]
cmb_format= ttk.Combobox(option_frame, state="readonly" , values=opt_format , width=10)
cmb_format.current(0)
cmb_format.pack(side="left")

progress_frame = LabelFrame(root, text="진행상황")
progress_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

p_var = DoubleVar() 
progress_bar = ttk.Progressbar(progress_frame, maximum = 100 , variable=p_var) 
progress_bar.pack(fill="x" ,padx=5, pady=5)

run_frame = Frame(root)
run_frame.pack(fill="x"  , padx=5, pady=5)

btn_close = Button(run_frame, padx=5, pady=5, width=12, text="닫기" , command=root.quit) 
btn_close.pack(side="right" , padx=5, pady=5)

btn_start = Button(run_frame, padx=5, pady=5, width=12, text="시작" , command=start)  # command=start 추가
btn_start.pack(side="right" , padx=5, pady=5)

root.resizable(False, False)
root.mainloop() 

 

4. 이미지 합치기

 

코드
import os
import tkinter.ttk as ttk 
import tkinter.messagebox as msgbox 
from tkinter import *
from tkinter import filedialog  
from PIL import Image # Paython Image Library 사용 위해

root = Tk()
root.title("nado gui")

file_frame = Frame(root)
file_frame.pack(fill="x" , padx=5, pady=5) 

# 함수정의

# 파일추가 #선택한 목록 저장
def add_file():
    files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요.", \
        filetypes=(("PNG파일","*.png"),("모든파일","*")),  \
        initialdir=r"E:\PythonWorkspace\pygame_project\images") #최초에 C:/경로를 보여줌 r을 스트링이 그대로 쓰여진다.
        
    #사용자가 선택한 파일 목록 파일 리스트에 뿌리기 
    for file in files:
        list_file.insert(END,file)

# 선택삭제
def del_file():
    #print(list_file.curselection()) #index 값 반환

    for index in reversed(list_file.curselection()):
        list_file.delete(index)   
        
        
# 저장 경로 (폴더)
def browse_dest_path():
    folder_selected = filedialog.askdirectory()
    if folder_selected == '': #사용자가 취소를 누를때
        return
    #print(folder_selected)
    txt_dest_path.delete(0,END)
    txt_dest_path.insert(0, folder_selected)

# 이미지 통합
def merge_image():
    #print(list_file.get(0,END))
    images = [Image.open(x) for x in list_file.get(0,END)]
    # size -> size[0] : width, size[1] : height
    # 가장 넓은 이미지의 넓이와 전체 높이를 구한다.
    widths = [x.size[0] for x in images]
    heights = [x.size[1] for x in images]
    # print("width :", widths)
    # print("height :", heights)

    #스케치북 준비
    max_width,total_height = max(widths),sum(heights)
    # print("max width:", max_width)
    # print("totalheight:", total_height)

    result_img = Image.new("RGB", (max_width, total_height), (255,255,255)) # 배경 흰색
    y_offset = 0 # x좌표는 그대로 지만 y좌표는 변경

    for img in images:
        result_img.paste(img,(0,y_offset)) #이미지 붙이고
        y_offset += img.size[1] #height 값 만큼 더해줌

    dest_path = os.path.join(txt_dest_path.get(), "nado_photo.jpg") # 최종 결과물 이미지명
    result_img.save(dest_path)    # 저장
    msgbox.showinfo("알림", "작업이 완료 되었습니다.")

#시작
def start():
    # 각 옵션들 값을 확인
    print("가로넓이 : ", cmb_width.get())
    print("간격 : ", cmb_space.get())
    print("포맷 : ", cmb_format.get())

    #파일 목록 확인
    if list_file.size() == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","이미지 파일을 추가하세요")
        return # 빠져나간다.

    #저장 경로 확인
    if len(txt_dest_path.get()) == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","저장 경로를 선택하세요.")
        return # 빠져나간다.

     # 이미지 통합 작업
    merge_image()   

btn_add_file = Button(file_frame, padx=5, pady=5, width=12, text="파일추가", command=add_file)  
btn_add_file.pack(side="left")

btn_del_file = Button(file_frame, padx=5, pady=5, width=12, text="선택삭제" ,command=del_file)  
btn_del_file.pack(side="right")

list_frame = Frame(root)
list_frame.pack(fill="both" , padx=5, pady=5) 

scrollbar = Scrollbar(list_frame)
scrollbar.pack(side="right" , fill="y") 

list_file = Listbox(list_frame, selectmode = "extended" , height=15 , yscrollcommand=scrollbar.set) 
list_file.pack(side="left" , fill="both" , expand=True)
scrollbar.config(command=list_file.yview) 

path_frame = LabelFrame(root, text="저장경로")
path_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

txt_dest_path = Entry(path_frame)
txt_dest_path.pack(side="left", fill="x", expand=True ,padx=5, pady=5 , ipady= 4) 

btn_dest_path = Button(path_frame, text="찾아보기" , width=10 , command=browse_dest_path)
btn_dest_path.pack(side="right" , padx=5, pady=5)

option_frame = LabelFrame(root, text="옵션")
option_frame.pack(padx=5, pady=5 ,ipadx=5, ipady= 10) 

lbl_width = Label(option_frame, text="가로넓이" , width=8)
lbl_width.pack(side="left")

opt_width = ["원본유지","1024","800","640"]
cmb_width = ttk.Combobox(option_frame, state="readonly" , values=opt_width , width=10)
cmb_width.current(0)
cmb_width.pack(side="left")

lbl_space = Label(option_frame, text="간격" , width=8)
lbl_space.pack(side="left")

opt_space = ["없음","좁게","보통","넓게"]
cmb_space= ttk.Combobox(option_frame, state="readonly" , values=opt_space , width=10)
cmb_space.current(0)
cmb_space.pack(side="left")
 
lbl_format = Label(option_frame, text="포맷" , width=8)
lbl_format.pack(side="left")

opt_format = ["PNG","JPG","BMP"]
cmb_format= ttk.Combobox(option_frame, state="readonly" , values=opt_format , width=10)
cmb_format.current(0)
cmb_format.pack(side="left")

progress_frame = LabelFrame(root, text="진행상황")
progress_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

p_var = DoubleVar() 
progress_bar = ttk.Progressbar(progress_frame, maximum = 100 , variable=p_var) 
progress_bar.pack(fill="x" ,padx=5, pady=5)

run_frame = Frame(root)
run_frame.pack(fill="x"  , padx=5, pady=5)

btn_close = Button(run_frame, padx=5, pady=5, width=12, text="닫기" , command=root.quit) 
btn_close.pack(side="right" , padx=5, pady=5)

btn_start = Button(run_frame, padx=5, pady=5, width=12, text="시작" , command=start)  # command=start 추가
btn_start.pack(side="right" , padx=5, pady=5)

root.resizable(False, False)
root.mainloop() 


 

5. 프로그레스바 연동

import os
import tkinter.ttk as ttk 
import tkinter.messagebox as msgbox 
from tkinter import *
from tkinter import filedialog  
from PIL import Image # Paython Image Library 사용 위해

root = Tk()
root.title("nado gui")

file_frame = Frame(root)
file_frame.pack(fill="x" , padx=5, pady=5) 

def add_file():
    files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요.", \
        filetypes=(("PNG파일","*.png"),("모든파일","*")),  \
        initialdir=r"E:\PythonWorkspace\pygame_project\images") #최초에 C:/경로를 보여줌 r을 스트링이 그대로 쓰여진다.
        

    for file in files:
        list_file.insert(END,file)

def del_file():

    for index in reversed(list_file.curselection()):
        list_file.delete(index)   
        
        

def browse_dest_path():
    folder_selected = filedialog.askdirectory()
    if folder_selected == '': 
        return
    
    txt_dest_path.delete(0,END)
    txt_dest_path.insert(0, folder_selected)

def merge_image():

    images = [Image.open(x) for x in list_file.get(0,END)]
    widths = [x.size[0] for x in images]
    heights = [x.size[1] for x in images]

    max_width,total_height = max(widths),sum(heights)
   
    result_img = Image.new("RGB", (max_width, total_height), (255,255,255)) 
    y_offset = 0 

    # for img in images:
    #     result_img.paste(img,(0,y_offset)) 
    #     y_offset += img.size[1] 
    # 프로그레스바에 표현 하기 위해 다시 처리
    for idx , img in enumerate(images):
        result_img.paste(img,(0,y_offset)) 
        y_offset += img.size[1] 

        progress = (idx + 1) / len(images) * 100 # 실제 % 정보를 계산, idx 는 0부터 시작하므로 더하기 1
        p_var.set(progress)
        progress_bar.update()

    dest_path = os.path.join(txt_dest_path.get(), "nado_photo.jpg") 
    result_img.save(dest_path)    
    msgbox.showinfo("알림", "작업이 완료 되었습니다.")

#시작
def start():
    # 각 옵션들 값을 확인
    print("가로넓이 : ", cmb_width.get())
    print("간격 : ", cmb_space.get())
    print("포맷 : ", cmb_format.get())

    #파일 목록 확인
    if list_file.size() == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","이미지 파일을 추가하세요")
        return # 빠져나간다.

    #저장 경로 확인
    if len(txt_dest_path.get()) == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","저장 경로를 선택하세요.")
        return # 빠져나간다.

     # 이미지 통합 작업
    merge_image()   

btn_add_file = Button(file_frame, padx=5, pady=5, width=12, text="파일추가", command=add_file)  
btn_add_file.pack(side="left")

btn_del_file = Button(file_frame, padx=5, pady=5, width=12, text="선택삭제" ,command=del_file)  
btn_del_file.pack(side="right")

list_frame = Frame(root)
list_frame.pack(fill="both" , padx=5, pady=5) 

scrollbar = Scrollbar(list_frame)
scrollbar.pack(side="right" , fill="y") 

list_file = Listbox(list_frame, selectmode = "extended" , height=15 , yscrollcommand=scrollbar.set) 
list_file.pack(side="left" , fill="both" , expand=True)
scrollbar.config(command=list_file.yview) 

path_frame = LabelFrame(root, text="저장경로")
path_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

txt_dest_path = Entry(path_frame)
txt_dest_path.pack(side="left", fill="x", expand=True ,padx=5, pady=5 , ipady= 4) 

btn_dest_path = Button(path_frame, text="찾아보기" , width=10 , command=browse_dest_path)
btn_dest_path.pack(side="right" , padx=5, pady=5)

option_frame = LabelFrame(root, text="옵션")
option_frame.pack(padx=5, pady=5 ,ipadx=5, ipady= 10) 

lbl_width = Label(option_frame, text="가로넓이" , width=8)
lbl_width.pack(side="left")

opt_width = ["원본유지","1024","800","640"]
cmb_width = ttk.Combobox(option_frame, state="readonly" , values=opt_width , width=10)
cmb_width.current(0)
cmb_width.pack(side="left")

lbl_space = Label(option_frame, text="간격" , width=8)
lbl_space.pack(side="left")

opt_space = ["없음","좁게","보통","넓게"]
cmb_space= ttk.Combobox(option_frame, state="readonly" , values=opt_space , width=10)
cmb_space.current(0)
cmb_space.pack(side="left")
 
lbl_format = Label(option_frame, text="포맷" , width=8)
lbl_format.pack(side="left")

opt_format = ["PNG","JPG","BMP"]
cmb_format= ttk.Combobox(option_frame, state="readonly" , values=opt_format , width=10)
cmb_format.current(0)
cmb_format.pack(side="left")

progress_frame = LabelFrame(root, text="진행상황")
progress_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

p_var = DoubleVar() 
progress_bar = ttk.Progressbar(progress_frame, maximum = 100 , variable=p_var) 
progress_bar.pack(fill="x" ,padx=5, pady=5)

run_frame = Frame(root)
run_frame.pack(fill="x"  , padx=5, pady=5)

btn_close = Button(run_frame, padx=5, pady=5, width=12, text="닫기" , command=root.quit) 
btn_close.pack(side="right" , padx=5, pady=5)

btn_start = Button(run_frame, padx=5, pady=5, width=12, text="시작" , command=start)  # command=start 추가
btn_start.pack(side="right" , padx=5, pady=5)

root.resizable(False, False)
root.mainloop() 

 

6. zip 이용해보기

 

import os
import tkinter.ttk as ttk 
import tkinter.messagebox as msgbox 
from tkinter import *
from tkinter import filedialog  
from PIL import Image # Paython Image Library 사용 위해

root = Tk()
root.title("nado gui")

file_frame = Frame(root)
file_frame.pack(fill="x" , padx=5, pady=5) 

def add_file():
    files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요.", \
        filetypes=(("PNG파일","*.png"),("모든파일","*")),  \
        initialdir=r"E:\PythonWorkspace\pygame_project\images") #최초에 C:/경로를 보여줌 r을 스트링이 그대로 쓰여진다.
        

    for file in files:
        list_file.insert(END,file)

def del_file():

    for index in reversed(list_file.curselection()):
        list_file.delete(index)   
        
        

def browse_dest_path():
    folder_selected = filedialog.askdirectory()
    if folder_selected == '': 
        return
    
    txt_dest_path.delete(0,END)
    txt_dest_path.insert(0, folder_selected)

def merge_image():

    images = [Image.open(x) for x in list_file.get(0,END)]
    # widths = [x.size[0] for x in images]
    # heights = [x.size[1] for x in images]

    #zip 을 이용 위 두줄과 같은 결과
    widths,heights = zip(*(x.size for x in images))

    max_width,total_height = max(widths),sum(heights)
   
    result_img = Image.new("RGB", (max_width, total_height), (255,255,255)) 
    y_offset = 0 

    
    for idx , img in enumerate(images):
        result_img.paste(img,(0,y_offset)) 
        y_offset += img.size[1] 

        progress = (idx + 1) / len(images) * 100 # 실제 % 정보를 계산, idx 는 0부터 시작하므로 더하기 1
        p_var.set(progress)
        progress_bar.update()

    dest_path = os.path.join(txt_dest_path.get(), "nado_photo.jpg") 
    result_img.save(dest_path)    
    msgbox.showinfo("알림", "작업이 완료 되었습니다.")

#시작
def start():
    # 각 옵션들 값을 확인
    print("가로넓이 : ", cmb_width.get())
    print("간격 : ", cmb_space.get())
    print("포맷 : ", cmb_format.get())

    #파일 목록 확인
    if list_file.size() == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","이미지 파일을 추가하세요")
        return # 빠져나간다.

    #저장 경로 확인
    if len(txt_dest_path.get()) == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","저장 경로를 선택하세요.")
        return # 빠져나간다.

     # 이미지 통합 작업
    merge_image()   

btn_add_file = Button(file_frame, padx=5, pady=5, width=12, text="파일추가", command=add_file)  
btn_add_file.pack(side="left")

btn_del_file = Button(file_frame, padx=5, pady=5, width=12, text="선택삭제" ,command=del_file)  
btn_del_file.pack(side="right")

list_frame = Frame(root)
list_frame.pack(fill="both" , padx=5, pady=5) 

scrollbar = Scrollbar(list_frame)
scrollbar.pack(side="right" , fill="y") 

list_file = Listbox(list_frame, selectmode = "extended" , height=15 , yscrollcommand=scrollbar.set) 
list_file.pack(side="left" , fill="both" , expand=True)
scrollbar.config(command=list_file.yview) 

path_frame = LabelFrame(root, text="저장경로")
path_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

txt_dest_path = Entry(path_frame)
txt_dest_path.pack(side="left", fill="x", expand=True ,padx=5, pady=5 , ipady= 4) 

btn_dest_path = Button(path_frame, text="찾아보기" , width=10 , command=browse_dest_path)
btn_dest_path.pack(side="right" , padx=5, pady=5)

option_frame = LabelFrame(root, text="옵션")
option_frame.pack(padx=5, pady=5 ,ipadx=5, ipady= 10) 

lbl_width = Label(option_frame, text="가로넓이" , width=8)
lbl_width.pack(side="left")

opt_width = ["원본유지","1024","800","640"]
cmb_width = ttk.Combobox(option_frame, state="readonly" , values=opt_width , width=10)
cmb_width.current(0)
cmb_width.pack(side="left")

lbl_space = Label(option_frame, text="간격" , width=8)
lbl_space.pack(side="left")

opt_space = ["없음","좁게","보통","넓게"]
cmb_space= ttk.Combobox(option_frame, state="readonly" , values=opt_space , width=10)
cmb_space.current(0)
cmb_space.pack(side="left")
 
lbl_format = Label(option_frame, text="포맷" , width=8)
lbl_format.pack(side="left")

opt_format = ["PNG","JPG","BMP"]
cmb_format= ttk.Combobox(option_frame, state="readonly" , values=opt_format , width=10)
cmb_format.current(0)
cmb_format.pack(side="left")

progress_frame = LabelFrame(root, text="진행상황")
progress_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

p_var = DoubleVar() 
progress_bar = ttk.Progressbar(progress_frame, maximum = 100 , variable=p_var) 
progress_bar.pack(fill="x" ,padx=5, pady=5)

run_frame = Frame(root)
run_frame.pack(fill="x"  , padx=5, pady=5)

btn_close = Button(run_frame, padx=5, pady=5, width=12, text="닫기" , command=root.quit) 
btn_close.pack(side="right" , padx=5, pady=5)

btn_start = Button(run_frame, padx=5, pady=5, width=12, text="시작" , command=start)  # command=start 추가
btn_start.pack(side="right" , padx=5, pady=5)

root.resizable(False, False)
root.mainloop() 

 

5. 옵션처리

 

import os
import tkinter.ttk as ttk 
import tkinter.messagebox as msgbox 
from tkinter import *
from tkinter import filedialog  
from PIL import Image # Paython Image Library 사용 위해

root = Tk()
root.title("nado gui")

file_frame = Frame(root)
file_frame.pack(fill="x" , padx=5, pady=5) 

def add_file():
    files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요.", \
        filetypes=(("PNG파일","*.png"),("모든파일","*")),  \
        initialdir=r"E:\PythonWorkspace\pygame_project\images") #최초에 C:/경로를 보여줌 r을 스트링이 그대로 쓰여진다.
        

    for file in files:
        list_file.insert(END,file)

def del_file():

    for index in reversed(list_file.curselection()):
        list_file.delete(index)   
        
        

def browse_dest_path():
    folder_selected = filedialog.askdirectory()
    if folder_selected == '': 
        return
    
    txt_dest_path.delete(0,END)
    txt_dest_path.insert(0, folder_selected)

# 이미지 통합
def merge_image():
    # print("가로넓이 : ", cmb_width.get())
    # print("간격 : ", cmb_space.get())
    # print("포맷 : ", cmb_format.get())

    # 가로 넓이
    img_width = cmb_width.get()  #콤보의 선택값 가지고 온다
    if img_width == "원본유지":
        img_width= -1 # -1일 때는 원본 기준으로 원본을 통합해라
    else:
        img_width= int(img_width)

    #간격
    img_space = cmb_space.get()
    if img_space == "좁게" :
        img_space = 30
    elif img_space == "보통" :
        img_space = 60
    elif img_space == "넓게" :
        img_space = 90

    else :
            img_space = 0

    
    img_format = cmb_format.get().lower()  #PNG, JPG,BMP 를 소문자로 변경

    images = [Image.open(x) for x in list_file.get(0,END)]

    #이미지 사이즈 리스트 넣어서 하나씩 처리
    imate_sizes = [] # [(width1,height1),(width2,height2), ...]
    if img_width > -1:
        # width 값 변경
        image_sizes = [int(img_width), int(img_width * x.size[1]/x.size[0]) for x in images]
    else:
        image_sizes = [(x.size[0],x.size[1]) for x in images] # 원본 사이즈 사용

        #계산식
        # 100x 60 이미지 -> width를 80으로 줄이면 height는?
        # 원본 width: 원본 height = 변경 width : 변경 height
        # 100 : 60 = 80 : ?
        #  x  :  y = x' : y'
        # xy' = x'y
        # y; = x'y / x -> 이 식을 적용하면 된다.
        # 10: 60 = 80 : 48

        # 코드에 대입
        # x = width = size[0]
        # y = height = size[1]
        # x' = img_width # 이 값으로 변경 해야 함
        # y' = x'y / x = img_width * size[1] / size[0] 

    widths,heights = zip(*(image_sizes))

    max_width,total_height = max(widths),sum(heights)
   
    result_img = Image.new("RGB", (max_width, total_height), (255,255,255)) 
    y_offset = 0 

    
    for idx , img in enumerate(images):
        result_img.paste(img,(0,y_offset)) 
        y_offset += img.size[1] 

        progress = (idx + 1) / len(images) * 100 # 실제 % 정보를 계산, idx 는 0부터 시작하므로 더하기 1
        p_var.set(progress)
        progress_bar.update()

    dest_path = os.path.join(txt_dest_path.get(), "nado_photo.jpg") 
    result_img.save(dest_path)    
    msgbox.showinfo("알림", "작업이 완료 되었습니다.")

#시작
def start():
    # 각 옵션들 값을 확인
    # print("가로넓이 : ", cmb_width.get())
    # print("간격 : ", cmb_space.get())
    # print("포맷 : ", cmb_format.get())

    #파일 목록 확인
    if list_file.size() == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","이미지 파일을 추가하세요")
        return # 빠져나간다.

    #저장 경로 확인
    if len(txt_dest_path.get()) == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","저장 경로를 선택하세요.")
        return # 빠져나간다.

     # 이미지 통합 작업
    merge_image()   

btn_add_file = Button(file_frame, padx=5, pady=5, width=12, text="파일추가", command=add_file)  
btn_add_file.pack(side="left")

btn_del_file = Button(file_frame, padx=5, pady=5, width=12, text="선택삭제" ,command=del_file)  
btn_del_file.pack(side="right")

list_frame = Frame(root)
list_frame.pack(fill="both" , padx=5, pady=5) 

scrollbar = Scrollbar(list_frame)
scrollbar.pack(side="right" , fill="y") 

list_file = Listbox(list_frame, selectmode = "extended" , height=15 , yscrollcommand=scrollbar.set) 
list_file.pack(side="left" , fill="both" , expand=True)
scrollbar.config(command=list_file.yview) 

path_frame = LabelFrame(root, text="저장경로")
path_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

txt_dest_path = Entry(path_frame)
txt_dest_path.pack(side="left", fill="x", expand=True ,padx=5, pady=5 , ipady= 4) 

btn_dest_path = Button(path_frame, text="찾아보기" , width=10 , command=browse_dest_path)
btn_dest_path.pack(side="right" , padx=5, pady=5)

option_frame = LabelFrame(root, text="옵션")
option_frame.pack(padx=5, pady=5 ,ipadx=5, ipady= 10) 

lbl_width = Label(option_frame, text="가로넓이" , width=8)
lbl_width.pack(side="left")

opt_width = ["원본유지","1024","800","640"]
cmb_width = ttk.Combobox(option_frame, state="readonly" , values=opt_width , width=10)
cmb_width.current(0)
cmb_width.pack(side="left")

lbl_space = Label(option_frame, text="간격" , width=8)
lbl_space.pack(side="left")

opt_space = ["없음","좁게","보통","넓게"]
cmb_space= ttk.Combobox(option_frame, state="readonly" , values=opt_space , width=10)
cmb_space.current(0)
cmb_space.pack(side="left")
 
lbl_format = Label(option_frame, text="포맷" , width=8)
lbl_format.pack(side="left")

opt_format = ["PNG","JPG","BMP"]
cmb_format= ttk.Combobox(option_frame, state="readonly" , values=opt_format , width=10)
cmb_format.current(0)
cmb_format.pack(side="left")

progress_frame = LabelFrame(root, text="진행상황")
progress_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

p_var = DoubleVar() 
progress_bar = ttk.Progressbar(progress_frame, maximum = 100 , variable=p_var) 
progress_bar.pack(fill="x" ,padx=5, pady=5)

run_frame = Frame(root)
run_frame.pack(fill="x"  , padx=5, pady=5)

btn_close = Button(run_frame, padx=5, pady=5, width=12, text="닫기" , command=root.quit) 
btn_close.pack(side="right" , padx=5, pady=5)

btn_start = Button(run_frame, padx=5, pady=5, width=12, text="시작" , command=start)  # command=start 추가
btn_start.pack(side="right" , padx=5, pady=5)

root.resizable(False, False)
root.mainloop() 

 

최종

 

import os
import tkinter.ttk as ttk 
import tkinter.messagebox as msgbox 
from tkinter import *
from tkinter import filedialog  
from PIL import Image

root = Tk()
root.title("nado gui")

file_frame = Frame(root)
file_frame.pack(fill="x" , padx=5, pady=5) 

def add_file():
    files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요.", \
        filetypes=(("PNG파일","*.png"),("모든파일","*")),  \
        initialdir=r"E:\PythonWorkspace\pygame_project\images")
        

    for file in files:
        list_file.insert(END,file)

def del_file():

    for index in reversed(list_file.curselection()):
        list_file.delete(index)   
        
        

def browse_dest_path():
    folder_selected = filedialog.askdirectory()
    if folder_selected == '': 
        return
    
    txt_dest_path.delete(0,END)
    txt_dest_path.insert(0, folder_selected)

def merge_image():
   
    try:
        img_width = cmb_width.get() 
        if img_width == "원본유지":
            img_width= -1 
        else:
            img_width= int(img_width)

    
        img_space = cmb_space.get()
        if img_space == "좁게" :
            img_space = 30
        elif img_space == "보통" :
            img_space = 60
        elif img_space == "넓게" :
            img_space = 90
        else :
            img_space = 0

        
        img_format = cmb_format.get().lower() 

        images = [Image.open(x) for x in list_file.get(0,END)]

        
        image_sizes = [] 
        if img_width > -1 :
            image_sizes = [(int(img_width), int(img_width * x.size[1] / x.size[0])) for x in images]
        else :
            image_sizes = [(x.size[0],x.size[1]) for x in images] 

    

        widths,heights = zip(*(image_sizes))

        max_width,total_height = max(widths),sum(heights)

        #스케치북 준비 
        # 옵션 중 스페이스 주기 위한 작업
        if img_space > 0 : 
            total_height += (img_space * (len(images) - 1))
    

        result_img = Image.new("RGB", (max_width, total_height), (255,255,255)) 
        y_offset = 0 

        
        for idx , img in enumerate(images):
            #width가 원본유지가 아닐 때에는 이미지 크기 조정
            if img_width > -1:
                img = img.resize(image_sizes[idx])

            result_img.paste(img,(0,y_offset)) 
            y_offset += (img.size[1] + img_space) #heigt 값 + 사용자가 지정한 간격

            progress = (idx + 1) / len(images) * 100 
            p_var.set(progress)
            progress_bar.update()

        # 포맷 옵션 처리
        file_name = "nado_photo." + img_format
        dest_path = os.path.join(txt_dest_path.get(), file_name) 
        result_img.save(dest_path)    
        msgbox.showinfo("알림", "작업이 완료 되었습니다.")

    except Exception as err :  # 예외처리
        msgbox.showerror("에러" , err)

#시작
def start():

    #파일 목록 확인
    if list_file.size() == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","이미지 파일을 추가하세요")
        return # 빠져나간다.

    #저장 경로 확인
    if len(txt_dest_path.get()) == 0 :   #파일이 하나도 없다면
        msgbox.showwarning("경고","저장 경로를 선택하세요.")
        return # 빠져나간다.

     # 이미지 통합 작업
    merge_image()   

btn_add_file = Button(file_frame, padx=5, pady=5, width=12, text="파일추가", command=add_file)  
btn_add_file.pack(side="left")

btn_del_file = Button(file_frame, padx=5, pady=5, width=12, text="선택삭제" ,command=del_file)  
btn_del_file.pack(side="right")

list_frame = Frame(root)
list_frame.pack(fill="both" , padx=5, pady=5) 

scrollbar = Scrollbar(list_frame)
scrollbar.pack(side="right" , fill="y") 

list_file = Listbox(list_frame, selectmode = "extended" , height=15 , yscrollcommand=scrollbar.set) 
list_file.pack(side="left" , fill="both" , expand=True)
scrollbar.config(command=list_file.yview) 

path_frame = LabelFrame(root, text="저장경로")
path_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

txt_dest_path = Entry(path_frame)
txt_dest_path.pack(side="left", fill="x", expand=True ,padx=5, pady=5 , ipady= 4) 

btn_dest_path = Button(path_frame, text="찾아보기" , width=10 , command=browse_dest_path)
btn_dest_path.pack(side="right" , padx=5, pady=5)

option_frame = LabelFrame(root, text="옵션")
option_frame.pack(padx=5, pady=5 ,ipadx=5, ipady= 10) 

lbl_width = Label(option_frame, text="가로넓이" , width=8)
lbl_width.pack(side="left")

opt_width = ["원본유지","1024","800","640"]
cmb_width = ttk.Combobox(option_frame, state="readonly" , values=opt_width , width=10)
cmb_width.current(0)
cmb_width.pack(side="left")

lbl_space = Label(option_frame, text="간격" , width=8)
lbl_space.pack(side="left")

opt_space = ["없음","좁게","보통","넓게"]
cmb_space= ttk.Combobox(option_frame, state="readonly" , values=opt_space , width=10)
cmb_space.current(0)
cmb_space.pack(side="left")
 
lbl_format = Label(option_frame, text="포맷" , width=8)
lbl_format.pack(side="left")

opt_format = ["PNG","JPG","BMP"]
cmb_format= ttk.Combobox(option_frame, state="readonly" , values=opt_format , width=10)
cmb_format.current(0)
cmb_format.pack(side="left")

progress_frame = LabelFrame(root, text="진행상황")
progress_frame.pack(fill="x" , padx=5, pady=5 , ipady= 5) 

p_var = DoubleVar() 
progress_bar = ttk.Progressbar(progress_frame, maximum = 100 , variable=p_var) 
progress_bar.pack(fill="x" ,padx=5, pady=5)

run_frame = Frame(root)
run_frame.pack(fill="x"  , padx=5, pady=5)

btn_close = Button(run_frame, padx=5, pady=5, width=12, text="닫기" , command=root.quit) 
btn_close.pack(side="right" , padx=5, pady=5)

btn_start = Button(run_frame, padx=5, pady=5, width=12, text="시작" , command=start)  # command=start 추가
btn_start.pack(side="right" , padx=5, pady=5)

root.resizable(False, False)
root.mainloop() 

 

 

 

더보기

블로그에는 우클릭 방지가 걸려있어요. 코드복사는 카페에서 가능 합니다.

투딩카페의 이 컨텐츠보기

 

 

파이썬 기초는 나도코딩님이 인프런사이트에서 강의한 내용을 바탕으로 공부한 내용을 정리한 것 입니다.
추천 :  ★

 

 

'코딩(개발) > 파이썬' 카테고리의 다른 글

파이썬 스크랩데이터 csv로 저장하기  (0) 2021.01.01
파이썬 웹스크래핑1  (0) 2020.12.31
파이썬 오토스크린 샷  (0) 2020.12.30
파이썬 GUI  (0) 2020.12.29
파이썬 게임 2  (0) 2020.12.27

댓글