[python] Tkinter의 이벤트 루프와 함께 자신의 코드를 어떻게 실행합니까?

제 동생은 이제 막 프로그래밍을 시작하고 있습니다. 그의 Science Fair 프로젝트를 위해 그는 하늘의 새 무리를 시뮬레이션하고 있습니다. 그는 대부분의 코드를 작성했고 잘 작동하지만 새들은 매 순간 움직여야 합니다 .

그러나 Tkinter는 자체 이벤트 루프에 대한 시간을 낭비하므로 코드가 실행되지 않습니다. 이렇게 root.mainloop()실행, 실행을, 그리고 것은 실행 유지하고, 그것을 실행하는 유일한 방법은 이벤트 핸들러입니다.

그의 코드를 메인 루프와 함께 실행할 수있는 방법이 있습니까 (멀티 스레딩이 없으면 혼란스럽고 간단해야합니다). 그렇다면 무엇입니까?

바로 지금, 그는 그의 move()기능을에 묶는 추악한 해킹을 생각해 냈습니다 <b1-motion>. 그래서 그가 버튼을 누르고 있고 마우스를 흔들면 작동합니다. 하지만 더 나은 방법이 있어야합니다.



답변

개체 after에 대한 방법을 사용하십시오 Tk.

from tkinter import *

root = Tk()

def task():
    print("hello")
    root.after(2000, task)  # reschedule event in 2 seconds

root.after(2000, task)
root.mainloop()

다음은 after메서드에 대한 선언 및 문서입니다 .

def after(self, ms, func=None, *args):
    """Call function once after given time.

    MS specifies the time in milliseconds. FUNC gives the
    function which shall be called. Additional parameters
    are given as parameters to the function call.  Return
    identifier to cancel scheduling with after_cancel."""


답변

비요른에 의해 게시 솔루션 A의 결과 “RuntimeError에 : 다른 아파트에서 Tcl의 호출”메시지가 내 컴퓨터에 (레드햇 엔터프라이즈 5, 파이썬 2.6.1). Bjorn은 내가 확인한 한 곳에 따르면 Tkinter로 스레딩을 잘못 처리하는 것은 예측할 수없고 플랫폼에 따라 다르기 때문에이 메시지를받지 못했을 수 있습니다 .

문제는 app.start()앱에 Tk 요소가 포함되어 있기 때문에 Tk에 대한 참조로 간주되는 것 같습니다 . 이 문제를 내부 로 교체 app.start()하여 수정했습니다 . 또한 모든 TK에 참조가 내부에 하나가되도록 만들었 호출하는 기능 또는 내부에 의해 호출되는 함수 호출하는 기능 (이것은 “다른 아파트”오류가 발생하지 않도록하는 것이 분명히 중요하다)가.self.start()__init__mainloop()mainloop()

마지막으로 콜백이있는 프로토콜 핸들러를 추가했습니다. 이것이 없으면 사용자가 Tk 창을 닫을 때 프로그램이 오류와 함께 종료되기 때문입니다.

수정 된 코드는 다음과 같습니다.

# Run tkinter code in another thread

import tkinter as tk
import threading

class App(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.start()

    def callback(self):
        self.root.quit()

    def run(self):
        self.root = tk.Tk()
        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        label = tk.Label(self.root, text="Hello World")
        label.pack()

        self.root.mainloop()


app = App()
print('Now we can continue running code while mainloop runs!')

for i in range(100000):
    print(i)


답변

시뮬레이션에서와 같이 자신의 루프를 작성할 때 (내가 가정) update수행하는 작업을 수행 하는 함수 를 호출해야 mainloop합니다. 변경 사항으로 창을 업데이트하지만 루프에서 수행합니다.

def task():
   # do something
   root.update()

while 1:
   task()  


답변

또 다른 옵션은 tkinter가 별도의 스레드에서 실행되도록하는 것입니다. 한 가지 방법은 다음과 같습니다.

import Tkinter
import threading

class MyTkApp(threading.Thread):
    def __init__(self):
        self.root=Tkinter.Tk()
        self.s = Tkinter.StringVar()
        self.s.set('Foo')
        l = Tkinter.Label(self.root,textvariable=self.s)
        l.pack()
        threading.Thread.__init__(self)

    def run(self):
        self.root.mainloop()


app = MyTkApp()
app.start()

# Now the app should be running and the value shown on the label
# can be changed by changing the member variable s.
# Like this:
# app.s.set('Bar')

하지만 멀티 스레드 프로그래밍은 어렵고 발에 자신을 쏘는 것은 정말 쉽습니다. 예를 들어 위의 샘플 클래스의 멤버 변수를 변경할 때주의해야합니다. 그래야 Tkinter의 이벤트 루프를 방해하지 않습니다.


답변

이것은 GPS 리더 및 데이터 발표자가 될 첫 번째 작업 버전입니다. tkinter는 오류 메시지가 너무 적은 매우 취약한 것입니다. 그것은 물건을 올려 놓지 않고 왜 많은 시간을 말하지 않습니다. 좋은 WYSIWYG 양식 개발자에게서 오는 것은 매우 어렵습니다. 어쨌든 이것은 1 초에 10 번 작은 루틴을 실행하고 양식에 정보를 표시합니다. 그것을 실현하는 데 시간이 걸렸습니다. 타이머 값 0을 시도했을 때 양식이 나타나지 않았습니다. 이제 머리가 아파요! 초당 10 회 이상이면 충분합니다. 다른 사람에게 도움이되기를 바랍니다. 마이크 모로

import tkinter as tk
import time

def GetDateTime():
  # Get current date and time in ISO8601
  # https://en.wikipedia.org/wiki/ISO_8601 
  # https://xkcd.com/1179/
  return (time.strftime("%Y%m%d", time.gmtime()),
          time.strftime("%H%M%S", time.gmtime()),
          time.strftime("%Y%m%d", time.localtime()),
          time.strftime("%H%M%S", time.localtime()))

class Application(tk.Frame):

  def __init__(self, master):

    fontsize = 12
    textwidth = 9

    tk.Frame.__init__(self, master)
    self.pack()

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Time').grid(row=0, column=0)
    self.LocalDate = tk.StringVar()
    self.LocalDate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalDate).grid(row=0, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             text='Local Date').grid(row=1, column=0)
    self.LocalTime = tk.StringVar()
    self.LocalTime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
             textvariable=self.LocalTime).grid(row=1, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Time').grid(row=2, column=0)
    self.nowGdate = tk.StringVar()
    self.nowGdate.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGdate).grid(row=2, column=1)

    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             text='GMT Date').grid(row=3, column=0)
    self.nowGtime = tk.StringVar()
    self.nowGtime.set('waiting...')
    tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
             textvariable=self.nowGtime).grid(row=3, column=1)

    tk.Button(self, text='Exit', width = 10, bg = '#FF8080', command=root.destroy).grid(row=4, columnspan=2)

    self.gettime()
  pass

  def gettime(self):
    gdt, gtm, ldt, ltm = GetDateTime()
    gdt = gdt[0:4] + '/' + gdt[4:6] + '/' + gdt[6:8]
    gtm = gtm[0:2] + ':' + gtm[2:4] + ':' + gtm[4:6] + ' Z'
    ldt = ldt[0:4] + '/' + ldt[4:6] + '/' + ldt[6:8]
    ltm = ltm[0:2] + ':' + ltm[2:4] + ':' + ltm[4:6]
    self.nowGtime.set(gdt)
    self.nowGdate.set(gtm)
    self.LocalTime.set(ldt)
    self.LocalDate.set(ltm)

    self.after(100, self.gettime)
   #print (ltm)  # Prove it is running this and the external code, too.
  pass

root = tk.Tk()
root.wm_title('Temp Converter')
app = Application(master=root)

w = 200 # width for the Tk root
h = 125 # height for the Tk root

# get display screen width and height
ws = root.winfo_screenwidth()  # width of the screen
hs = root.winfo_screenheight() # height of the screen

# calculate x and y coordinates for positioning the Tk root window

#centered
#x = (ws/2) - (w/2)
#y = (hs/2) - (h/2)

#right bottom corner (misfires in Win10 putting it too low. OK in Ubuntu)
x = ws - w
y = hs - h - 35  # -35 fixes it, more or less, for Win10

#set the dimensions of the screen and where it is placed
root.geometry('%dx%d+%d+%d' % (w, h, x, y))

root.mainloop()


답변