Dynamically nested loop function on screen

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
moonae
Newbie
Posts: 4
Joined: Sun Jun 06, 2021 8:29 am
Contact:

Dynamically nested loop function on screen

#1 Post by moonae » Sun Jun 06, 2021 9:11 am

(I've tried searching the forums but haven't found an answer to this. If there's already a thread about it somewhere, I apologize)

I have several dictionaries that contain "results". They are all of different lengths and sometimes they include nested dicionaries. Here's an example of what one can look like:

Code: Select all

default dict_with_results_1 = {'result 1' : { 'b' : 2,'c': {'inner c' : 3, 'inner c2' : 3.5} },
                               'result 2' : { 'e' : 4 , 'f' : 5 , 'g' : 6 },
                               'result 3' : { 'h' : 7, 'i' : 8 },
                               'result 4' : { 'j' : 9 } }     
And I have a screen that is called with 1 such dictionary as a parameter.
The screen then displays the key/value pairs of the dictionary as text with some fomatting (that I've mostly cut out here). And if there is a nested dictionary it should be displayed with the same type of formatting, which requires unpacking the nested dict as well.

Code: Select all

label start:
    '...'
    menu:
        'Option 1':
            call screen screen_test(dict_with_results_1)
        'Option 2':
            call screen screen_test(dict_with_results_2)  

Code: Select all

        
screen screen_test(d):
    frame:
        vbox:
            for result,x in d.items():
                hbox:
                    text '[result] -- '
                    vbox:
                        for k,v in x.items():
                            hbox:
                                text '[k] - '
                                if isinstance(v, dict):
                                    vbox:
                                        for innerK,innerV in v.items():
                                            hbox:
                                                text '[innerK] - '
                                                text '[innerV]'
                                else:
                                    text '[v]'
  
(See image in attachment for how this will look (sans styling, 'cause 1 step at a time))

The thing is that since all the dictionaries look different I don't know how many levels of nested dictionaries will be neccessary. And I would rather not just have to write the same code over and over again.

In regular python code I would do this by just recalling the same function. See this example from a different part of the code, where "inner_func" gets called over and over until all levels of nesting have been reached:

Code: Select all

def checkDict(old_dict):
    new_dict = { }
    def inner_func(n,d):
        for k, v in d.items():
            if isinstance(v,dict):
                n[k] = { }
                inner_func(n[k],v) #re-run for nested dictionaries
            else:
                #Stuff I actually want to do the the values of the dict
    inner_func(new_dict,old_dict)
    return new_dict
The problem is I'm not sure how to combine this type of code with renpy screens? Is there a way to dynamically set the number of h/vboxes and text inside a python function and display it on a screen?
Or should I try a different approach all together?

(A later goal is the replace all the h/vboxes with grids or vpgrids without making the sizes all bonkers, but that's for another day)
Attachments
Skärmbild 2021-06-06 145932.jpg
Skärmbild 2021-06-06 145932.jpg (21.62 KiB) Viewed 712 times

User avatar
emz911
Regular
Posts: 103
Joined: Fri Jun 23, 2017 2:23 pm
Contact:

Re: Dynamically nested loop function on screen

#2 Post by emz911 » Sun Jun 06, 2021 2:12 pm

If you have a working function, you can do action Function()

User avatar
Alex
Lemma-Class Veteran
Posts: 2981
Joined: Fri Dec 11, 2009 5:25 pm
Contact:

Re: Dynamically nested loop function on screen

#3 Post by Alex » Sun Jun 06, 2021 3:57 pm

moonae wrote:
Sun Jun 06, 2021 9:11 am
...
Well, that's just an ugly workaround... But if you could create screens programmatically...

Code: Select all

screen my_vbox_scr_0(some_dict):
    frame:
        vbox:
            for k, v in some_dict.items():
                hbox:
                    text "[k] -- "
                    if isinstance(v, dict):
                        use my_vbox_scr_1(v)
                    else:
                        text "[v]"
                    
screen my_vbox_scr_1(some_dict):
    frame:
        vbox:
            for k, v in some_dict.items():
                hbox:
                    text "[k] -- "
                    if isinstance(v, dict):
                        use my_vbox_scr_2(v)
                    else:
                        text "[v]"
                    
screen my_vbox_scr_2(some_dict):
    frame:
        vbox:
            for k, v in some_dict.items():
                hbox:
                    text "[k] -- "
                    if isinstance(v, dict):
                        use my_vbox_scr_3(v)
                    else:
                        text "[v]"
                    
screen my_vbox_scr_3(some_dict):
    frame:
        vbox:
            for k, v in some_dict.items():
                hbox:
                    text "[k] -- "
                    if isinstance(v, dict):
                        use my_vbox_scr_4(v)
                    else:
                        text "[v]"
                    
screen my_vbox_scr_4(some_dict):
    frame:
        vbox:
            for k, v in some_dict.items():
                hbox:
                    text "[k] -- "
                    if isinstance(v, dict):
                        pass # use my_vbox_scr_5(v)
                    else:
                        text "[v]"

                    
screen my_test_scr(some_dict):
    frame:
        align (0.5, 0.1)
        vbox:
            for k, v in some_dict.items():
                hbox:
                    text "[k] -- "
                    if isinstance(v, dict):
                        use my_vbox_scr_0(v)
                    else:
                        text "[v]"
                    
default my_dict = {'result 1' : { 'b' : 2,'c': {'inner c' : 3, 'inner c2' : 3.5} },
                               'result 2' : { 'e' : 4 , 'f' : 5 , 'g' : 6 },
                               'result 3' : { 'h' : 7, 'i' : 8 },
                               'result 4' : { 'j' : 9 } 
                }

    
# The game starts here.
label start:
    "..."
    show screen my_test_scr(my_dict)
    "?!"

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

Re: Dynamically nested loop function on screen

#4 Post by Ocelot » Sun Jun 06, 2021 4:03 pm

Alex wrote:
Sun Jun 06, 2021 3:57 pm
Well, that's just an ugly workaround... But if you could create screens programmatically...
You actually can.
https://www.renpy.org/doc/html/screen_python.html
Using combination of renpy.define_screen and ui functions you can define your own screen (page does warn that they will be slower than normal counterparts). IIRC this is how screens were defined before introduction of screen language.

Now I wonder if you can recursively use screen inside itself...
< < insert Rick Cook quote here > >

User avatar
Alex
Lemma-Class Veteran
Posts: 2981
Joined: Fri Dec 11, 2009 5:25 pm
Contact:

Re: Dynamically nested loop function on screen

#5 Post by Alex » Sun Jun 06, 2021 4:07 pm

Ocelot wrote:
Sun Jun 06, 2021 4:03 pm
...Now I wonder if you can recursively use screen inside itself...
It turns out that you can't... that's why this workaround... Or maybe I've done it all wrong...

moonae
Newbie
Posts: 4
Joined: Sun Jun 06, 2021 8:29 am
Contact:

Re: Dynamically nested loop function on screen

#6 Post by moonae » Mon Jun 07, 2021 8:24 am

Ocelot wrote:
Sun Jun 06, 2021 4:03 pm
Alex wrote:
Sun Jun 06, 2021 3:57 pm
Well, that's just an ugly workaround... But if you could create screens programmatically...
You actually can.
https://www.renpy.org/doc/html/screen_python.html
Using combination of renpy.define_screen and ui functions you can define your own screen (page does warn that they will be slower than normal counterparts). IIRC this is how screens were defined before introduction of screen language.

Now I wonder if you can recursively use screen inside itself...
This works, thank you!

I can probably live with this particular screen being slower, as long as it works.
Do you know if there are other issues with using old ui script like this beside load times?

Code: Select all

init python:
    def test3(d = None, **kwargs):
        ui.vbox(id='vbox1')
        for result,x in d.items():
            ui.hbox(id='hbox1')
            ui.text(result+' -- ',id='text1')
            ui.vbox(id='vbox2')

            def foo(x):

                for k,v in x.items():
                    ui.hbox(id='hbox')
                    ui.text(k+' - ')
                    if isinstance(v, dict):
                        ui.vbox(id='vbox')
                        foo(v)
                        ui.close() #vbox
                    else:
                        ui.text(str(v))
                    ui.close() #hbox

            foo(x)

            ui.close() #vbox2
            ui.close() #hbox1

        ui.close() #vbox1

    renpy.define_screen('test3', test3)

Post Reply

Who is online

Users browsing this forum: No registered users