Scrollable GridLayout in a Boxlayout - python

I've been at this for hours now, trying every solution I could find on here and trying random things....
I'm trying to build a layout consisting of 3 buttons at the top, then a scroll-able GridLayout or BoxLayout. I just cant figure out whats wrong... I've read on one response "bind the layout's size to adapt itself:" but I'm using screenmanagement and cant figure out how to do that with my code setup
<HomeScreen>:
BoxLayout:
orientation: "vertical"
BoxLayout:
size_hint: 1,.1
orientation: "horizontal"
Button:
text:"1"
Button:
text:"2"
Button:
text:"3"
ScrollView:
GridLayout:
orientation: "vertical"
size_hint_y: None
row_default_height: 60
cols:1
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:

Your code is correct, you just need to specify the height of the GridLayout. You can use height: self.minimum_height.
Reproducible example:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
kv_text = '''
<MyScreenManager>:
HomeScreen:
<HomeScreen>:
BoxLayout:
orientation: "vertical"
BoxLayout:
size_hint: 1,.1
orientation: "horizontal"
Button:
text:"1"
Button:
text:"2"
Button:
text:"3"
ScrollView:
GridLayout:
orientation: "vertical"
size_hint_y: None
height: self.minimum_height #<<<<<<<<<<<<<<<<<<<<
row_default_height: 60
cols:1
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
'''
class MyScreenManager(ScreenManager):
pass
class HomeScreen(Screen):
pass
class MyApp(App):
def build(self):
return HomeScreen()
def main():
Builder.load_string(kv_text)
app = MyApp()
app.run()
if __name__ == '__main__':
main()
Output:

I tried like #Ishinomori to recreate the app of #FJSevilla in pure Python3.7.
After a few changes I have done it!
# -*- coding: utf-8 -*-
# Kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.core.window import Window
class HomeScreen(BoxLayout):
def __init__(self, **kwargs):
# Initiate Box Layout and change orientation to vertical
super().__init__(**kwargs)
self.orientation = "vertical"
# Top Bar with Buttons "1", "2" & "3"
self.top_bar = BoxLayout(orientation="horizontal", size_hint=(1, .1))
self.top_bar.add_widget(Button(text="1"))
self.top_bar.add_widget(Button(text="2"))
self.top_bar.add_widget(Button(text="3"))
# Create the Gridlayout for the Scroll View and add height bounding
self.contend_scroll_view = GridLayout(size_hint_y=None, row_default_height=60, cols=1)
self.contend_scroll_view.bind(minimum_height=self.contend_scroll_view.setter('height'))
# 30 Dummy Buttons (real data here!)
for _ in range(30):
self.contend_scroll_view.add_widget(Button())
# Add the contend to the Scroll View
self.scroll_view = ScrollView()
self.scroll_view.add_widget(self.contend_scroll_view)
# Add the two Widgets to Home Screen
self.add_widget(self.top_bar)
self.add_widget(self.scroll_view)
class MyApp(App):
def build(self):
return HomeScreen()
if __name__ == '__main__':
# Only runs if file is executed directly, but not if importet
MyApp().run()

Related

Kivy Python - previously created layouts aren't displayed in scroll view and in page layout

this code returns me a black screen, what am I doing wrong?
python file:
from kivy.app import App
from kivy.metrics import dp
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.stacklayout import StackLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.pagelayout import PageLayout
class myPageLayout(PageLayout):
pass
class myScrollView(ScrollView):
pass
class myStackLayout(StackLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
for i in range(1,101):
size=dp(100)
b= Button(text=str(i),size_hint=(None,None),size=(size,size))
self.add_widget(b)
class myGridLayout(GridLayout):
pass
class myAnchorLayout(AnchorLayout):
pass
class myBoxLayout(BoxLayout):
pass
class myWidget(Widget):
pass
class gameApp(App):
pass
gameApp().run()
kivy file:
myPageLayout:
<myBoxLayout>:
orientation: "horizontal"
spacing: "10dp"
Button:
text: "ciao"
size_hint: 1,.5
size: "10dp","10dp"
pos_hint: { "y": .5 }
BoxLayout:
orientation: "vertical"
spacing: "10dp"
Button:
text: "b1"
Button:
text: "b2"
Button:
text: "b3"
Button:
text: "hi2"
<myWidget>:
Button:
text: "hi"
size: "100dp","20dp"
pos: 300, 100
<myAnchorLayout>:
#anchor_x: "right"
anchor_y: "bottom"
BoxLayout:
size_hint: .1,.1
Button:
text: "hi"
Button:
text: "hi2"
<myGridLayout>:
cols: 3
rows: 3
Button:
text:"hi"
size_hint:.5,1
Button:
text:"hi"
Button:
text:"hi"
myAnchorLayout:
size_hint:.5,1
Button:
text:"hi"
myBoxLayout:
Button:
text:"hi"
size_hint:.5,1
myWidget:
Button:
text:"hi"
<myStackLayout>:
#orientation:"tb-lr"
#padding:("20dp","20dp","20dp","20dp")
#spacing: "10dp","10dp"
<myScrollView>:
myStackLayout:
size_hint:1,None
height: self.minimum_height
<myPageLayout>:
myBoxLayout:
myAnchorLayout:
myWidget:
myGridLayout:
singularly layouts work and even if embedded like in the case of "myAnchorLayout" and "myGridLayouts".
I also tried to create a new StackLayout in "myScrollView" this way:
<myScrollView>:
StackLayout:
size_hint:1,None
height: self.minimum_height
Button:
text:"hi"
size_hint:.7,None
height:"500dp"
Button:
text:"hi"
size_hint:.7,None
height:"500dp"
and it worked, but now it doesn't work anymore, this appened with "myPageLayout" too.
The problem is that the kv language insists that class names start with a capital letter. The documentation says:
Keep class names capitalized to avoid syntax errors
I think your code will work if you change all your class names to meet that requirement.
.

Overlapping of custom Widget in ScrollView GridLayout in Kivy

this is a trial code that I want to implement in my final project.
Python Code:
import kivy
kivy.require('1.0.6')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class Wid(BoxLayout):
def settxt(self,i):
lab = self.ids['lab']
but = self.ids['but']
lab.text = "Label Number {}".format(i)
but.text = "Button Number {}".format(i)
class Win1(Screen):
i=0
def addw(self):
box1 = self.ids['box1']
self.i = self.i +1
w = Wid()
w.settxt(self.i)
box1.add_widget(w)
def switch(self):
sm.current="win2"
class Win2(Screen):
def switch(self):
sm.current="win1"
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("test.kv")
sm = WindowManager()
screens = [Win1(name="win1"), Win2(name="win2")]
for screen in screens:
sm.add_widget(screen)
sm.current = "win1"
class Test(App):
def build(self):
return sm
if __name__ == '__main__':
Test().run()
Kivy Code:
<Wid>:
lab:lab
but:but
BoxLayout:
height: self.minimum_height
size: root.size
Label:
id: lab
Button:
id: but
<Win1>
name:"win1"
box1:box1
BoxLayout:
height: self.minimum_height
orientation: "vertical"
BoxLayout:
size_hint: 1,0.2
Button:
text:"window 2"
on_release:
root.switch()
Button:
text:"add wid"
on_release:
root.addw()
ScrollView:
GridLayout:
id:box1
orientation: "vertical"
spacing: 2
size_hint_y: None
height: self.minimum_height
row_default_height: 60
cols:1
<Win2>
name: "win2"
BoxLayout:
id: bl
height: bl.minimum_height
size_hint_y: None
Button:
text:"window 2"
on_release:
root.switch()
With the press of the switch, I expect that my custom widget to get in the gridlayout in the scrollview, one below the other. But instead, each new widget appears in the last cell of the layout and overlaps on the previous one and empty cells keep on forming above them.
Don't know where it's going wrong.
Here I have moved the kv to a separate file, and dynamically created the screens.
Key points: I add the screens dynamically in on_start, this is after the build has completed. I create the ScreenManager in kv, and use the id to add the screens. In the kv code I put the ScreenManger in a BoxLayout. This is a personal preference. I do this so when accessing objects the root widget is not the screen manager. Therefore in the switch() methods, the addressing uses the assigned id, rather than relying on the root widget being a screenmanager.
FWIW: If the switch code is only going to change screens I would move those single lines into KV.
import kivy
kivy.require('1.0.6')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen
class Wid(BoxLayout): # Change to layout
def settxt(self, i):
lab = self.ids['lab']
but = self.ids['but']
lab.text = "Label Number {}".format(i)
but.text = "Button Number {}".format(i)
class Win1(Screen):
i = 0
def addw(self):
box1 = self.ids['box1']
self.i = self.i + 1
w = Wid()
w.settxt(self.i)
box1.add_widget(w)
#staticmethod
def switch():
app = App.get_running_app()
app.root.ids.sm.current = "win2"
class Win2(Screen):
#staticmethod
def switch():
app = App.get_running_app()
app.root.ids.sm.current = "win1"
class WidgetQ1App(App):
def build(self):
return Builder.load_file('widgetq.kv')
def on_start(self):
screens = [Win1(name="win1"), Win2(name="win2")]
sm = self.root.ids.sm
for screen in screens:
sm.add_widget(screen)
WidgetQ1App().run()
And the KV code:
<Wid>: # Put widgets in a layout, not a widget.
lab:lab
but:but
BoxLayout:
size: root.size
Label:
id: lab
Button:
id: but
<Win1>
# name:"win1"
box1:box1
BoxLayout:
orientation: "vertical"
BoxLayout:
size_hint: 1,0.2
Button:
text:"window 2"
on_release:
root.switch()
Button:
text:"add wid"
on_release:
root.addw()
ScrollView:
GridLayout:
id:box1
orientation: "vertical"
spacing: 2
size_hint_y: None
height: self.minimum_height
row_default_height: 60
cols:1
<Win2>:
# name: "win2"
BoxLayout:
Button:
text:"window 2"
on_release:
root.switch()
BoxLayout:
ScreenManager:
id: sm
<win2>
name: "win2"
size_hint_y: None
height: bl.minimum_height
BoxLayout:
id: bl
Button:
text:"window 2"
on_release:
root.switch()
Your custom widgets don't have a height defined, try changing to something like the above.
Also, start your class names with an upper case letter, kv requires this in some cases. For instance, win2 should be Win2.
A number of issues here, see the comments. The biggest problem I see is putting items in a widget. Put Widgets in a layout, not other widgets.
import kivy
kivy.require('1.0.6')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
kv = """
<Wid>: # Put widgets in a layout, not a widget.
lab:lab
but:but
BoxLayout:
size: root.size
Label:
id: lab
Button:
id: but
<Win1>
# name:"win1"
box1:box1
BoxLayout:
orientation: "vertical"
BoxLayout:
size_hint: 1,0.2
Button:
text:"window 2"
on_release:
root.switch()
Button:
text:"add wid"
on_release:
root.addw()
ScrollView:
GridLayout:
id:box1
orientation: "vertical"
spacing: 2
size_hint_y: None
height: self.minimum_height
row_default_height: 60
cols:1
<Win2>:
# name: "win2"
BoxLayout:
Button:
text:"window 2"
on_release:
root.switch()
ScreenManager:
id: sm
Win1:
name: 'win1'
Win2:
name: 'win2'
"""
class Wid(BoxLayout): # Change to layout
def settxt(self,i):
lab = self.ids['lab']
but = self.ids['but']
lab.text = "Label Number {}".format(i)
but.text = "Button Number {}".format(i)
class Win1(Screen):
i = 0
def addw(self):
box1 = self.ids['box1']
self.i = self.i + 1
w = Wid()
w.settxt(self.i)
box1.add_widget(w)
#staticmethod
def switch():
app = App.get_running_app()
app.root.current = "win2"
class Win2(Screen):
#staticmethod
def switch():
app = App.get_running_app()
app.root.current = "win1"
# class WindowManager(ScreenManager):
# pass
# kv = Builder.load_file("test.kv")
# sm = WindowManager()
#
# screens = [win1(name="win1"), win2(name="win2")]
# for screen in screens:
# sm.add_widget(screen)
#
# sm.current = "win1"
class WidgetQApp(App):
def build(self):
return Builder.load_string(kv)
WidgetQApp().run()

kivy Screen Manager and popup

I have a app in kivy with a screen manager and a popup within it. The popup works until the point I put a button with the close function into the popup window. At this point i get the message:
PopupException: Popup can have only one widget as content
There is another post on this topic but it does not seem to work.
The python code
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.lang import Builder
from kivy.uix.popup import Popup
class CustomPopup(Popup):
pass
class MainScreen(Screen):
pass
class ContentScreen(Screen):
def open_popup(self):
the_popup = CustomPopup()
the_popup.open()
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("am.kv")
class AMApp(App):
def build(self):
return presentation
if __name__ == "__main__":
AMApp().run()
The kivy file is below. The issue seems to come in the button function when calling the custompop
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
ContentScreen:
<CustomPopup>:
size_hint: .5 , .5
auto_dismiss: False
title: "The Popup"
Button:
text: "Close"
on_press: root.dismiss()
<MainScreen>:
name: "Welcome"
Button:
text: "First Screen"
size_hint: 1, .5
font_size: 40
pos_hint: {'center_x': 0.5, 'center_y': 0.7}
on_release: app.root.current = "other"
Button:
text: 'Welcome Mr and Mrs Shaw'
size_hint: 1, .5
font_size: 25
pos_hint: {'center_x': 0.5, 'center_y': 0.3}
on_release: app.root.current = "other"
<ContentScreen>:
name: "other"
BoxLayout:
orientation: "vertical"
size_hint_x: .22
Button:
text: "open Popup"
on_press: root.open_popup()
The code posted above, runs fine on Linux Buster, Kivy 1.11.0-dev and 1.10.1, and Python 3.7.3rc1
Solution
Try adding a layout e.g. BoxLayout into CustomPopup to solve the PopupException.
Example
The following example illustrates a popup window with a message and a button.
Snippets
<CustomPopup>:
size_hint: .5 , .5
auto_dismiss: False
title: "The Popup"
BoxLayout:
orientation: 'vertical'
Label:
text: "Hello Kivy"
Button:
text: "Close"
on_press: root.dismiss()

How can I put one label with time on all of my screens

I'm starting with kivy and have some question. I'm have code with couple screens and buttons. How can I put one label with time or something like that on all of my screens? In the below code I created a screens but i don't wanna put 3 separate labels on the individual screens
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.properties import StringProperty, ObjectProperty
from kivy.clock import Clock
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<StartScreen>:
BoxLayout:
orientation: 'vertical'
Button:
text: 'Start >'
size_hint_y: None
hight: '40dp'
on_press: root.manager.current = 'second'
<SecondScreen>:
BoxLayout:
orientation: 'vertical'
Button:
text: 'Test2'
size_hint_y: None
hight: '40dp'
on_press: root.manager.current = 'end'
<EndScreen>:
BoxLayout:
Button:
text: 'Test3'
size_hint_y: None
hight: '40dp'
on_press: root.manager.current = 'start'
""")
#declarate both screens
class StartScreen(Screen):
pass
class SecondScreen(Screen):
pass
class EndScreen(Screen):
pass
#create the screen manager
sm = ScreenManager()
sm.add_widget(StartScreen(name='start'))
sm.add_widget(SecondScreen(name='second'))
sm.add_widget(EndScreen(name='end'))
class TutorialApp(App):
def build(self):
return sm
if __name__ == '__main__':
TutorialApp().run()
This is one possible solution based on https://stackoverflow.com/a/18926863/6646710
The code below defines a label which displays the time.
import time
class IncrediblyCrudeClock(Label):
def __init__(self, **kwargs):
super(IncrediblyCrudeClock, self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1)
def update(self, *args):
self.text = time.asctime()
The update function gets the current time from the python time module. The time is used to update the text property of the label. In the init method, the clock module is used to schedule calls to this update function each second.
Next I added this Label to all your kivy screens inside the kv string.
<StartScreen>:
BoxLayout:
orientation: 'vertical'
Button:
text: 'Start >'
size_hint_y: None
hight: '40dp'
on_press: root.manager.current = 'second'
IncrediblyCrudeClock:
<SecondScreen>:
BoxLayout:
orientation: 'vertical'
Button:
text: 'Test2'
size_hint_y: None
hight: '40dp'
on_press: root.manager.current = 'end'
IncrediblyCrudeClock:
<EndScreen>:
BoxLayout:
Button:
text: 'Test3'
size_hint_y: None
hight: '40dp'
on_press: root.manager.current = 'start'
IncrediblyCrudeClock:
The final result looks like that

How to change a space when a button is pressed with kivy?

I am trying create a GUI by implementing the template of the ComicCreator GUI sample as a template for my own project. The code is easy to follow, but I would like to be able to reconfigure the drawingspace.kv, each time a button is pushed, say for example something like this:
Q: How could I configure the drawingspace.kv to have a different layout with different widgets for each button that is pressed?
A neat way to do this is to use screen.
Since I allready have an example of this app from you earlier question, it was easy to implement the screens, and rewrite the classes a bit.
When a button is pressed, you set the screenmanager's current to whatever the name you named the screen you want.
Then you just edit the layouts as you want inside of each screen, in the kv file, or python file.
I choose to make most of the layout stuff in kv language here. Because I find it easier to develop a layout the way I want it this way.
Later I could rewrite it to python if I want that.
So my python file looks like this now:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.clock import Clock
from kivy.uix.screenmanager import Screen,ScreenManager,NoTransition
from kivy.lang import Builder
import time
Builder.load_file("kv.kv")
class MyLayout(BoxLayout):
def __init__(self,**kwargs):
super(MyLayout,self).__init__(**kwargs)
self.orientation = "vertical"
self.padding = 10
class MainScreen(Screen):
pass
class RemoveScreen(Screen):
pass
class GroupScreen(Screen):
pass
class MyLogo(BoxLayout):
your_time = StringProperty()
def __init__(self,**kwargs):
super(MyLogo,self).__init__(**kwargs)
Clock.schedule_interval(self.set_time, 0.1)
def set_time(self,dt):
self.your_time = time.strftime("%m/%d/%Y %H:%M")
class MyApp(App):
def __init__(self,**kwargs):
super(MyApp,self).__init__(**kwargs)
self.sm = ScreenManager(transition=NoTransition())
self.sm.add_widget(MainScreen(name = "main"))
self.sm.add_widget(RemoveScreen(name = "remove"))
self.sm.add_widget(GroupScreen(name = "group"))
self.sm.current = "main"
def build(self):
return self.sm
if __name__ == "__main__":
MyApp().run()
And kv.kv file looks like this:
#:kivy 1.9.1
<MyButtons#BoxLayout>:
padding: 10,10,10,0
spacing: 10
size_hint: 1,0.3
orientation: "horizontal"
Button:
text: "Clear"
on_press: app.sm.current = "main"
Button:
text: "Remove"
on_press: app.sm.current = "remove"
Button:
text: "Group"
on_press: app.sm.current = "group"
Button:
text: "Color"
Button:
text: "Gestures"
<MyLogo>:
spacing: 10
padding: 10,10,10,0
orientation: "horizontal"
BoxLayout:
orientation: "vertical"
size_hint: 0.3,1
canvas:
Rectangle:
pos: self.pos
size: self.size
AsyncImage
source: 'http://lmsotfy.com/so.png'
Label:
size_hint: 1,0.3
text: root.your_time
color: [0,0,0,1]
Label:
size_hint: 1,0.3
text: "NYC, New York, USA"
color: [0,0,0,1]
<MainScreen>:
MyLayout:
MyLogo:
#Button:
# text: "main"
MyButtons:
#buttons
BoxLayout:
padding: 10,10,10,10
size_hint: 1,0.3
Button:
text: "Total figures: 1 Kivy Started"
<RemoveScreen>:
MyLayout:
MyLogo:
BoxLayout:
orientation: "horizontal"
Label:
font_size: "40sp"
text: "Remove"
Button:
font_size: "20sp"
text: "Remove this or something"
MyButtons:
#buttons
BoxLayout:
padding: 10,10,10,10
size_hint: 1,0.3
Button:
text: "Total figures: 1 Kivy Started"
<GroupScreen>:
MyLayout:
MyLogo:
BoxLayout:
orientation: "vertical"
Label:
font_size: "40sp"
text: "Group"
Button:
font_size: "20sp"
text: "Something groups stuff"
MyButtons:
#buttons
BoxLayout:
padding: 10,10,10,10
size_hint: 1,0.3
Button:
text: "Total figures: 1 Kivy Started"
The layout frame should be a screen manager, and each layout a screen. Screen transitions would be then triggered by pressing the buttons. You can also watch a tutorial here if you don't know how to do this, but the documentation should be enough.

Categories