4

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()
LuckyLuke
  • 1,028
  • 2
  • 14
  • 28
  • Have you checked [the tkinter documentation on "Events and Bindings"](http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm)? Unless your aim is to create your own events system reusing tkinter's will prove much easier. – Aaron Dec 03 '18 at 15:34
  • I know that, but the thing is, this is a side project. I am building bigger project. This project is for me to understand how the events work at the first place. :) I must implement Event class. I think I am not allowed can't achieve that with tkinter events and bindings. – LuckyLuke Dec 03 '18 at 15:37

2 Answers2

7

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.

Nagev
  • 10,835
  • 4
  • 58
  • 69
2

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, 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.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Shon_Shon
  • 21
  • 3
  • 2
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 14 '22 at 16:42