change gridlayout (or other layout) on the fly

665 views
Skip to first unread message

Bill Eaton

unread,
Dec 27, 2013, 10:55:22 PM12/27/13
to kivy-...@googlegroups.com
I want to be able to be able to run the __init__ method of my GridLayout at will. I have a score keeping app where if I change the number of players, I need to add or remove columns from a GridLayout. 

KV file looks something like

RootWidget:


<RootWidget>:
   
BoxLayout:
       
PlayerLayout:
       
ScrollableGridLayout:




<ScrollableGridLayout>:
   
ScrollView:
       
CustomGridLayout:
           
<CustomGridLayout>:

In the KV file I don't have any layout info. I do all of the add_widgets in the __init__ method of the CustomGridLayout in the PY file.

Separately, I have a popup menu for settings (including number of players). When I close the settings popup, I'd like to be able to change the number of widgets in the CustomGridLayout, but I cannot figure out how to do this. I cannot think of a clever way to accomplish this gracefully. At the moment, my only option is for the user to manually close the app and restart. 



Message has been deleted

Bill Eaton

unread,
Dec 29, 2013, 1:13:43 AM12/29/13
to kivy-...@googlegroups.com
Arghh. Still working on this. I have an idea I need to try out. It goes along the lines of the same trick I used to populate the Gridlayout. For every widget I added by add_widget, I also assigned it to a 2D list called WidgetGrid, e.g.    

for i in range(rows):
 
for j in range(cols):
    widget
= Label(text= 'hey look at me')  # create an instance of Label and assign it to widget
   
self.add_widget(widget)
   
WidgetGrid[i][j]=widget  #assign same widget to WidgetGrid. WidgetGrid can be modified outside of layout definition

It's a very inelegant solution but allows me to fool around with the contents of the widgets in Python very easily. It's kind of retarded that I end up keeping two copies of the widget, but it sort of works. So I'm gonna try the same thing one level up in the hierarchy: create a GridLayout in Python and keep another copy of it. Then hopefully I can mess with the copy when I want to change the number of rows and columns, but doing a clear_widgets and then loop through add_widgets.

Is there a more elegant way to do this?

ZenCODE

unread,
Dec 29, 2013, 3:49:54 AM12/29/13
to kivy-...@googlegroups.com
Hi

Widgets have a "children" property which is a list containing all the widgets you have added to it. Would this not work for you?

ps. In Python, what you are doing above in not really keeping duplicate copies, only duplicate references to the same object. So it's not that inefficient, but yes, we always want to find the most elegant way. We want perfection! ;-)

Cheers

Bill Eaton

unread,
Dec 29, 2013, 10:38:01 AM12/29/13
to kivy-...@googlegroups.com
Hey Zencode,

Thanks for correcting my inelegant verbiage. I knew "copy" wasn't quite right.

I'm not sure if children will help much. I think you can only talk directly to the root widget. So to burrow down the hierarchy of children to get to the level you want could be quite tedious. Maybe keeping a reference to the widgets you care about is the least inelegant solution.

ZenCODE

unread,
Dec 29, 2013, 2:06:14 PM12/29/13
to kivy-...@googlegroups.com
Hi Bill

You can use an ObjectProperty to keep a reference to any widget, so that should not be an issue. Just keep an ObjectProperty reference to the parent widget?

http://kivy.org/docs/api-kivy.properties.html

Cheers

Bill Eaton

unread,
Dec 29, 2013, 11:54:39 PM12/29/13
to kivy-...@googlegroups.com
So now I'm keeping a reference to my CustomGridLayout by assigning it to self in the __init__  method. That seems to work well. 

class CustomGridLayout(GridLayout):
 
def __init__(self, **kwargs):
   
global MyCustomGridLayout
   
super(CustomGridLayout, self).__init__(**kwargs)
   
MyCustomGridLayout = self  #MyCustomGridLayout now can be changed outside the
                               
#class definition and the on screen form will change too


Do people do this all the time in Kivy and I'm just (re)discovering it on my own? I don't know that I've seen it in any of the tutorials. 

ZenCODE

unread,
Dec 30, 2013, 2:01:53 PM12/30/13
to kivy-...@googlegroups.com
Hmm. global's are generally frowned upon in modern languages, and it's preferable to try and avoid them. There is seldom a real need for them. http://en.wikipedia.org/wiki/Global_variables

Having said that, it's difficult to suggest an alternative without knowing more about what you are trying to do. In the above example, why can the creator of CustomGridLayout not keep the reference itself when it creates it? If it's created in the KV file, using an ObjectProperty would be the way to go...

Cheers
Message has been deleted

Noob Saibot

unread,
Dec 30, 2013, 11:46:59 PM12/30/13
to kivy-...@googlegroups.com
I agree with ZenCODE in that it's difficult to understand what you're after; but as i try my best, i don't see why you can't just add your CustomGridLayout to a Widget class, and let the parent Widget class handle all the referencing & such. Like so:

class MyWidget(Widget):
    container 
= ObjectProperty(None) #`container` will be your `CustomGridLayout` AFTER initialization

    
def some_callback(self, conditions, *args):
        
if conditions == True:
            self.container.clear_widgets()
            
self.container.rows = args[?]; self.container.cols = args[?]

            
for i in range(self.container.rows):
                for j in range(self.container.cols):

                    widget 
= Label(text='hey look at me')

                    
self.container.add_widget(widget)
                    
WidgetGrid[i][j] = widget #...Whatever that is.

RootWidget:

<CustomGridLayout>:
    foo
: self.foo
    bar
: self.bar

<MyWidget>:
    container
: container_id

    
CustomGridLayout:
        id
: container_id
        pos
: root.pos
        size
: root.size

<ScrollableGridLayout>:
    
ScrollView:
        
MyWidget:

<RootWidget>:
    
BoxLayout:
        
PlayerLayout:
        
ScrollableGridLayout:

Does this make more sense, or did i miss your point entirely?

Bill Eaton

unread,
Jan 1, 2014, 3:09:09 PM1/1/14
to kivy-...@googlegroups.com
@Noob, Zencode

Thanks for your replies. I see what you're getting at. 

Let me back up a little. A general problem I have with my Kivy projects is I cannot figure out how to talk to the class instances created. I can only create a class. For example in Noob's code, I don't know how to talk to container outside of MyWidget. Let's say I have a settings menu, and I want to change something in container well after MyWidget is initialized. Then I have to do start from MyApp and traverse the children until I find container, right? 

ZenCODE

unread,
Jan 1, 2014, 4:20:44 PM1/1/14
to kivy-...@googlegroups.com
Hi Bill

if you want to be notified of changes, you can always store a callback? Ot, if you want to do it the Kivy way, define an "event" that can catch from anywhere?

http://kivy.org/docs/api-kivy.event.html?highlight=events#kivy.event.EventDispatcher.events

Again, it's still kinda vague. Best would be to post a code example that we have something more concrete to work on ;-) Yay, it's 2014! ;-)

Cheers

Bill Eaton

unread,
Jan 1, 2014, 5:16:59 PM1/1/14
to kivy-...@googlegroups.com
On Wednesday, January 1, 2014 1:20:44 PM UTC-8, ZenCODE wrote:
. . .  if you want to be notified of changes, you can always store a callback? Ot, if you want to do it the Kivy way, define an "event" that can catch from anywhere? . . .

I don't understand how an event can help. 

Let me restate problem. Let's say I have a 4x3 grid layout at app launch. The app is a score keeper. Sometime after launch, the user decides they need a 4x4 grid layout (maybe another player was added). I have made a setup menu for the user to configure rows and columns. Once the menu is closed, I want to reconfigure the grid if necessary. In this case I don't see how events are going to help me. 

Another case in the same program is for summing up scores. They are stored in the grid, but I don't know how to access the grid to do the summing.

In both cases, setting the KV widget and a global widget to the same widget gets the job done. I was hoping there was a more elegant way of accomplishing this.


Noob Saibot

unread,
Jan 2, 2014, 3:15:03 PM1/2/14
to kivy-...@googlegroups.com
I still don't understand, but i think the issue is your approach to this problem. You need to answer the Five W's before you try to answer the How.
  • Who: Who is going to be communicating with your CustomGridLayout widget? -- Is it another widget? A global callback? The user through Motion Events?
  • What: What are they communicating to CustomGridLayout? -- Passing values? Changing properties? etc.
  • When: Under which conditions is this to occur? -- e.g., if conditions == Trueself._do_callback()else: return
  • Where: Where will this interaction take place, and where are the two parties located in relation to one another? -- Is this interaction going on behind the scenes? Do the two widgets share a parent?
  • Why: Is this really the best way to do this? -- Many people skip this step, which (IMHO) is the most important.
You see where our confusion comes from? Different scenarios require different solutions for How. I think you should sit down and outline what you need in this way. If the How still doesn't reveal itself afterwards, give us these details and we'll take it on together. Even if it seems a bit condescending, this breakdown is VERY important (with everything).

Noob Saibot

unread,
Jan 2, 2014, 3:35:34 PM1/2/14
to kivy-...@googlegroups.com
Looping back to your example:
  • Who: User interacts with the "# of Players widget" which then interacts with the CustomGridLayout. So it follows that the Who is "# of Players widget" --> CustomGridLayout
  • What: # of players dictates the number of cols/rows
  • When: "Sometime after launch..."?
  • Where: [Not Clear]
  • Why: Gotta have enough cols/rows for everybody.
  • How: Perhaps something like this...?
class CustomGridLayout(GridLayout):

   
def on_rows(self, *args):
       
# If the # of players change...
       
self.clear_widgets()
       
self.re_populate_widgets()

#############################################

<NumberOfPlayers@Widget>:
    container
: container_id
    num_of_players
: self.num_of_players

   
CustomGridLayout:
        id
: container_id
        cols
: 4
        rows
: root.num_of_players

...And that's IF the two widgets are parent & child. You got it now?
Reply all
Reply to author
Forward
0 new messages