나도 코딩 [활용편2]
공부 & 정리
| 공부 자료
[1] 소개
활용편 1과 동일
[2] 활용편 2 소개
GUI(Graphical User Interface) 프로그래밍 :
눈으로 볼 수 있는 프로그래밍
여러 GUI 라이브러리 중에서 tkinter를 공부할 것
프로젝트 소개 :
여러 이미지를 합치는 툴을 만들 것
[3] 기본 프레임
파이썬을 설치할 때 tkinter 모듈이 포함이 되어있기 때문에 따로 설치가 필요하지 않다
BUT
구름IDE에서는 템플릿 지정을 tkinter 프로젝트로 설정을 한 후에 해야한다.
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry("640x480") # 가로 * 세로
# root.geometry("640x480+100+300") # 가로 * 세로 + x좌표 + y좌표
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
[4] 버튼
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
btn1 = Button(root, text="버튼1")
btn1.pack()
btn2 = Button(root, padx=10, pady=5, text="버튼2")
btn2.pack()
btn3 = Button(root, padx=5, pady=10, text="버튼3")
btn3.pack()
btn4 = Button(root, width=10, height=3, text="버튼4")
btn4.pack()
# pad는 여백을 지정하는 느낌이기 때문에 글자에 맞춰서 변하지만
# width, height는 값이 고정되기 때문에 글자가 늘어나도 박스 크기 동일 (글자가 잘리더라도)
btn5 = Button(root, fg="red", bg="yellow", text="버튼5")
btn5.pack()
photo = PhotoImage(file="/workspace/NC_Utl_2_GUI2/gui_basic/image.png")
btn6 = Button(root, image=photo)
btn6.pack()
def btncmd():
print("버튼이 클릭되었습니다")
btn7 = Button(root, text="버튼이 클릭되었습니다", command = btncmd )
btn7.pack()
root.mainloop()
[5] 레이블
| CODE
# 레이블은 글자나 이미지를 보여주는 것이고 실제 어떠한 행동을 하지는 않음
from tkinter import *
root = Tk()
root.title("Hero GUI")
label1 = Label(root, text="안녕하세요")
label1.pack()
photo = PhotoImage(file="/workspace/NC_Utl_2_GUI2/gui_basic/image.png")
label2 = Label(root, image=photo)
label2.pack()
def change():
label1.config(text="또 만나요")
global photo2
photo2 = PhotoImage(file="/workspace/NC_Utl_2_GUI2/gui_basic/image2.png")
label2.config(image=photo2)
btn = Button(root, text="클릭", command=change)
btn.pack()
root.mainloop()
# 함수 내에서 레이블의 이미지 값등을 바꾸기 위해서는 그 값은 전역 변수로 선언해야함
[6] 텍스트 & 엔트리
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry = ("640*480") # 가로 * 세로
txt = Text(root, width=30, height=5)
txt.pack()
txt.insert(END, "글자를 입력하세요")
e = Entry(root, width=30)
e.pack()
e.insert(0, "한 줄만 입력하세요")
def btncmd():
# 내용 출력
print(txt.get("1.0", END)) # 1 : 첫번째 라인, 0 : 0번째 column 위치
print(e.get())
# 내용 삭제
txt.delete("1.0", END)
e.delete(0, END)
btn = Button(root, text="클릭", command=btncmd)
btn.pack()
root.mainloop()
# 여러줄이 필요한 것은 txt를 쓴다
# e는 엔터를 입력할 수 없다
# e는 한줄로 입력을 받을때, such as 아이디, 비밀번호등
[7] 리스트 박스
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry = ("640*480") # 가로 * 세로
listbox = Listbox(root, selectmode="extended", height=0)
# height가 0일때는 리스트에 있는 값을 다 보여주고, 다른 수 (such as 3)일 때는 그 수만큼 보여주고 스크롤이 됌
listbox.insert(0,"Apple")
listbox.insert(1,"Strawberry")
listbox.insert(2,"Banana")
listbox.insert(END,"Watermelon")
listbox.insert(END,"Grape")
listbox.pack()
def btncmd():
# 삭제
# listbox.delete(0) # 맨 앞 항목을 삭제, END는 맨 뒤 항복을 삭제
# 갯수 확인
# print("리스트에는", listbox.size(), "개가 있어요")
# 항목 확인 ( 시작 idx, 끝 idx )
# print("1번째부터 3번째까지의 항목 : ", listbox.get(0,2))
# 선택된 항목 확인
# print("선택된 항목 : ", listbox.curselection())
btn = Button(root, text="클릭", command=btncmd)
btn.pack()
root.mainloop()
[8] 체크 버튼
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry = ("640*480")
chkvar = IntVar() # chkvar에 int형으로 값을 저장한다
chkbox = Checkbutton(root, text="오늘 하루 보지 않기", variable=chkvar)
# chkbox.select() # 자동 선택 처리
# chkbox.deselect() # 선택 해제 처리
chkbox.pack()
chkvar2 = Intvar()
chkbox2 = Checkbutton(root, text="일주일동안 보지 않기", variable=chkvar2)
chkbox2.pack()
def btncmd():
print(chkvar.get()) # 0 : 체크 해제, 1 : 체크
print(chkvar2.get())
btn = Button(root, text="클릭", command=btncmd)
btn.pack()
root.mainloop()
[9] 라디오 버튼
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry = ("640*480") # 가로 * 세로
Label(root, text="메뉴를 선택하세요").pack()
burger_var = IntVar() # 여기에 int형으로 값을 저장한다
btn_burger1 = Radiobutton(root, text="햄버거", value=1, variable=burger_var)
btn_burger2 = Radiobutton(root, text="치즈버거", value=2, variable=burger_var)
btn_burger3 = Radiobutton(root, text="치킨버거", value=3, variable=burger_var)
btn_burger1.pack()
btn_burger2.pack()
btn_burger3.pack()
Label(root, text="음료를 선택하세요").pack() # 기존에 pack()을 별도로 쓰는 것과 동일한 실행
drink_var = StringVar() # 여기에 String형으로 값을 저장한다
btn_drink1 = Radiobutton(root, text="콜라", value="콜라", variable=drink_var)
btn_drink1.select() # 기본값 선택
btn_drink2 = Radiobutton(root, text="사이다", value="사이다", variable=drink_var)
btn_drink1.pack()
btn_drink2.pack()
def btncmd():
print(burger_var.get()) # 햄버거 중 선택된 라디오 항목의 값(Value)를 출력
print(drink_var.get()) # 음료 중 선택된 라디오 항목의 값(Value)를 출력
btn = Button(root, text="클릭", command=btncmd)
btn.pack()
root.mainloop()
[10] 콤보 박스
import tkinter.ttk as ttk
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry = ("640*480") # 가로 * 세로
values = [ str(i) + "일" for i in range(1,32)] # 1~31까지의 숫자
combobox = ttk.Combobox(root, height=5, values=values) # height 값은 목록 보여주는 길이 차이
combobox.pack()
combobox.set("카드 결제일") # 최초 목록 제목 설정 (+ 버튼 클릭을 통한 값 설정도 가능)
readonly_combobox = ttk.Combobox(root, height=10, values=values, state="readonly") # state="readonly"는 박스에 텍스트 입력이 안되는 것
readonly_combobox.current(0) #0번째 인덱스 값 선택
readonly_combobox.pack()
def btncmd():
print(combobox.get()) # 선택된 값 출력
print(readonly_combobox.get())
btn = Button(root, text="클릭", command=btncmd)
btn.pack()
root.mainloop()
[11] 프로그레스 바
| CODE
import time
import tkinter.ttk as ttk
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry = ("640*480") # 가로 * 세로
# progressbar = ttk.Progressbar(root, maximum=100, mode="indeterminate")
# progressbar = ttk.Progressbar(root, maximum=100, mode="determinate")
# progressbar.start(5) # 5ms마다 움직임
# progressbar.pack()
# def btncmd():
# progressbar.stop() # 작동 중지
# btn = Button(root, text="중지", command=btncmd)
# btn.pack()
p_var2 = DoubleVar()
progressbar2 = ttk.Progressbar(root, maximum=100, length=150, variable=p_var2)
progressbar2.pack()
def btncmd():
for i in range(1,101) : # 1~2100
time.sleep(0.01) # 0.01초 대기
p_var2.set(i) # progressbar의 값 설정
progressbar2.update() # ui 업데이트
print(p_var2.get())
btn = Button(root, text="시작", command=btncmd)
btn.pack()
root.mainloop()
[12] 메뉴
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry("640x480")
def create_new_file():
print("새 파일이 만들어졌습니다")
menu = Menu(root)
# File 메뉴
menu_file = Menu(menu, tearoff=0)
menu_file.add_command(label="New File", command = create_new_file)
menu_file.add_command(label="New Window")
menu_file.add_separator()
menu_file.add_command(label="Open File")
menu_file.add_separator()
menu_file.add_command(label="Save All", state="disable") #비활성화
menu_file.add_separator()
menu_file.add_command(label="Exit", command=root.quit)
menu.add_cascade(label="File", menu=menu_file)
# Edit 메뉴 (빈값)
menu.add_cascade(label="Edit")
# Language 메뉴 추가 (radio 버튼을 통해서 택1)
menu_lang = Menu(menu, tearoff=0)
menu_lang.add_radiobutton(label="C++")
menu_lang.add_radiobutton(label="Python")
menu_lang.add_radiobutton(label="Java")
menu.add_cascade(label="Language", menu=menu_lang)
# View 메뉴
menu_view = Menu(menu, tearoff=0)
menu_view.add_checkbutton(label="Show Minimap")
menu.add_cascade(label="View", menu=menu_view)
root.config(menu=menu)
root.mainloop()
[13] 메시지 박스
| CODE
import tkinter.messagebox as msgbox
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry("640x480")
def info():
msgbox.showinfo("알림", "정상적으로 예매 완료되었습니다")
def warn():
msgbox.showwarning("경고", "해당 좌석은 매진되었습니다")
def error():
msgbox.showerror("에러", "결제 오류가 발생했습니다")
def okcancel():
msgbox.askokcancel("확인/취소", "해당 좌석은 유아동반석입니다. 예매하겠습니까?")
def retrycancel():
msgbox.askretrycancel("확인/취소", "일시적인 오류입니다. 다시 시도하겠습니까?")
def yesno():
msgbox.askyesno("확인/취소", "해당 좌석은 역방향입니다. 예매하겠습니까?")
def yesnocancel():
response = msgbox.askyesnocancel(title=None, message="예매 내역이 저장되지 않았습니다 \n 저장 후 프로그램을 종료하겠습니까?")
# 네 : 저장 후 종료
# 아니오 : 저장하지 않고 종료
# 취소 : 프로그램 종료 취소 (현재 화면에서 계속 작업)
print("응답:", response) # True, False, None -> 예 1, 아니오 0, 그 외
if response ==1: # 네
print("예")
elif response ==0: # 아니오
print("아니오")
else :
print("취소")
# True 값의 경우(Ex : 네, 재시도, 확인등)는 값이 1,
# False 값의 경우(Ex : 아니오, 취소등)는 값이 0,
# 그외의 값(Ex : 네/아니오/취소에서 취소 같은)은 else
Button(root, command=info, text="알림").pack()
Button(root, command=warn, text="경고").pack()
Button(root, command=error, text="에러").pack()
Button(root, command=okcancel, text="확인 취소").pack()
Button(root, command=retrycancel, text="재시도 취소").pack()
Button(root, command=yesno, text="예 아니오").pack()
Button(root, command=yesnocancel, text="예 아니오 취소").pack()
root.mainloop()
[14] 프레임
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry("640x480")
Label(root, text="메뉴를 선택하세요").pack(side="top")
Button(root, text="주문하기").pack(side="bottom")
# 버거 프레임
frame_burger = Frame(root, relief="solid", bd=1)
frame_burger.pack(side="left", fill="both", expand=True)
Button(frame_burger, text="햄버거").pack()
Button(frame_burger, text="치즈버거").pack()
Button(frame_burger, text="치킨버거").pack()
# 음료 프레임
frame_drink = LabelFrame(root, text="음료")
frame_drink.pack(side="right", fill="both", expand=True)
Button(frame_drink, text="콜라").pack()
Button(frame_drink, text="사이다").pack()
root.mainloop()
[15] 스크롤 바
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry("640x480")
# 스크롤바와 스크롤바 대상이 되는 위젯을 하나의 프레임에 넣는게 관리가 편함
frame = Frame(root)
frame.pack()
scrollbar = Scrollbar(frame)
scrollbar.pack(side="right", fill="y")
# set이 없으면 스크롤을 내려도 다시 올라움
listbox = Listbox(frame, selectmode="extended", height=10, yscrollcommand = scrollbar.set)
for i in range(1,32): # 1일 ~31일
listbox.insert(END, str(i) + "일") # 1일, 2일, ...
listbox.pack(side="left")
# 서로 맵핑을 해줌으로써 스크롤바 움직임에 맞추어서 리스트도 움직임
scrollbar.config(command=listbox.yview)
root.mainloop()
[16] 그리드 기본 &
[17] 그리드 심화
| Explan
Greed : 격자
| CODE
from tkinter import *
root = Tk()
root.title("Hero GUI")
root.geometry("640x480")
# btn1 = Button(root, text="버튼1")
# btn2 = Button(root, text="버튼2")
# pack이 쌓는 느낌이라면 greed는 지정된 좌표에 넣는 느낌이다
# btn1.pack()
# btn2.pack()
# btn1.pack(side="left")
# btn2.pack(side="right")
# btn1.grid(row=0, column=0)
# btn2.grid(row=1, column=1)
# sticky는 N(북), E(동), W(서), S(남) 내가 원하는 방향으로 붙여서 칸을 붙이는 것
# padx=..., pady=...,
# Button에 pad를 넣으면 글자 기준으로 칸에 공간을 두는 것
# grid에 pad를 넣으면 그리드 간에 공간을 두는 것
# width, height
# padx, pady는 글자 기준으로 공간을 두는 것이기 때문에 크기가 살짝 다르다
# BUT width, height을 쓰면 값이 고정
# 맨 윗줄
btn_f16 = Button(root, text="f16", width=5, height=2)
btn_f17 = Button(root, text="f17", width=5, height=2)
btn_f18 = Button(root, text="f18", width=5, height=2)
btn_f19 = Button(root, text="f19", width=5, height=2)
btn_f16.grid(row=0, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_f17.grid(row=0, column=1, sticky=N+E+W+S, padx=3, pady=3)
btn_f18.grid(row=0, column=2, sticky=N+E+W+S, padx=3, pady=3)
btn_f19.grid(row=0, column=3, sticky=N+E+W+S, padx=3, pady=3)
# clear 줄
btn_clear = Button(root, text="clear", width=5, height=2)
btn_equal = Button(root, text="=", width=5, height=2)
btn_div = Button(root, text="/", width=5, height=2)
btn_mul = Button(root, text="*", width=5, height=2)
btn_clear.grid(row=1, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_equal.grid(row=1, column=1, sticky=N+E+W+S, padx=3, pady=3)
btn_div.grid(row=1, column=2, sticky=N+E+W+S, padx=3, pady=3)
btn_mul.grid(row=1, column=3, sticky=N+E+W+S, padx=3, pady=3)
# 7 시작 줄
btn_7 = Button(root, text="7", width=5, height=2)
btn_8 = Button(root, text="8", width=5, height=2)
btn_9 = Button(root, text="9", width=5, height=2)
btn_sub = Button(root, text="-", width=5, height=2)
btn_7.grid(row=2, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_8.grid(row=2, column=1, sticky=N+E+W+S, padx=3, pady=3)
btn_9.grid(row=2, column=2, sticky=N+E+W+S, padx=3, pady=3)
btn_sub.grid(row=2, column=3, sticky=N+E+W+S, padx=3, pady=3)
# 4 시작 줄
btn_4 = Button(root, text="4", width=5, height=2)
btn_5 = Button(root, text="5", width=5, height=2)
btn_6 = Button(root, text="6", width=5, height=2)
btn_add = Button(root, text="+", width=5, height=2)
btn_4.grid(row=3, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_5.grid(row=3, column=1, sticky=N+E+W+S, padx=3, pady=3)
btn_6.grid(row=3, column=2, sticky=N+E+W+S, padx=3, pady=3)
btn_add.grid(row=3, column=3, sticky=N+E+W+S, padx=3, pady=3)
# 1 시작 줄
btn_1 = Button(root, text="1", width=5, height=2)
btn_2 = Button(root, text="2", width=5, height=2)
btn_3 = Button(root, text="3", width=5, height=2)
btn_enter = Button(root, text="enter", width=5, height=2) # 세로로 합쳐짐
btn_1.grid(row=4, column=0, sticky=N+E+W+S, padx=3, pady=3)
btn_2.grid(row=4, column=1, sticky=N+E+W+S, padx=3, pady=3)
btn_3.grid(row=4, column=2, sticky=N+E+W+S, padx=3, pady=3)
btn_enter.grid(row=4, column=3, rowspan=2, sticky=N+E+W+S, padx=3, pady=3) # 현재 위치로부터 아래쪽으로 몇줄을 더함
# 0 시작 줄
btn_0 = Button(root, text="0", width=5, height=2) # 가로로 합쳐짐
btn_point = Button(root, text=".", width=5, height=2)
btn_0.grid(row=5, column=0, columnspan=2, sticky=N+E+W+S, padx=3, pady=3) # 현재 위치로부터 오른쪽으로 몇칸 더함
btn_point.grid(row=5, column=2, sticky=N+E+W+S, padx=3, pady=3)
root.mainloop()
[18] QUIZ
'''
Quiz) tkinter를 이용한 메모장 프로그램 만들기
GUI 조건)
1. title : 제목 없음 - Windows 메모장
2. 메뉴 : 파일, 편집, 서식, 보기, 도움말
3. 실제 메뉴 구현 : 파일 메뉴 내에서 열기, 저장, 끝내기 3개만 처리
3-1. 열기 : mynote.txt 파일 내용 열어서 보여주기
3-2. 저장 : mynote.txt 파일에 현재 내용 저장하기
3-3. 끝내기 : 프로그램 종료
4. 프로그램 시작 시 본문은 비어 있는 상태
5. 하단 status 바는 필요 없음
6. 프로그램 크기, 위치는 자유롭게 하되, 크기 조절 가능해야 함
7. 본문 우측에 상하 스크롤바 넣기
'''
import os
from tkinter import *
root = Tk()
root.title("Windows 메모장")
root.geometry("640x480")
root.resizable(True, True)
# 열기, 저장 파일 이름
filename = "mynote.txt"
def open_file():
if os.path.isfile(filename): # 파일 있으면 True, 없으면 False
with open(filename, "r", encoding="utf8") as file :
txt.delete("1.0", END) # 텍스트 위젯 본문 삭제
txt.insert(END, file.read()) # 파일 내용을 본문에 입력
def save_file():
with open(filename, "w", encoding="utf8") as file:
file.write(txt.get("1.0", END)) # 모든 내용을 가져와서 저장
menu = Menu(root)
# 파일 메뉴
menu_file = Menu(menu, tearoff=0)
menu_file.add_command(label="열기", command = open_file )
menu_file.add_command(label="저장", command = save_file )
menu_file.add_command(label="끝내기", command = root.quit )
menu.add_cascade(label="파일", menu=menu_file)
# 편집 메뉴
menu_edit = Menu(menu, tearoff=0)
menu.add_cascade(label="편집", menu=menu_edit)
# 서식 메뉴
menu_form = Menu(menu, tearoff=0)
menu.add_cascade(label="서식", menu=menu_form)
# 보기 메뉴
menu_view = Menu(menu, tearoff=0)
menu.add_cascade(label="보기", menu=menu_view)
# 도움말 메뉴
menu_help = Menu(menu, tearoff=0)
menu.add_cascade(label="도움말", menu=menu_help)
# 스크롤바
scrollbar = Scrollbar(root)
scrollbar.pack(side="right", fill="y")
# 본문 영역
txt = Text(root, yscrollcommand = scrollbar.set)
txt.pack(side="left", fill="both", expand=True)
scrollbar.config(command=txt.yview)
root.config(menu=menu)
root.mainloop()
[19] 프로젝트 [이미지 합치기 프로그램 만들기]
| Explanation
[Project]
여러 이미지를 합치는 프로그램을 만드시오
[사용자 시나리오]
1. 사용자는 합치려는 이미지를 1개 이상 선택한다.
2. 합쳐진 이미지가 저장될 경로를 지정한다.
3. 가로넓이, 간격, 포맷 옵션을 지정한다.
4. 시작 버튼을 통해 이미지를 합친다.
5. 닫기 버튼을 통해 프로그램을 종료한다.
[기능 명세]
1. 파일 추가 : 리스트 박스에 파일 추가
2. 선택삭제 : 리스트 박스에서 선택된 항목 삭제
3. 찾아보기 : 저장 폴더를 선택하면 텍스트 위젯에 입력
4. 가로넓이 : 이미지 넓이 지정 ( 원본유지, 1024, 800, 640 )
5. 간격 : 이미지 간의 간격 지정 ( 없음, 좁게, 보통, 넓게 )
6. 포맷 : 저장 이미지 포맷 지정 ( PNG, JPG, BMP )
7. 시작 : 이미지 합치기 작업 실행
8. 진행상황 : 현재 진행중인 파일 순서에 맞게 반영
9. 닫기 : 프로그램 종료
| Example
[20] 레이아웃 전반전 &
[21] 레이아웃 후반전 &
[22] 레이아웃 연장전
| CODE
import tkinter.ttk as ttk
from tkinter import *
root = Tk()
root.title("Hero 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")
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)
# 옵션 프레임
frame_option = LabelFrame(root, text="옵션")
frame_option.pack(padx=5, pady=5, ipady=5)
# 1. 가로 넓이 옵션
# 가로 넓이 레이블
lbl_width = Label(frame_option, text="가로넓이", width=8)
lbl_width.pack(side="left", padx=5, pady=5)
# 가로 넓이 콤보
opt_width = ["원본유지", "1024", "800", "640"]
cmb_width = ttk.Combobox(frame_option, state="readonly", values=opt_width, width=10)
cmb_width.current(0)
cmb_width.pack(side="left", padx=5, pady=5)
# 2. 간격 옵션
# 간격 레이블
lbl_space = Label(frame_option, text="간격", width=8)
lbl_space.pack(side="left", padx=5, pady=5)
# 간격 콤보
opt_space = ["없음", "좁게", "보통", "넓게"]
cmb_space = ttk.Combobox(frame_option, state="readonly", values=opt_space, width=10)
cmb_space.current(0)
cmb_space.pack(side="left", padx=5, pady=5)
# 3. 포맷 옵션
# 포맷 레이블
lbl_format = Label(frame_option, text="포맷", width=8)
lbl_format.pack(side="left", padx=5, pady=5)
# 포맷 콤보
opt_format = ["PNG", "JPG", "BMP"]
cmb_format = ttk.Combobox(frame_option, state="readonly", values=opt_format, width=10)
cmb_format.current(0)
cmb_format.pack(side="left", padx=5, pady=5)
# 진행상황 Progress Bar
frame_progress = LabelFrame(root, text="진행상황")
frame_progress.pack(fill="x", padx=5, pady=5, ipady=5)
p_var = DoubleVar()
progress_bar = ttk.Progressbar(frame_progress, maximum=100, variable=p_var)
progress_bar.pack(fill="x", padx=5, pady=5)
# 실행 프레임
frame_run = Frame(root)
frame_run.pack(fill="x", padx=5, pady=5)
btn_close = Button(frame_run, padx=5, pady=5, text="닫기", width=10, command=root.quit)
btn_close.pack(side="right", padx=5, pady=5)
btn_start = Button(frame_run, padx=5, pady=5, text="시작", width=10)
btn_start.pack(side="right", padx=5, pady=5)
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
[23] 파일 추가 & 선택 삭제
| CODE
import tkinter.ttk as ttk
from tkinter import * # _all_
from tkinter import filedialog # 서브 모듈이기 때문에 별도로 improt
root = Tk()
root.title("Hero GUI")
# 파일 추가
def add_file():
files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요",\
filetypes=(("PNG 파일", "*.png"), ("모든 파일", "*.*"),), \
initialdir=r"C:/workspace/NC_Utl_2_GUI@/gui_basic") # 최초 위치 지정이 작동을 하지 않음. WHY?
# 최초에 사용자가 지정한 경로를 보여줌
# r은 에스케이프 문자를 무시하는, raw string
for file in files:
list_file.insert(END, file)
# 선택 삭제
def del_file():
print(list_file.curselection())
# 인덱스 뒤에 것부터 지워야 인덱스에 변화가 안 생김
for index in reversed(list_file.curselection()):
list_file.delete(index)
# 파일 프레임 ( 파일추가, 선택 삭제 )
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="파일추가", 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)
# 옵션 프레임
frame_option = LabelFrame(root, text="옵션")
frame_option.pack(padx=5, pady=5, ipady=5)
# 1. 가로 넓이 옵션
# 가로 넓이 레이블
lbl_width = Label(frame_option, text="가로넓이", width=8)
lbl_width.pack(side="left", padx=5, pady=5)
# 가로 넓이 콤보
opt_width = ["원본유지", "1024", "800", "640"]
cmb_width = ttk.Combobox(frame_option, state="readonly", values=opt_width, width=10)
cmb_width.current(0)
cmb_width.pack(side="left", padx=5, pady=5)
# 2. 간격 옵션
# 간격 레이블
lbl_space = Label(frame_option, text="간격", width=8)
lbl_space.pack(side="left", padx=5, pady=5)
# 간격 콤보
opt_space = ["없음", "좁게", "보통", "넓게"]
cmb_space = ttk.Combobox(frame_option, state="readonly", values=opt_space, width=10)
cmb_space.current(0)
cmb_space.pack(side="left", padx=5, pady=5)
# 3. 포맷 옵션
# 포맷 레이블
lbl_format = Label(frame_option, text="포맷", width=8)
lbl_format.pack(side="left", padx=5, pady=5)
# 포맷 콤보
opt_format = ["PNG", "JPG", "BMP"]
cmb_format = ttk.Combobox(frame_option, state="readonly", values=opt_format, width=10)
cmb_format.current(0)
cmb_format.pack(side="left", padx=5, pady=5)
# 진행상황 Progress Bar
frame_progress = LabelFrame(root, text="진행상황")
frame_progress.pack(fill="x", padx=5, pady=5, ipady=5)
p_var = DoubleVar()
progress_bar = ttk.Progressbar(frame_progress, maximum=100, variable=p_var)
progress_bar.pack(fill="x", padx=5, pady=5)
# 실행 프레임
frame_run = Frame(root)
frame_run.pack(fill="x", padx=5, pady=5)
btn_close = Button(frame_run, padx=5, pady=5, text="닫기", width=10, command=root.quit)
btn_close.pack(side="right", padx=5, pady=5)
btn_start = Button(frame_run, padx=5, pady=5, text="시작", width=10)
btn_start.pack(side="right", padx=5, pady=5)
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
[24] 저장 경로
| CODE
import tkinter.ttk as ttk
import tkinter.messagebox as msgbox
from tkinter import * # _all_
from tkinter import filedialog # 서브 모듈이기 때문에 별도로 improt
root = Tk()
root.title("Hero GUI")
# 파일 추가
def add_file():
files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요",\
filetypes=(("PNG 파일", "*.png"), ("모든 파일", "*.*"),), \
initialdir=r"C:/workspace/NC_Utl_2_GUI@/gui_basic") # 최초 위치 지정이 작동을 하지 않음. WHY?
# 최초에 사용자가 지정한 경로를 보여줌
# r은 에스케이프 문자를 무시하는, raw string
for file in files:
list_file.insert(END, file)
# 선택 삭제
def del_file():
print(list_file.curselection())
# 인덱스 뒤에 것부터 지워야 인덱스에 변화가 안 생김
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 start():
# 각 옵션들 값 확인
print("가로넓이 : ", cmb_width.get())
print("간격 : ", cmb_space.get())
print("포맷 : ", cmb_foramt.get())
# 파일 목록 확인
if list_file.size() == 0:
msgbox.showwarning("경고", "이미지 파일을 추가하세요")
return
# 저장 경로 확인
if len(txt_dest_path.get()) == 0:
msgbox.showwarning("경고", "저장 경로를 선택하세요")
return
# 파일 프레임 ( 파일추가, 선택 삭제 )
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="파일추가", 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) # Entry로 만들었기 때문에 위에서 (0,END)로 쓰는 것이고 txt였다면 ("1.0", END)
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)
# 옵션 프레임
frame_option = LabelFrame(root, text="옵션")
frame_option.pack(padx=5, pady=5, ipady=5)
# 1. 가로 넓이 옵션
# 가로 넓이 레이블
lbl_width = Label(frame_option, text="가로넓이", width=8)
lbl_width.pack(side="left", padx=5, pady=5)
# 가로 넓이 콤보
opt_width = ["원본유지", "1024", "800", "640"]
cmb_width = ttk.Combobox(frame_option, state="readonly", values=opt_width, width=10)
cmb_width.current(0)
cmb_width.pack(side="left", padx=5, pady=5)
# 2. 간격 옵션
# 간격 레이블
lbl_space = Label(frame_option, text="간격", width=8)
lbl_space.pack(side="left", padx=5, pady=5)
# 간격 콤보
opt_space = ["없음", "좁게", "보통", "넓게"]
cmb_space = ttk.Combobox(frame_option, state="readonly", values=opt_space, width=10)
cmb_space.current(0)
cmb_space.pack(side="left", padx=5, pady=5)
# 3. 포맷 옵션
# 포맷 레이블
lbl_format = Label(frame_option, text="포맷", width=8)
lbl_format.pack(side="left", padx=5, pady=5)
# 포맷 콤보
opt_format = ["PNG", "JPG", "BMP"]
cmb_format = ttk.Combobox(frame_option, state="readonly", values=opt_format, width=10)
cmb_format.current(0)
cmb_format.pack(side="left", padx=5, pady=5)
# 진행상황 Progress Bar
frame_progress = LabelFrame(root, text="진행상황")
frame_progress.pack(fill="x", padx=5, pady=5, ipady=5)
p_var = DoubleVar()
progress_bar = ttk.Progressbar(frame_progress, maximum=100, variable=p_var)
progress_bar.pack(fill="x", padx=5, pady=5)
# 실행 프레임
frame_run = Frame(root)
frame_run.pack(fill="x", padx=5, pady=5)
btn_close = Button(frame_run, padx=5, pady=5, text="닫기", width=10, command=root.quit)
btn_close.pack(side="right", padx=5, pady=5)
btn_start = Button(frame_run, padx=5, pady=5, text="시작", width=10, command=start)
btn_start.pack(side="right", padx=5, pady=5)
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
[25] 자동 스크린샷
| Explanation
"자동 스크린샷 프로그램"은 기존 메인 프로젝트인 "이미지 합치기 프로그램"과
별도인 프로그램이다. "이미지 합치기 프로그램"에서 쓰일 이미지를 위해서
만들어낸 유틸리티일 뿐이다.
| CODE
import time
from PIL import ImageGrab # Python Image Library
time.sleep(5) # 5초 대기 : 사용자가 준비하는 시간
for i in range(1,11): #2초 간격으로 10개 이미지 저장
img = ImageGrab.grab() # 현재 스크린 이미지를 가져옴
img.save("image{}.png".format(i)) # 파일로 저장 (image1.png ~ image10.png)
time.sleep(2) # 2초 단위
[26] 이미지 합치기
| CODE
import os
import tkinter.ttk as ttk
import tkinter.messagebox as msgbox
from tkinter import * # _all_
from tkinter import filedialog # 서브 모듈이기 때문에 별도로 improt
from PIL import Image
root = Tk()
root.title("Hero GUI")
# 파일 추가
def add_file():
files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요",\
filetypes=(("PNG 파일", "*.png"), ("모든 파일", "*.*"),), \
initialdir=r"C:/workspace/NC_Utl_2_GUI") # 최초 위치 지정이 작동을 하지 않음. WHY?
# 최초에 사용자가 지정한 경로를 보여줌
# r은 에스케이프 문자를 무시하는, raw string
for file in files:
list_file.insert(END, file)
# 선택 삭제
def del_file():
print(list_file.curselection())
# 인덱스 뒤에 것부터 지워야 인덱스에 변화가 안 생김
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(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]
height = [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 # 이미지를 합치면서 변해가는 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(), "hero_photo.jpg")
result_img.save(dest_path)
msgbox.showinfo("알림", "작업이 완료되었습니다.")
# 시작
def start():
# 각 옵션들 값 확인
print("가로넓이 : ", cmb_width.get())
print("간격 : ", cmb_space.get())
print("포맷 : ", cmb_foramt.get())
# 파일 목록 확인
if list_file.size() == 0:
msgbox.showwarning("경고", "이미지 파일을 추가하세요")
return
# 저장 경로 확인
if len(txt_dest_path.get()) == 0:
msgbox.showwarning("경고", "저장 경로를 선택하세요")
return
# 이미지 통합 작업
merge_image() # 함수가 길어질 것 같아서 별도로 빼서 함수 정의
# 파일 프레임 ( 파일추가, 선택 삭제 )
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="파일추가", 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) # Entry로 만들었기 때문에 위에서 (0,END)로 쓰는 것이고 txt였다면 ("1.0", END)
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)
# 옵션 프레임
frame_option = LabelFrame(root, text="옵션")
frame_option.pack(padx=5, pady=5, ipady=5)
# 1. 가로 넓이 옵션
# 가로 넓이 레이블
lbl_width = Label(frame_option, text="가로넓이", width=8)
lbl_width.pack(side="left", padx=5, pady=5)
# 가로 넓이 콤보
opt_width = ["원본유지", "1024", "800", "640"]
cmb_width = ttk.Combobox(frame_option, state="readonly", values=opt_width, width=10)
cmb_width.current(0)
cmb_width.pack(side="left", padx=5, pady=5)
# 2. 간격 옵션
# 간격 레이블
lbl_space = Label(frame_option, text="간격", width=8)
lbl_space.pack(side="left", padx=5, pady=5)
# 간격 콤보
opt_space = ["없음", "좁게", "보통", "넓게"]
cmb_space = ttk.Combobox(frame_option, state="readonly", values=opt_space, width=10)
cmb_space.current(0)
cmb_space.pack(side="left", padx=5, pady=5)
# 3. 포맷 옵션
# 포맷 레이블
lbl_format = Label(frame_option, text="포맷", width=8)
lbl_format.pack(side="left", padx=5, pady=5)
# 포맷 콤보
opt_format = ["PNG", "JPG", "BMP"]
cmb_format = ttk.Combobox(frame_option, state="readonly", values=opt_format, width=10)
cmb_format.current(0)
cmb_format.pack(side="left", padx=5, pady=5)
# 진행상황 Progress Bar
frame_progress = LabelFrame(root, text="진행상황")
frame_progress.pack(fill="x", padx=5, pady=5, ipady=5)
p_var = DoubleVar()
progress_bar = ttk.Progressbar(frame_progress, maximum=100, variable=p_var)
progress_bar.pack(fill="x", padx=5, pady=5)
# 실행 프레임
frame_run = Frame(root)
frame_run.pack(fill="x", padx=5, pady=5)
btn_close = Button(frame_run, padx=5, pady=5, text="닫기", width=10, command=root.quit)
btn_close.pack(side="right", padx=5, pady=5)
btn_start = Button(frame_run, padx=5, pady=5, text="시작", width=10, command=start)
btn_start.pack(side="right", padx=5, pady=5)
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
[27] 프로그레스 바 연동
| CODE
import os
import tkinter.ttk as ttk
import tkinter.messagebox as msgbox
from tkinter import * # _all_
from tkinter import filedialog # 서브 모듈이기 때문에 별도로 improt
from PIL import Image
root = Tk()
root.title("Hero GUI")
# 파일 추가
def add_file():
files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요",\
filetypes=(("PNG 파일", "*.png"), ("모든 파일", "*.*"),), \
initialdir=r"C:/workspace/NC_Utl_2_GUI") # 최초 위치 지정이 작동을 하지 않음. WHY?
# 최초에 사용자가 지정한 경로를 보여줌
# r은 에스케이프 문자를 무시하는, raw string
for file in files:
list_file.insert(END, file)
# 선택 삭제
def del_file():
print(list_file.curselection())
# 인덱스 뒤에 것부터 지워야 인덱스에 변화가 안 생김
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(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]
height = [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 # 이미지를 합치면서 변해가는 y 위치
for img in images:
result_img.paste(img, (0, y_offset))
y_offset += img.size[1] # height 값만큼 더하기
for idx, img in enumerate(images):
result_img.paste(img, (0, y_offset))
y_offset += img.size[1]
progress = (idx+1) / len(images) * 100 # 실제 퍼센트 정보를 계산
p_var.set(progress)
progress_bar.update()
dest_path = os.path.join(txt_dest_path.get(), "hero_photo.jpg")
result_img.save(dest_path)
msgbox.showinfo("알림", "작업이 완료되었습니다.")
# 시작
def start():
# 각 옵션들 값 확인
print("가로넓이 : ", cmb_width.get())
print("간격 : ", cmb_space.get())
print("포맷 : ", cmb_foramt.get())
# 파일 목록 확인
if list_file.size() == 0:
msgbox.showwarning("경고", "이미지 파일을 추가하세요")
return
# 저장 경로 확인
if len(txt_dest_path.get()) == 0:
msgbox.showwarning("경고", "저장 경로를 선택하세요")
return
# 이미지 통합 작업
merge_image() # 함수가 길어질 것 같아서 별도로 빼서 함수 정의
# 파일 프레임 ( 파일추가, 선택 삭제 )
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="파일추가", 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) # Entry로 만들었기 때문에 위에서 (0,END)로 쓰는 것이고 txt였다면 ("1.0", END)
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)
# 옵션 프레임
frame_option = LabelFrame(root, text="옵션")
frame_option.pack(padx=5, pady=5, ipady=5)
# 1. 가로 넓이 옵션
# 가로 넓이 레이블
lbl_width = Label(frame_option, text="가로넓이", width=8)
lbl_width.pack(side="left", padx=5, pady=5)
# 가로 넓이 콤보
opt_width = ["원본유지", "1024", "800", "640"]
cmb_width = ttk.Combobox(frame_option, state="readonly", values=opt_width, width=10)
cmb_width.current(0)
cmb_width.pack(side="left", padx=5, pady=5)
# 2. 간격 옵션
# 간격 레이블
lbl_space = Label(frame_option, text="간격", width=8)
lbl_space.pack(side="left", padx=5, pady=5)
# 간격 콤보
opt_space = ["없음", "좁게", "보통", "넓게"]
cmb_space = ttk.Combobox(frame_option, state="readonly", values=opt_space, width=10)
cmb_space.current(0)
cmb_space.pack(side="left", padx=5, pady=5)
# 3. 포맷 옵션
# 포맷 레이블
lbl_format = Label(frame_option, text="포맷", width=8)
lbl_format.pack(side="left", padx=5, pady=5)
# 포맷 콤보
opt_format = ["PNG", "JPG", "BMP"]
cmb_format = ttk.Combobox(frame_option, state="readonly", values=opt_format, width=10)
cmb_format.current(0)
cmb_format.pack(side="left", padx=5, pady=5)
# 진행상황 Progress Bar
frame_progress = LabelFrame(root, text="진행상황")
frame_progress.pack(fill="x", padx=5, pady=5, ipady=5)
p_var = DoubleVar()
progress_bar = ttk.Progressbar(frame_progress, maximum=100, variable=p_var)
progress_bar.pack(fill="x", padx=5, pady=5)
# 실행 프레임
frame_run = Frame(root)
frame_run.pack(fill="x", padx=5, pady=5)
btn_close = Button(frame_run, padx=5, pady=5, text="닫기", width=10, command=root.quit)
btn_close.pack(side="right", padx=5, pady=5)
btn_start = Button(frame_run, padx=5, pady=5, text="시작", width=10, command=start)
btn_start.pack(side="right", padx=5, pady=5)
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
[28] zip
| Explanation
zip 라이브러리에 대한 설명.
프로젝트에 필요한 부분, 보충 설명
기존에 있던 부분을 zip 라이브러리를 이용해서 교체함
| CODE
import os
import tkinter.ttk as ttk
import tkinter.messagebox as msgbox
from tkinter import * # _all_
from tkinter import filedialog # 서브 모듈이기 때문에 별도로 improt
from PIL import Image
root = Tk()
root.title("Hero GUI")
# 파일 추가
def add_file():
files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요",\
filetypes=(("PNG 파일", "*.png"), ("모든 파일", "*.*"),), \
initialdir=r"C:/workspace/NC_Utl_2_GUI") # 최초 위치 지정이 작동을 하지 않음. WHY?
# 최초에 사용자가 지정한 경로를 보여줌
# r은 에스케이프 문자를 무시하는, raw string
for file in files:
list_file.insert(END, file)
# 선택 삭제
def del_file():
print(list_file.curselection())
# 인덱스 뒤에 것부터 지워야 인덱스에 변화가 안 생김
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(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] # zip을 사용하기 전 방법
# heights = [x.size[1] for x in images] # zip을 사용하기 전 방법
# [(10,10). (20,20), (30,30)]
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 # 이미지를 합치면서 변해가는 y 위치
for img in images:
result_img.paste(img, (0, y_offset))
y_offset += img.size[1] # height 값만큼 더하기
for idx, img in enumerate(images):
result_img.paste(img, (0, y_offset))
y_offset += img.size[1]
progress = (idx+1) / len(images) * 100 # 실제 퍼센트 정보를 계산
p_var.set(progress)
progress_bar.update()
dest_path = os.path.join(txt_dest_path.get(), "hero_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() # 함수가 길어질 것 같아서 별도로 빼서 함수 정의
# 파일 프레임 ( 파일추가, 선택 삭제 )
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="파일추가", 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) # Entry로 만들었기 때문에 위에서 (0,END)로 쓰는 것이고 txt였다면 ("1.0", END)
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)
# 옵션 프레임
frame_option = LabelFrame(root, text="옵션")
frame_option.pack(padx=5, pady=5, ipady=5)
# 1. 가로 넓이 옵션
# 가로 넓이 레이블
lbl_width = Label(frame_option, text="가로넓이", width=8)
lbl_width.pack(side="left", padx=5, pady=5)
# 가로 넓이 콤보
opt_width = ["원본유지", "1024", "800", "640"]
cmb_width = ttk.Combobox(frame_option, state="readonly", values=opt_width, width=10)
cmb_width.current(0)
cmb_width.pack(side="left", padx=5, pady=5)
# 2. 간격 옵션
# 간격 레이블
lbl_space = Label(frame_option, text="간격", width=8)
lbl_space.pack(side="left", padx=5, pady=5)
# 간격 콤보
opt_space = ["없음", "좁게", "보통", "넓게"]
cmb_space = ttk.Combobox(frame_option, state="readonly", values=opt_space, width=10)
cmb_space.current(0)
cmb_space.pack(side="left", padx=5, pady=5)
# 3. 포맷 옵션
# 포맷 레이블
lbl_format = Label(frame_option, text="포맷", width=8)
lbl_format.pack(side="left", padx=5, pady=5)
# 포맷 콤보
opt_format = ["PNG", "JPG", "BMP"]
cmb_format = ttk.Combobox(frame_option, state="readonly", values=opt_format, width=10)
cmb_format.current(0)
cmb_format.pack(side="left", padx=5, pady=5)
# 진행상황 Progress Bar
frame_progress = LabelFrame(root, text="진행상황")
frame_progress.pack(fill="x", padx=5, pady=5, ipady=5)
p_var = DoubleVar()
progress_bar = ttk.Progressbar(frame_progress, maximum=100, variable=p_var)
progress_bar.pack(fill="x", padx=5, pady=5)
# 실행 프레임
frame_run = Frame(root)
frame_run.pack(fill="x", padx=5, pady=5)
btn_close = Button(frame_run, padx=5, pady=5, text="닫기", width=10, command=root.quit)
btn_close.pack(side="right", padx=5, pady=5)
btn_start = Button(frame_run, padx=5, pady=5, text="시작", width=10, command=start)
btn_start.pack(side="right", padx=5, pady=5)
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
[29] 옵션 전반전
| CODE
import os
import tkinter.ttk as ttk
import tkinter.messagebox as msgbox
from tkinter import * # _all_
from tkinter import filedialog # 서브 모듈이기 때문에 별도로 improt
from PIL import Image
root = Tk()
root.title("Hero GUI")
# 파일 추가
def add_file():
files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요",\
filetypes=(("PNG 파일", "*.png"), ("모든 파일", "*.*"),), \
initialdir=r"C:/workspace/NC_Utl_2_GUI") # 최초 위치 지정이 작동을 하지 않음. WHY?
# 최초에 사용자가 지정한 경로를 보여줌
# r은 에스케이프 문자를 무시하는, raw string
for file in files:
list_file.insert(END, file)
# 선택 삭제
def del_file():
print(list_file.curselection())
# 인덱스 뒤에 것부터 지워야 인덱스에 변화가 안 생김
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)
# 간격
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)]
# 이미지 사이즈 리스트에 넣어서 하나씩 처리
image_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] # 원본 사이즈 사용
'''
계산식
100*60 이미지가 있음 -> width를 80으로 줄이면 height는?
(원본 width) : (원본 height) = (변경 width) : (변경 height)
100 : 60 = 80 : ?
x : y = x' : y'
yx' = xy'
y' = yx' / x -> 이 식을 적용
100:60=80:48
우리 코드에 대입하려면?
x = width = size[0]
x = width = size[0]
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 # 이미지를 합치면서 변해가는 y 위치
for img in images:
result_img.paste(img, (0, y_offset))
y_offset += img.size[1] # height 값만큼 더하기
for idx, img in enumerate(images):
result_img.paste(img, (0, y_offset))
y_offset += img.size[1]
progress = (idx+1) / len(images) * 100 # 실제 퍼센트 정보를 계산
p_var.set(progress)
progress_bar.update()
dest_path = os.path.join(txt_dest_path.get(), "hero_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() # 함수가 길어질 것 같아서 별도로 빼서 함수 정의
# 파일 프레임 ( 파일추가, 선택 삭제 )
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="파일추가", 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) # Entry로 만들었기 때문에 위에서 (0,END)로 쓰는 것이고 txt였다면 ("1.0", END)
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)
# 옵션 프레임
frame_option = LabelFrame(root, text="옵션")
frame_option.pack(padx=5, pady=5, ipady=5)
# 1. 가로 넓이 옵션
# 가로 넓이 레이블
lbl_width = Label(frame_option, text="가로넓이", width=8)
lbl_width.pack(side="left", padx=5, pady=5)
# 가로 넓이 콤보
opt_width = ["원본유지", "1024", "800", "640"]
cmb_width = ttk.Combobox(frame_option, state="readonly", values=opt_width, width=10)
cmb_width.current(0)
cmb_width.pack(side="left", padx=5, pady=5)
# 2. 간격 옵션
# 간격 레이블
lbl_space = Label(frame_option, text="간격", width=8)
lbl_space.pack(side="left", padx=5, pady=5)
# 간격 콤보
opt_space = ["없음", "좁게", "보통", "넓게"]
cmb_space = ttk.Combobox(frame_option, state="readonly", values=opt_space, width=10)
cmb_space.current(0)
cmb_space.pack(side="left", padx=5, pady=5)
# 3. 포맷 옵션
# 포맷 레이블
lbl_format = Label(frame_option, text="포맷", width=8)
lbl_format.pack(side="left", padx=5, pady=5)
# 포맷 콤보
opt_format = ["PNG", "JPG", "BMP"]
cmb_format = ttk.Combobox(frame_option, state="readonly", values=opt_format, width=10)
cmb_format.current(0)
cmb_format.pack(side="left", padx=5, pady=5)
# 진행상황 Progress Bar
frame_progress = LabelFrame(root, text="진행상황")
frame_progress.pack(fill="x", padx=5, pady=5, ipady=5)
p_var = DoubleVar()
progress_bar = ttk.Progressbar(frame_progress, maximum=100, variable=p_var)
progress_bar.pack(fill="x", padx=5, pady=5)
# 실행 프레임
frame_run = Frame(root)
frame_run.pack(fill="x", padx=5, pady=5)
btn_close = Button(frame_run, padx=5, pady=5, text="닫기", width=10, command=root.quit)
btn_close.pack(side="right", padx=5, pady=5)
btn_start = Button(frame_run, padx=5, pady=5, text="시작", width=10, command=start)
btn_start.pack(side="right", padx=5, pady=5)
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
[30] 옵션 후반전
| CODE
import os
import tkinter.ttk as ttk
import tkinter.messagebox as msgbox
from tkinter import * # _all_
from tkinter import filedialog # 서브 모듈이기 때문에 별도로 improt
from PIL import Image
root = Tk()
root.title("Hero GUI")
# 파일 추가
def add_file():
files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요",\
filetypes=(("PNG 파일", "*.png"), ("모든 파일", "*.*"),), \
initialdir=r"C:/workspace/NC_Utl_2_GUI") # 최초 위치 지정이 작동을 하지 않음. WHY?
# 최초에 사용자가 지정한 경로를 보여줌
# r은 에스케이프 문자를 무시하는, raw string
for file in files:
list_file.insert(END, file)
# 선택 삭제
def del_file():
print(list_file.curselection())
# 인덱스 뒤에 것부터 지워야 인덱스에 변화가 안 생김
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)
# 간격
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)]
# 이미지 사이즈 리스트에 넣어서 하나씩 처리
image_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] # 원본 사이즈 사용
'''
계산식
100*60 이미지가 있음 -> width를 80으로 줄이면 height는?
(원본 width) : (원본 height) = (변경 width) : (변경 height)
100 : 60 = 80 : ?
x : y = x' : y'
yx' = xy'
y' = yx' / x -> 이 식을 적용
100:60=80:48
우리 코드에 대입하려면?
x = width = size[0]
x = width = size[0]
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)
# 스케치북 준비
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 # 이미지를 합치면서 변해가는 y 위치
for img in images:
result_img.paste(img, (0, y_offset))
y_offset += img.size[1] # height 값만큼 더하기
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) # height 값 + 사용자가 지정한 간격
progress = (idx+1) / len(images) * 100 # 실제 퍼센트 정보를 계산
p_var.set(progress)
progress_bar.update()
# 포맷 옵션 처리
file_name = "hero_photo." + img_format
dest_path = os.path.join(txt_dest_path.get(), "file_name")
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() # 함수가 길어질 것 같아서 별도로 빼서 함수 정의
# 파일 프레임 ( 파일추가, 선택 삭제 )
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="파일추가", 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) # Entry로 만들었기 때문에 위에서 (0,END)로 쓰는 것이고 txt였다면 ("1.0", END)
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)
# 옵션 프레임
frame_option = LabelFrame(root, text="옵션")
frame_option.pack(padx=5, pady=5, ipady=5)
# 1. 가로 넓이 옵션
# 가로 넓이 레이블
lbl_width = Label(frame_option, text="가로넓이", width=8)
lbl_width.pack(side="left", padx=5, pady=5)
# 가로 넓이 콤보
opt_width = ["원본유지", "1024", "800", "640"]
cmb_width = ttk.Combobox(frame_option, state="readonly", values=opt_width, width=10)
cmb_width.current(0)
cmb_width.pack(side="left", padx=5, pady=5)
# 2. 간격 옵션
# 간격 레이블
lbl_space = Label(frame_option, text="간격", width=8)
lbl_space.pack(side="left", padx=5, pady=5)
# 간격 콤보
opt_space = ["없음", "좁게", "보통", "넓게"]
cmb_space = ttk.Combobox(frame_option, state="readonly", values=opt_space, width=10)
cmb_space.current(0)
cmb_space.pack(side="left", padx=5, pady=5)
# 3. 포맷 옵션
# 포맷 레이블
lbl_format = Label(frame_option, text="포맷", width=8)
lbl_format.pack(side="left", padx=5, pady=5)
# 포맷 콤보
opt_format = ["PNG", "JPG", "BMP"]
cmb_format = ttk.Combobox(frame_option, state="readonly", values=opt_format, width=10)
cmb_format.current(0)
cmb_format.pack(side="left", padx=5, pady=5)
# 진행상황 Progress Bar
frame_progress = LabelFrame(root, text="진행상황")
frame_progress.pack(fill="x", padx=5, pady=5, ipady=5)
p_var = DoubleVar()
progress_bar = ttk.Progressbar(frame_progress, maximum=100, variable=p_var)
progress_bar.pack(fill="x", padx=5, pady=5)
# 실행 프레임
frame_run = Frame(root)
frame_run.pack(fill="x", padx=5, pady=5)
btn_close = Button(frame_run, padx=5, pady=5, text="닫기", width=10, command=root.quit)
btn_close.pack(side="right", padx=5, pady=5)
btn_start = Button(frame_run, padx=5, pady=5, text="시작", width=10, command=start)
btn_start.pack(side="right", padx=5, pady=5)
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
[31] 버그 수정
| CODE
import os
import tkinter.ttk as ttk
import tkinter.messagebox as msgbox
from tkinter import * # _all_
from tkinter import filedialog # 서브 모듈이기 때문에 별도로 improt
from PIL import Image
root = Tk()
root.title("Hero GUI")
# 파일 추가
def add_file():
files = filedialog.askopenfilenames(title="이미지 파일을 선택하세요",\
filetypes=(("PNG 파일", "*.png"), ("모든 파일", "*.*"),), \
initialdir=r"C:/workspace/NC_Utl_2_GUI") # 최초 위치 지정이 작동을 하지 않음. WHY?
# 최초에 사용자가 지정한 경로를 보여줌
# r은 에스케이프 문자를 무시하는, raw string
for file in files:
list_file.insert(END, file)
# 선택 삭제
def del_file():
print(list_file.curselection())
# 인덱스 뒤에 것부터 지워야 인덱스에 변화가 안 생김
for index in reversed(list_file.curselection()):
list_file.delete(index)
# 저장 경로 (폴더)
def browse_dest_path():
folder_selected = filedialog.askdirectory()
if folder_selected == '': # 사용자가 취소를 누를 때
print("폴더 선택 취소")
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())
try:
# 가로넓이
img_width = cmb_width.get()
if img_width == "원본 유지":
img_width = -1 #-1일때는 원본 기준으로
else:
img_width = int(img_width)
# 간격
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)]
# 이미지 사이즈 리스트에 넣어서 하나씩 처리
image_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] # 원본 사이즈 사용
'''
계산식
100*60 이미지가 있음 -> width를 80으로 줄이면 height는?
(원본 width) : (원본 height) = (변경 width) : (변경 height)
100 : 60 = 80 : ?
x : y = x' : y'
yx' = xy'
y' = yx' / x -> 이 식을 적용
100:60=80:48
우리 코드에 대입하려면?
x = width = size[0]
x = width = size[0]
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)
# 스케치북 준비
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 # 이미지를 합치면서 변해가는 y 위치
for img in images:
result_img.paste(img, (0, y_offset))
y_offset += img.size[1] # height 값만큼 더하기
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) # height 값 + 사용자가 지정한 간격
progress = (idx+1) / len(images) * 100 # 실제 퍼센트 정보를 계산
p_var.set(progress)
progress_bar.update()
# 포맷 옵션 처리
file_name = "hero_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():
# 각 옵션들 값 확인
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() # 함수가 길어질 것 같아서 별도로 빼서 함수 정의
# 파일 프레임 ( 파일추가, 선택 삭제 )
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="파일추가", 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) # Entry로 만들었기 때문에 위에서 (0,END)로 쓰는 것이고 txt였다면 ("1.0", END)
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)
# 옵션 프레임
frame_option = LabelFrame(root, text="옵션")
frame_option.pack(padx=5, pady=5, ipady=5)
# 1. 가로 넓이 옵션
# 가로 넓이 레이블
lbl_width = Label(frame_option, text="가로넓이", width=8)
lbl_width.pack(side="left", padx=5, pady=5)
# 가로 넓이 콤보
opt_width = ["원본유지", "1024", "800", "640"]
cmb_width = ttk.Combobox(frame_option, state="readonly", values=opt_width, width=10)
cmb_width.current(0)
cmb_width.pack(side="left", padx=5, pady=5)
# 2. 간격 옵션
# 간격 레이블
lbl_space = Label(frame_option, text="간격", width=8)
lbl_space.pack(side="left", padx=5, pady=5)
# 간격 콤보
opt_space = ["없음", "좁게", "보통", "넓게"]
cmb_space = ttk.Combobox(frame_option, state="readonly", values=opt_space, width=10)
cmb_space.current(0)
cmb_space.pack(side="left", padx=5, pady=5)
# 3. 포맷 옵션
# 포맷 레이블
lbl_format = Label(frame_option, text="포맷", width=8)
lbl_format.pack(side="left", padx=5, pady=5)
# 포맷 콤보
opt_format = ["PNG", "JPG", "BMP"]
cmb_format = ttk.Combobox(frame_option, state="readonly", values=opt_format, width=10)
cmb_format.current(0)
cmb_format.pack(side="left", padx=5, pady=5)
# 진행상황 Progress Bar
frame_progress = LabelFrame(root, text="진행상황")
frame_progress.pack(fill="x", padx=5, pady=5, ipady=5)
p_var = DoubleVar()
progress_bar = ttk.Progressbar(frame_progress, maximum=100, variable=p_var)
progress_bar.pack(fill="x", padx=5, pady=5)
# 실행 프레임
frame_run = Frame(root)
frame_run.pack(fill="x", padx=5, pady=5)
btn_close = Button(frame_run, padx=5, pady=5, text="닫기", width=10, command=root.quit)
btn_close.pack(side="right", padx=5, pady=5)
btn_start = Button(frame_run, padx=5, pady=5, text="시작", width=10, command=start)
btn_start.pack(side="right", padx=5, pady=5)
root.resizable(False, False) # x(너비), y(높이) 값 변경 불가 ( 창 크기 변경 불가 )
root.mainloop()
| Problem
프로그램 실행과 기타 모든 기능은 정상이나,
합쳐지는 것이 안됌. 문제 해결 요망
[32] BONUS : 스크린 샷 프로그램
| CODE
import time
import keyboard
from PIL import ImageGrab
def screenshot():
# 2020년 9월 17일 23시 44분 30초 -> _20200917_234430
curr_time = time.strftime("_%Y%m%d_%H%M%S")
img = ImageGrab.grab()
img.save("image{}.png".format(curr_time)) # ex) image_20200917_234315.png
keyboard.add_hotkey("F9", screenshot) # 사용자가 F9 키를 누르면 스크린샷 저장
# keyboard.add_hotkey("a", screenshot) # 사용자가 a를 누르면 스크린샷 저장
# keyboard.add_hotkey("ctrl+shift+s", screenshot)
# 사용자가 'ctrl+shift+s'키를 누르면 스크린샷 저장
keyboard.wait("esc") # 사용자가 esc를 누를 때까지 프로그램 수행
Reference
[나도 코딩] 파이썬 코딩 활용편 2 - GUI 프로그래밍, 이미지 합치기 / https://www.youtube.com/watch?v=bKPIcoou9N8
'[Programming] > [Language]' 카테고리의 다른 글
[Python] 나도코딩_활용편3 (0) | 2022.09.14 |
---|---|
[Python] 나도코딩_활용편1 / Finished 22.08.17 (0) | 2022.07.18 |
[Python] 나도코딩_기본편 / Finished 22.07.18 (0) | 2022.06.03 |
[Java] Ch13. 예외 처리 / 객체 지향 프로그래밍 중급 / K-Digital Credit Java / Summary (0) | 2022.06.01 |
[Java] Ch12. 내부 클래스, 람다식, 스트림 / 객체 지향 프로그래밍 중급 / K-Digital Credit Java / Summary (0) | 2022.05.29 |