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

StackOverflow https://stackoverflow.com/questions/459083

  •  19-08-2019
  •  | 
  •  

質問

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

しかし、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()

<=>メソッドの宣言とドキュメントは次のとおりです。

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."""

他のヒント

Bjornが投稿したソリューションにより、<!> quot; RuntimeError:Calling Tcl from different appartment <!> quot;コンピューター上のメッセージ(RedHat Enterprise 5、python 2.6.1)。 1か所によると、Bjornはこのメッセージを受け取っていない可能性があります。オンにすると、Tkinterでのスレッド処理の誤操作は予測不能であり、プラットフォームに依存します。

アプリにはTk要素が含まれているため、app.start()はTkへの参照としてカウントされるという問題があるようです。 self.start()内の__init__mainloop()に置き換えることでこれを修正しました。また、すべてのTk参照が、<=> を呼び出す関数内にあるか、<=>を呼び出す関数によって呼び出される関数内にあるようにしました(これは、 <!> quot; different apartment <!> quot; error)を避けるために明らかに重要です。

最後に、コールバック付きのプロトコルハンドラを追加しました。これがないと、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を別のスレッドで実行することです。その方法の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のイベントループで中断しないように注意する必要があります。

これは、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()
scroll top