Issue
I was adjusting this code to my needs in PyCharm where it worked well, without any exceptions and errors. 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
.
The traceback is: line 90, in run - line 51, in do action - line 30, in try_move
I tried to find the solution, but I only found mtTkinter for Python2.
Since I am new to threading, I don't know how to solve this problem and why it is only showing in Jupyter Notebook. Is it possible that Jupyter Notebook is the source of the problem?
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()
Solution
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)
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.
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
#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.
It may need also master.protocol("WM_DELETE_WINDOW", on_delete)
to execute function when you click closing button [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()
Answered By - furas
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.