Kivy Layout Background Not Painted on ScrollView - python

Please consider the following code:
Builder.load_string(
"""
<TestView>:
canvas.before:
Color:
rgba: 0, 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
""")
class TestView(GridLayout):
def __init__(self, **kwargs):
super(TestView, self).__init__(**kwargs)
self.cols = 1
self.spacing = 20
self.add_widget(Label(text="I'm on the TestView", height=80 ))
class TestScreen(Screen):
def __init__(self, **kwargs):
super(TestScreen, self).__init__(**kwargs)
self.content_grid_layout = GridLayout(cols=1, spacing=20, size_hint_y=None)
self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))
self.content_grid_layout.add_widget(Label(text="A"))
self.content_grid_layout.add_widget(Label(text="B"))
self.content_grid_layout.add_widget(TestView())
self.scroll_view = ScrollView(size_hint=(1, 1), size=(self.width, self.height))
self.scroll_view.add_widget(self.content_grid_layout)
self.add_widget(self.scroll_view)
sm = ScreenManager()
sm.add_widget(TestScreen(name="test_screen"))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
(I've left out the imports to save space). So my specific problem here is that I expect the background of TestView to be painted green, but it isn't here. It would be if you just add TestView straight onto the Screen's layout, i.e. change the init of TestScreen to just do:
self.content_grid_layout = GridLayout(cols=1, spacing=20, size_hint_y=None)
self.content_grid_layout.add_widget(TestView())
self.add_widget(self.content_grid_layout)
However, I need my content to be on a ScrollView here. So I also note that if I just comment out this line:
self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))
from the original code, everything appears to work as expected and the background of the TestView is painted green. However, if I do that in turns out the ScrollView isn't doing what I expect (it doesn't scroll).
If I give the TestView an explicit height, then everything works as expected, e.g. add:
self.size_hint_y = None
self.height = 40
to the TestView init. So I guess the problem is something not really knowing what height to give the TestView, but then weirdly being able to put the Label in approximately the right place, but not paint the background, or paint it off the screen or something. Anyway, hard-coding the TestView doesn't work for me because IRL it will have some dynamic content that will have varying heights.
I feel like in a sane world the height of a layout would be the height of its contents, unless otherwise specified, but maybe that's just me. I think if I can size the TestView to its contents then I'd get the behaviour I'm expecting?
Edit:
OK, I think I can get it to do what I want by following the strategy described here: https://groups.google.com/d/msg/kivy-users/xRl2l8-1qLs/zzpk-QG4C0MJ
(in particular the CustomGridLayout described there which is like my TestView I guess).
So the basic idea it seems is that we set the custom GridLayout (TestView) to zero height initially, then manually update its height for each child we add, so something like:
class TestView(GridLayout):
def __init__(self, **kwargs):
super(TestView, self).__init__(**kwargs)
self.cols = 1
self.spacing = 20, 20
self.size_hint_y = None
self.height = 0
self.height = self.height + 30 + self.spacing[1]
self.add_widget(Label(text="I'm on the TestView 1", size_hint_y=None, height=30))
self.height = self.height + 30 + self.spacing[1]
self.add_widget(Label(text="I'm on the TestView 2", size_hint_y=None, height=30))
self.height = self.height + 30 + self.spacing[1]
self.add_widget(Label(text="I'm on the TestView 3", size_hint_y=None, height=30))
I'm not going to lie, I think this is pretty ugly. It really seems that the layout should be able to work out its new height when I add a widget to it without having to spoon feed it like this. Anyway, it seems to work. I'm going to leave the question open in case someone has a not-horrible way of doing this.

By commenting out the line you mentioned (self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))) and adding height: self.minimum_height to the Builder string I was able to get your TestView to be green. The TestScreen is also scrollable.
Consider changing the TestScreen initializer to this:
def __init__(self, **kwargs):
super(TestScreen, self).__init__(**kwargs)
self.content_grid_layout = GridLayout(cols=1, spacing=20, size_hint_y=None)
# self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))
self.content_grid_layout.add_widget(Label(text="A"))
self.content_grid_layout.add_widget(Label(text="B"))
self.content_grid_layout.add_widget(TestView())
self.scroll_view = ScrollView(size_hint=(1, 1), size=(self.width, self.height))
self.scroll_view.add_widget(self.content_grid_layout)
self.add_widget(self.scroll_view)
And the Builder string to this:
Builder.load_string(
"""
<TestView>:
height: self.minimum_height
canvas.before:
Color:
rgba: 0, 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
""")

OK, I think I've cracked it - Edvardas' notion of binding the TestView's minimum height to its height is correct (and I guess maybe this should have been obvious to me since it's also what we do for the 'content_grid_layout'). However, it wasn't working because we also need 'size_hint_y: None'.
I don't think we should take out the height / minimum height binding from 'content_grid_layout' because that stops scrolling working.
Doing this it works as expected and we don't have to manually set the TestView's height. Here's a full working example to save any confusion - I've changed the TestView from a GridLayout to a BoxLayout but it works as a GridLayout too:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
Builder.load_string(
"""
<TestView>:
size_hint_y: None # <----------------- MAKE SURE YOU DO THIS!
height: self.minimum_height # <----------------- AND THIS!
canvas.before:
Color:
rgba: 0, 0.8, 0.06, 0.5
RoundedRectangle:
pos: self.pos
size: self.size
radius: [16,16,16,16]
""")
class TestView(BoxLayout):
def __init__(self, **kwargs):
super(TestView, self).__init__(**kwargs)
self.orientation = 'vertical'
self.padding = [20, 20, 20, 20]
self.spacing = 20
self.add_widget(Label(text="I'm on the TestView 1"))
self.add_widget(Label(text="I'm on the TestView 2"))
self.add_widget(Label(text="I'm on the TestView 3"))
class TestScreen(Screen):
def __init__(self, **kwargs):
super(TestScreen, self).__init__(**kwargs)
self.content_grid_layout = GridLayout(cols=1, spacing=20, size_hint_y=None)
self.content_grid_layout.bind(minimum_height=self.content_grid_layout.setter('height'))
# Give us something to scroll:
for i in range(20):
btn = Button(text=str(i), size_hint_y=None, height=40)
self.content_grid_layout.add_widget(btn)
if i == 5:
self.content_grid_layout.add_widget(TestView())
self.scroll_view = ScrollView(size_hint=(1, 1), size=(self.width, self.height))
self.scroll_view.add_widget(self.content_grid_layout)
self.add_widget(self.scroll_view)
sm = ScreenManager()
sm.add_widget(TestScreen(name="test_screen"))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()

Related

Why won't my Kivy program update the font size when I tell it to?

I'm making a choose your own adventure game, but sometimes I need to change the font size and Kivy isn't giving me the results I'm expecting. This is the full code so feel free to run it and see what I mean.
Here is the python file:
# A Choose your own adventure game
import kivy
kivy.require('1.11.1')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.lang import Builder
global root
root = BoxLayout() #If I don't define root immediately the program won't work
#root is given a proper definition in class Main()
#Easily add new pages to the program
def add_page(pagenum):
root.clear_widgets()
root.add_widget(pagenum)
#The main window that encapsulates all other widgets
class RootBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super(RootBoxLayout, self).__init__(**kwargs)
# The Menu that drives the game
class Menu(BoxLayout):
def __init__(self, **kwargs):
super(Menu, self).__init__(**kwargs)
# The Main Menu
class StartMenu(Menu):
def __init__(self, **kwargs):
super(Menu, self).__init__(**kwargs)
#Text Box
self.ids.textbox.text = "Opening Screen"
# Button 1
self.ids.button1.text = "Play"
self.ids.button1.bind(on_press = self.nextpage1)
def nextpage1(self, *args):
add_page(HappyBee())
class HappyBee(Menu):
def __init__(self, **kwargs):
super(Menu, self).__init__(**kwargs)
#############################################
### This is where the problem seems to be ###
#############################################
self.ids.textbox.font_size = self.ids.textbox.height/10 #Kivy says nah I don't feel like doing this
self.ids.textbox.text = "This is a very large block of text that I would like " \
"to decrease the font size of. Pressing the button below changes it but I don't " \
"want users to have to press a button just to get the game to function " \
"how it should function from the start."
# Button 1
self.ids.button1.text = "y tho"
self.ids.button1.bind(on_press = self.nextpage1)
# What to do when each button is pressed
def nextpage1(self, *args):
self.ids.textbox.font_size = self.ids.textbox.height/10 # Kivy says ok I can change it now lol
# An App class that will be used to umbrella everything else in the application
class Main(App):
def build(self):
Builder.load_file("cyoa.kv")
global root # Other classes and functions need to easily access root
root = RootBoxLayout()
first_screen = StartMenu()
add_page(first_screen) # Add the Main Menu to the root window
return root
if __name__ == '__main__':
Main().run()
and here is the corresponding kv file, which I have saved as cyoa.kv
<RootBoxLayout>:
orientation: 'vertical'
# Create the background color of the root layout
canvas.before:
Color:
rgba: 0,0,0,1 # black
Rectangle:
pos: self.pos
size: self.size
# This custom button allows to font size to change dynamically with the window
<MyButton#Button>:
font_size: self.height/3
halign: 'center'
valign: 'center'
text_size: self.size
size_hint_y: 0.14
<Menu>:
BoxLayout:
orientation: 'vertical'
Label:
id: textbox
font_size: self.height/6
text_size: self.size # Allows text to wrap
halign: 'center'
valign: 'center'
size_hint_y: 0.6
MyButton:
id: button1
text: 'Play'
I can change font_size in __init__ only if I remove font_size from .kv. It seems it gets value from .kv after running __init__ and this makes problem. There is also other problem: height (and width) in __init__ is 100 instead of expected size. Probably it calculates it after running __init__.
Searching in internet I found on Reddit: How can I use init for screen ids?
It uses Clock to run some function after all updates and in this function change values.
def __init__(self, **kwargs):
#super(...)
Clock.schedule_once(self._do_setup)
def _do_setup(self, *l):
self.ids.something = '....'
In your code it would be
from kivy.clock import Clock # <---
class HappyBee(Menu):
def __init__(self, **kwargs):
super(Menu, self).__init__(**kwargs)
self.ids.textbox.text = "This is a very large block of text that I would like " \
"to decrease the font size of. Pressing the button below changes it but I don't " \
"want users to have to press a button just to get the game to function " \
"how it should function from the start."
self.ids.button1.text = "y tho"
self.ids.button1.bind(on_press = self.nextpage1)
Clock.schedule_once(self.on_init_complete) # <---
def on_init_complete(self, *args, **kwargs):
self.ids.textbox.font_size = self.ids.textbox.height/10 # <---
It works but has one small problem - it display text in original size for few milliseconds. But if you don't know this then you may not notice this.
EDIT: Similar problem: How to Load Kivy IDs Before Class Method is Initialized (Python with Kivy)

Kivy Bing images layout

I'm trying to make something like a "Bing images" layout.
That is:
Images are divided into several columns of the same width.
All images are the same width.
Images are added down in such a way that the images added first are
at the top of the layout.
Layout can be added to ScrollView to scroll with mousewheel
I did not find a way to do this using Stack Layout, so I decided to create my own layout.
I stopped here:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.properties import NumericProperty
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.scrollview import ScrollView
KV = '''
#:import Window kivy.core.window.Window
ScrollView
size_hint: (1, None)
size: Window.size
MyLayout
id:my_l
Button
text:'1'
Button
size_hint_y: None
height: 900
text:'2'
Button
text:'3'
Button
text:'4'
Button
text:'5'
size_hint_y: None
height: 900
<MyLayout>:
#height: self.minimum_height
cols: 3
spacing: 10
size_hint_y:None
row_width: 300
'''
class MyLayout(FloatLayout):
def __init__(self, **kwargs):
super(MyLayout, self).__init__(**kwargs)
cols = NumericProperty(3)
row_width = NumericProperty(300)
spacing = NumericProperty(0)
def do_layout(self, *args):
self.i = 0
self.last_x = [self.height]*self.cols
for child in self.children[::-1]:
child.width = self.row_width
if isinstance(child, Image):
child.height = child.width / child.image_ratio
child.size_hint_y= None
child.size_hint_x= None
self.i+=1
if self.i == self.cols+1: self.i = 1
child.x = self.x+(self.i-1)*(self.row_width+self.spacing)
child.y = self.last_x[self.i-1]-child.height
self.last_x[self.i-1]-=child.height+self.spacing
def on_pos(self, *args):
self.do_layout()
def on_size(self, *args):
self.do_layout()
def add_widget(self, widget):
super(SuperGrid, self).add_widget(widget)
self.do_layout()
def remove_widget(self, widget):
super(SuperGrid, self).remove_widget(widget)
self.do_layout()
class MyApp(App):
def build(self):
self.root = Builder.load_string(KV)
Window.bind(on_dropfile=self.add)
def add(self, *args):
name= list(args)[1]
self.root.ids.my_l.add_widget(Image(source=name))
MyApp().run()
It is already partially working (you can run it and dragndrop some images from your folders to see what I'm about), but the problem is that I don't understand how to connect a ScrollView to it.
It looks like I need to add a line with something like height: self.minimum_height to KV string.
but it’s not clear where in layout class I need to calculate minimum_height.
How to make the code work with ScrollView?
You just need to calculate the height of your MyLayout instance. In your kv file add:
size_hint: (1, None)
in your MyLayout section
Then, in the do_layout method, calculate the height of your MyLayout. Do a self.height = just once at the end do_layout (to avoid infinite loop due to on_size method). For example here is a modified version of your do_layout:
def do_layout(self, *args):
self.i = 0
col_heights = [0] * self.cols # keeps track of the height of each column
self.last_x = [self.height]*self.cols
for child in self.children[::-1]:
child.width = self.row_width
if isinstance(child, Image):
child.height = child.width / child.image_ratio
child.size_hint_y= None
child.size_hint_x= None
self.i+=1
if self.i == self.cols+1:
self.i = 1
col_heights[self.i-1] += child.height + self.spacing
child.x = self.x+(self.i-1)*(self.row_width+self.spacing)
child.y = self.last_x[self.i-1]-child.height
self.last_x[self.i-1]-=child.height+self.spacing
if len(self.children) > 0:
self.height = max(col_heights)

Unable to use "root.top-self.height" for placement properly in kivy

I am trying to create a menu using box layout in kivy. I wanted to use "root.top-self.height" so that it sticks the vertical layout from the top of screen but its still sticking from bottom. Also when I print(root.top) its strangely giving 100 which is not my screen resolution. Please let me know how can I place it accurately.
Furthermore I read somewhere that I need to use root=BoxLayout(), now after using this the button's are not clickable after adding that, before adding this I could use the buttons. Please do let me know how to deal with "root" ie screen or App size functionality.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.label import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.layout import Layout
from kivy.uix.button import Button
from kivy.lang import Builder
## CREATING A CLASS WHICH HAS SCREEN CONTENT:
class firstScreen(BoxLayout):
def __init__(self,**kwargs):
super(firstScreen, self).__init__(**kwargs)
self.orientation = 'vertical'
root = BoxLayout()
self.pos = (0 ,root.top-self.height)
print(root.top)
self.myButton1 = Button(text='Home',
color = (1,0,0,1),
size_hint = (0.1,None),
## pos_hint = {'x':.8, 'y':'.7'},
## pos_hint = {'x':0, 'top':'0'},
pos = (0,0)
)
self.myButton2 = Button(text='Buy Now',
color = (1,0,0,1),
size_hint = (0.1,None))
self.myButton3 = Button(text='Blog',
color = (1,0,0,1),
size_hint = (0.1,None))
self.myButton4 = Button(text='Contant Us',
color = (1,0,0,1),
size_hint = (0.1,None))
self.add_widget(self.myButton1)
self.add_widget(self.myButton2)
self.add_widget(self.myButton3)
self.add_widget(self.myButton4)
def on_touch_down(self,touch):
print(touch)
def on_touch_move(self,touch):
print(touch)
def on_touch_up(self,touch):
print(touch)
## CREATING A CLASS WHICH RETURNS SOME SCREEN:
class myKivyApp(App):
def build(self):
return firstScreen()
## THIS CODE RUNS THE CLASS WHICH HAS SOME SCREEN
if __name__ == "__main__":
myKivyApp().run()
Eliminating the unused BoxLayout and setting the y component of size_hint to 1.0 means that each button will share equally in the vertical space available for the firstScreen.
class firstScreen(BoxLayout):
def __init__(self,**kwargs):
super(firstScreen, self).__init__(**kwargs)
self.orientation = 'vertical'
self.myButton1 = Button(text='Home',
color = (1,0,0,1),
size_hint = (0.1,1.0))
self.myButton2 = Button(text='Buy Now',
color = (1,0,0,1),
size_hint = (0.1,1.0))
self.myButton3 = Button(text='Blog',
color = (1,0,0,1),
size_hint = (0.1,1.0))
self.myButton4 = Button(text='Contant Us',
color = (1,0,0,1),
size_hint = (0.1,1.0))
self.add_widget(self.myButton1)
self.add_widget(self.myButton2)
self.add_widget(self.myButton3)
self.add_widget(self.myButton4)
By the way, the root.top will always be 100 in the __init__() method. The 100 is the default value and is not updated until the app is actually displayed.

How to change background colour in Kivy

I need a bit of a hand with a program I'm trying to code using kivy and python 3, however I'm relatively new to both.
What I need in my program is to setup two different background colours, that the user can switch between (a night mode, and one to use in daylight)
#globalvariable
backgroundcolour = [50, 50, 50]
class MainScreen(Screen):
rgb = StringProperty()
rgb = backgroundcolour
def changebackground(self):
self.canvas.clear()
backgroundcolour = [55, 5, 99]
print("DONE")
Kivy file:
<MainScreen>:
name: 'main'
canvas:
Color:
rgb: root.rgb
However all I get after I run the changebackground subroutine, my kivy window just replaces itself with a blank black screen.
I presume what I'm doing wrong is I'm not refreshing the window, or something, but I've got no idea how to go about doing that.]
Many thanks
canvas:
Color:
rgb: root.rgb
After this part you have to draw something that will cover the widget background:
Rectangle:
size: self.size
pos: self.pos
or in your changebackground():
with self.canvas:
Color(rgb=self.rgb) # rgba might be better
Rectangle(size=self.size, pos=self.pos)
which is probably more optimal if you intend to use it when changing the color styles not so often. And the best thing would be using canvas.before, especially if you have a widget that draws something (e.g. Button).
Also, the color is in range 0 - 1, therefore your color will be some kind of really bright purple-ish something. And just a note: this will change only the widget's background, therefore your Window background will still be the default one (currently black). For this to change you'll need to use Window.clearcolor.
This is a pure python code on how to put background on layout, no Kivy design language
Code of App class not include
For future reference:
from kivy.uix.gridlayout import GridLayout
from kivy.graphics.vertex_instructions import Rectangle
from kivy.graphics.context_instructions import Color
from kivy.uix import button
class MyLayout(GridLayout):
def __init__(self, **kwargs):
super(MyLayout, self).__init__(**kwargs)
self.cols = 1
self.bind(
size=self._update_rect,
pos=self._update_rect
)
with self.canvas.before:
Color(.20, .06, .31, 1)
self.rect = Rectangle(
size=self.size,
pos=self.pos
)
def _update_rect(self, instance, value):
self.rect.pos = instance.pos
self.rect.size = instance.size
Note:
To play with the color: my rgb values are (46, 14, 71), then just divide it with 255 (.20, .06, .31, 1). The last value is the alpha, 1 is 100%
Hope it can help you guys. Just up vote to help others who's also looking the answer.
Solution to your problem
Add this code init:
self.daylightBtn = button.Button(text="Day Light")
self.daylightBtn.bind(on_press=self.daylight_bg)
self.nightlightBtn = button.Button(text="Night Light")
self.nightlightBtn.bind(on_press=self.nighlight_bg)
self.add_widget(self.daylightBtn)
self.add_widget(self.nightlightBtn)
Button event:
def daylight_bg(self, instance):
with self.canvas.before:
Color(1, 1, 1, 1)
self.rect = Rectangle(
size=self.size,
pos=self.pos
)
def nighlight_bg(self, instance):
with self.canvas.before:
Color(0, 0, 0, 1)
self.rect = Rectangle(
size=self.size,
pos=self.pos
)
In .kv file
canvas.before:
Color:
rgba: (128, 0, 128, 1)
Rectangle:
size: self.size
pos: self.pos
Hope to help you

Kivy layout positioning problems - not understanding/properly using __init__, kv, kv properties

I am a new programmer (and first time stackoverflow poster) so please correct me if I use terminology incorrectly or make any other missteps in etiquette or proper coding style.
I am trying to write a game where you draw tiles to your tile rack and then play them on a board. I have already written a game that works without graphics via text input. Now I would like to use Kivy to make a graphical interface.
One of my problems involves the positioning of widgets. I would like to center my rack widget at the center of the screen x-axis. I can have it draw a rectangle there and have it appear to be positioned where I want it, but its x position is (as you might guess) 0. I think part of my problem is that I have passed a Game object to my widget and using a list of symbols (game.symbols) and an init method, I tried to load create tile widgets with a label(text=symbol) and then load them on the rack. As you probably have guessed, my tiles also are not positioned correctly.
How can I center my tile rack and load my tiles correctly so they have the proper position (which I think is necessary for my collision detection).
Please explain the way init method and the KV file are executed when both are used.
What is the proper way to pass objects and attributes to widgets in regards to my issues here. Should I have created an ObjectProperty?
I also may just have a fundamental misunderstanding of positioning and layouts in Kivy and if so, please educate me.
Thank you,
Cliff
import kivy
kivy.require('1.7.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
kv_Game= '''
<TileWidget>:
size_hint: None, None
size: 50,50
canvas.before:
Color:
rgba: 0.5,0.5,1,0.3
Rectangle:
size: self.width, self.height
canvas.after:
Line:
rectangle: self.x, self.y, self.width, self.height
dash_offset: 5
dash_length: 3
<RackWidget>:
size_hint: None, None
size: 350, 50
pos_hint: {'center_x': 0.5}
y: 75
canvas.after:
Color:
rgba: 1,0,0,0.5
Line:
rectangle: self.x, self.y, self.width, self.height
'''
Builder.load_string(kv_Game)
class Game(FloatLayout):
def __init__(self, **kwargs):
super(Game, self).__init__(**kwargs)
self.symbols = ['!','#','#','$','%','^','&']
self.rackWidget = RackWidget(self)
self.add_widget(self.rackWidget)
class TileWidget(Scatter):
def __init__(self, symbol="?", **kwargs):
super(TileWidget, self).__init__(**kwargs)
tileLabel = Label(text=symbol, size_hint=(None,None), size=(50,50))
self.add_widget(tileLabel)
class RackWidget(FloatLayout):
def __init__(self, game, **kwargs):
super(RackWidget, self).__init__(**kwargs)
print("TileRackWidget pos:", self.pos)
x, y = self.pos
for symbol in game.symbols:
tileWidget = TileWidget(symbol=symbol, pos= (x,y))
self.add_widget(tileWidget)
print("tileWidget pos:", tileWidget.pos)
x+=50
class GameTest1App(App):
def build(self):
game = Game()
return game
if __name__=="__main__":
GameTest1App().run()
pos is not set to a usable value yet when you create your RackWidget instance. When __init__ is running, the widget has not yet been added to the Game widget, so it has no parent and no positioning information. You could solve this by binding to the changes in RackWidget.pos, but there's an easier way to do this: RelativeLayout. The position of each child of a RelativeLayout will be based on the position of the layout itself.
Here's a modified version of RackWidget using RelativeLayout:
class RackWidget(RelativeLayout):
def __init__(self, game, **kwargs):
super(RackWidget, self).__init__(**kwargs)
print("TileRackWidget pos:", self.pos)
x, y = 0, 0
for symbol in game.symbols:
tileWidget = TileWidget(symbol=symbol, pos= (x,y))
self.add_widget(tileWidget)
print("tileWidget pos:", tileWidget.pos)
x+=50

Categories

Resources