Kivy - Scrollview - How to know which widgets are shown currently - python

I have a scroll view with GridLayout child and lany varying height labels are added in gridlayout. My question is, How to know which widget is shown in the screen at any time? Reason is, I want to show the details of topmost visible label in other part of the screen(to show which widget is top of the visible scrollview area now). For ex, if the gridlayout has 50 labels, when we scroll, screen might show 4 labels(due to size of the scrollview) for ex 23,24,25 and 26. In this case, how to know label 23 is being shown at the top of the scroll view visible area?
UPDATED:
sample code showing above explanation
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from kivymd.uix.behaviors.focus_behavior import ButtonBehavior
KV = '''
BoxLayout:
orientation: 'vertical'
MDLabel:
id : lbl_id
text: 'This should show the top label in scrollview below(red canvas) dynamically as we scroll'
size_hint : (1,None)
height : self.texture_size[1]
MDSeparator:
ScrollView:
id : scroll_id
size_hint_y:None
height : root.height - (lbl_id.height)
do_scroll_x: False
do_scroll_y: True
scroll_type: ['bars', 'content']
canvas:
Color:
rgba: [1,0,0,0.5]
Rectangle:
pos: self.pos[0],self.height-100
size: self.size
GridLayout:
id:grid_id
cols: 1
height: self.minimum_height
size_hint_y: None
'''
class MyMDLabel(ButtonBehavior, MDLabel ):
pass
class Example(MDApp):
def build(self):
self.res=Builder.load_string(KV)
for i in range(20):
lbl = MyMDLabel(text='pressme Label-%d ' % (i), size_hint=(1, None) )
lbl.bind(on_release=self.update_lbl)
self.res.ids.grid_id.add_widget(lbl)
return self.res
def update_lbl(self,instance):
content='\nThis should show the top label in scrollview below(red canvas) dynamically as we scroll'
self.res.ids.lbl_id.text=instance.text + content
Example().run()

Related

How to resize a widget so it size is fit to its text in kivy

When I add a widget, namely Label, sometimes its size cannot fit it's text height. Changing its height manually doesn't helped because i can type a text so its height will exceed the label's height. Here is the example of the code
.py file
#testing.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
import random
import GlobalVariable
from GlobalVariable import *
import time
class ScreenOne(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("testing.kv")
class testing(App):
def build(self):
return kv
if __name__ == "__main__":
testing().run()
.kv file
#testing.kv
WindowManager:
ScreenOne:
<ScreenOne>:
ScrollView:
size: self.size
GridLayout:
size_hint_y: None
size: root.height, root.width
cols: 2
Label:
text: "boom\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nboom"
Label:
text: "boom\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nboom"
Label:
text: "boom\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nboom"
Label:
text: "boom\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nboom"
Label:
text: "boom\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nboom"
Label:
text: "boom\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nboom"
When you run the code, you will see that there is text that exceed it's label height. I expect to make those label always fit the text (not shorter but also not longer than its text) whatever the text is, and is scrollable. Any help or suggestion is appreciated. Thanks.
Use these properties for your Label
Label:
text_size: self.width, None
size_hint: 1, None
height: self.texture_size[1]

Update Label based on dynamic buttons clicked on Stack Layout in Python and Kivy

I'm trying to update text in label based on dynamic buttons clicked in stack layout.
Here is my main.py code :
kivy.require('2.1.0')
from kivy.app import App
from kivy.metrics import dp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.stacklayout import StackLayout
class SPKList(StackLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
for i in range(0, 500):
width = dp(250)
height = dp(150)
b = Button (
text=str(i+1),
size_hint=(None,None),
size=(width,height),
font_size=dp(20)
)
self.add_widget(b)
class MainFrame(BoxLayout):
pass
class SPKMonitorApp(App):
pass
SPKMonitorApp().run()
and here is the SPKMonitor.kv code :
#:kivy 2.1.0
MainFrame:
<MainFrame#BoxLayout>:
orientation: "vertical"
Label:
id:room1
text:"This is Free Space"
size_hint: 1, .3
font_size: "20dp"
BoxLayout:
orientation:"horizontal"
BoxLayout:
orientation: "vertical"
size_hint: .4, 1
Label:
id:room2
text:"This is Free Space"
font_size: "20dp"
Button:
id:submitButton
text:"SUBMIT"
size_hint: 1, .2
font_size: "20dp"
SPKFieldView:
<SPKDetailView#BoxLayout>:
orientation: "vertical"
Label:
text: "No. SPK"
BoxLayout:
orientation: "horizontal"
<SPKFieldView#ScrollView>:
SPKList:
size_hint: 1, None
height: self.minimum_height
<SPKList>:
and here is the UI :
Please help me update the text in label with ID: room2 to any number I clicked in the right side buttons (the stack layout)
You can just add a method, triggered by the Button, that does what you want. Just modify your SPKList slightly:
class SPKList(StackLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
for i in range(0, 500):
width = dp(250)
height = dp(150)
b = Button (
text=str(i+1),
size_hint=(None,None),
size=(width,height),
font_size=dp(20),
on_release=self.do_button # added
)
self.add_widget(b)
def do_button(self, button):
# set the text of the Label
App.get_running_app().root.ids.room1.text = button.text

KIVY : BoxLayout containing horizontal BoxLayout

Am trying to have 4 Horizontal box layout stacked in a BoxLayout in a vertical way.
My KV file :
<HBoxWidget>:
BoxLayout:
size: root.size
pos: root.pos
id: boxlayout_h
orientation: 'horizontal'
Image:
source: '/Users/driftking9987/Desktop/fp.gif'
<VBoxWidget1>:
BoxLayout:
spacing: 10
orientation: "horizontal"
size: [1,.25]
pos: root.pos
Label:
text: "Status : "
color: [0,84,80,19]
Label:
text: "Pending"
color: [0,84,80,19]
Widget:
<ContainerBox>:
orientation: 'horizontal'
HBoxWidget:
VBoxWidget1:
I planned to have multiple VBoxWidget in a vertical way but it's just not working out.
Moreover to achieve the [9] Label, I thought I will have a BoxLayout with 2 rows, in horizontal, 2nd row will have the above mentioned properties. But this is just not working out. Below is what am getting. I tried setting the size_hint as 1,.25 i.e the whole area will be divided into 4 parts but it's not giving the desired result.
PY File :
from kivy.app import App
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
class HBoxWidget(Widget):
def __init__(self, **kwargs):
super(HBoxWidget, self).__init__(**kwargs)
class VBoxWidget1(Widget):
def __init__(self, **kwargs):
super(VBoxWidget1, self).__init__(**kwargs)
class ContainerBox(BoxLayout):
def __init__(self, **kwargs):
super(ContainerBox, self).__init__(**kwargs)
class TestApp(App):
def build(self):
return ContainerBox()
if __name__ == '__main__':
TestApp().run()
What about this: I changed HBOX and VBOX widget to BoxLayout and added Label and another BoxLayouts to ContainerBox. It looks quite like on your drawing
<HBoxWidget>:
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
Image:
source: 'duck.jpg'
<VBoxWidget1>:
BoxLayout:
orientation: "horizontal"
size: [1,.25]
pos: root.pos
Label:
text: "Status : "
color: [0,84,80,19]
Label:
text: "Pending"
color: [0,84,80,19]
Widget: # Because of this widget Labels are not in the middle, its not on your drawing tough
<ContainerBox>:
orientation: 'vertical'
Label:
text: 'Label'
size_hint_y: 0.1
BoxLayout:
id: four_horizontals
orientation: 'horizontal'
HBoxWidget:
BoxLayout:
orientation:'vertical'
# One solution
#GridLayout:
# cols:1
# padding: 100
# VBoxWidget1:
# VBoxWidget1:
# VBoxWidget1:
# VBoxWidget1:
#Second Solution
BoxLayout:
#size_hint_y: 0 to 1 - it says how much you reduce height. Now its 1/3 of its parent, because there are 3 boxlayouts. if you set 0.5, it will have 1/3*0.5 and the other 2 boxlayouts takes the height which you took from this one
BoxLayout:
orientation:'vertical'
VBoxWidget1:
VBoxWidget1:
VBoxWidget1:
VBoxWidget1:
BoxLayout:
python
from kivy.app import App
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
class HBoxWidget(BoxLayout):
def __init__(self, **kwargs):
super(HBoxWidget, self).__init__(**kwargs)
class VBoxWidget1(BoxLayout):
def __init__(self, **kwargs):
super(VBoxWidget1, self).__init__(**kwargs)
class ContainerBox(BoxLayout):
def __init__(self, **kwargs):
super(ContainerBox, self).__init__(**kwargs)
class TestApp(App):
def build(self):
return ContainerBox()
if __name__ == '__main__':
TestApp().run()
Additional info:
The way I managed those 4 labels is not really correct way. I will give you hint how to solve it more correctly.
Check https://kivy.org/doc/stable/api-kivy.uix.floatlayout.html#module-kivy.uix.floatlayout
On BoxLayout, widgets organize themself automaticly - horizontaly or verticaly and they scale based on how much space belongs to them.
With FloatLayout, there is no restriction so you can position labels as you wish.
In general, if you have changing resolution, its better to solve it with BoxLayouts. If you want more freedom, use FloatLayout, but you will have to manage widget scaling and positioning by yourself

kivy scrollview inside gridlayout on label

I trying to create a one screen app with one root rule set. by pressing a button i should see a scrollable text in the grid next to the button. In all the example solutions I see that scrollview is working in similar KV files that I have seen in other questions. Could someone please identify what I have missed in KV file.
my .py file:
import kivy
import string
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
from kivy.properties import StringProperty
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
class RootContainer(BoxLayout):
instance = ObjectProperty(None)
def __init__(self, **kwargs):
super(RootContainer, self).__init__(**kwargs)
def clickAction1(self, instance):
#identify the button pressed
buttonText = instance.text
self.lbl1.text = instance.text + " some text goes here ... "
myresult = " this is scrolling text.\n " * 30
self.lbl5.text = myresult
class MBApp(App):
def build(self):
return RootContainer()
if __name__ == '__main__':
MBApp().run()
my KV file:
#:kivy 1.0.9
<RootContainer>:
id: theRoot
lbl1: my_labelC
lbl5: my_labelCS
BoxLayout:
orientation: 'vertical'
spacing: 20
padding: 20
canvas:
Color:
rgb: 0, .33, 0
Rectangle:
pos: self.pos
size: self.size
Button:
text: "This is 1st button"
text_size: self.size
size_hint: (.5,1)
on_press: theRoot.clickAction1(self)
Button:
text: "This is 2nd button"
text_size: self.size
size_hint: (.5,1)
on_press: root.clickAction1(self)
GridLayout:
rows: 2
cols: 1
spacing: 10
padding: 10
canvas:
Color:
rgb: .7, .63, 0
Rectangle:
pos: self.pos
size: self.size
Label:
id: my_labelC
canvas.before:
Color:
rgb: 0,0,0
Rectangle:
pos: self.pos
size: self.size
text: "Header text for button clicked ......."
text_size: self.size
ScrollView:
GridLayout:
cols:1
rows:1
height: self.minimum_height
Label:
id: my_labelCS
text: "Scrolling text goes here ....."
I hope this is not a duplicate. Any other suggestions to code are also welcome. Thank you.
You don't set the size of your Label, so the default applies, as any widget, the defaults are
size: 100, 100
size_hint: 1, 1
since size_hintis 1, 1, and the parent is a layout, size itself is overriden by the parent's available space
since you set size: minimum_size in the parent layout, it'll give the minimum size its children require, but the Label doesn't ask for any space, it's size_hint of 1, 1 means that it's happy to take all the available space. Kivy solve this situation by not giving it any space, so the Label size ends up being 0, 0, and the GridLayout's size as well.
So you want to disable size_hint (at least for the height, of the Label, and instead set it to the texture heights
size_hint_y: None
height: self.texture_size[1]
Which is usually sided with setting the texture width to the available width, by setting text_size.
text_size: self.width, None
size_hint_x: 1 # this is the default, so this line is not required, but you want to be sure not to disable this default
Do you really need the GridLayout inside your scrollview? This works for me:
ScrollView:
Label:
id: my_labelCS
size_hint: 1, None
text_size: self.width, None
height: self.texture_size[1]
text: "Scrolling text goes here ....."

Centering TextInput within a BoxLayout in Kivy

Here is a screenshot of my kivy app. I am trying to get the TextInput in the bottom left to be centered in the BoxLayout which it is in, and I don't want it to be the same size as the layout, I want it to be much smaller. This BoxLayout in question resides in the bottom half of the screen. I have tried setting the TextInputs propertycenter:self.parent.center but this doesn't work. As you can see, I have printed the center coords from the BoxLayout into the TextInput using that very line, self.parent.center, with the correct result. Yet, setting the TextInputs center or position to these coords is not centering it, it doesn't move... what am I doing wrong?
py file:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
class TimeTabler(Widget):
pass
class TimerApp(App):
def build(self):
return TimeTabler()
if __name__ == "__main__":
TimerApp().run()
****kv file:****
#:kivy 1.0
BoxLayout:
orientation: 'vertical'
size: root.size
BoxLayout:
orientation: 'vertical'
Label:
text: 'TimeTabler'
BoxLayout:
TextInput:
text: '%s' % (self.parent.center) # why does this work here
size_hint: None, None
width: sp(200)
height: sp(30)
center: self.parent.center # but not here
You gave the TextInput size_hint: None, None, therefore the BoxLayout doesn't try to manually give it the right size, and it assumes the default size of 100, 100. Just delete the size_hint line to fix it.
Also, several widgets have lines like size: self.size. This is meaningless, self refers to the widget itself, and clearly the line does nothing since it just tries to set the size to what it already is.
Things would also be simpler if you made your TimeTabler inherit from BoxLayout instead of Widget. That way you wouldn't need to manually set it's child BoxLayout's size.
Edit: It looks like I misunderstood what you wanted, here's an example that uses an AnchorLayout to center the TextInput:
<TimeTabler>
BoxLayout:
orientation: 'vertical'
size: root.size
on_touch_down: print self.pos, self.size
canvas:
Color:
rgba: 0, 1, 1, .3
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
orientation: 'vertical'
size: self.size
Label:
text: 'TimeTabler'
BoxLayout:
id: bl
on_touch_down: print 'center', self.center
canvas:
Color:
rgb: 1,1,1
Line:
rectangle: self.x, self.y, self.width, self.height
AnchorLayout:
TextInput:
size_hint: None, None
text: '%s, %s' % (self.get_center_x(), self.get_center_y())
I think your problem was the BoxLayout automatically sets the position of the TextInput even when it is setting its own size. A simple way around this is to just next the TextInput in another widget, in this case an AnchorLayout that takes care of the centering for you. You could also just use a Widget and your previous mechanism of setting the TextInput center.

Categories

Resources