[Solved] Easy way to prevent a textbutton from being clicked twice?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
henvu50
Veteran
Posts: 337
Joined: Wed Aug 22, 2018 1:22 am
Contact:

[Solved] Easy way to prevent a textbutton from being clicked twice?

#1 Post by henvu50 »

Anyone know an easy way to prevent a textbutton from being clicked twice? I can think of a few ways, but looking for the easiest, simplest option.

Code: Select all

# PSUEDO CODE
textbutton "You can only click me once":
     insensitive [CONDITION]  self.selected # if this existed, it would return True
I'm still inexperienced with Ren'py, so I have to be careful I don't jump through ten hoops to do something that might be super simple, but I just don't know about it.
Last edited by henvu50 on Fri Jul 30, 2021 4:22 pm, edited 1 time in total.

User avatar
Ocelot
Lemma-Class Veteran
Posts: 2376
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Easy way to prevent a textbutton from being clicked twice?

#2 Post by Ocelot »

As usual, it depends on what do you actually want and for what reason. Quickest soulution will be to set some variable as one of button actions and test for that variable in insensitive.

However, insensitive have a special meaning in gui design. You should think if it is what you really need. If your only goal is to make button do no actions if clicked, you can wrap existing actions into If() action.

I prefer to separate code from presentation, so my screen code would probably look like:

Code: Select all

textbutton "option 1" action SetField(option_screen_1, "value", 1)
textbutton "option 2" action SetField(option_screen_1, "value", 2)
And in the code I will have something like

Code: Select all

init python:
    @filter_change_event(new_value=1)
    def opt1():
        # Stuff which happens when option 1 is selected

    @filter_change_event(new_value=2)
    def opt2():
        # Stuff which happens when option 2 is selected

define option_screen_1 = RadiobuttonController(value=1, on_change=[opt1, opt2])
The controller code is responsible that events would not be called, unless value was actually changed, filter_change_event decorator will take care of conforming to the change event interface and writing conditional statements. A lot of complexity can be hidden behind a simple SetField action.

If I find myself using radiobuttons often, I might even make a custom action and my screen code might look like:

Code: Select all

textbutton "option 1" action RadiobuttonOption("option_screen_1", 1, act1)
textbutton "option 2" action RadiobuttonOption("option_screen_1", 2, act2)
# act1 and act2 are actual action I want to run if current button is not selected
< < insert Rick Cook quote here > >

User avatar
hell_oh_world
Miko-Class Veteran
Posts: 777
Joined: Fri Jul 12, 2019 5:21 am
Contact:

Re: Easy way to prevent a textbutton from being clicked twice?

#3 Post by hell_oh_world »

Anyone know an easy way to prevent a textbutton from being clicked twice? I can think of a few ways, but looking for the easiest, simplest option.

Code: Select all

# PSUEDO CODE
textbutton "You can only click me once":
     insensitive [CONDITION]  self.selected # if this existed, it would return True
I'm still inexperienced with Ren'py, so I have to be careful I don't jump through ten hoops to do something that might be super simple, but I just don't know about it.
You could try giving an id to your button and use not renpy.get_displayable(...).selected as the expression for the sensitive property of the button.

Edit: Just looked up the docs for renpy.get_widget, seems that pytom changed it now to renpy.get_displayable, much better. Still, you can use renpy.get_widget with the same parameters as the new one.

henvu50
Veteran
Posts: 337
Joined: Wed Aug 22, 2018 1:22 am
Contact:

Re: Easy way to prevent a textbutton from being clicked twice?

#4 Post by henvu50 »

hell_oh_world wrote: Thu Jul 29, 2021 6:47 pm You could try giving an id to your button and use not renpy.get_displayable(...).selected as the expression for the sensitive property of the button.

Edit: Just looked up the docs for renpy.get_widget, seems that pytom changed it now to renpy.get_displayable, much better. Still, you can use renpy.get_widget with the same parameters as the new one.
I'm experimenting with renpy.get_displayable. I was able to create a reference to a button, but it returns error if i try .selected

Code: Select all

AttributeError: 'NoneType' object has no attribute 'selected'

Code: Select all

db5 = renpy.get_displayable(screen='main_menu', id='btn_pref', layer='screens')
print db5 #prints the text: Button
print db5.selected # returns error
I think I need to use renpy.get_displayable_properties, but it returns an empty dictionary.

Code: Select all

db5 = renpy.get_displayable_properties(screen='main_menu', id='btn_pref', layer='screens')
print db5 #prints an empty dictionary
I think I'm gonna have to go with Ocelot's solution, although that code seems incomplete ( I don't know how to fill in the gaps, i'm still a python rookie), but it looks like a good solution.

It throws an error on: filter_change_event ... and I'm not sure what RadiobuttonController is. Is it a function i have to create myself?

User avatar
hell_oh_world
Miko-Class Veteran
Posts: 777
Joined: Fri Jul 12, 2019 5:21 am
Contact:

Re: Easy way to prevent a textbutton from being clicked twice?

#5 Post by hell_oh_world »

Code: Select all

$ displayable = renpy.get_displayable(None, "btn")
textbutton id "btn":
  sensitive (displayable.selected if displayable else None)
you don't really need the screen tbh, afaik the function will automatically pick the current screen if it is set to None.

User avatar
Ocelot
Lemma-Class Veteran
Posts: 2376
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Easy way to prevent a textbutton from being clicked twice?

#6 Post by Ocelot »

henvu50 wrote: Fri Jul 30, 2021 5:01 am I think I'm gonna have to go with Ocelot's solution, although that code seems incomplete ( I don't know how to fill in the gaps, i'm still a python rookie), but it looks like a good solution.

It throws an error on: filter_change_event ... and I'm not sure what RadiobuttonController is. Is it a function i have to create myself?
If you are interested, here is complete code with (heavily gutted) implementations of controller and filter decorators:

Code: Select all

# This is the core of RadiobuttonController.
# Best served hidden in some auxilary rpy file
init -100 python:
    class RadiobuttonController(object):
        def __init__(self, value, on_change=[]):
            self.__value = value
            self.on_change = on_change[:]

        @property
        def value(self):
            return self.__value

        @value.setter
        def value(self, new_val):
            old_val, self.__value = self.__value, new_val
            if old_val != new_val:
                for callback in self.on_change:
                    callback(old_val, new_val)

        def callbacks(self):
            return on_change


    def filter_change_event(*args, **kwargs):
        def inner(func):
            def wrapper(old_value, new_value):
                if 'old_value' in kwargs and kwargs['old_value'] != old_value:
                    return
                if 'new_value' in kwargs and kwargs['new_value'] != new_value:
                    return
                func()
            return wrapper
        return inner


# test code
screen test:
    vbox:
        textbutton "option 1" action SetField(option_screen_1, "value", 1)
        textbutton "option 2" action SetField(option_screen_1, "value", 2)
        text '[foo]'

init python:
    @filter_change_event(new_value=1)
    def opt1():
        print store.foo
        print "setting to 100"
        store.foo = 100

    @filter_change_event(new_value=2)
    def opt2():
        print store.foo
        print "setting to 200"
        store.foo = 200

define option_screen_1 = RadiobuttonController(value=1, on_change=[opt1, opt2])
default foo = 100

label start:
    show screen test
    '.....'
    return
You can run this code, click buttons a few times, open console with Shift+O and see that value of foo is never set to the value it already had.

If you want to learn about decorators, here is the link: https://realpython.com/primer-on-python-decorators
< < insert Rick Cook quote here > >

henvu50
Veteran
Posts: 337
Joined: Wed Aug 22, 2018 1:22 am
Contact:

Re: Easy way to prevent a textbutton from being clicked twice?

#7 Post by henvu50 »

Thanks, that is work of beauty!

henvu50
Veteran
Posts: 337
Joined: Wed Aug 22, 2018 1:22 am
Contact:

Re: Easy way to prevent a textbutton from being clicked twice?

#8 Post by henvu50 »

hell_oh_world wrote: Fri Jul 30, 2021 6:25 am

Code: Select all

$ displayable = renpy.get_displayable(None, "btn")
textbutton id "btn":
  sensitive (displayable.selected if displayable else None)
you don't really need the screen tbh, afaik the function will automatically pick the current screen if it is set to None.
thank you thank you!

Post Reply

Who is online

Users browsing this forum: No registered users