ThorPy

A GUI library for pygame

Tutorials - Reactions

We present here how ThorPy provides a way for elements to react to input events. If you just want to learn how to capture specific ThorPy events like when a button is clicked, dragged, hovered, etc, you can directly go to the section on ThorPy events in the user guide. However, we advise to use reactions since they make the code clear and short.

So, what is a reaction in the context of a ThorPy code? The answer is : a reaction to an event define what happens when this event occurs. Therefore, a reaction is defined by:

  • the event to which it reacts;
  • the function that is called when this event occurs - we call it the reaction function;
  • the parameters that are passed to the reaction function;
In fact, we are going to add one more thing to a reaction: a way to better filter the event. Indeed, in pygame, events have attribute - for example, pygame.MOUSEBUTTONDOWN event has the attribute button which contains the id of the button clicked. So, in addition to the event to which the reaction reacts, you may want to precise the arguments required for this event (for instance imagine that you want to capture left click only). Of course, you can also filter the event manually in the reaction function, by checking the event attributes with a cascade of if statements.

We are now going to explore different possibilities using reactions.

Tutorial

1. Creating a reaction

Let's first write a basis code on top of which we will work. The code declare a single element, which is a Background (line 13). Then, we put the background in a menu (line 16). We also declare a reaction named my_reaction (line 8) reacting to pygame.MOUSEBUTTONDOWN and a reaction function named my_reaction_func (line 4). We finally add my_reaction to the reactions of the background (line 14).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#ThorPy reactions tutorial : step 1
import thorpy, pygame

def my_func_reaction(event):#Reactions functions must take an event as first arg
    print("My reaction displays the pos of event:", event.pos)

#We declare a Reaction. Note that we do not filter the event here.
my_reaction = thorpy.Reaction(reacts_to=pygame.MOUSEBUTTONDOWN,
                              reac_func=my_func_reaction)

application = thorpy.Application(size=(300, 300), caption="Reaction tuto")

background = thorpy.Background.make(color=(255,255,255))
background.add_reaction(my_reaction) #add my_reaction to background's reactions

menu = thorpy.Menu(background) #create a menu for auto events handling
menu.play() #launch the menu

application.quit()

You can click anywhere on the screen and you will see that the reaction function is launched as expected. You can also try to create another element that you set as children of the background, and then assign the reaction to this one instead of he background, and you will observe that this do not change anything, since the reaction function's behaviour does not depend on the element to which the reaction is attached.

2. How the menu works

In order to understand what the menu does, let us print the value of menu.get_events(). You will obtain something like : {5: {<thorpy.elements.background.Background object at 0x00000000032ADEB8>}}

This is the dictionary (actually a dictionary of sets) that the menu uses to determine what to do when an event occurs. Noticing that the value pygame.MOUSEBUTTONDOWN is 5, you can interpret the first entry of the dictionary as : 'if the event number 5 occurs, then the menu will ask the element thorpy.elements.background.Background object at 0x00000000032ADEB8 to react.

3. User functions

You have just learned how to add a reaction to an element. However, all elements classes that inherits from thorpy.Pressable class provides a way for user to quickly associate a reaction to the button click. For example, if you declare my_button = thorpy.make_button("my text", func=my_function, params=my_params) with my_params a dictionnary (empty by default), then you implicitely associate a reaction to the mouse click of my_button, and this reaction will call my_function(**my_params).

Note that if you did not created your element from thorpy.make_button function, you can reproduce the same behaviour with the following instructions : my_button.user_func = my_func and my_button.user_params = my_params.

The difference between the user_func and the reactions is that the reactions are much more general, while the user_func is just a way to quickly create a reaction that occurs when the button is clicked ; if you need a reaction to something else, then you need to create your own reaction instance. In particular, a reaction can capture the event to which it reacts (like the one we declared above), and then process in relation to the value of the event.

4. ConstantReactions

In the first code we wrote above, we do use the value of event into the reaction function. However, it often happens that you do not need it. When this situation occurs, you may prefer to use a thorpy.ConstantReaction instead of a thorpy.Reaction for creating your reaction. The only difference is that normal reaction functions must take the event as first argument, while the constant reaction functions have no constraint on their arguments, expect the fact that they should be consistant with what you specified as parameters arguments during the creation of the reaction. You can see a use of a constant reaction that is similar to the code above here.

5. ThorPy events

Until now, we have made reactions that were reacting to pygame events. However, imagine that you want, for instance, to make a reaction that reacts to text insertion. Of course pygame does not even know that ThorPy exists - therefore no pygame event related to the fact that user inserted a text will occur. ThorPy, however, automatically post events related to many things (see the cheat sheet table), and you can use them to react to events related to ThorPy elements. In our case, what we need is a reaction reacting to event of type thorpy.constants.THORPY_EVENT and which has argument id of value thorpy.constants.EVENT_INSERT.

For doing this, we first create an element of type thorpy.Inserter (line 17) that provides a way for user to insert a text. Now it is the only child of the background element. Then, we modify my_func_reaction so that is display to the console a text summarizing the name of the inserter element and the new value of the inserter. The element who made the event appear is stored into event.el - thus it is easy to obtain its text and its new value (lines 5 and 6). Finally, we modify my_reaction : now, it has to react to the event thorpy.constants.THORPY_EVENT (line 11) with id thorpy.constants.EVENT_INSERT (line 13). That's all and it works : when you insert a new text into the inserter, the new text and the name of the inserter are printed to the console.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ThorPy reactions tutorial : step 2 - ThorPy events
import thorpy, pygame

def my_func_reaction(event):#Reactions functions must take an event as first arg
    element_name = event.el.get_text()
    element_value = event.el.get_value()
    print("You have just inserted a text into element named:", element_name)
    print("By the way, you inserted the following text: '", element_value, "'")

#We declare a Reaction reaction to THORPY_EVENTs with id EVENT_INSERT
my_reaction = thorpy.Reaction(reacts_to=thorpy.constants.THORPY_EVENT,
                              reac_func=my_func_reaction,
                              event_args={"id":thorpy.constants.EVENT_INSERT})

application = thorpy.Application(size=(300, 300), caption="Reaction tuto")

inserter = thorpy.Inserter.make(name="My Inserter", value="Write here!")
background = thorpy.Background.make(color=(255,255,255), elements=[inserter])
background.add_reaction(my_reaction) #add my_reaction to background's reactions

thorpy.store(background)

menu = thorpy.Menu(background) #create a menu for auto events handling
menu.play() #launch the menu

application.quit()

6. Dynamically create and modify reactions

Sometimes, one needs to change at runtime a previously assigned reaction of an element. The example below is very simple : the only ThorPy element is the background. When starting the menu, background has a constant reaction called reac_1 reacting to the mouse click and whose function reaction my_func_reaction1 is written at line 7. This function reaction itself creates another constant reaction (lines 8-9), then removes the previous reaction (line 10), add the new one (line 11), and refresh the menu (line 12). This last instruction is important, otherwise the menu won't be aware that it has to update its reaction dictionnary. Of course, if you are manually handling events and you don't use any ThorPy menu, you won't need to refresh anything. Note that for doing all this, my_func_reaction1 takes 2 arguments : the first one correspond to the element whose reactions are to replace, and the seconde one is the old reaction to remove. Finally, at line 19 we create the actual reac_1, passing it the constant parameters in two times since one of these parameters is the reaction itself.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ThorPy reactions tutorial : step 3 - Dynamically modify events
import thorpy, pygame

def my_func_reaction2(): #constant reaction do not take event as first arg
    print("reaction 2 launched")

def my_func_reaction1(el, reac_1):
    new_reaction = thorpy.ConstantReaction(reacts_to=pygame.MOUSEBUTTONDOWN,
                                           reac_func=my_func_reaction2)
    el.remove_reaction(reac_1)
    el.add_reaction(new_reaction)
    thorpy.functions.refresh_current_menu() #tell menu to refresh reactions!
    print("reaction 1 launched - replacing reac 1 by reac 2")

application = thorpy.Application(size=(300, 300), caption="Reaction tuto")

background = thorpy.Background.make(color=(255,255,255))

reac_1 = thorpy.ConstantReaction(reacts_to=pygame.MOUSEBUTTONDOWN,
                                 reac_func=my_func_reaction1,
                                 params={"el":background,
                                         "reac_1":None})
reac_1.params["reac_1"] = reac_1
background.add_reaction(reac_1)

menu = thorpy.Menu(background)
menu.play()

application.quit()