tkinter 어플리케이션을 구성하는 가장 좋은 방법은?


136

다음은 일반적인 파이썬 tkinter 프로그램의 전체 구조입니다.

def funA():
    def funA1():
        def funA12():
            # stuff

    def funA2():
        # stuff

def funB():
    def funB1():
        # stuff

    def funB2():
        # stuff

def funC():
    def funC1():
        # stuff

    def funC2():
        # stuff


root = tk.Tk()

button1 = tk.Button(root, command=funA)
button1.pack()
button2 = tk.Button(root, command=funB)
button2.pack()
button3 = tk.Button(root, command=funC)
button3.pack()

funA funB그리고 funC또 다른 나타납니다 Toplevel위젯 창을 때 버튼 1, 2, 3에 사용자가 클릭.

이것이 파이썬 tkinter 프로그램을 작성하는 올바른 방법인지 궁금합니다. 물론, 이런 식으로 쓰더라도 작동하지만 최선의 방법입니까? 바보처럼 들리지만 다른 사람들이 작성한 코드를 볼 때 코드는 많은 함수로 엉망이 아니며 대부분 클래스가 있습니다.

모범 사례로 따라야 할 특정 구조가 있습니까? 파이썬 프로그램을 작성하기 전에 어떻게 계획해야합니까?

나는 프로그래밍에 모범 사례와 같은 것이 없다는 것을 알고 있으며 그것을 요구하지도 않습니다. 나는 혼자서 파이썬을 배우면서 올바른 방향으로 나아갈 수 있도록 조언과 설명을 원합니다.


2
다음은 tkinter GUI 디자인에 대한 훌륭한 튜토리얼입니다. python-textbok.readthedocs.org/en/latest/… MVC 디자인 패턴을 가진 또 다른 예제는 다음과 같습니다 -sukhbinder.wordpress.com/2014/12/ 25 /…
Bondolin

12
이 질문은 광범위 할 수 있지만, 상대적으로 인기있는 답변 (거의 모든 다른 [tkinter] 답변에 대한 답변)으로서 유용하고 h입니다. 다시 여는 것이 닫히는 것보다 더 유용하다는 것을 알기 때문에 다시 열 것을 추천합니다.
Bryan Oakley

답변:


271

나는 객체 지향 접근법을 옹호한다. 이것은 처음부터 시작하는 템플릿입니다.

# Use Tkinter for python 2, tkinter for python 3
import tkinter as tk

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        <create the rest of your GUI here>

if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

주목해야 할 중요한 사항은 다음과 같습니다.

  • 와일드 카드 가져 오기를 사용하지 않습니다. 패키지를 "tk"로 가져옵니다 tk.. 모든 명령 앞에 접두사가 있어야합니다 . 이것은 전역 네임 스페이스 오염을 방지하고 Tkinter 클래스, ttk 클래스 또는 자체 클래스를 사용할 때 코드를 완전히 분명하게 만듭니다.

  • 주요 응용 프로그램은 클래스 입니다. 이렇게하면 모든 콜백 및 개인 함수에 대한 개인 네임 스페이스가 제공되며 일반적으로 코드를 쉽게 구성 할 수 있습니다. 절차 스타일에서는 하향식을 코딩하고 함수를 사용하기 전에 함수를 정의하는 등의 작업을 수행해야합니다.이 방법을 사용하면 마지막 단계까지 실제로 메인 창을 만들지 않기 때문에 수행 할 수 없습니다. 나는 tk.Frame일반적으로 프레임을 만드는 것으로 시작하기 때문에 상속을 선호 하지만 꼭 필요한 것은 아닙니다.

앱에 최상위 창이 추가로 있으면에서 각각을 별도의 클래스로 만드는 것이 좋습니다 tk.Toplevel. 이것은 위에서 언급 한 것과 동일한 장점을 제공합니다. 창은 원자 적이며 고유 한 네임 스페이스가 있으며 코드가 잘 구성되어 있습니다. 또한 코드가 커지기 시작하면 각 모듈을 자체 모듈에 쉽게 넣을 수 있습니다.

마지막으로, 인터페이스의 모든 주요 부분에 클래스를 사용하는 것이 좋습니다. 예를 들어 도구 모음, 탐색 창, 상태 표시 줄 및 기본 영역을 사용하여 앱을 만드는 경우 해당 클래스를 각각 만들 수 있습니다. 이렇게하면 메인 코드가 매우 작고 이해하기 쉽습니다.

class Navbar(tk.Frame): ...
class Toolbar(tk.Frame): ...
class Statusbar(tk.Frame): ...
class Main(tk.Frame): ...

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.statusbar = Statusbar(self, ...)
        self.toolbar = Toolbar(self, ...)
        self.navbar = Navbar(self, ...)
        self.main = Main(self, ...)

        self.statusbar.pack(side="bottom", fill="x")
        self.toolbar.pack(side="top", fill="x")
        self.navbar.pack(side="left", fill="y")
        self.main.pack(side="right", fill="both", expand=True)

이러한 모든 인스턴스가 공통 상위를 공유하므로 상위는 효과적으로 모델 뷰 컨트롤러 아키텍처의 "제어기"부분이됩니다. 예를 들어 주 창은을 호출하여 상태 표시 줄에 무언가를 배치 할 수 self.parent.statusbar.set("Hello, world")있습니다. 이를 통해 구성 요소 간 간단한 인터페이스를 정의 할 수있어 미니 문에 대한 연결을 유지할 수 있습니다.


22
@ Brian Oakley 인터넷에서 구조를 연구 할 수있는 좋은 샘플 코드를 알고 있습니까?
Chris Aung

2
두 번째로 객체 지향 접근 방식입니다. 그러나 내 경험상 GUI를 호출하는 클래스에서 상속을 사용하지 않는 것이 좋습니다. Tk 및 Frame 객체가 모두 상속되지 않는 클래스의 속성 인 경우 더 많은 유연성을 제공합니다. 이런 식으로 Tk 및 Frame 객체에 더 쉽게 (그리고 모호하지 않게) 액세스 할 수 있으며, 그것을 파괴해도 원하지 않는 클래스의 모든 것을 파괴하지는 않습니다. 일부 프로그램에서 이것이 중요한 이유를 잊었지만 더 많은 일을 할 수 있습니다.
Brōtsyorfuzthrāx 21시 38 분

1
단순히 클래스가 개인 네임 스페이스를 제공하지 않습니까? 왜 프레임을 서브 클래스 화하면 개선됩니까?
gcb

3
@ gcb : 예, 모든 클래스는 개인 네임 스페이스를 제공합니다. 왜 프레임을 서브 클래 싱해야합니까? 어쨌든 일반적으로 프레임을 만들 것이므로 관리 할 클래스가 적습니다 (프레임의 하위 클래스 대 프레임을 속성으로 사용하여 객체에서 상속되는 클래스). 더 명확하게하기 위해 대답을 약간 다시 표현했습니다. 피드백 감사드립니다.
Bryan Oakley

2
@madtyn : parent나중에 사용하지 않는 한에 대한 참조를 저장할 필요가 없습니다 . 내 예제의 코드 중 어느 것도 저장해야 할 필요가 없기 때문에 저장하지 않았습니다.
Bryan Oakley

39

각 최상위 창을 별도의 클래스에 배치하면 코드를 재사용하고 코드를보다 잘 구성 할 수 있습니다. 창에있는 모든 버튼과 관련 메소드는이 클래스 내에 정의해야합니다. 다음은 예제입니다 ( 여기 에서 가져옴 ).

import tkinter as tk

class Demo1:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.button1 = tk.Button(self.frame, text = 'New Window', width = 25, command = self.new_window)
        self.button1.pack()
        self.frame.pack()
    def new_window(self):
        self.newWindow = tk.Toplevel(self.master)
        self.app = Demo2(self.newWindow)

class Demo2:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
        self.quitButton.pack()
        self.frame.pack()
    def close_windows(self):
        self.master.destroy()

def main(): 
    root = tk.Tk()
    app = Demo1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

참조 :

희망이 도움이됩니다.


6

이것은 나쁜 구조가 아닙니다. 잘 작동합니다. 그러나 누군가가 버튼이나 무언가를 클릭 할 때 명령을 수행하려면 함수에 함수가 있어야합니다.

그래서 당신이 할 수있는 것은 이것들을 위해 클래스를 작성하고 버튼 클릭과 같은 명령을 처리하는 메소드를 클래스에 가지고 있습니다.

예를 들면 다음과 같습니다.

import tkinter as tk

class Window1:
    def __init__(self, master):
        pass
        # Create labels, entries,buttons
    def button_click(self):
        pass
        # If button is clicked, run this method and open window 2


class Window2:
    def __init__(self, master):
        #create buttons,entries,etc

    def button_method(self):
        #run this when button click to close window
        self.master.destroy()

def main(): #run mianloop 
    root = tk.Tk()
    app = Window1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

일반적으로 여러 창이있는 tk 프로그램은 여러 개의 큰 클래스이며 __init__모든 항목, 레이블 등이 만들어지며 각 방법은 버튼 클릭 이벤트를 처리하는 것입니다

프로그램을 쉽게 설명 할 수 없다면 아마도 더 나은 방법이있을 수 있기 때문에 실제로 그것을 할 수있는 올바른 방법은 없습니다. .

Tkinter의 Thinking을 살펴보십시오 .


3
"Thinking in Tkinter"는 전 세계 수입을 옹호하는데, 이는 매우 나쁜 조언이라고 생각합니다.
Bryan Oakley

1
난 당신이 전역을 사용하는 것이 좋습니다 해달라고 Thts 사실 바로 메인 클래스의 Methos는 구조 개봉 오른쪽 :)의 일부
시리얼

2

OOP는 접근 방식 frame이어야하며 인스턴스 변수 대신 클래스 변수 여야 합니다 .

from Tkinter import *
class App:
  def __init__(self, master):
    frame = Frame(master)
    frame.pack()
    self.button = Button(frame, 
                         text="QUIT", fg="red",
                         command=frame.quit)
    self.button.pack(side=LEFT)
    self.slogan = Button(frame,
                         text="Hello",
                         command=self.write_slogan)
    self.slogan.pack(side=LEFT)
  def write_slogan(self):
    print "Tkinter is easy to use!"

root = Tk()
app = App(root)
root.mainloop()

여기에 이미지 설명을 입력하십시오

참조 : http://www.python-course.eu/tkinter_buttons.php


2
TKinterPython 2 에서만 사용할 수 있습니다. tkinterPython 3에 사용 하는 것이 좋습니다. 또한 마지막 세 줄의 코드를 main()함수 아래 에 놓고 프로그램 끝에서 호출합니다. 내가 것이다 확실히 사용하지 않는 from module_name import *것이 글로벌 네임 스페이스를 오염하고 가독성을 줄일 수있다.
Zac

1
확장 모듈도 가져 오는 경우 button1 = tk.Button(root, command=funA)와 가져 오는 button1 = ttk.Button(root, command=funA)경우 의 차이점을 어떻게 알 수 tkinter있습니까? *구문을 사용하면 두 코드 줄이 모두 것처럼 보입니다 button1 = Button(root, command=funA). 해당 구문을 사용하지 않는 것이 좋습니다.
Zac

0

클래스를 사용하여 응용 프로그램을 구성하면 나와 나와 함께 일하는 다른 사람들이 문제를 쉽게 디버깅하고 응용 프로그램을 쉽게 개선 할 수 있습니다.

다음과 같이 응용 프로그램을 쉽게 구성 할 수 있습니다.

class hello(Tk):
    def __init__(self):
        super(hello, self).__init__()
        self.btn = Button(text = "Click me", command=close)
        self.btn.pack()
    def close():
        self.destroy()

app = hello()
app.mainloop()

-2

아마도 프로그램을 구성하는 방법을 배우는 가장 좋은 방법은 다른 사람들의 코드를 읽는 것입니다. 특히 많은 사람들이 기여한 큰 프로그램 인 경우에 특히 그렇습니다. 많은 프로젝트의 코드를 살펴본 후 컨센서스 스타일이 무엇인지 알아야합니다.

언어로서의 파이썬은 코드를 어떻게 포맷해야하는지에 대한 강력한 지침이 있다는 점에서 특별합니다. 첫 번째는 소위 "Zen of Python"입니다.

  • 못생긴 것보다 아름답습니다.
  • 암시적인 것보다 명시적인 것이 좋습니다.
  • 단순한 것보다 복잡한 것이 좋습니다.
  • 복잡한 것이 복잡한 것보다 낫습니다.
  • 평평한 것이 중첩보다 낫습니다.
  • 스파 스가 밀도보다 낫습니다.
  • 가독성이 중요합니다.
  • 특별한 경우는 규칙을 어길만큼 특별하지 않습니다.
  • 실용성은 순도를 능가하지만.
  • 오류가 자동으로 전달되지 않아야합니다.
  • 명시 적으로 침묵하지 않는 한.
  • 모호함에 직면하여 추측하려는 유혹을 거부하십시오.
  • 그것을하는 명백한 방법이 있어야합니다.
  • 네덜란드 인이 아니라면 처음에는 그 방법이 명확하지 않을 수 있습니다.
  • 지금보다 결코 낫습니다.
  • 결코 있지만 종종 더 나은보다 바로 지금.
  • 구현이 설명하기 어렵다면 나쁜 생각입니다.
  • 구현이 설명하기 쉬운 경우 좋은 생각 일 수 있습니다.
  • 네임 스페이스는 훌륭한 아이디어 중 하나입니다. 더 많은 것을 해보자!

보다 실용적인 수준에는 Python의 스타일 가이드 인 PEP8이 있습니다.

이를 염두에두고 코드 스타일, 특히 중첩 함수에 적합하지 않다고 말하고 싶습니다. 클래스를 사용하거나 별도의 모듈로 이동하여 플랫 화하는 방법을 찾으십시오. 이렇게하면 프로그램 구조를 훨씬 쉽게 이해할 수 있습니다.


12
Python의 Zen을 사용하는 경우 -1입니다. 모두 좋은 조언이지만, 질문 된 내용을 직접 다루지는 않습니다. 마지막 단락을 꺼내면이 답변은이 사이트의 거의 모든 파이썬 질문에 적용될 수 있습니다. 좋은 긍정적 인 조언이지만 질문에 대답하지 않습니다.
Bryan Oakley

1
@BryanOakley 나는 그것에 동의하지 않습니다. 그렇습니다. 파이썬의 선 (Zen of Python)은 광범위하며 많은 질문을 처리하는 데 사용될 수 있습니다. 그는 마지막 단락에서 클래스를 선택하거나 함수를 별도의 모듈에 배치한다고 언급했습니다. 또한 파이썬을위한 스타일 가이드 인 PEP8도 언급했다. 직접적인 답변은 아니지만이 답변은 취할 수있는 많은 다른 경로를 언급한다는 점에서 신뢰할 만하다고 생각합니다. 그건 내 의견 일 뿐이야
Zac

1
나는이 특정 질문에 대한 답변을 찾기 위해 여기에 왔습니다. 개방형 질문 조차도이 답변으로 아무것도 할 수 없습니다. 나도 -1했다.
조나단

절대로 문제는 tkinter 앱 을 구성하는 것입니다. 스타일링 / 코딩 / 선 지침에 대해서는 아무것도 없습니다. @Arbiter를 "직접 답변은 아니지만"따옴표로 묶는 것처럼 쉬우므로 답이 아닙니다. 이것은 "아마도 그렇고 아예"와 같으며 선 (zen)이 앞에 붙습니다.
m3nda

-7

나는 개인적으로 반대 지향적 접근법을 사용하지 않는다. 왜냐하면 주로 a) 방해가되기 때문이다. b) 절대 모듈로 재사용 하지 않습니다 .

하지만 여기에서 논의되지 않은 무언가가, 당신이 있다는 것입니다 해야한다 스레딩 또는 멀티 프로세싱을 사용합니다. 항상. 그렇지 않으면 신청서가 끔찍할 것입니다.

간단한 테스트를 수행하십시오. 창을 시작한 다음 URL이나 다른 것을 가져옵니다. 네트워크 요청이 진행되는 동안 UI가 업데이트되지 않습니다. 즉, 응용 프로그램 창이 손상됩니다. 프로세스가 TK 메인 루프로 돌아올 때까지 대부분의 경우 OS가 다시 그려지지 않으며 창 위로 드래그하는 모든 것이 그 위에 표시됩니다.


4
당신이 말하는 것은 단순히 사실이 아닙니다. 개인 및 상업용 tk 기반 응용 프로그램을 작성했으며 스레드를 사용할 필요가 거의 없었습니다. 쓰레드가 그 자리를 차지하지만 tkinter 프로그램을 작성할 때 쓰레드를 사용해야 한다는 것은 사실이 아닙니다 . runnng 함수가 길면 스레드 또는 멀티 프로세싱이 필요할 수 있지만 쓰레드가 필요없는 많은 유형의 프로그램을 작성할 수 있습니다.
Bryan Oakley

나는 당신이 당신의 대답을 좀 더 명확하게 표현한다면 더 나은 대답이라고 생각합니다. 또한 tkinter와 함께 쓰레드를 사용하는 일반적인 예를 갖는 것이 실제로 도움이 될 것입니다.
Bryan Oakley

여기서 가장 좋은 답변이되는 것에 관심이 없었습니다. 스레딩 / 멀티부터 시작하는 것은 매우 쉽다는 것을 명심하십시오. 나중에 추가해야한다면 잃어버린 전투입니다. 오늘날에는 네트워크와 통신하지 않는 응용 프로그램이 전혀 없습니다. 그리고 '나는 디스크 IO가 거의 없다'는 것을 무시하고 생각하더라도 내일 클라이언트는 파일이 NFS에서 작동 할 것으로 결정하고 네트워크 IO를 기다리고 있으며 앱이 죽은 것처럼 보입니다.
gcb

2
@ erm3nda : "네트워크에 연결되거나 IO 쓰기를하는 모든 앱은 스레드 나 하위 프로세스를 사용하면 훨씬 빠릅니다." -이는 사실이 아닙니다. 스레딩으로 인해 프로그램이 더 빨라지는 것은 아니며 경우에 따라 속도가 느려질 수도 있습니다. GUI 프로그래밍에서 스레드를 사용하는 주된 이유는 GUI를 차단하는 일부 코드를 실행할 수 있기 때문입니다.
Bryan Oakley

2
@ erm3nda : 아니, 내가 하지 말 스레드가 필요하지 않은 전혀 . 그들은 많은 것들을 위해 반드시 필요합니다 (좋은 스레드 또는 다중 처리). tkinter가 적합하지만 스레드가 필요하지 않은 매우 큰 GUI 응용 프로그램 클래스가 있다는 것입니다. 그렇습니다. "설치자, 메모장 및 기타 쉬운 도구"가 해당 범주에 속합니다. 세계는 단어, 엑셀, 포토샵 등과 같은 것보다 이러한 "쉬운 도구"로 구성되어 있습니다. 또한 여기의 문맥은 tkinter 입니다. Tkinter는 일반적으로 매우 크고 복잡한 응용 프로그램에는 사용되지 않습니다.
Bryan Oakley
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.