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

Flask - Getting a 304 status for my external .css file
User Domains for Office Online
Python - Django - Sum values of a list, too slow
Selenium - Select fields won't save via Remote Webdrive
Find blocks (and their sizes) of same consecutive elements in Pandas
With python dataframes add column of counts of rows that meet condition to each row that meets it
Pandas pivot_table with aggfunc works differently on little different data
`ndimage.distance_transform_bf` transforms the wrong elements?
Generate random words with different lengths
Error in alpha beta prunning algorithm in python
How to do the below task in shell script using grep but without a for loop?
ValueError: could not broadcast input array from shape (97,97) into shape (1)
How to share a Django database with another Python app?
Unzipping a .gz json file into a pandas df
How to create Python Regex that replaces characters?
Python: Speed up encryption of a list

Categories

HOME
cakephp-3.x
numpy
sass
elixir
websphere
winapi
webrtc
cpu-architecture
json-ld
mapbox-gl-js
bro
nodemailer
vsftpd
intellij-plugin
rapidjson
sendkeys
bootstrap-popover
tree-traversal
sonarqube-msbuild-runner
url.action
go-cd
pipelinedb
edge-detection
helper
picturebox
helix-3d-toolkit
skygear
oracle-xml-db
pdflatex
geo
modelandview
sidr
codesys
mediawiki-extensions
upsert
cppunit
context-free-language
chown
hpcc
settimeout
tomee
galleriffic
multiple-file-upload
windows-nt
date-format
scriptlet
logcat
flexjson
bluesnap
self-hosting
direct3d12
android-cursoradapter
intellitest
hyperthreading
app.xaml
twython
pyopengl
graphical-logo
ubercart
debian-based
uptodate
pgagent
dto
humanizer
yahoo-boss-api
argument-passing
picat
ssms-addin
android-looper
anjuta
svcutil.exe
jquery-tabs
angulartics
installshield-2011
entity-framework-4.1
carddav
argb
justgage
gdataxml
pstree
hibernate3
point-sprites
datacontract
chatroom
osx-leopard
unreachable-code
file-encodings
front-controller
virtualquery
msf
usergroups

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