LuckyLuke
LuckyLuke

Reputation: 1054

How to implement events in Python?

Can't figure out how to implement event system. I am doing project with tkinter. And I need to use events. How to have events like Java or C# events?

I searched a lot but can't figure out the right way.

Here is the event class I am trying to implement.

class Event(object):

    def __init__(self):
        self.handlers = []

    def add(self, handler):
        self.handlers.append(handler)
        return self

    def remove(self, handler):
        self.handlers.remove(handler)
        return self

    def fire(self, sender, earg=None):
        for handler in self.handlers:
            value = handler()
            self.remove(handler)
            return value


    __iadd__ = add
    __isub__ = remove
    __call__ = fire

Here is Car class

class Car:
    _speed = 0
    events = Event()

    def speed_up(self):
        self._speed += 10

    def speed_down(self):
        self._speed -= 10

    def get_speed(self):
        return self._speed

And for last there is Window class (tkinter window)

class Window(tk.Tk):
    def __init__(self):
        super().__init__()
        self.car = Car()
        tk.Button(self, text="Speed Up", command=self.increase_speed).grid(sticky="nsew")
        tk.Button(self, text="Speed Down", command=self.decrease_speed).grid(sticky="nsew")
        self.speed_label = tk.Label(self, text="0")
        self.speed_label.grid(sticky="nsew")
        self.mainloop()

    def increase_speed(self):
        self.car

    def decrease_speed(self):
        pass

Here is tkinter window: tkinter window

I want to accomplish: 1) On Speed Up button click "speed_up" should be added to events. 2) It should change value of self.speed_label. 3) It should be something like c# / Java events or c# delagetes.

Trying to learn this new concept to me. But having difficult time implementing this...

UPDATED! I was searching / editing and came up with a solution. Don't know if this solution is good. I will ask my teacher if this is a good way for implementing events. But for now the code look like this:

import tkinter as tk

class Observer():
    _observers = []
    def __init__(self):
        self._observers.append(self)
        self._observed_events = []
    def observe(self, event_name, callback_fn):
        self._observed_events.append({'event_name' : event_name, 'callback_fn' : callback_fn})


class Event():
    def send(self, event_name, *callback_args):
        for observer in Observer._observers:
            for observable in observer._observed_events:
                if observable['event_name'] == event_name:
                    observable['callback_fn'](*callback_args)

    def receive(self, event_name, *callback_args):
        for observer in Observer._observers:
            for observable in observer._observed_events:
                if observable['event_name'] == event_name:
                    response = observable['callback_fn'](*callback_args)
                    return response

class Car(Observer):
    def __init__(self):
        Observer.__init__(self)
        self._current_speed = 0

    def speed(self):
        self._current_speed += 10

    def slow(self):
        self._current_speed -= 10

    def current(self):
        return self._current_speed


class Window(tk.Tk):
    def __init__(self):
        super().__init__()
        self._car = Car()
        self.store()
        self.events = Event()
        tk.Button(self, text="Speed Up", command=lambda:self.change_speed("speed")).grid(sticky="nsew")
        tk.Button(self, text="Slow Down", command=lambda:self.change_speed("slow")).grid(sticky="nsew")
        self.label = tk.Label(self, text=0)
        self.label.grid()
        self.settings()


    def store(self):
        self._car.observe("speed", self._car.speed)
        self._car.observe("slow", self._car.slow)
        self._car.observe("current", self._car.current)

    def settings(self):
        self.mainloop()

    def change_speed(self, event):
        self.events.send(event)
        self.label.config(text=self.events.receive("current"))



Window()

Upvotes: 4

Views: 22523

Answers (2)

Shon_Shon
Shon_Shon

Reputation: 53

You can find a good explanation on how to implement observers and events in a language that doesn't have them by default in here (Game Programming Patterns, Observer pattern), it is written in c++, but the implementation is almost the same in python.

I would probably have used a list of observers for each event, so instead of the event having to search for its observers in the observers' list it just has to cycle through its personal list of observers to notify them. If the event has specific parameters that are important like the id of the entity who activated it you could include it on the notification, each observer decides what to do with that, they can simply do nothing if that information isn't relevant to them.

Upvotes: 2

Nagev
Nagev

Reputation: 13207

Without fully analyzing the code in the question, I'd say you're in the right direction by using function callbacks. That's because Python, as far as I know, does not have a native implementation of events.

Some useful libraries or examples that build on this can be seen in articles such as the observer pattern, mimicking events or in answers to a related question.

This is the simplest code I can think of that illustrates the callback concept, without arguments:

   def on_notify():
       print("OK, I'm up-to-date")

   def do_something(update):
       # do whatever I need to do
       print("I've changed\n")
       update()

   do_something(on_notify)

Which outputs:

I've changed

OK, I'm up-to-date

Our "worker" function takes a function parameter to be called when an event happens. In this case it's only one, but a list could be used, so that we have many watchers, which is what the other more complete examples do.

Also relevant are event objects, a mechanism for objects to communicate between threads, which is something worth considering for user interfaces. My guess is that most libraries and frameworks out there which implement the "missing events" functionality are based on either or both of these core methods.

Upvotes: 7

Related Questions