So i'm making a program that will have a bunch of labels connected to one gridlayout. I want a few of these labels to be "under" the screen so that you can scroll down to it. However, I am having trouble with keeping the formatting of what I have currently and scrolling at the same time.
Below is what I want, with the "stuff7" label being under the screen. However I am unable to scroll.Image of non-scrollable screen
However, I noticed when I put size_hint_y: None I am able to scroll but it messes up my formatting completely.
Messed up format but able to scroll
Below is my MRE
KV code
#:import utils kivy.utils
<HomeScreen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#FFFFFF")
Rectangle:
size: self.size
pos: self.pos
ScrollView:
size: self.size
GridLayout:
rows: 6
cols: 1
spacing: 10
size_hint_y: None
height: self.minimum_height
width: self.minimum_width
Label:
text:"stuff1"
color: "black"
size_hint: 1,.18
Label:
text:"stuff2"
color: "black"
size_hint: 1,.18
Label:
text:"stuff3"
color: "black"
size_hint: 1,.18
Label:
text:"stuff4"
color: "black"
size_hint: 1,.18
Label:
text:"stuff5"
color: "black"
size_hint: 1,.18
Label:
text:"stuff6"
color: "black"
size_hint: 1,.18
Label:
text:"stuff7"
color: "black"
size_hint: 1,.18
If you are using height: self.minimum_height for the GridLayout, then you cannot use size_hint: 1,.18 for the Labels. The GridLayout is trying to change its height based on the height of its children (the Labels). But the Labels are trying to set their height based on the height of the GridLaout. Try modifying the Labels as:
Label:
text:"stuff2"
color: "black"
size_hint: 1, None
height: self.texture_size[1]
I am trying to display a scrollable widget that gets text from wikipedia , but I can't get it to scroll with scrollview. Here is what I am trying:
def crypto(self):
wiki_wiki = wikipediaapi.Wikipedia (
language='en',
extract_format=wikipediaapi.ExtractFormat.WIKI
)
p_wiki = wiki_wiki.page ('page')
return (p_wiki.text)
and in my .kv file:
ScrollView:
size: self.size
Label:
padding: root.width * .03, root.height * .03
text: app.crypto()
halign: "left"
markup: True
font_size: self.height / 15
text_size: self.width, None
height: self.texture_size[1]
center_y: .5
multiline: True
I am new to kivy, so apologies if the question is dumb and I am making a rookie mistake
It scrolls now I made a ScrollableLabel:
ScrollableLabel:
Label:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
text: app.cyber()
and in main.py:
class ScrollableLabel(ScrollView):
text = StringProperty('')
I am looking to add a border to the main screen of my application but I'm not sure how.
I tried to take some tips from this question:
Kivy-how can i make my canvas height smaller than the parent height
But I can't seem to figure it out.
The issue is that I am also using a KivyMD's Navigation Drawer, I would like the border be separate from the top bar, enclosing everything below the bar. Please let me know if I'm not being clear.
Here is some sample code that replicates my setup.
Perhaps I could add some random rectangles to act as a border?
EDIT:
Okay almost there, I got the 'border' but I now need the size_hint added in the AnchorLayout to ignore the top portion of the screen where the menu bar is. Here is the updated code.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
kv = '''
#:import hex kivy.utils.get_color_from_hex
NavigationLayout:
canvas.before:
Color:
rgb: hex('#C0C0C0')
Rectangle:
size: self.size
pos: self.pos
ScreenManager:
id: screen_manager
Screen:
name: "home_screen"
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Name of the App!'
elevation: 10
Widget:
FloatLayout:
orientation: 'vertical'
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
Widget:
canvas.before:
Color:
rgb: hex('#F5F5F5')
Rectangle:
size: self.size
pos: self.pos
size_hint: .95, .95
MDLabel:
text: "Some More Text"
halign: "center"
color: 0,0,0,1
pos_hint: {"center_x": .5, "center_y": .75}
size_hint: .7, .1
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
orientation: "vertical"
padding: "8dp"
spacing: "8dp"
AnchorLayout:
anchor_x: "left"
size_hint_y: None
height: avatar.height
Image:
id: avatar
source: "image.jpg"
MDLabel:
text: "Text here"
font_style: "Button"
size_hint_y: None
height: self.texture_size[1]
'''
class ContentNavigationDrawer(BoxLayout):
pass
class MyApp(MDApp):
def build(self):
return Builder.load_string(kv)
MyApp().run()
I think you will get what you want if you indent the FloatLayout so that it is in the BoxLayout. Like this:
#:import hex kivy.utils.get_color_from_hex
NavigationLayout:
canvas.before:
Color:
rgb: hex('#C0C0C0')
Rectangle:
size: self.size
pos: self.pos
ScreenManager:
id: screen_manager
Screen:
name: "home_screen"
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Name of the App!'
elevation: 10
# Widget: # not needed
FloatLayout:
orientation: 'vertical'
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
Widget:
canvas.before:
Color:
rgb: hex('#F5F5F5')
Rectangle:
size: self.size
pos: self.pos
size_hint: .95, .95
MDLabel:
text: "Some More Text"
halign: "center"
color: 0,0,0,1
pos_hint: {"center_x": .5, "center_y": .75}
size_hint: .7, .1
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
orientation: "vertical"
padding: "8dp"
spacing: "8dp"
AnchorLayout:
anchor_x: "left"
size_hint_y: None
height: avatar.height
Image:
id: avatar
# source: "image.jpg"
source: 'tester.png'
MDLabel:
text: "Text here"
font_style: "Button"
size_hint_y: None
height: self.texture_size[1]
I'm clicking the button in the fixed menu at the top even though there is a Button in the vertical Scrollview, it doesn't effect the fixed menu.
I'm trying to click at the button in the fixed menu at the top but the actions are being sent to the horizontal Scrollview below.
I'm new with kivy and even programming and I'm creating an app with Python 3.7, and Kivy(1.11.1) that kinda looks like Netflix's Page layout, in which there is a menu fixed at the top and below it there is a vertical Scrollview with some horizontal scrollviews inside (just like the Netlix's catalogues, eg.: My List, Continue Watching, Series, etc). The problem is that when I scroll down the vertical ScrollView and one of the horizontal scrollview gets behind the fixed menu at the top, the events are sent to this horizontal scrollview and not to the fixed menu that is be above the Scrollview content. If I scroll the vertical Scrollview down and one of the horizontal scrollviews gets "behind" (in quotes because I imagine they aren't supposed to collide with each other) the menu at the top, if I click in the menu the event is being sent to the horizontal scrollview.
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
Builder.load_string(
'''
<ScrollApp>:
orientation: 'vertical'
BoxLayout:
size_hint: 1, 0.12
Button:
text: 'Fixed Menu'
on_press: print('This button stops working if there is a horizontal scrollview "behind"')
ScrollView:
bar_width: 10
scroll_type: ['bars', 'content']
BoxLayout:
orientation: 'vertical'
size_hint: 1, None
height: self.minimum_height
padding: 22, 0, 22, 50
spacing: 50
canvas:
Color:
rgba: .15, .15, .15, .9
Rectangle:
size: self.size
pos: self.pos
Button:
size_hint: None, None
width: 100
height: 100
on_press: print('This button does not overlap the menu above')
# "ScrollViews containers"
Custom
Custom
Custom
Custom
Custom
BoxLayout:
size_hint: 1, 0.12
Button:
on_press: print("This menu at the bottom is not affected by the problem that occurs with the top one")
<Custom#BoxLayout>:
orientation: 'vertical'
size_hint: 1, None
height: self.minimum_height
Label:
size_hint: None, None
size: self.texture_size
id: label
font_size: 20
text: 'Teste'
ScrollView:
do_scroll: True, True
size_hint: 1, None
height: 150
GridLayout:
id: grid
size_hint: None, 1.01
width: self.minimum_width
spacing: 5
cols: 3
Button:
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
Button:
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
Button:
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
''' )
class ScrollApp(BoxLayout):
pass
class Test(App):
def build(self):
return ScrollApp()
Test().run()
You do manage to find obscure situations in kivy :-).
I believe the problem here is the order of touch event processing. The touch events are normally processed by the top level widgets, and they dispatch the event to their children, all the way down the Widget tree. In any Widget with multiple children, the touch event is dispatched to children in the reverse order that they have been add to their parent. And in kv, child Widgets are added in order as they appear (top to bottom). So, in your case, the ScrollApp has three children, and the touch events will be dispatched to its children starting with the BoxLayout that contains the bottom menu, then to the ScrollView, and lastly, to the BoxLayout that contains the top menu. So the ScrollView gets the touch event before the top menu, and dispatches the touch to its children (including the Custom instances). So the Buttons in the Custom see the touch event before the top menu sees it, and they claim it as their own. The obvious way to fix it would be to change the order of Widgets that appear in the ScrollApp, but since ScrollApp is a BoxLayout, that would dramatically change how your App looks. A different fix is to use a Layout that doesn't depend on the order of its children. Something like a FloatLayout has that characteristic. So, here is a modified version of your code, that makes ScrollApp extend FloatLayout instead of BoxLayout:
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string(
'''
<ScrollApp>:
ScrollView:
size_hint: 1.0, 0.76
pos_hint: {'center_y':0.5}
bar_width: 10
scroll_type: ['bars', 'content']
BoxLayout:
orientation: 'vertical'
size_hint: 1, None
height: self.minimum_height
padding: 22, 0, 22, 50
spacing: 50
canvas:
Color:
rgba: .15, .15, .15, .9
Rectangle:
size: self.size
pos: self.pos
Button:
size_hint: None, None
width: 100
height: 100
on_press: print('This button does not overlap the menu above')
# "ScrollViews containers"
Custom
Custom
Custom
Custom
Custom
BoxLayout:
size_hint: 1, 0.12
pos_hint: {'y':0}
Button:
on_press: print("This menu at the bottom is not affected by the problem that occurs with the top one")
BoxLayout:
size_hint: 1, 0.12
pos_hint: {'top':1.0}
Button:
text: 'Fixed Menu'
on_press: print('This button stops working if there is a horizontal scrollview "behind"')
<Custom#BoxLayout>:
orientation: 'vertical'
size_hint: 1, None
height: self.minimum_height
Label:
size_hint: None, None
size: self.texture_size
id: label
font_size: 20
text: 'Teste'
ScrollView:
do_scroll: True, True
size_hint: 1, None
height: 150
GridLayout:
id: grid
size_hint: None, 1.01
width: self.minimum_width
spacing: 5
cols: 3
Button:
text: 'button 1'
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
Button:
text: 'button 2'
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
Button:
text: 'button 3'
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
''' )
class ScrollApp(FloatLayout):
pass
class Test(App):
def build(self):
return ScrollApp()
Test().run()
So, with this child order in ScrollApp, the top and bottom menus get their shot at the touch events before the ScrollView.
I actually think your code should work without this change, so there may be something else happening that I couldn't find.
I am trying to a Boxlayout with ScrollBar in Kivy, but I am not able to do it. Below excerpt of .kv file. I am dynamically adding controls to Boxlayout once the Boxlayout overflows controls are hidden and there is not Scrollbar. Please advise.
<ProcessorUI>: #GridLayout
cols: 1
rows: 3
Label:
text: 'Output'
size_hint_x: None
width: 100
size_hint_y: None
height: 20
ScrollView:
size_hint: (None, None)
size: (400, 400)
BoxLayout:
id: output
orientation: 'vertical'
GridLayout
cols: 2
TextInput:
id: input
multiline: True
size_hint_y: None
height: 40
Button:
id: btn_process
text: 'Process'
size_hint_x: None
width: 100
size_hint_y: None
height: 40
on_press: root.on_event()
ScrollView:
size_hint: (None, None)
size: (400, 400)
BoxLayout:
id: output
orientation: 'vertical'
The BoxLayout has no manually set height, so it always precisely fills the Scrollview, and never needs a scrollbar.
You probably actually want something like the following
ScrollView:
size_hint: (None, None)
size: (400, 400)
GridLayout:
id: output
cols: 1
size_hint_y: None
height: self.minimum_height
These last two lines set the gridlayout height to track the sum of heights of its children. You could also set the height to anything else.