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.
Let's say I want a button that sorts a list. Would the best way to approach that be something like extending the Action class with python's sorted function:
Let's say I want a button that sorts a list. Would the best way to approach that be something like extending the Action class with python's sorted function:
Or does Ren'Py have a function built-in to sort lists and lists of lists?
Ren'Py doesn't, at least I know of none. I've didn't get a chance to mess with Action yet (but really want to). You code doesn't look like it'll work, unless someone posts a proper reply, I'll see if I can do the messing around ina dummy project.
init python:
from random import shuffle
from copy import copy
class SortContainer(Action):
"""
Sorts an iterable container on key parameter.
"""
def __init__(self, container, key=None):
self.container = container
self.key = key
def __call__(self):
for key in renpy.store.__dict__:
if renpy.store.__dict__[key] is self.container:
renpy.store.__dict__[key] = sorted(self.container, self.key)
return 1
label start:
$ l = range(100)
$ shuffle(l)
$ z = copy(l)
show screen test_sorting
while True:
$result = ui.interact()
screen test_sorting:
hbox:
xalign 0.5
xmaximum config.screen_width
box_wrap True
for i in l:
text "[i]"
hbox:
xalign 0.5
yalign 0.4
xmaximum config.screen_width
box_wrap True
for i in z:
text "[i]"
textbutton "Sort":
align (0.5, 0.9)
action SortContainer(l)
This works and it doesn't sort z due to "is" instead of "==" but this is still an awkward code, I couldn't figure out how to do it differently with Action inheritance...
init python:
from random import shuffle
from copy import copy
class SortContainer(Action):
"""
Sorts an iterable container on key parameter.
"""
def __init__(self, container, key=None):
self.container = container
self.key = key
def __call__(self):
for key in renpy.store.__dict__:
if renpy.store.__dict__[key] is self.container:
renpy.store.__dict__[key] = sorted(self.container, self.key)
return 1
label start:
$ l = range(100)
$ shuffle(l)
$ z = copy(l)
show screen test_sorting
while True:
$result = ui.interact()
screen test_sorting:
hbox:
xalign 0.5
xmaximum config.screen_width
box_wrap True
for i in l:
text "[i]"
hbox:
xalign 0.5
yalign 0.4
xmaximum config.screen_width
box_wrap True
for i in z:
text "[i]"
textbutton "Sort":
align (0.5, 0.9)
action SortContainer(l)
This works and it doesn't sort z due to "is" instead of "==" but this is still an awkward code, I couldn't figure out how to do it differently with Action inheritance...
That's really interesting, I wouldn't have thought of using renpy.store.__dict__
My biggest issue was getting the sorted list assigned to the right variable.
Off the top of my head, a small tweak would be an option for reverse sorting, like...
Here's a modified example that removes the need for "while True:", but I'm afraid I don't know quite enough about the consequences of "renpy.restart_interaction()" to say if this is correct.
Human Bolt Diary wrote:Here's a modified example that removes the need for "while True:", but I'm afraid I don't know quite enough about the consequences of "renpy.restart_interaction()" to say if this is correct.
It's not correct and while loop is not required for this class to work in any way or form. It was there to prevent label from running out and return to main menu so I could watch the results of sorting in peace. Assuming that you were planning using this in game's context, you'll be ok without the loop.
PS: With loop in the game, it would have been simpler to return the list and have it sorted inside it without the need to access store's dictionary.
Human Bolt Diary wrote:Here's a modified example that removes the need for "while True:", but I'm afraid I don't know quite enough about the consequences of "renpy.restart_interaction()" to say if this is correct.
It's not correct and while loop is not required for this class to work in any way or form. It was there to prevent label from running out and return to main menu so I could watch the results of sorting in peace. Assuming that you were planning using this in game's context, you'll be ok without the loop.
PS: With loop in the game, it would have been simpler to return the list and have it sorted inside it without the need to access store's dictionary.
I see now. I was confused about the reason for the loop.
If I'm understanding correctly, your class sorts the list and changes the variable in the store's dictionary to the sorted list. This is independent from the while loop and ui.interact(), which is keeping the screen alive to refresh the numbers. Without the loop, the variable is stored and the player is returned to where they left off in the script.
Human Bolt Diary wrote:
If I'm understanding correctly, your class sorts the list and changes the variable in the store's dictionary to the sorted list. This is independent from the while loop and ui.interact(), which is keeping the screen alive to refresh the numbers. Without the loop, the variable is stored and the player is returned to where they left off in the script.
Thanks for the help.
Yeap, that's all right except the loop kept the entire game alive, screen would have died with the game (screen that is shown, not called has to be hidden, called screen will disappear on the first successful interaction). Show screen doesn't really take you out of game context so you can't really say that you would be returned to where ever you came from, screen can be active and you can also go on with your game. That's besides the point, that sorting class got nothing to go with it, it's just how Ren'Py is setup to work.
That approach has a drawback though.
If the list is referenced by another name, that name will still point to the old list. Of course that will only cause trouble if there is more than one reference to the list, but it's something to keep in mind.
Anima wrote:That approach has a drawback though.
If the list is referenced by another name, that name will still point to the old list. Of course that will only cause trouble if there is more than one reference to the list, but it's something to keep in mind.
That's not true. We iterate over the whole global namespace, searching for any variable (label) that points to but does not necessarily equals to the list (or label to a list if you want to get technical since Python just passes "labels" around). If we wrote z = l in my example instead of making a shallow copy, both variables would point to the new list and old list would have been killed by the garbage collector unless referenced to from outside of global scope or Ren'Py not do something crazy for rollback support.
That said, iterating over the whole global namespace just to sort a list is not the best way to sort it I just used this as an exercise to start using custom screen actions in my game (and already am).
True, it doesn't refer to the old list, but it would still not refer to the same list since you assign a new list to it. So if a,b refer to the same list object and you use sort on a once, a and b will not point to the same list any more. If you'd use sort on a again b wouldn't be affected.
Anima wrote:True, it doesn't refer to the old list, but it would still not refer to the same list since you assign a new list to it. So if a,b refer to the same list object and you use sort on a once, a and b will not point to the same list any more. If you'd use sort on a again b wouldn't be affected.
Yeap... so we get a different behavior from if we simply used sorted (would sort one list but leave the other one along). We can't break out of the for loop either since there is no way of knowing witch list to sort...
class SortContainer(Action):
"""
Sorts an iterable container on key parameter.
Expects a string representation of a variable assigned to a container as first argument.
"""
def __init__(self, container):
self.container = container
def __call__(self):
renpy.store.__dict__[self.container] = sorted(renpy.store.__dict__[self.container])
return "__"
SetVariable() approach: where they pass a string representation of a variable as argument. Add args and kwargs and this is fairly close to what I'd be willing to use in my game
Great job finding holes to poke with the last approach btw.
It sorts the list in place, so it doesn't change any references. As a plus point it also works for lists that aren't directly referenced from the store. Like lists that are referenced by an objects field.