[英]Threading in Tkinter and Python3
I was adjusting this code to my needs in PyCharm where it worked well, without any exceptions and errors.我在 PyCharm 中根据我的需要调整这段代码,它运行良好,没有任何异常和错误。 When I was trying it out in Jupyter Notebook it worked, but when I closed the Tkinter window, I get the exception
Exception in thread Thread-:
and the Error RuntimeError: main thread is not in main loop
.当我在 Jupyter Notebook 中尝试它时它起作用了,但是当我关闭 Tkinter window 时,我得到异常
Exception in thread Thread-:
和错误RuntimeError: main thread is not in main loop
。
The traceback is: line 90, in run - line 51, in do action - line 30, in try_move回溯是:第 90 行,在运行中 - 第 51 行,在执行操作中 - 第 30 行,在 try_move 中
I tried to find the solution, but I only found mtTkinter for Python2.我试图找到解决方案,但我只找到了适用于 Python2 的 mtTkinter。
Since I am new to threading, I don't know how to solve this problem and why it is only showing in Jupyter Notebook.由于我是线程新手,我不知道如何解决这个问题,也不知道为什么它只显示在 Jupyter Notebook 中。 Is it possible that Jupyter Notebook is the source of the problem?
Jupyter Notebook 是否可能是问题的根源?
The code is:代码是:
from tkinter import *
import threading
import time
def render_grid():
global specials, walls, WIDTH, x, y, player
for i in range(x):
for j in range(y):
board.create_rectangle(i * WIDTH, j * WIDTH, (i + 1) * WIDTH, (j + 1) * WIDTH, fill="misty rose", width=1)
temp = {}
cell_scores[(i, j)] = temp
for (i, j, c, w) in specials:
board.create_rectangle(i * WIDTH, j * WIDTH, (i + 1) * WIDTH, (j + 1) * WIDTH, fill=c, width=1)
for (i, j) in walls:
board.create_rectangle(i * WIDTH, j * WIDTH, (i + 1) * WIDTH, (j + 1) * WIDTH, fill="wheat4", width=1)
def set_cell_score(state, action, val):
global cell_score_min, cell_score_max
def try_move(dx, dy):
global player, x, y, score, walk_reward, me, restart
if restart:
restart_game()
new_x = player[0] + dx
new_y = player[1] + dy
score += walk_reward
if (new_x >= 0) and (new_x < x) and (new_y >= 0) and (new_y < y) and not ((new_x, new_y) in walls):
board.coords(me, new_x * WIDTH + WIDTH * 2 / 10, new_y * WIDTH + WIDTH * 2 / 10, new_x * WIDTH + WIDTH * 8 / 10,
new_y * WIDTH + WIDTH * 8 / 10)
player = (new_x, new_y)
for (i, j, c, w) in specials:
if new_x == i and new_y == j:
score -= walk_reward
score += w
if score > 0:
print("Success! score: ", score)
else:
print("Fail! score: ", score)
restart = True
return
# print "score: ", score
def call_up(event):
try_move(0, -1)
def call_down(event):
try_move(0, 1)
def call_left(event):
try_move(-1, 0)
def call_right(event):
try_move(1, 0)
def restart_game():
global player, score, me, restart
player = (0, y - 1)
score = 1
restart = False
board.coords(me, player[0] * WIDTH + WIDTH * 2 / 10, player[1] * WIDTH + WIDTH * 2 / 10,
player[0] * WIDTH + WIDTH * 8 / 10, player[1] * WIDTH + WIDTH * 8 / 10)
def has_restarted():
return restart
def start_game():
master.mainloop()
master = Tk()
master.resizable(False, False)
cell_score_min = -0.2
cell_score_max = 0.2
WIDTH = 100
(x, y) = (5, 5)
actions = ["up", "down", "left", "right"]
board = Canvas(master, width=x * WIDTH, height=y * WIDTH)
player = (0, y - 1)
score = 1
restart = False
walk_reward = -0.04
walls = [(1, 1), (1, 2), (2, 1), (2, 2)]
specials = [(4, 1, "salmon1", -1), (4, 0, "medium sea green", 1)]
cell_scores = {}
discount = 0.3
states = []
Q = {}
for i in range(x):
for j in range(y):
states.append((i, j))
for state in states:
temp = {}
for action in actions:
temp[action] = 0.1
set_cell_score(state, action, temp[action])
Q[state] = temp
for (i, j, c, w) in specials:
for action in actions:
Q[(i, j)][action] = w
set_cell_score((i, j), action, w)
def do_action(action):
s = player
r = -score
if action == actions[0]:
try_move(0, -1)
elif action == actions[1]:
try_move(0, 1)
elif action == actions[2]:
try_move(-1, 0)
elif action == actions[3]:
try_move(1, 0)
else:
return
s2 = player
r += score
return s, action, r, s2
def max_Q(s):
val = None
act = None
for a, q in Q[s].items():
if val is None or (q > val):
val = q
act = a
return act, val
def inc_Q(s, a, alpha, inc):
Q[s][a] *= 1 - alpha
Q[s][a] += alpha * inc
set_cell_score(s, a, Q[s][a])
def run():
global discount
time.sleep(1)
alpha = 1
t = 1
while True:
# Pick the right action
s = player
max_act, max_val = max_Q(s)
(s, a, r, s2) = do_action(max_act)
# Update Q
max_act, max_val = max_Q(s2)
inc_Q(s, a, alpha, r + discount * max_val)
t += 1.0
if has_restarted():
restart_game()
time.sleep(0.01)
t = 1.0
time.sleep(0.1)
render_grid()
master.bind("<Up>", call_up)
master.bind("<Down>", call_down)
master.bind("<Right>", call_right)
master.bind("<Left>", call_left)
me = board.create_rectangle(player[0] * WIDTH + WIDTH * 2 / 10, player[1] * WIDTH + WIDTH * 2 / 10,
player[0] * WIDTH + WIDTH * 8 / 10, player[1] * WIDTH + WIDTH * 8 / 10, fill="sandy brown",
width=1, tag="me")
board.grid(row=0, column=0)
t = threading.Thread(target=run)
t.setDaemon(True)
t.start()
start_game()
Probably all GUIs don't like to run in threads and all changes in widgets should be in main thread (but calculations still can be in separated threads)可能所有的 GUI 都不喜欢在线程中运行,小部件中的所有更改都应该在主线程中(但计算仍然可以在单独的线程中)
In tkinter
you could use master.after(milliseconds, function_name)
instead of thread
and while
-loop to run code periodically - and this will works like loop but in current thread.在
tkinter
,您可以使用master.after(milliseconds, function_name)
而不是thread
和while
-loop 来定期运行代码 - 这将像循环一样工作,但在当前线程中。
def run():
global t
# Pick the right action
s = player
max_act, max_val = max_Q(s)
(s, a, r, s2) = do_action(max_act)
# Update Q
max_act, max_val = max_Q(s2)
inc_Q(s, a, alpha, r + discount * max_val)
t += 1.0
if has_restarted():
restart_game()
time.sleep(0.01)
t = 1.0
# run again after 100ms
master.after(100, run)
and later start it as normal function然后正常启动它 function
#master.after(100, run) # run after 100ms
run() # run at once
start_game()
Full working code:完整的工作代码:
EDIT:编辑:
You may also use global variable - ie.您也可以使用全局变量 - 即。
running = True
- to stop looping before destroying GUI. running = True
- 在销毁 GUI 之前停止循环。
It may need also master.protocol("WM_DELETE_WINDOW", on_delete)
to execute function when you click closing button [X]
它可能还需要
master.protocol("WM_DELETE_WINDOW", on_delete)
当你点击关闭按钮时执行 function [X]
from tkinter import *
#import threading
import time
def render_grid():
global specials, walls, WIDTH, x, y, player
for i in range(x):
for j in range(y):
board.create_rectangle(i * WIDTH, j * WIDTH, (i + 1) * WIDTH, (j + 1) * WIDTH, fill="misty rose", width=1)
temp = {}
cell_scores[(i, j)] = temp
for (i, j, c, w) in specials:
board.create_rectangle(i * WIDTH, j * WIDTH, (i + 1) * WIDTH, (j + 1) * WIDTH, fill=c, width=1)
for (i, j) in walls:
board.create_rectangle(i * WIDTH, j * WIDTH, (i + 1) * WIDTH, (j + 1) * WIDTH, fill="wheat4", width=1)
def set_cell_score(state, action, val):
global cell_score_min, cell_score_max
def try_move(dx, dy):
global player, x, y, score, walk_reward, me, restart
if restart:
restart_game()
new_x = player[0] + dx
new_y = player[1] + dy
score += walk_reward
if (new_x >= 0) and (new_x < x) and (new_y >= 0) and (new_y < y) and not ((new_x, new_y) in walls):
board.coords(me, new_x * WIDTH + WIDTH * 2 / 10, new_y * WIDTH + WIDTH * 2 / 10, new_x * WIDTH + WIDTH * 8 / 10,
new_y * WIDTH + WIDTH * 8 / 10)
player = (new_x, new_y)
for (i, j, c, w) in specials:
if new_x == i and new_y == j:
score -= walk_reward
score += w
if score > 0:
print("Success! score: ", score)
else:
print("Fail! score: ", score)
restart = True
return
# print "score: ", score
def call_up(event):
try_move(0, -1)
def call_down(event):
try_move(0, 1)
def call_left(event):
try_move(-1, 0)
def call_right(event):
try_move(1, 0)
def restart_game():
global player, score, me, restart
player = (0, y - 1)
score = 1
restart = False
board.coords(me, player[0] * WIDTH + WIDTH * 2 / 10, player[1] * WIDTH + WIDTH * 2 / 10,
player[0] * WIDTH + WIDTH * 8 / 10, player[1] * WIDTH + WIDTH * 8 / 10)
def has_restarted():
return restart
def start_game():
master.mainloop()
def do_action(action):
s = player
r = -score
if action == actions[0]:
try_move(0, -1)
elif action == actions[1]:
try_move(0, 1)
elif action == actions[2]:
try_move(-1, 0)
elif action == actions[3]:
try_move(1, 0)
else:
return
s2 = player
r += score
return s, action, r, s2
def max_Q(s):
val = None
act = None
for a, q in Q[s].items():
if val is None or (q > val):
val = q
act = a
return act, val
def inc_Q(s, a, alpha, inc):
Q[s][a] *= 1 - alpha
Q[s][a] += alpha * inc
set_cell_score(s, a, Q[s][a])
def run():
global t
# Pick the right action
s = player
max_act, max_val = max_Q(s)
(s, a, r, s2) = do_action(max_act)
# Update Q
max_act, max_val = max_Q(s2)
inc_Q(s, a, alpha, r + discount * max_val)
t += 1.0
if has_restarted():
restart_game()
time.sleep(0.01)
t = 1.0
# run again after 100ms
if running:
master.after(100, run)
def on_delete():
global running
running = False
master.destroy()
# --- main ---
master = Tk()
master.resizable(False, False)
cell_score_min = -0.2
cell_score_max = 0.2
WIDTH = 100
(x, y) = (5, 5)
actions = ["up", "down", "left", "right"]
board = Canvas(master, width=x * WIDTH, height=y * WIDTH)
player = (0, y - 1)
score = 1
restart = False
walk_reward = -0.04
walls = [(1, 1), (1, 2), (2, 1), (2, 2)]
specials = [(4, 1, "salmon1", -1), (4, 0, "medium sea green", 1)]
cell_scores = {}
discount = 0.3
states = []
Q = {}
for i in range(x):
for j in range(y):
states.append((i, j))
for state in states:
temp = {}
for action in actions:
temp[action] = 0.1
set_cell_score(state, action, temp[action])
Q[state] = temp
for (i, j, c, w) in specials:
for action in actions:
Q[(i, j)][action] = w
set_cell_score((i, j), action, w)
render_grid()
master.bind("<Up>", call_up)
master.bind("<Down>", call_down)
master.bind("<Right>", call_right)
master.bind("<Left>", call_left)
me = board.create_rectangle(player[0] * WIDTH + WIDTH * 2 / 10, player[1] * WIDTH + WIDTH * 2 / 10,
player[0] * WIDTH + WIDTH * 8 / 10, player[1] * WIDTH + WIDTH * 8 / 10, fill="sandy brown",
width=1, tag="me")
board.grid(row=0, column=0)
#t = threading.Thread(target=run)
#t.setDaemon(True)
#t.start()
running = True
alpha = 1
t = 1
run() # run at once
#master.after(100, run) # run after 100ms
master.protocol("WM_DELETE_WINDOW", on_delete)
start_game()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.