Bottom Page

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 [Tkinter] How to create a delay for AI without freezing the GUI
#1
I want to create an AI move into my game, after the player does one. Is there any function I could use in order to let AI wait for few seconds before it makes a move? I already tried a canvas.after(), but it didn´t work properly (it also freezed my move).
Yoriz wrote May-03-2019, 04:19 PM:
I'm going to assume this is using tkinter and move it to GUI, in future please give more information and post a minimal code sample.
Quote
#2
see: https://python-forum.io/Thread-WxPython-...ng-the-gui
Quote
#3
So I have read the Threads Guide, but got stucked on this point:

class Program:

    def __init__(self):
        self.width = 1000
        self.height = 800
        self.panel_width = 200
        #self.root = Tk()
        self.canvas = tkinter.Canvas(width=self.width, height=self.height, bg="black")
        self.canvas.pack()
        self.canvas.bind("<ButtonPress>", self.click)
        self.figures_images = {"0": tkinter.PhotoImage(file="white_pawn.png"), ...}
        self.figures = {"0":[<class Pawn...>, (x,y), image_id],"1":[<class Pawn...>, (x,y), image_id],"2":[<class Pawn...>, (x,y), image_id],...}
        ...

    def create_layout(self):
        # not important for this problem

    def start_ai(self):
        ...
        move = Move_ai(self)

    # next methods are not important now

class Pawn:
    def __init__(self, ...):
        ...
        # not important for this problem

class Move_ai:
    def __init__(self, program):
        self.main = program
        thread = threading.Thread(target=self.move)
        thread.start()

    def move(self):
        time.sleep(3)    # just to pretend PC is thinking
        ... # some code calculating which figure to move
        selected_id = ...    # id of figure it decided to move with
        self.main.canvas.coords(self.main.figures_images[selected_id], tuple_of_coords)
       # ^ and this is that line raising an error to me, this one:
Error:
Exception in thread Thread-1: Traceback (most recent call last): File "C:\python\lib\threading.py", line 917, in _bootstrap_inner self.run() File "C:\python\lib\threading.py", line 865, in run self._target(*self._args, **self._kwargs) File "C:\Users\user\Documents\PYTHON\chess\hra.py", line 581, in move self.main.canvas.coords(self.main.figures_images_[selected_id], tuple_of_coords) File "C:\python\lib\tkinter\__init__.py", line 2469, in coords self.tk.call((self._w, 'coords') + args))] RuntimeError: main thread is not in main loop
To be honest, I don´t understand that solution described here very well, could you help me to solve this problem using and showing only that version of this solution, which my program really needs?
Yoriz wrote May-04-2019, 01:22 PM:
Please post all code, output and errors (in it's entirety) between their respective tags. I added error tags for you this time, Here are instructions on how to do it yourself next time.
Quote
#4
I've added headings to the various parts of the code in the link.
You have the error shown against the heading "Example of adding a thread but still getting a error"
The solution is in the following code examples one with and one without decorators.
Quote
#5
Well I´m looking at the part "Example of adding a thread but still getting a error" but I have no idea how to suit it to my program. I have no buttons there and as I see, self.listbox.insert(tk.END, item) works with listbox I´m not using in my program.
Quote
#6
Look at "Example of a solution to the problem"
Quote
#7
Well, so I have changed my code so:

class Program:
 
    def __init__(self):
        self.width = 1000
        self.height = 800
        self.panel_width = 200
        #self.root = Tk()
        self.canvas = tkinter.Canvas(width=self.width, height=self.height, bg="black")
        self.canvas.pack()
        self.canvas.bind("<ButtonPress>", self.click)
        self.figures_images = {"0": tkinter.PhotoImage(file="white_pawn.png"), ...}
        self.figures = {"0":[<class Pawn...>, (x,y), image_id],"1":[<class Pawn...>, (x,y), image_id],"2":[<class Pawn...>, (x,y), image_id],...}
        ...
 
    def create_layout(self):
        # not important for this problem
 
    def start_ai(self):
        ...
        move = Move_ai(self)
 
    # next methods are not important now
 
class Pawn:
    def __init__(self, ...):
        ...
        # not important for this problem
 
class Move_ai:
    def __init__(self, program):
        self.main = program
        thread_pool_executor = futures.ThreadPoolExecutor(max_workers=1)
        thread_pool_executor.submit(self.move)
 
    def move(self):
        time.sleep(3)    # just to pretend PC is thinking
        ... # some code calculating which figure to move
        selected_id = ...    # id of figure it decided to move with
        self.main.canvas.coords(self.main.figures_images[selected_id], tuple_of_coords)
       # ^ and this is that line raising an error to me, this one:
And now it does not raise any error message, but it just freezes at point of canvas.coords (and no other moves are able to do). Did I forgot to implement something more from "Example of a solution to the problem"?
Quote
#8
You have to use the canvas.after method for calling methods of the gui, it moves the calls back into the mainloops thread.
The follwing is an code from that thread
class MainFrame(tk.Frame):
 
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        ....
        ....

    def set_label_text(self, text=''):
        self.label['text'] = text

    def blocking_code(self):
        self.after(0, self.set_label_text, 'running')
 
        for number in range(5):
            self.after(0, self.listbox_insert, number)
            print(number)
            time.sleep(1)
 
        self.after(0, self.set_label_text, ' not running')
In the method blocking_code self.after(0, self.set_label_text, 'running') is calling self.set_label_text back in the mainloops thread.
Quote
#9
If you just want to delay something the first argument to after is how long to wait in millisecs before calling the passed in callback.
import tkinter as tk
import time

class MainFrame(tk.Frame):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.label = tk.Label(self, text='Player 1 turn')
        self.label.pack()
        self.button = tk.Button(
            self, text='Player 1 Move', command=self.on_button)
        self.button.pack(pady=15)
        self.pack()

    def on_button(self):
        print('Button clicked')
        self.label['text'] = 'player 2 thinking'
        self.button['state'] = 'disabled'
        self.after(3000, self.delayed_player_2)


    def delayed_player_2(self):
        self.label['text'] = 'player 2 moving'
        self.after(1000, self.player_2_finsihed)

    def player_2_finsihed(self):
        self.label['text'] = 'player 1 turn'
        self.button['state'] = 'normal'


if __name__ == '__main__':
    app = tk.Tk()
    main_frame = MainFrame()
    app.mainloop()
Quote

Top Page

Possibly Related Threads...
Thread Author Replies Views Last Post
  [PyQt] How to open a program with python without freezing LavaCreeperKing 9 500 Aug-17-2019, 08:48 PM
Last Post: LavaCreeperKing
  Gi module window freezing problem loss 0 545 May-05-2018, 04:42 PM
Last Post: loss

Forum Jump:


Users browsing this thread: 1 Guest(s)