python


Kivy Layout Background Not Painted on ScrollView


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 Links

Python find out if a folder exists
Python Order of Operations - Addition and subtraction
Python Beautifulsoup: Unable to select element despite of it's there
Google news crawler to return results with url,title and briefing
Getting specific data values out of a dataframe - python pandas
Django import issue in Pycharm
Why is this concatenation of the float values in pandas dataframe is giving NaN output?
windows7 python36: how send to gdrive using righ click context menu?
Switch between content on TKinter / Python
PhantomJS session to Chrome/Firefox session
Rank keys of dict with lists
Ajax returns No 'Access-Control-Allow-Origin' header is present on the requested resource
How to call One function into another function in python 2.6
Writing a babel message parser and file extension
How to filter spark's RDD based on one specific condition?
Converting multiple xls files to xlsx- issues with scaling up from single file

Categories

HOME
tensorflow
erlang
redis
visual-studio-2013
formal-verification
avro
h2
apollo
javamail
java-home
schemacrawler
avl-tree
onenote-api
data-synchronization
flann
mongodb-query
x-frame-options
cloudflare
rapidjson
scaling
bootstrap-popover
phpbb
android-toolbar
ada
anychart
internet-explorer-8
jsonserializer
primes
vs2017
iis-10
tampermonkey
classpath
hana-studio
winscp
blackboard
invoke-command
strophe
clockwork
preg-grep
fractals
ocs
node-horseman
shibboleth
duktape
soundjs
worker
amazon-clouddrive
rdw
timesten
floor
production-environment
jenkins-jira-trigger
slot
hpcc
shinyjs
barcode-printing
objloader
settimeout
iostat
hspec
picasa
uwp-maps
aerogear
qt-linguist
altbeacon
anythingslider
self-hosting
registrykey
sem
retro-computing
vine
xaml-designer
ctrlp
named-ranges
gmaps4rails
ultrawingrid
mser
grunt-contrib-concat
sortable
snmp4j
korma
picat
httpcontext
magic-numbers
delphi-xe3
wic
java.util.date
threadx
ng-pattern
sametime
mozart-mvc
pys60
dropdownlistfor
socketstream
mpmovieplayer
expression-evaluation
getstring
gjs
userid
objectbrowser
ou
nemerle
datacontract
callgrind

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App