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
Related
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
I'm creating a simple Database-GUI and want to add two lines to a form. each line is a DataField-Object, and contains a label and a TextInput organised by a horizontal Boxlayout. up to here all fine.
The Form-Widget contains also a BoxLayout (Orientaton: "Vertical") organizing the two DataField-Objects by listing them up under each other, but it doesn't work...
My Question: WHY??? and how can I fix it?
dbgui3.py
from kivy import require
require("2.0.0")
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import StringProperty, ObjectProperty
class DataField(Widget):
title = StringProperty()
pass
class Form(Widget):
b = ObjectProperty()
def __init__(self, *rec, **kwargs):
super().__init__()
for df in rec:
self.b.add_widget(DataField(title=df))
class DbGui3(App):
def build(self):
return Form("hello", "world")
if __name__ == '__main__':
DbGui3().run()
dbgui3.kv
<DataField>:
text: txt.text
BoxLayout:
orientation: "horizontal"
size_hint: None, None
size: root.width, 30
canvas:
Color:
rgb: 0.2,0.2,0.2
Rectangle:
size: self.size
Label:
text: root.title
size_hint: None, 1
width: self.texture_size[0]
TextInput:
id:txt
multiline: False
on_text_validate: print("text validated:" + root.text)
<Form>:
b:b
BoxLayout:
id: b
orientation: "vertical"
size_hint: None, None
size: root.width, root.height
canvas:
Color:
rgb: 0.1,0.2,0.3
Rectangle:
size: self.size
The problem is that you are using Widget as a base class for your DataField and Form classes. The Widget class does not take any notice of things like size_hint, pos_hint, and others. It is not intended to be a Layout. I suggest you use BoxLayout as the base for your DataField and some other Layout (perhaps FloatLayout) as the base class for Form. Using this suggestion, your class definitions could be:
class DataField(BoxLayout):
title = StringProperty("example")
class Form(FloatLayout):
pass
Then modifying your kv:
<DataField>:
size_hint: 1, None
height: 30
orientation: "horizontal"
Label:
size_hint: None, None
size: self.texture_size[0]*1.3, root.height
text: root.title
TextInput:
multiline: False
on_text_validate: print(root.title)
<Form>:
BoxLayout:
orientation: "vertical"
size_hint: None, None
size: root.width/2, root.height/2
DataField:
title: "hello"
DataField:
title: "world"
I think this will do what you want.
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()
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 ....."
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.