Ren'Py Refresh Images Upon Button Click

Meinos Kaen

Papa Italiano
Game Developer
Jun 17, 2020
150
206
Hello thar, Meinos Kaen here!

So, I'm currently working on a custom Agenda Screen for my MHA management based game. Now, everything is working smoothly except for one thing.

1622976196627.png

To explain what's happening here: you select the heroine, the time of day you want to alter and then from the boxes below you choose the activity for the time of day. The screen changes all variables seamlessly, the activity panel at the bottom even lights up as selected_idle but...!

1622976367519.png

The image above displaying the chosen activity only changes after switching to a different heroine and then back, it doesn't update in real time.

Is there a way to force the image to refresh itself upon button press? (I've already tried a couple of things while researching on this forum and others but I'm open to all suggestions anyway). This is the code I'm using.

Python:
image activity_rest = "gui/ag/activity_rest_hover.jpg"
image activity_school = "gui/ag/activity_school_hover.jpg"
image activity_patrol = "gui/ag/activity_patrol_hover.jpg"

image mina_morn_panel = ConditionSwitch(
    "mina_morn == 'School'", "activity_school",
    "mina_morn == 'Rest'", "activity_rest",
    "mina_morn == 'Patrol'", "activity_patrol",
)

image hero_activity_morn = ConditionSwitch(
    "heroine_name == 'Ikuyo Midoriya'", "ikuyo_morn_panel",
    "heroine_name == 'Toru Hagakure'", "toru_morn_panel",
    "heroine_name == 'Remi Usagiyama'", "miruko_morn_panel",
    "heroine_name == 'Ochaco Uraraka'", "ochaco_morn_panel",
    "heroine_name == 'Mina Ashido'", "mina_morn_panel")

screen agenda():

    tag menu

    add "gui/ag/agenda_bg.png"
    add "hero_image" xpos 1041 ypos 270

    #Selected Activities
    add "hero_activity_morn" xpos 1870 ypos 547
    add "hero_activity_aft" xpos 2391 ypos 547
    add "hero_activity_eve" xpos 2917 ypos 547
    
    
    ##Rest
    imagebutton:
        auto "gui/ag/activity_rest_%s.jpg" xpos 1222 ypos 1167 focus_mask None hover_sound "audio/SFX/hover_rare.ogg" activate_sound "audio/SFX/click_rare.ogg"
        if heroine_name == "Ikuyo Midoriya":
            if activity_time == "Morning":
                action [SetVariable("ikuyo_morn", "Rest"), SetVariable("activity_time", "Afternoon")]
            elif activity_time == "Afternoon":
                action [SetVariable("ikuyo_aft", "Rest"), SetVariable("activity_time", "Evening")]
            elif activity_time == "Evening":
                action [SetVariable("ikuyo_eve", "Rest"), SetVariable("activity_time", "Morning")]
        elif heroine_name == "Mina Ashido":
            if activity_time == "Morning":
                action [SetVariable("mina_morn", "Rest"), SetVariable("activity_time", "Afternoon")]
            elif activity_time == "Afternoon":
                action [SetVariable("mina_aft", "Rest"), SetVariable("activity_time", "Evening")]
            elif activity_time == "Evening":
                action [SetVariable("mina_eve", "Rest"), SetVariable("activity_time", "Morning")]
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,364
15,281
Python:
image activity_rest = "gui/ag/activity_rest_hover.jpg"
image activity_school = "gui/ag/activity_school_hover.jpg"
image activity_patrol = "gui/ag/activity_patrol_hover.jpg"

image mina_morn_panel = ConditionSwitch(
    "mina_morn == 'School'", "activity_school",
    "mina_morn == 'Rest'", "activity_rest",
    "mina_morn == 'Patrol'", "activity_patrol",
)

image hero_activity_morn = ConditionSwitch(
    "heroine_name == 'Ikuyo Midoriya'", "ikuyo_morn_panel",
    "heroine_name == 'Toru Hagakure'", "toru_morn_panel",
    "heroine_name == 'Remi Usagiyama'", "miruko_morn_panel",
    "heroine_name == 'Ochaco Uraraka'", "ochaco_morn_panel",
    "heroine_name == 'Mina Ashido'", "mina_morn_panel")

You don't need ConditionSwitch for something like this. Ren'py is perfectly capable of building the strings by itself without that.

I don't know the whole structure of your game, but your screen could looks like something among those lines :
Python:
screen agenda():
    tag menu

    # /ID/ of the girl actually selected.
    default selectedGirl = "ikuyo"
    # Is the player currently editing an agenda ?
    default editingPeriod = None

    add "gui/ag/agenda_bg.png"

    #  Display the images of all the girls that can be selected.
    vbox:
        imagebutton:
            # I assume that it's the path to their icon
            auto "gui/as/ikuyo_%s.jpg"
            # Update /selectedGirl/ accordingly to the selected girl
            action SetScreenVariable( "selectedGirl", "ikuyo" )
            # Display the button as selected if it correspond to the
            # effectively selected girl
            # /!\ If you want to shorten this by using a /for/ loop,
            # You'll need to use /If/ screen action here. /!\
            selected selectedGirl == "ikuyo"
        imagebutton:
            auto "gui/as/toru_%s.jpg"
            action SetScreenVariable( "selectedGirl", "toru" )
            selected selectedGirl == "toru"
        [... code for the other girls ...]

    #  Display the image corresponding to the effectively selected activity,
    # this for the selected girl and for each period.
    hbox:
        imagebutton:
            # The image is "gui/ag/activity_[activity_name]_%s.jpg", just fill the blank
            # by getting the value where it is stored.
            #  I assume that the activity is stored in "[girl_ID]_[period]", like it seem to be.
            auto ( "gui/ag/activity_{}_%s.jpg".format( getattr( store, selectedGirl + "_morn" ) ) )
            # A click mean that the player will edit the value
            action SetScreenVariable( "editingPeriod", "morn" )

        imagebutton:
            auto ( "gui/ag/activity_{}_%s.jpg".format( getattr( store, selectedGirl + "_aft" ) ) )
            action SetScreenVariable( "editingPeriod", "aft" )

        imagebutton:
            auto ( "gui/ag/activity_{}_%s.jpg".format( getattr( store, selectedGirl + "_eve" ) ) )
            action SetScreenVariable( "editingPeriod", "eve" )


    # If the player is editing a period, do it directly from this screen.
    # It's small enough for that.
    if not editingPeriod is None:
        # Display a full back background surrounding the buttons.
        # Note that the size and position value are purely arbitrary here.
        add Solid( "#000" ) size ( 100, 100 ) pos( 200, 200 )

        hbox:
            imagebutton:
                auto "gui/ag/activity_rest_%s.jpg"
                #  Automatically update the girl agenda. Still assuming that
                # the activity is stored in "[girl_ID]_[period]", like it seem to be.
                #  Then, now that an activity have been choose, end the edition.
                # /!\ Note that the activity do *not* anymore have an uppercase
                # initial. The value *must* match the image name. /!\
                action [ SetField( store, selectedGirl + "_" + editingPeriod, "rest" ), SetScreenVariable( "editingPeriod", None ) ]
            imagebutton:
                auto "gui/ag/activity_patrol_%s.jpg"
                action [ SetField( store, selectedGirl + "_" + editingPeriod, "patrol" ), SetScreenVariable( "editingPeriod", None ) ]
            imagebutton:
                auto "gui/ag/activity_school_%s.jpg"
                action [ SetField( store, selectedGirl + "_" + editingPeriod, "school" ), SetScreenVariable( "editingPeriod", None ) ]
I wrote it on the fly, and by guessing the context and variables of your game, so it would probably not works as it. But still it show you the logic and structure, what should be enough for you to write the same by using the effective paths and variables defined in your game.

I used vbox and hbox for the formatting, but it's solely because "how the screen will be formatted" have no meaning here. What matter is how the values are given to the imagebuttons and how the variables are changed, so I kept the rest as basic as possible. You can perfectly keep them, or position manually each button, or even, surely better, use a grid to display the imagebuttons.
Same for the background for the edition part. There's better one than something full solid black, but it was not part of the relevant information needed here.


Alternatively, since I say few words regarding this, you can probably also shorten the list of the available girls this way:
Python:
    #  Display the images of all the girls that can be selected.
    vbox:
        # Iterate through all the girls /ID/.
        for g in [ "ikuyo", "toru", "miruko", "ochaco", ""mina" ]:
            imagebutton:
                # Use the icons for the girl defined for this iteration.
                auto ( "gui/as/{}_%s.jpg".format( g ) )
                # The value to set will be defined when the object is
                # created. Therefore it will be the value of /g/ during
                # this iteration ; what mean "the accurate one".
                action SetScreenVariable( "selectedGirl", g )
                # Same than above.
                selected If( selectedGirl == g, True, False )
 
  • Like
Reactions: Meinos Kaen

Meinos Kaen

Papa Italiano
Game Developer
Jun 17, 2020
150
206
Try calling the screen again on button click by adding ShowMenu("agenda") to the list of actions.
The most brutally simple ways sometimes are the best. That alone didn't work but add HideMenu("agenda") before that and pronto...!

1622982505398.png


You don't need ConditionSwitch for something like this. Ren'py is perfectly capable of building the strings by itself without that.

I don't know the whole structure of your game, but your screen could looks like something among those lines :
Python:
screen agenda():
    tag menu

    # /ID/ of the girl actually selected.
    default selectedGirl = "ikuyo"
    # Is the player currently editing an agenda ?
    default editingPeriod = None

    add "gui/ag/agenda_bg.png"

    #  Display the images of all the girls that can be selected.
    vbox:
        imagebutton:
            # I assume that it's the path to their icon
            auto "gui/as/ikuyo_%s.jpg"
            # Update /selectedGirl/ accordingly to the selected girl
            action SetScreenVariable( "selectedGirl", "ikuyo" )
            # Display the button as selected if it correspond to the
            # effectively selected girl
            # /!\ If you want to shorten this by using a /for/ loop,
            # You'll need to use /If/ screen action here. /!\
            selected selectedGirl == "ikuyo"
        imagebutton:
            auto "gui/as/toru_%s.jpg"
            action SetScreenVariable( "selectedGirl", "toru" )
            selected selectedGirl == "toru"
        [... code for the other girls ...]

    #  Display the image corresponding to the effectively selected activity,
    # this for the selected girl and for each period.
    hbox:
        imagebutton:
            # The image is "gui/ag/activity_[activity_name]_%s.jpg", just fill the blank
            # by getting the value where it is stored.
            #  I assume that the activity is stored in "[girl_ID]_[period]", like it seem to be.
            auto ( "gui/ag/activity_{}_%s.jpg".format( getattr( store, selectedGirl + "_morn" ) ) )
            # A click mean that the player will edit the value
            action SetScreenVariable( "editingPeriod", "morn" )

        imagebutton:
            auto ( "gui/ag/activity_{}_%s.jpg".format( getattr( store, selectedGirl + "_aft" ) ) )
            action SetScreenVariable( "editingPeriod", "aft" )

        imagebutton:
            auto ( "gui/ag/activity_{}_%s.jpg".format( getattr( store, selectedGirl + "_eve" ) ) )
            action SetScreenVariable( "editingPeriod", "eve" )


    # If the player is editing a period, do it directly from this screen.
    # It's small enough for that.
    if not editingPeriod is None:
        # Display a full back background surrounding the buttons.
        # Note that the size and position value are purely arbitrary here.
        add Solid( "#000" ) size ( 100, 100 ) pos( 200, 200 )

        hbox:
            imagebutton:
                auto "gui/ag/activity_rest_%s.jpg"
                #  Automatically update the girl agenda. Still assuming that
                # the activity is stored in "[girl_ID]_[period]", like it seem to be.
                #  Then, now that an activity have been choose, end the edition.
                # /!\ Note that the activity do *not* anymore have an uppercase
                # initial. The value *must* match the image name. /!\
                action [ SetField( store, selectedGirl + "_" + editingPeriod, "rest" ), SetScreenVariable( "editingPeriod", None ) ]
            imagebutton:
                auto "gui/ag/activity_patrol_%s.jpg"
                action [ SetField( store, selectedGirl + "_" + editingPeriod, "patrol" ), SetScreenVariable( "editingPeriod", None ) ]
            imagebutton:
                auto "gui/ag/activity_school_%s.jpg"
                action [ SetField( store, selectedGirl + "_" + editingPeriod, "school" ), SetScreenVariable( "editingPeriod", None ) ]
I wrote it on the fly, and by guessing the context and variables of your game, so it would probably not works as it. But still it show you the logic and structure, what should be enough for you to write the same by using the effective paths and variables defined in your game.

I used vbox and hbox for the formatting, but it's solely because "how the screen will be formatted" have no meaning here. What matter is how the values are given to the imagebuttons and how the variables are changed, so I kept the rest as basic as possible. You can perfectly keep them, or position manually each button, or even, surely better, use a grid to display the imagebuttons.
Same for the background for the edition part. There's better one than something full solid black, but it was not part of the relevant information needed here.


Alternatively, since I say few words regarding this, you can probably also shorten the list of the available girls this way:
Python:
    #  Display the images of all the girls that can be selected.
    vbox:
        # Iterate through all the girls /ID/.
        for g in [ "ikuyo", "toru", "miruko", "ochaco", ""mina" ]:
            imagebutton:
                # Use the icons for the girl defined for this iteration.
                auto ( "gui/as/{}_%s.jpg".format( g ) )
                # The value to set will be defined when the object is
                # created. Therefore it will be the value of /g/ during
                # this iteration ; what mean "the accurate one".
                action SetScreenVariable( "selectedGirl", g )
                # Same than above.
                selected If( selectedGirl == g, True, False )
This is great stuff, thanks. I don't understand all of it but enough to see that is much more streamlined than my code lol
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,364
15,281
This is great stuff, thanks. I don't understand all of it but enough to see that is much more streamlined than my code lol
Not just that. Since Ren'py automatically update its screen when there's a change impacting them, it would also solve your issue.
It's a wild guess, but I assume that it's precisely because you use ConditionSwitch that you have this problem.


It do not effectively describe the process, but is close enough to explain it:

Regularly (at the end of an interaction), Ren'py pass through the screen and look at each elements.
It see a text that use a variable, then think, "well, perhaps the variable have changed, I'll update the string, just in case".
It see an image that use a variable, then think, "well, perhaps that this variable have also changed, I'll display the image again, just in case".
Then, it see the image using a ConditionSwitch. It then talk to it, "Hey buddy, you should look if the variable you rely on have changed", to what ConditionSwitch answer, "Yeah, yeah, I'll do it... after my nap... maybe". Seeing this, Ren'py quit and display the image it was previously displaying.

But when Ren'py is displaying the screen for the first time, it shake ConditionSwitch until it fully awake and do its thing, because there's no image to display else.