본문 바로가기
Python/GUI(tkinter)

독학 Python tkinter(GUI) - 19.Last piece1(thread)

by To올라운더 2023. 11. 22.
반응형

이제 우리가 원하는 간단한 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 값을 초기화 해주는 과정이 필요하다.

반응형