web-dev-qa-db-ja.com

Tkinterのイベントループと一緒にどのように独自のコードを実行しますか?

私の弟はプログラミングを始めたばかりで、彼のサイエンスフェアプロジェクトでは、空を飛ぶ鳥の群れのシミュレーションを行っています。彼は自分のコードのほとんどを書かれており、うまく機能していますが、鳥は動く必要があります毎回

ただし、Tkinterは独自のイベントループに時間を費やしているため、彼のコードは実行されません。 root.mainloop()を実行すると、実行され、実行を継続し、実行されるのはイベントハンドラーだけです。

彼のコードをメインループと並行して実行する方法はありますか(マルチスレッドを使用せず、混乱を招きます。これは単純に保つべきです)、もしそうなら、それは何ですか?

現在、彼は_いハックを思いつき、move()関数を<b1-motion>に結び付けているため、ボタンを押したままマウスを動かしている限り機能します。しかし、もっと良い方法があるはずです。

109
Allan S

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."""
128
Dave Ray

シミュレーションのように独自のループを作成するとき(私は推測します)、updateが行うことを行うmainloop関数を呼び出す必要があります。変更でウィンドウを更新しますが、ループ。

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

while 1:
   task()  
17
jma

別のオプションは、tkinterを別のスレッドで実行させることです。その方法の1つは次のとおりです。

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のイベントループで中断しないように注意する必要があります。

5
Bjorn

これは、GPSリーダーおよびデータプレゼンターとなるものの最初の動作バージョンです。 tkinterは非常に壊れやすく、エラーメッセージが少なすぎます。それは物を入れませんし、なぜ多くの時間を伝えません。優れたWYSIWYGフォーム開発者から来るのは非常に困難です。とにかく、これは1秒間に10回小さなルーチンを実行し、フォームに情報を表示します。それを実現するためにしばらく時間がかかりました。 0のタイマー値を試したとき、フォームが表示されませんでした。頭が痛い! 1秒間に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()
2
Micheal Morrow