How to properly use ScrollView in Kivy Python? - python

I have this code where i want to change the position of the buttons, but if i change the position, the scroll is no longer working. How to manage to make it work? Below is the working version. If i change size_hint: 1, .1 to size_hint: 1, .7, the scroll no longer works...
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string('''
<Root>:
ScrollView:
size_hint: 1, .1
GridLayout:
size_hint_y: None
cols: 1
# minimum_height: self.height
Button
text: 'one'
Button:
text: 'two'
Button:
text: 'three'
Button:
text: 'four'
''')
class Root(FloatLayout):
pass
class DemoApp(App):
def build(self):
return Root()
if __name__ == '__main__':
DemoApp().run()

You are doing it right. The ScrollView allows you to scroll to see parts of your GridLayout that don't fit in the ScrollView. When you set the size_hint to (1, .7), everything fits within the ScrollView, so it does not scroll.
You can force scrolling by adding Widgets to take up more space (like Labels with no text):
<Root>:
ScrollView:
size_hint: 1, .7
GridLayout:
size_hint_y: None
cols: 1
height: self.minimum_height
Label:
text: ''
size_hint_y: None
height: 300
Button
text: 'one'
size_hint: 1, None
height: self.texture_size[1]
Button:
text: 'two'
size_hint: 1, None
height: self.texture_size[1]
Button:
text: 'three'
size_hint: 1, None
height: self.texture_size[1]
Button:
text: 'four'
size_hint: 1, None
height: self.texture_size[1]
Label:
text: ''
size_hint_y: None
height: 300

Once theres a size_hint_y = None in the parent widget, then you have to manually declare the height of the child widget since size_hint_y deals with the height or Y axis.. so i modified your code to make things a lil clearer
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string('''
<Root>:
ScrollView:
size_hint_y:None
height:root.width / 2
GridLayout:
size_hint_y: None
cols: 1
height:self.minimum_height
spacing:dp(20)
Button
text: 'one'
size_hint_y:None
height:dp(50)
Button:
text: 'two'
size_hint_y:None
height:dp(50)
Button:
text: 'three'
size_hint_y:None
height:dp(50)
Button:
text: 'four'
size_hint_y:None
height:dp(50)
''')
class Root(FloatLayout):
pass
class DemoApp(App):
def build(self):
return Root()
if __name__ == '__main__':
DemoApp().run()

Related

Delete a row in Kivy's BoxLayout

With Kivy, I want a layout that is different when the window size is portrait or landscape (a grid of buttons, and for exemple the buttons on a row are shown in a column).
I tried first to hide some widgets, but that doesn't work well (even at a size of 0, some text appears, and this doesn't work with spacing)
So I wanted to delete a row directly, it worked, but...
The space for the raw remains with, like this
I tried to delete the second row, but there is now a gap.
Here is the code for that example
test3.kv file
<MDBoxLayout>:
spacing:5
<MyLayout>:
MDBoxLayout:
orientation: "vertical"
size: root.width, root.height
id: vbox
# Texte
MDLabel:
size_hint: 1,0.4
text: "Text"
halign: 'left'
valign: 'bottom'
font_size: 48
id: label
# Ligne 1
MDBoxLayout:
orientation: "horizontal"
size_hint: 1,0.15
id: ligne1
MDFillRoundFlatButton:
size_hint: 0.25,1
text: "A"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"B"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"C"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"D"
# Ligne 2
MDBoxLayout:
orientation: "horizontal"
size_hint: 1,0.15
id: ligne2
MDFillRoundFlatButton:
size_hint: 0.25,1
text: "1"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"2"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"3"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"4"
# Ligne 3
MDBoxLayout:
orientation: "horizontal"
size_hint: 1,0.15
MDFillRoundFlatButton:
size_hint: 0.25,1
text: "E"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"F"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"G"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"H"
# Ligne 4
MDBoxLayout:
orientation: "horizontal"
size_hint: 1,0.15
MDFillRoundFlatButton:
size_hint: 0.25,1
text: "I"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"J"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"K"
MDFillRoundFlatButton:
size_hint: 0.25,1
text:"L"
test3.py file
import kivy
kivy.require('1.1.1')
from kivymd.app import MDApp
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivymd.uix.boxlayout import MDBoxLayout
Builder.load_file('test3.kv')
class MyLayout(Widget):
def on_size(self, *args):
self.ids.label.text = 'on_size'
if self.width > self.height:
self.ids.label.text = self.ids.label.text + f' horizontal {self.ids.ligne2}'
# orientation : horizontal
if isinstance(self.ids.ligne2, MDBoxLayout):
self.ids.label.text = self.ids.label.text + ' ligne 2 existe: supprimée'
self.ids.ligne2.clear_widgets()
self.remove_widget(self.ids.ligne2)
self.ids.ligne2 = None
class app(MDApp):
def build(self):
return MyLayout()
if __name__ == '__main__':
app().run()
Is this something missing in my code, or should I try another method to change dynamically the layout?

Kivy - centering BoxLayout

I'm learning Kivy and I'm trying to center the main vertical BoxLayout with the content (boxlayouts, text, inputs, image, ...). The root window is 1200px wide and the BoxLayout is 1000px.
I tried to use AnchorLayout instead of the BoxLayout but the content goes out of the window or everything goes in the corner, and I can't make it centered.
Also, the content could be higher than the root window. How can I make that it doesn't follow the root height?
Can someone help me on this?
Here is the Py file:
Import kivy
from kivy.app import App
from kivy.core.window import Window
Window.size = (1440, 720)
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.image import Image
from kivy.core.window import Window
class Exec(Widget):
def __init__(self, **kwargs):
super(Exec, self).__init__(**kwargs)
class TC(App):
def build(self):
Window.clearcolor = (25/255,26/255,25/255,0)
return Exec()
if __name__ == '__main__':
TC().run()
And KV file...
<Exec>
BoxLayout:
orientation: 'vertical'
BoxLayout:
size: 1000, 700
position_hint: {'center_x':0.5, 'center_y':0.5}
orientation: 'vertical'
position_hint: None, None
position_x: 150
GridLayout:
cols: 2
size_hint_y: None
height: 75
Image:
source: 'traffic-light.png'
size: self.texture_size
size_hint_x: None
size_hint_y: None
width: 120
height: 50
Label:
multiline: True
font_size: 24
markup: True
text: "[b]Intelligent Traffic Control System[/b] \n[size=18][color=b4afaf]Developed for testing purposes only[/color][/size]"
text_size: self.size
halign: 'left'
GridLayout:
cols: 3
size_hint_y: None
height: 150
Label:
text: "Deviation"
font_size: 18
text_size: self.size
halign: 'left'
Label:
text: "Deviation muliplier"
font_size: 18
text_size: self.size
halign: 'left'
Label:
text: "Envelope Inflate [+]/ Deflate [-]"
font_size: 18
text_size: self.size
halign: 'left'
TextInput:
multiline:False
font_size: 32
foreground_color: (1,1,1,1)
background_color: (25/255,26/255,25/255,0)
TextInput:
multiline:False
font_size: 32
foreground_color: (1,1,1,1)
background_color: (25/255,26/255,25/255,0)
TextInput:
multiline:False
font_size: 32
foreground_color: (1,1,1,1)
background_color: (25/255,26/255,25/255,0)
GridLayout:
cols: 3
Label:
text: "Week days"
font_size: 18
Label:
text: "Saturday"
font_size: 18
Label:
text: "Sunday"
font_size: 18
GridLayout:
cols: 3
size_hint_y: None
height: 75
Label:
text: "Generate random envelope"
font_size: 18
TextInput:
multiline:False
font_size: 24
Button:
text: "Create & Save"
Here are the cropped images with content on how it should be but not entered and one where everything goes in the corner.
Thnx!
The problem is that your Exec class extends Widget, which is not intended to be used as a container. Try changing your Exec definition to:
class Exec(FloatLayout):
pass

position gridlayout inside gridlayout kivy

I'm trying to position a gridlayout(that contains 4 text input) at the center of another gridlayout(that is the rootwidget- ResgistrationWindow in my case) in kivy.but nothing seems to work.
This is my .kv file and the root widget is a grid layout
<RegistrationWindow>
cols:1
canvas.before:
Rectangle:
size: self.size
pos:self.pos
source:"emotion.jpg"
GridLayout:
cols: 2
size_hint: None,None
size:root.width,root.height/14
Label:
text: "Button 1"
size_hint_x:0.95
Button:
text: "X"
size_hint_x: 0.05
GridLayout:
cols:1
size_hint: None, None
size:root.width, root.height/2
TextInput:
multiline:False
TextInput:
multiline:False
TextInput:
multiline:False
TextInput:
multiline:False
And this is my .py file
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.lang.builder import Builder
class RegistrationWindow(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
kv = Builder.load_file("emotions.kv")
class RegistrationApp(App):
def build(self):
return RegistrationWindow()
if __name__ == "__main__":
RegistrationApp().run()
I think your problem is a matter of indentations. Since you want to add your GridLayouts inside your RegistrationWindow, your .kv file should look more like this:
(You can rearrange the GridLayouts further as you like, or indent a GridLayout even more to put it inside another GridLayout.)
<RegistrationWindow>
cols:1
canvas.before:
Rectangle:
size: self.size
pos:self.pos
source:"emotion.jpg"
GridLayout:
cols: 2
size_hint: None,None
size:root.width,root.height/14
Label:
text: "Button 1"
size_hint_x:0.95
Button:
text: "X"
size_hint_x: 0.05
GridLayout:
cols:1
size_hint: None, None
size:root.width, root.height/2
TextInput:
multiline:False
TextInput:
multiline:False
TextInput:
multiline:False
TextInput:
multiline:False

ReferenceError: weakly-referenced object no longer exists Kivy DropDown

Running this example of DropDown works. However, after some use/time I get the error ReferenceError: weakly-referenced object no longer exists
This is likely do to an issue in on_release:dropdown.open(self)
Bonus points as to why on_parent: self.dismiss() also doesn't work with the way I have these widgets set up. Without this, I have the submenu items appearing when the app first runs and with this enabled, the submenu items flash (appear and quickly disappear).
#!/usr/bin/kivy
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.properties import ObjectProperty
from kivy.uix.dropdown import DropDown
from kivy.core.window import Window
Window.size = (400, 240)
sm = """
ScreenManager:
id:manager
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 0.5
Rectangle:
pos: 0,0
size: 800, 480
Notes:
id:Notes
name: 'Notes'
manager: manager
<Notes>:
name: "Notes"
orientation: "vertical"
FloatLayout:
size_hint: None, None
canvas.before:
Color:
rgba: 1, 1, 0, 1
Button:
id: mainbutton
text: "Menu name"
font_size: 20
size_hint: None, None
size: 150, 50
pos: 20,400
on_release:dropdown.open(self)
CustomDropDown:
id: dropdown
#on_parent: self.dismiss()
on_select: mainbutton.text = '{}'.format(args[1])
Button:
id: button1
text: 'First Item'
size_hint_y: None
height: 40
font_size: 18
on_release: dropdown.select('First Item')
Button:
id: button2
text: 'Second Item'
size_hint_y: None
height: 40
font_size: 18
on_release: dropdown.select('Second Item')
Button:
id: button3
text: 'Third Item'
size_hint_y: None
height: 40
font_size: 18
on_release: dropdown.select('Third Item')
"""
class Notes(Screen):
pass
class CustomDropDown(DropDown):
pass
#dropdown = CustomDropDown()
class TestApp(App):
def build(self):
return Builder.load_string(sm)
if __name__ == '__main__':
TestApp().run()
In child widget, CustomDropDown
With on_parent: self.dismiss()
When you clicked on the main button, Menu name sometimes it gives an error, ReferenceError: weakly-referenced object no longer exists. If there is no ReferenceError, the drop-down list flash (appear and quickly disappear). The reason is that the DropDown was dismissed.
Without on_parent: self.dismiss()
It will display the CustomDropDown list at app startup. When the main button, Menu name is clicked, the drop-down list that appeared at startup disappeared but it displayed a drop-down list twice as long i.e. submenu items repeated twice.
Note
Drop-Down List is similar to Popup. They are special widget. Don't try to add it as a child to any other widget. If you do, they will be handled like an ordinary widget and won't be created hidden in the background.
Please refer to the example below illustrating how to create Drop-Down list.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.dropdown import DropDown
from kivy.core.window import Window
Window.size = (800, 480)
class CustomDropDown(DropDown):
pass
class Notes(Screen):
pass
class MyScreenManager(ScreenManager):
pass
class TestApp(App):
title = "Kivy Drop-Down List Demo"
def build(self):
return MyScreenManager()
if __name__ == '__main__':
TestApp().run()
test.kv
#:kivy 1.10.0
#:import Factory kivy.factory.Factory
<CustomDropDown>:
on_select: app.root.ids.Notes.ids.mainbutton.text = '{}'.format(args[1])
Button:
id: button1
text: 'First Item'
size_hint_y: None
height: 40
font_size: 18
on_release: root.select(self.text)
Button:
id: button2
text: 'Second Item'
size_hint_y: None
height: 40
font_size: 18
on_release: root.select(self.text)
Button:
id: button3
text: 'Third Item'
size_hint_y: None
height: 40
font_size: 18
on_release: root.select(self.text)
<MyScreenManager>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 0.5
Rectangle:
pos: 0,0
size: 800, 480
Notes:
id:Notes
name: 'Notes'
<Notes>:
orientation: "vertical"
FloatLayout:
size_hint: None, None
canvas.before:
Color:
rgba: 1, 1, 0, 1
Button:
id: mainbutton
text: "Menu name"
font_size: 20
size_hint: None, None
size: 150, 50
pos: 20,400
on_release: Factory.CustomDropDown().open(self)
Output
from : $Yourkivydir/kivy-examples/demo/showcase/data/screens
ShowcaseScreen:
fullscreen: True
name: 'DropDown'
# trick to not lost the Dropdown instance
# Dropdown itself is not really made to be used in kv.
__safe_id: [dropdown.__self__]
Button:
id: btn
text: '-'
on_release: dropdown.open(self)
size_hint_y: None
height: '48dp'
Widget:
on_parent: dropdown.dismiss()
DropDown:
id: dropdown
on_select: btn.text = 'Selected value: {}'.format(args[1])
Button:
text: 'Value A'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('A')
Button:
text: 'Value B'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('B')
Button:
text: 'Value C'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('C')

how to use GridLayout in TabeedPanel using kivy in python

I am trying to make a GUI in python using kivy and TabeedPanel . some problems are coming for putting on exact location of label, TextInput , button. I'm unable to put multiple label, TextInput altogether. That's why I commented in the code. I tried GridLayout also, but Unable to arrange exactly.
Can you help me? Thanks in advance.
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelItem
from kivy.lang import Builder
from kivy.uix.checkbox import CheckBox
from kivy.uix.button import Button
from kivy.app import App
from kivy.uix.textinput import TextInput
import json
Builder.load_string("""
<Test>:
do_default_tab: False
TabbedPanelItem:
text: 'page1'
BoxLayout:
Label:
text: 'label'
TextInput:
text: 'TextInput'
CheckBox:
text: 'CheckBox'
Button:
text: 'save'
#BoxLayout:
# orientation: 'vertical'
# BoxLayout:
# orientation: 'horizontal'
# Label:
# text: 'label'
TabbedPanelItem:
text: 'page2'
BoxLayout:
Label:
text: 'number1'
#TextInput:
# text: 'TextInput'
Label:
text: 'number2'
# TextInput:
# text: 'TextInput'
Button:
text: 'button'
""")
class Test(TabbedPanel):
pass
class MyApp(App):
def build(self):
test = Test()
return test
if __name__ == '__main__':
MyApp().run()
Following your example, you can use BoxLayouts but you need to nest them correctly:
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.lang import Builder
Builder.load_string("""
<Test>:
do_default_tab: False
TabbedPanelItem:
text: 'page1'
BoxLayout:
padding: 50, 50, 50, 50
orientation: 'horizontal'
BoxLayout:
spacing: 50
orientation: 'vertical'
size_hint_x: 1
Label:
text: 'label'
Label:
text: 'label'
Label:
text: 'label'
BoxLayout:
spacing: 50
orientation: 'vertical'
TextInput:
text: 'TextInput'
TextInput:
text: 'TextInput'
TextInput:
text: 'TextInput'
BoxLayout:
spacing: 50
orientation: 'vertical'
size_hint_x: 0.40
CheckBox:
text: 'CheckBox'
CheckBox:
text: 'CheckBox'
CheckBox:
text: 'CheckBox'
BoxLayout:
spacing: 50
orientation: 'vertical'
size_hint_x: 0.60
Button:
text: 'save'
Button:
text: 'save'
Button:
text: 'save'
TabbedPanelItem:
text: 'page2'
BoxLayout:
padding: 50, 50, 50, 50
orientation: 'horizontal'
BoxLayout:
spacing: 50
orientation: 'vertical'
Label:
text: 'label'
Label:
text: 'label'
Label:
BoxLayout:
spacing: 50
orientation: 'vertical'
TextInput:
text: 'TextInput'
TextInput:
text: 'TextInput'
Button:
spacing: 100
text: 'button'
""")
class Test(TabbedPanel):
pass
class MyApp(App):
def build(self):
test = Test()
return test
if __name__ == '__main__':
MyApp().run()
Output:
Here's an example using GridLayout that I made a reference to in your other question. FYI, there are many ways you could go about this. I personally like using gridlayout with forms because it's easy to put then ScrollViews if need be.
Read up on the kv language here to help keep things DRY and other things. If a widget is defined in kv, then you don't need to import them at the top of your file.
Example:
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.lang import Builder
Builder.load_string("""
<MyLabel#Label>:
size_hint: (None, None)
size: (400, 100)
<MyTextInput#TextInput>:
size_hint: (None, None)
size: (600, 100)
<MyButton#Button>:
size_hint: (None, None)
size: (400, 100)
<MyCheckBox#AnchorLayout>:
# I'm nesting the checkbox here b/c it is hard to see if the background is not lightened.
size_hint: (None, None)
size: (100, 100)
anchor_x: "center"
anchor_y: "center"
canvas.before:
Color:
rgba: [0.7, 0.7, 0.7, 1]
Rectangle:
pos: self.pos
size: self.size
CheckBox:
<Test>:
do_default_tab: False
TabbedPanelItem:
text: 'page1'
GridLayout:
rows: 3
cols: 4
padding: [10, 100]
spacing: [10, 50]
MyLabel:
text: "Label 1"
MyTextInput:
MyCheckBox:
MyButton:
text: "Button 1"
MyLabel:
text: "Label 3"
MyTextInput:
MyCheckBox:
MyButton:
text: "Button 2"
MyLabel:
text: "Label 3"
MyTextInput:
MyCheckBox:
MyButton:
text: "Button 3"
TabbedPanelItem:
text: 'page2'
GridLayout:
rows: 3
cols: 2
padding: [10, 100]
spacing: [10, 50]
MyLabel:
text: "Label 1"
MyTextInput:
MyLabel:
text: "Label 2"
MyTextInput:
# blank spacer widget
Widget:
size_hint: (None, None)
size: (400, 100)
MyButton:
text: "Button"
""")
class Test(TabbedPanel):
pass
class MyApp(App):
def build(self):
return Test()
if __name__ == '__main__':
MyApp().run()

Categories