😟 문제 상황
ui.py 파일 속 시작 버튼에 command를 연결해주는 과정에서 문제가 발생했다.
함수를 분리하기 위해 func.py 파일을 만든 후 함수를 연결을 했으나 프로그램이 실행하자마자 함수가 자동으로 실행된 것이다. 그 후에는 버튼을 눌러도 동작하지 않는 현상이 발생했다. 💦
아래는 문제의 코드이다.
# ui.py 파일 속 일부 코드
start_Button = Button(
frame_run,
text="시작",
width=10,
height=2,
font=normal_font,
command=lotto_start(list_file, game_combobox)
)
# func.py 파일 속 일부 코드
def lotto_start(list_file, game_combobox):
print("시작 버튼입니다!")
해결하기 위해 인터넷을 검색해보니 함수 객체와 함수 호출에 대한 차이라는 것을 알게 되었다. 알게 된 내용을 토대로 글을 정리해보고자 포스팅을 하게 되었다.
✅ 함수 객체 VS 함수 호출
Python에서 함수는 하나의 값(객체)으로 다룰 수 있다.
def sample():
print("안녕하세요!")
say_hello = sample # 함수 객체를 변수에 저장
say_hello() # => '안녕하세요!' 출력
이처럼 sample는 함수 객체이고, sample()는 함수 호출이다.
🎯 Tkinter command=는 함수 객체를 기대함
Tkinter에서 command 옵션은 버튼이 눌렸을 때 "나중에 실행할 함수"를 등록하는 역할을 한다.
그러므로 아래처럼 작성해야 올바른 동작을 한다.
Button(root, text="시작", command=lotto_start) # 함수 객체만 전달
즉, 나는 command에 함수 호출을 하였기 때문에 문제가 발생한 것이였다. 왜냐하면 아래 코드와 같이 인자 값을 2개를 넘겨줘야하기 때문에 강제로 괄호()를 사용했기 때문.
# ❌ 이렇게 하면 즉시 실행됨
Button(root, text="시작", command=lotto_start(listbox, game_combobox))
이 코드는 실행될 때 lotto_start(listbox, game_combobox)가 즉시 호출되고 그 반환값이 command에 전달된다.
따라서 프로그램이 시작하자마자 함수가 실행되고 버튼을 눌러도 아무 동작이 없다.
✅ 해결 방법: lambda 또는 functools.partial
1. lambda 사용
Button(root, text="시작", command=lambda: lotto_start(listbox, game_combobox))
lambda는 익명 함수로 버튼을 눌렀을 때 lotto_start(listbox, game_combobox)가 실행되도록 한다.
2. functools.partial 사용
from functools import partial
Button(root, text="시작", command=partial(lotto_start, listbox, game_combobox))
partial은 인자가 있는 함수를 미리 정의해 두고 나중에 호출할 수 있도록 만들어준다.
✏️ 정리
작성 방식 | 의미 | 동작 |
command=func | 함수 객체 전달 | 버튼 클릭 시 실행 |
command=func() | 함수 호출 후 반환값 전달 | 즉시 실행됨 |
command=lambda: func(arg) | 익명 함수로 감싸서 실행 지연 | 버튼 클릭 시 실행 |
command=partial(func, arg) | 미리 인자 고정한 함수 전달 | 버튼 클릭 시 실행 |
나는 lambda 방식을 채택하였고 아래와 같이 작성하였다.
# ui.py 파일 속 일부 코드
start_Button = Button(
frame_run,
text="시작",
width=10,
height=2,
font=normal_font,
command=lambda: lotto_start(list_file, game_combobox)
)
# func.py 파일 속 일부 코드
from tkinter import *
import random
def lotto_start(list_file, game_combobox):
list_file.delete(0, END) # 시작 누르면서 동시에 초기화
for i in range(int(game_combobox.get())):
lotto = random.sample(range(1, 46), 6)
lotto.sort()
list_file.insert(END, lotto)
게임 수에 지정된 숫자(game_combobox) 만큼 로또 번호 6개를 랜덤으로 list_file 내부에 insert 한 모습이다.
✍️ 마무리
- Tkinter에서 버튼을 누를 때 특정 동작이 실행되게 하려면 반드시 지연 실행이 가능한 함수 객체를 전달해야 한다.
- 특히 인자를 전달할 때는 lambda나 partial을 사용하는 것이 필수
- 이 개념은 Tkinter뿐 아니라 이벤트 기반 프로그래밍 전반에 걸쳐 자주 등장하니 꼭 익혀두자!
'STUDY > Python' 카테고리의 다른 글
[Tkinter] 아이콘 설정 후 경계선이 깨지는 현상 (0) | 2025.06.26 |
---|---|
[Tkinter] Tkinter 폰트(font) 설정과 활용 정리 (0) | 2025.06.05 |
[Tkinter] Tkinter를 활용하여 간단한 메모장 프로그램 만들기 (0) | 2025.04.27 |
Python의 Zip 함수를 활용한 2차원 배열 전환 방법 (0) | 2024.03.19 |