from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
class Ckbox(BoxLayout):
orientation = "vertical"
cc =[]
def __init__(self):
super().__init__()
for i in range(5):
self.bb = Button()
self.cq = CheckBox()
self.cc.append(self.cq)
self.add_widget(self.bb)
self.add_widget(self.cq)
class Ckboxapp(App):
def build(self):
return Ckbox()
Ckboxapp().run()
from the above code, i get this
but i want the checkbox and button to be together so i add the button variable to the add widget as self.bb.add_widget(self.cq) and got this
the second image show the checkbox attached to the button but only showing on the last button only. i want itlike in the second iamge but i want it to be on all the buttons. what can i do?
If you put each CheckBox/Button pair in another BoxLayout, I think you will get what you want. Try this:
class Ckbox(BoxLayout):
orientation = "vertical"
def __init__(self):
super().__init__()
for i in range(5):
bl = BoxLayout()
bb = Button(text='Button ' + str(i), size_hint_x=0.85)
cq = CheckBox(size_hint_x=0.15)
bl.add_widget(cq)
bl.add_widget(bb)
self.add_widget(bl)
I am new to kivy. I have chosen a background image and i want to insert a label on it. But it shows like this.
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
class MyApp(App):
def build(self):
self.box = BoxLayout()
self.img = Image(source = 'image4.png')
self.lbl = Label(text = "Total_Wealth")
self.box.add_widget(self.img)
self.box.add_widget(self.lbl)
return self.box
[![enter image description here][1]][1]MyApp().run()
The BoxLayout automatically stacks the widgets.
If you want to put Label on top of the image, you can use FloatLayout for more control on the placement of the widgets
I'm looking to make a popup on the python-side that has a dynamic height.
So far, I have this within the screens __init__ class. The kv file has another widget that called the popup on_release. Anyways, I have found that this produces a popup with very wonky formatting:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen, ScreenManager
kv = '''
ScreenManagement:
id: 'manager'
BrokenPopup:
name: 'broken'
manager: 'manager'
<BrokenPopup>:
BoxLayout:
Button:
text: 'Test'
on_release: root.p.open()
'''
class ScreenManagement(ScreenManager):
pass
class BrokenPopup(Screen):
def __init__(self, **kwargs):
super(BrokenPopup,self).__init__(**kwargs)
self.p = Popup(auto_dismiss=False, size_hint_x=.6, size_hint_y=None, title='A popup')
self.g = GridLayout(cols=1, spacing=10)
self.g.add_widget(Button(text='Test1', size_hint_y=None, height=32))
self.g.add_widget(Button(text='Test2', size_hint_y=None, height=32))
self.g.bind(minimum_height=self.g.setter('height'))
self.p.add_widget(self.g)
self.p.bind(height=self.g.setter('height')) #<- this does not work to change the popup height!
class TheApp(App):
def build(self):
return Builder.load_string(kv)
TheApp().run()
The popup size is set to fit only one widget, leaving the second button (and all others that may be included) to float beyond the confines of the popup border.
How should I change the code so that all of the widgets fit within the confines of the popup? I am trying to do that by dynamically setting the height of the popup, however that is not proving effective.
I have modified your code to do what I think you want. Basically it adds the minimum_height from the GridLayout, that is added to your Popup, to the calculated height of the title and the dividing bar. The first Button in the GridLayout now adds another Button to the GridLayout for testing.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen, ScreenManager
kv = '''
ScreenManagement:
id: 'manager'
BrokenPopup:
name: 'broken'
manager: 'manager'
<BrokenPopup>:
BoxLayout:
Button:
text: 'Test'
on_release: root.p.open()
'''
class ScreenManagement(ScreenManager):
pass
class BrokenPopup(Screen):
def __init__(self, **kwargs):
super(BrokenPopup,self).__init__(**kwargs)
self.popup_title_height = None
self.p = Popup(auto_dismiss=False, size_hint_x=.6, size_hint_y=None, title='A popup')
self.g = GridLayout(cols=1, spacing=10)
self.g.bind(minimum_height=self.fix_size)
self.g.add_widget(Button(text='Test1', size_hint_y=None, height=32, on_release=self.add_one))
self.g.add_widget(Button(text='Test2', size_hint_y=None, height=32))
self.p.add_widget(self.g)
def add_one(self, *args):
self.g.add_widget(Button(text='Another', size_hint_y=None, height=32))
def get_popup_title_height(self):
height = 0
popupGrid = self.p.children[0]
height += popupGrid.padding[1] + popupGrid.padding[3]
for child in popupGrid.children:
if isinstance(child, BoxLayout):
continue
else:
height += child.height + popupGrid.spacing[1]
self.popup_title_height = height
def fix_size(self, *args):
if self.popup_title_height is None:
self.get_popup_title_height()
self.p.height = self.g.minimum_height + self.popup_title_height
class TheApp(App):
def build(self):
return Builder.load_string(kv)
TheApp().run()
I cheated a bit by looking at the code for Popup and the style.kv file to see how the Popup is displayed. So, if any of that is changed, this may not work.
I have found a solution for my original problem that is influenced by John Anderson's answer. I'll provide a walkthrough below for how I came to this solution.
1) Here's a photo of my original problem; I needed to dynamically set the popup height based on the widgets that are assigned to it. Before finding the below solution, my popup looked like this with the code in the OP:
As you can see, the widgets go beyond the borders of the popup.
2) I found something interesting while looking inside the popup widget with the inspector tool.
python '/path/to/your/file.py' -m inspector
Using control-e, I can click widgets and inspect their attributes. I clicked the popup button and cycled through the parent widgets until I found the popup widget.
As you can see in the photo, the popup widget has one child: a grid layout. Here are the children of that grid layout:
Interestingly, the grid layout contains:
One label, with a height of 33
One line, with a height of 4
A box layout, which contains the contents of the popup
2 units of spacing between these three widgets
12 units of padding all-around; so 24 additional units to consider for the height
3) In my solution, I have hard-written the default heights of the label, the line widget, and all default popup spacing and padding. Then, I cycle through the children inside the box layout, and add their heights. I also add 10 to those children heights, as the gridlayout that contains all of these widgets uses a spacing of 10.
Solution:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import Screen, ScreenManager
kv = '''
ScreenManagement:
id: 'manager'
BrokenPopup:
name: 'broken'
manager: 'manager'
<BrokenPopup>:
BoxLayout:
Button:
text: 'Test'
on_release: root.p.open()
'''
class ScreenManagement(ScreenManager):
pass
class BrokenPopup(Screen):
def __init__(self, **kwargs):
super(BrokenPopup,self).__init__(**kwargs)
self.p = Popup(auto_dismiss=False, size_hint_x=.6, size_hint_y=None, title='A popup')
self.g = GridLayout(cols=1, spacing=10, padding=[0,10,0,-5])
self.g.bind(minimum_height=self.fix_popup_height) # <- here's the magic
self.g.add_widget(Button(text='Test1', size_hint_y=None, height=32))
self.g.add_widget(Button(text='Test2', size_hint_y=None, height=32))
self.p.add_widget(self.g)
def fix_popup_height(self, grid, *args):
# a generalized function that, when bound to minimum_height for a grid with popup widgets,
# this will set the height of the popup
height = 0
height += 33 # for popup label
height += 4 # for popup line widget
height += 24 # for popup padding
height += 2 # for spacing between main popup widgets
for child in grid.children:
height += child.height + 10 # adds 10 for the spacing between each child
grid.parent.parent.parent.height = height # sets popup height
pass
class TheApp(App):
def build(self):
return Builder.load_string(kv)
TheApp().run()
Notable changes from the OP:
Bind the minimum_height of the widget container to the fix_popup_height() function; this will trigger each time a widget is added to the popup.
Declare the fix_popup_height() within the screen class.
Here's the fixed popup:
My problem is in accessing container property of DropDown, which is a GridLayout by default and contains it's children.
Simple app:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.label import Label
class TestApp(App):
def build(self):
root = BoxLayout(orientation='vertical')
dropdown = DropDown()
for i in range(3):
dropdown.add_widget(Button(
text=str(i),
size_hint_y=None
)) # add 3 buttons to dropdown
dropdown.container.bind(spacing=8) # this line does not work
dropdown_button = Button(size_hint_y=.2, text='Open DropDown')
dropdown_button.bind(on_release=dropdown.open)
root.add_widget(dropdown_button)
root.add_widget(Label()) # empty space under button
return root
TestApp().run()
I tried using bind method for this, but there is no result. No indentation is set. I also would like to see the solution in kivy language because I want to use dp() function for setting spacing and it's not very convenient to pass the parameter to python file for this. Thanks in advance for any help.
Instead using bind you should just set the value directly:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.widget import Widget
class TestApp(App):
def build(self):
root = BoxLayout(orientation='vertical')
dropdown = DropDown()
for i in range(3):
dropdown.add_widget(Button(
text=str(i),
size_hint_y=None
)) # add 3 buttons to dropdown
dropdown.container.spacing = 10
dropdown.container.padding = (0, 10, 0, 0)
dropdown_button = Button(size_hint_y=.2, text='Open DropDown')
dropdown_button.bind(on_release=dropdown.open)
root.add_widget(dropdown_button)
root.add_widget(Widget()) # empty space under button
return root
TestApp().run()
Using a custom container class is not supported directly. You can do it like this, but its hacky and ugly:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_string('''
<MyContainer>:
# copied from kivy.uix.dropdown._grid_kv
size_hint_y: None
height: self.minimum_size[1]
cols: 1
# custom settings
spacing: 10
padding: (0, 10, 0, 0)
''')
class MyContainer(GridLayout):
pass
class TestApp(App):
def build(self):
root = BoxLayout(orientation='vertical')
container = MyContainer()
dropdown = DropDown(container=container)
super(DropDown, dropdown).add_widget(container)
dropdown.on_container(dropdown, container)
for i in range(3):
dropdown.add_widget(Button(
text=str(i),
size_hint_y=None
)) # add 3 buttons to dropdown
dropdown_button = Button(size_hint_y=.2, text='Open DropDown')
dropdown_button.bind(on_release=dropdown.open)
root.add_widget(dropdown_button)
root.add_widget(Widget()) # empty space under button
return root
TestApp().run()
So I'd say it'd more clean to make custom spacing class as a subclass of Widget to fill space between buttons:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_string('''
<DropDownSpacing>:
size_hint_y: None
height: 20
''')
class DropDownSpacing(Widget):
pass
class TestApp(App):
def build(self):
root = BoxLayout(orientation='vertical')
dropdown = DropDown()
for i in range(3):
dropdown.add_widget(DropDownSpacing())
dropdown.add_widget(Button(
text=str(i),
size_hint_y=None
)) # add 3 buttons to dropdown
dropdown_button = Button(size_hint_y=.2, text='Open DropDown')
dropdown_button.bind(on_release=dropdown.open)
root.add_widget(dropdown_button)
root.add_widget(Widget()) # empty space under button
return root
TestApp().run()
This is the same you're doing in your main BoxLayout, except I prefer to use Widget class directly instead of Label with no text.
I have these scrollable labels but I can't read the very beginning and the very end of them(the alphabet starting with 1 and the one starting with 8).
Another issue is that the scrollview starts in the center and jumps back automatically to the center when the scroll is released. It would be better to have it display the left part and let the label where I have stop to scroll.
I use python 3.6 and Kivy 1.9.2.dev0 and my code has to be in python (no .kv file or builder)
import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.label import Label
# from kivy.properties import StringProperty
from kivy.uix.scrollview import ScrollView
class Test(App):
def build(self):
layout_pop = GridLayout (cols=3)
for i in range(3):
l = Label(
text="1abcdefghijklmnopqrstuvwxyz_2abcdefghijklmnopqrstuvwxyz_3abcdefghijklmnopqrstuvwxyz_4abcdefghijklmnopqrstuvwxyz_5abcdefghijklmnopqrstuvwxyz_6abcdefghijklmnopqrstuvwxyz_7abcdefghijklmnopqrstuvwxyz_8abcdefghijklmnopqrstuvwxyz",
font_size=15,
color=(1,1,3,1),
size_hint_x= None,
width=600)
l.bind(size_hint_min_x=l.setter('width'))
scroll = ScrollView(size_hint=(None, None), size=(200, 30))
scroll.add_widget(l)
layout_pop.add_widget(scroll)
return layout_pop
Test().run()
I simply had to use l.bind(texture_size=l.setter('size')). That fixed the 2 issues.
This is the updated def function:
def build(self):
layout_pop = GridLayout (cols=3)
for i in range(3):
l = Label(
text="1abcdefghijklmnopqrstuvwxyz_2abcdefghijklmnopqrstuvwxyz_3abcdefghijklmnopqrstuvwxyz_4abcdefghijklmnopqrstuvwxyz_5abcdefghijklmnopqrstuvwxyz_6abcdefghijklmnopqrstuvwxyz_7abcdefghijklmnopqrstuvwxyz_8abcdefghijklmnopqrstuvwxyz \n1abcdefghijklmnopqrstuvwxyz_2abcdefghijklmnopqrstuvwxyz_3abcdefghijklmnopqrstuvwxyz_4abcdefghijklmnopqrstuvwxyz_5abcdefghijklmnopqrstuvwxyz_6abcdefghijklmnopqrstuvwxyz_7abcdefghijklmnopqrstuvwxyz_8abcdefghijklmnopqrstuvwxyz",
font_size=15,
color=(1,1,3,1),
size_hint_x= None)
l.bind(texture_size=l.setter('size'))
l.bind(size_hint_min_x=l.setter('width'))
scroll = ScrollView(size_hint=(None, None), size=(200, 30))
scroll.add_widget(l)
layout_pop.add_widget(scroll)
return layout_pop