이제 우리가 원하는 간단한 GUI는 취향에 맞춰 만들 수 있다는 자신감이 생겼을 것이다.
여기까지만 해도 훌륭하지만, 조금 아쉬운 부분이 있다.
아래의 요청사항을 처리하는 코드를 작성하며 문제점과 처리 방법에 대한 고민을 해보자.
Request.
- 버튼을 누르면 1부터 10까지 1초에 1씩 증가하는 타이머 GUI를 만들어 보세요.
위와 같은 요청사항을 받는다면, 어렵지 않게
시간을 보여줄 수 있는 레이블 또는 엔트리를 하나 만들고
time 라이브러리를 활용해 버튼을 누르면 1초를 기다린 뒤
위에서 생성한 레이블(또는 엔트리)의 값을 변경해주면 되는 간단한 GUI이다.
자신있게 코드를 작성해보면 아래와 크게 다르지 않을 거다.
1. 코드 작성하기
from tkinter import *
import time
root = Tk()
root.title('To올라운드의 알찬 GUI 강의')
root.geometry("300x400") # 가로 X 세로 / 대문자X 하면 실행안됨
def start_timer():
count = 0
while count < 10:
time.sleep(1)
count += 1
print(count)
show_timer_sec_label.config(text = str(count))
body_frame = Frame(root, relief='solid', bd=2, background='orange', padx=5, pady=5)
body_frame.pack(fill='both', expand=True)
show_timer_sec_label = Label(body_frame, text='시간(s)')
show_timer_sec_label.pack()
start_btn = Button(body_frame, text='시작', command=start_timer)
start_btn.pack()
root.resizable(False,False) # x너비, y 변경 허용 여부
root.mainloop()
위 코드를 실행해보면, 아래처럼 터미널과 GUI 상에 값이 변경이 되는데
내가 생각했던 동작과 달리 2가지 문제가 발생한다.
1) GUI에 바로 반영되지 않는 문제
- 터미널에서 증가하는 숫자를 보면 '시작' 버튼을 누른 뒤 정상동작하는 것 같지만
GUI상에는 '시간(s)' 라는 초기 값이 변하지 않고 있다가 10초가 완료되면 10으로 변경된다.
- 우리가 원한건 타이머처럼 1초마다 증가하는 모습을 보고 싶었는데, 정상적인 동작이 되지 않는다.
2) GUI 응답 없음 증상
- '시작' 버튼을 누른 뒤 GUI를 조작하려 하면 GUI가 응답 없음 상태가 된다.
- 이 말은 타이머를 '시작' 하고 나면,
진행하다 멈추고 싶을 때라던지 다른 동작을 하고 싶어도 해당 기능이 완료 될 때까지 기다려야한다는 의미가 된다.
이 두가지 문제를 해결하기 위해서는 앞에서 배운 thread를 이용하면 해당 과정을 처리 할 수 있다.
2023.11.17 - [Python/Tip & Etc] - python Thread(쓰레드) 병렬처리
python Thread(쓰레드) 병렬처리
파이썬은 한줄씩 코드를 처리하는 인터프리터 방식의 프로그래밍 언어이다. 한줄씩 처리한다는 의미대로 해석해보면 2개 이상의 코드를 실행(병렬처리)할 수 없다는 의미인데, 아래 코드를 실
to-all-rounder.tistory.com
2. thread 추가하여 코드 수정하기
- threading 라이브러리를 추가하고, 버튼을 실행할 때 lambda 함수와 threading 객체를 생성한다.
라이브러리 추가를 제외하곤 버튼과 연계된 command 파라미터만 바꿔도 두개의 프로그램이 따로 움직인다.
from tkinter import *
import time, threading
root = Tk()
root.title('To올라운드의 알찬 GUI 강의')
root.geometry("300x400") # 가로 X 세로 / 대문자X 하면 실행안됨
def start_timer():
count = 0
while count < 10:
time.sleep(1)
count += 1
print(count)
show_timer_sec_label.config(text = str(count))
body_frame = Frame(root, relief='solid', bd=2, background='orange', padx=5, pady=5)
body_frame.pack(fill='both', expand=True)
show_timer_sec_label = Label(body_frame, text='시간(s)')
show_timer_sec_label.pack()
#start_btn = Button(body_frame, text='시작', command=start_timer)
start_btn = Button(body_frame, text='시작', command=lambda:threading.Thread(target=start_timer).start())
start_btn.pack()
root.resizable(False,False) # x너비, y 변경 허용 여부
root.mainloop()
기능을 제현해 보면,
처음과는 다르게 GUI도 터미널과 같이 시간 값이 나타나고,
동작 중에 GUI를 조작해도 응답 없음 증상이 생기지 않는다.
3. 멈춤 기능 추가하기
- 이제 해당 기능을 누른 뒤 멈추는 기능을 만들어 보면, 오늘 포스팅에서 GUI의 마지막 조각은 완성된다.
- start_timer 메서드를 실행하기 위한 조건을 추가한다.
- stop_flag 라는 전역변수를 만들고 False 일 때는 기존 매서드 실행, True 일 때 실행 중인 매서드를 멈추는 Flag 값이다.
- stop_timer 매서드를 통해 중지를 하고 싶을 때 전역변수 stop_flag 를 True로 변경해준다.
from tkinter import *
import time, threading
root = Tk()
root.title('To올라운드의 알찬 GUI 강의')
root.geometry("300x400") # 가로 X 세로 / 대문자X 하면 실행안됨
def start_timer():
global stop_flag
count = 0
while count < 10 and stop_flag == False:
time.sleep(1)
count += 1
print(count)
show_timer_sec_label.config(text = str(count))
stop_flag = False # 재시작을 위한 초기화
stop_flag = False
def stop_timer():
global stop_flag
stop_flag = True
print('사용자 요청에 의해 중지합니다.')
body_frame = Frame(root, relief='solid', bd=2, background='orange', padx=5, pady=5)
body_frame.pack(fill='both', expand=True)
show_timer_sec_label = Label(body_frame, text='시간(s)')
show_timer_sec_label.pack()
#start_btn = Button(body_frame, text='시작', command=start_timer)
start_btn = Button(body_frame, text='시작', command=lambda:threading.Thread(target=start_timer).start())
start_btn.pack()
stop_btn = Button(body_frame, text='중지', command=lambda:threading.Thread(target=stop_timer).start())
stop_btn.pack()
root.resizable(False,False) # x너비, y 변경 허용 여부
root.mainloop()
- 시작과 중지가 정상적으로 진행되는 것을 확인할 수 있는데, 중요한 포인트 중 하나는 19라인의 stop_flag를 다시 Flase로 초기화 해주는 과정이다.
- while 문을 탈출하기 위해 True로 변경된 상태인데, 이상태로 끝이나면 시작버튼을 다시 눌렀을 때 while문이 동작할 수 없다.
- 그래서 while문을 시작하기 전 또는 실행하고 난 뒤 flag 값을 초기화 해주는 과정이 필요하다.
'Python > GUI(tkinter)' 카테고리의 다른 글
독학 Python tkinter(GUI) - 20. py to exe 로 변환하기 (2) | 2023.11.30 |
---|---|
독학 Python tkinter(GUI) - 18.Menubutton (1) | 2023.11.16 |
독학 Python tkinter(GUI) - 17.progressbar (0) | 2023.11.15 |
독학 Python tkinter(GUI) - 16.photoimage(+PIL) (0) | 2023.11.14 |
독학 Python tkinter(GUI) - 15.Combobox (0) | 2023.11.13 |