Search code examples
pythonmultithreadingpython-2.7gtkpython-decorators

Extending the functionality of the Tic-tac-toe game (gtk + decorators + raw_input)


Some time ago I wrote a simple Tic-tac-toe game in Python (2.7), multiplayer version with server script (thread per connection) and client script. Now I want to extend client with gtk. I would like this to be an example of the use of decorators. Client script works in such a way that it retrieves data through the prompt. I want to write a script with will be something like a patch for client (just decorate clients functions, call new gtk-client script and wallah). I thought to do it that way:

  • gtk-client starts run_game() function from client script in thread
  • functions responsible for user input have decorators, that executes code and at the end they puts data to prompt (it is possible?)

This is my idea to control client script by gtk interface, but I came across some problems:

  • is it possible to put data to prompt like a keyboard input?
  • is it possible to get data from raw_input() if it is not in main thread?

Edit: Ok, I found a solution. I will create decorator who will overwrite raw_input() function (monkey pathing). It should look like this:

  • gtk-client creates thread with run_game() func. from client
  • run_game() is decorated (overwrite raw_input() before function call)
  • now raw_input() waits for input data from gtk-client by Queue (queue.get())

It looks like a good solution, but here is my another problem. My gtk-client calls thread (run_game()). If I do not use thread.join(), my thread is blocked in execution or print function can't print whole data to console. If I use thread.join() that creates a conflict, because thread waits for data in queue. Example test codes:

gtk-client.py

import gtk
import client as cliApp
from threading import Thread
from Queue import Queue 

# gtk stuff etc...

# lets say it is called on y_button_click, part of GameGtk class
def y_button_click(self, widget):
    cliApp.q.put('test msg')


# lets say that it is called in x_button_click
@run_game_decorator(func):
    def wrapper(*args):
        # some connecting/logging stuff
        cliApp.q = Queue()
        t = Thread(target = cliApp.test)
        t.start()
        # t.join() - worked until I added q.get() to new raw_input()
    return wrapper
# gtk still working after this function

client.py

def new_raw_input(label):
    print label
    return q.get()

def test():
    print 'Thread start'
    raw_input = new_raw_input
    a = raw_input("Type something: ")
    print a
    print 'Thread finished'

In this case my thread prints only 'Thread start'. How to handle this problem?


Solution

  • Ok, finally I found solution for all my problems. To simulate user input to raw_input() just change function funcionality (monkey patching) as shown in question, edited section.

    Regarding to print errors in console - I resolvet it by adding sys.stdout.flush() to flush buffers (explained here), and waiting for thread after putting data to queue.

    gtk-client.py

    import gtk
    import client as cliApp
    from threading import Thread
    from Queue import Queue
    from time import sleep
    import sys
    
    
    # gtk stuff etc...
    
    # lets say it is called on y_button_click, part of GameGtk class
    def y_button_click(self, widget):
        cliApp.q.put('test msg')
        self.t.join()
    
    
    # lets say that it is called in x_button_click
    @run_game_decorator(func):
        def wrapper(*args):
            # some connecting/logging stuff
            cliApp.q = Queue()
            gtkWindow.t = Thread(target = cliApp.test)
            gtkWindow.t.start()
            sleep(0.1)
            sys.stdout.flusch()
        return wrapper
    # gtk still working after this function