I'm having issues with refreshing a BoxLayout of widgets by removing them and then rebuilding the widgets based on the list 'Groups'. When on the EditDeviceGroups screen, the 'create' button should add an element to the list and forward the user to the GroupTemplateScreen, which it does.
The issue occurs when the user uses the back button to return to the EditDeviceGroups screen. At that point, I thought the on_enter method would refresh the widgets to include the new element, but the list shows no changes.
I assume it's some sort of issue with classes and instances, but I cant quite see around this one as this is my first real attempt with Kivy.
soundclout.py
from kivy.app import App
from kivy.properties import ObjectProperty, ListProperty, StringProperty
from kivy.uix.listview import ListItemButton
from kivy.uix.screenmanager import ScreenManager, Screen, WipeTransition
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button,Label
from kivy.graphics import Color,Rectangle,InstructionGroup
class GroupTemplateScreen(Screen):
def remove(self):
pass
class HomeScreen(Screen):
skipBuild = 'build_timeline_screen_6'
#skips build option if already timeline is already built
def skip_build_screen(self,value):
if value is 1:
print('HomeScreen.skip_build_screen')
self.skipBuild = 'edit_timeline_screen_7'
class EditDeviceGroupsScreen(Screen):
#Groups = [GroupNo,null]
Groups = [[1,10],[2,20]]
def on_enter(self):
self.ids.glayout2.clear_widgets()
for i in xrange(0,len(EditDeviceGroupsScreen().Groups)):
##THISPRINT##
print(str(EditDeviceGroupsScreen().Groups[i][0]))
addedGroup = BoxLayout(size_hint_y=None,height='120sp',orientation='horizontal')
addedButton=Button(text="Group " + str(EditDeviceGroupsScreen().Groups[i][0]) + " Settings",font_size=25)
addedGroup.add_widget(addedButton)
self.ids.glayout2.add_widget(addedGroup)
#Removes all widget on leaving to prevent the creation of duplicate widgets
def nav_to_group(self):
self.manager.current = 'edit_group_behaviour_screen_9'
def create_group(self):
base = 1
for i in xrange(0,len(EditDeviceGroupsScreen().Groups)):
if base < EditDeviceGroupsScreen().Groups[i][0]:
base = EditDeviceGroupsScreen().Groups[i][0]
EditDeviceGroupsScreen().Groups.append([base+1,(base+1)*10])
#manages screens
class Manager(ScreenManager):
home_screen = ObjectProperty()
edit_device_groups_screen = ObjectProperty()
group_template_screen= ObjectProperty()
def update(self):
self.connected_device_list._trigger_reset_populate()
self.current_screen.update()
class SoundCloutApp(App):
def build(self):
return Manager(transition=WipeTransition())
if __name__=='__main__':
SoundCloutApp().run()
soundclout.kv
#: kivy 1.10.0
#: import hex kivy.utils.get_color_from_hex
#: import main soundclout
#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#: import ListItemButton kivy.uix.listview.ListItemButton
#: import ScrollView kivy.uix.scrollview
<GroupTemplateScreen>:
rows: 2
spacing: 10
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size_hint_y: None
height: 50
spacing: 10
pos: root.x, (root.height-50)
# Make the background for the toolbar blue
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
on_press: root.manager.current = 'homescreen_1'
background_normal: 'icons/home.png'
size_hint_x: None
width:50
BoxLayout:
size_hint_y: None
height: 500
spacing: 50
padding: 20
pos: root.x, (root.height-560)
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size_hint_y: None
orientation: "horizontal"
height: 50
spacing: 10
padding: 0
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
size_hint_x: None
size_hint_y: None
height: 50
width:100
text: 'Back'
on_release: root.manager.current = 'edit_device_groups_screen_5'
Button:
size_hint_x: None
size_hint_y: None
height: 50
width:100
text: 'Remove'
#have to change first argument, for now assume switch is on
on_press: root.remove()
on_release:root.manager.current = 'edit_device_groups_screen_5'
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#ffffff')
Rectangle:
pos: self.pos
size: self.size
#Devices connected
BoxLayout:
orientation: "horizontal"
canvas:
Color:
rgba: hex('#98a6b3')
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'Name:'
font_size: 24
#TODO
Label:
text: 'Group 1'
font_size: 24
#Devices connected
BoxLayout:
orientation: "horizontal"
canvas:
Color:
rgba: hex('#98a6b3')
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'Devices:'
font_size: 24
#TODO
Label:
text: '1,2,3,4'
font_size: 24
<HomeScreen>:
rows: 2
spacing: 10
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size_hint_y: None
height: 50
spacing: 10
pos: root.x, (root.height-50)
# Make the background for the toolbar blue
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
on_press: root.manager.current = 'homescreen_1'
background_normal: 'icons/home.png'
size_hint_x: None
width:50
BoxLayout:
size_hint_y: None
height: 500
spacing: 50
padding: 50
pos: root.x, (root.height-560)
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
text: "Start"
font_size: 20
Button:
text: "Device Tester"
font_size: 20
Button:
text: "Connect Devices"
font_size: 20
Button:
text: "Edit Device Groups"
font_size: 20
on_press: root.manager.current = 'edit_device_groups_screen_5'
Button:
text: "Edit Group Behavior"
font_size: 20
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Label:
text: "Connected Devices"
font_size: 30
<EditDeviceGroupsScreen>:
rows: 2
spacing: 10
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
#ToolBar
BoxLayout:
size_hint_y: None
height: 50
spacing: 10
pos: root.x, (root.height-50)
# Make the background for the toolbar blue
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
on_press: root.manager.current = 'homescreen_1'
background_normal: 'icons/home.png'
size_hint_x: None
width:50
BoxLayout:
size_hint_y: None
height: 500
spacing: 50
padding: 20
pos: root.x, (root.height-560)
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
size_hint_x: None
size_hint_y: None
height: 50
size: self.size
text: 'create group'
on_press: main.EditDeviceGroupsScreen().create_group()
on_release: root.manager.current = 'group_template_screen_11'
#Groups holder
BoxLayout:
orientation: "vertical"
spacing: 5
padding: 5
canvas:
Color:
rgba: hex('#ffffff')
Rectangle:
pos: self.pos
size: self.size
#Adds Scrollability
ScrollView:
do_scroll_x:False
do_scroll_Y:True
BoxLayout:
id: glayout2
orientation: 'vertical'
spacing: 5
size_hint_y: None
height: self.minimum_height
<Manager>:
id: screen_manager
home_screen: home_screen
edit_device_groups_screen: edit_device_groups_screen
group_template_screen: group_template_screen
HomeScreen:
id: home_screen
name: 'homescreen_1'
manager: screen_manager
EditDeviceGroupsScreen:
id: edit_device_groups_screen
name: 'edit_device_groups_screen_5'
manager: screen_manager
GroupTemplateScreen:
id: group_template_screen
name: 'group_template_screen_11'
manager: screen_manager
#Problem#
The EditDeviceGroups screen is not refreshed with the newly added widgets because when the Back button is pressed, another instance of EditDeviceGroupScreen was instantiated.
#Solution#
With the following changes, when the user clicked the back button to return to the EditDeviceGroups screen, the on_enter method refreshed the widgets to include the new element added. Please refer to the example and output for details.
##soundclout.kv##
Deleted #:import main soundclout
Replaced main.EditDeviceGroupsScreen().create_group() with root.create_group()
##soundclout.py##
Replace all occurrence of EditDeviceGroupsScreen(). with self.
#Example#
##main.py##
from kivy.app import App
from kivy.properties import ObjectProperty, ListProperty, StringProperty
from kivy.uix.listview import ListItemButton
from kivy.uix.screenmanager import ScreenManager, Screen, WipeTransition
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button, Label
from kivy.graphics import Color, Rectangle, InstructionGroup
class GroupTemplateScreen(Screen):
def remove(self):
pass
class HomeScreen(Screen):
skipBuild = 'build_timeline_screen_6'
# skips build option if already timeline is already built
def skip_build_screen(self, value):
if value is 1:
print('HomeScreen.skip_build_screen')
self.skipBuild = 'edit_timeline_screen_7'
class EditDeviceGroupsScreen(Screen):
# Groups = [GroupNo, null]
Groups = [[1, 10], [2, 20]]
def on_enter(self):
print("EditDeviceGroupsScreen.on_enter:")
self.ids.glayout2.clear_widgets()
for i in range(0, len(self.Groups)):
##THISPRINT##
print(str(self.Groups[i][0]))
addedGroup = BoxLayout(size_hint_y=None, height='120sp', orientation='horizontal')
addedButton = Button(text="Group " + str(self.Groups[i][0]) + " Settings", font_size=25)
addedGroup.add_widget(addedButton)
self.ids.glayout2.add_widget(addedGroup)
# Removes all widget on leaving to prevent the creation of duplicate widgets
def nav_to_group(self):
self.manager.current = 'edit_group_behaviour_screen_9'
def create_group(self):
base = 1
for i in range(0, len(self.Groups)):
if base < self.Groups[i][0]:
base = self.Groups[i][0]
self.Groups.append([base+1, (base+1)*10])
# manages screens
class Manager(ScreenManager):
home_screen = ObjectProperty()
edit_device_groups_screen = ObjectProperty()
group_template_screen= ObjectProperty()
def update(self):
self.connected_device_list._trigger_reset_populate()
self.current_screen.update()
class SoundCloutApp(App):
def build(self):
return Manager(transition=WipeTransition())
if __name__ == '__main__':
SoundCloutApp().run()
##soundclout.kv##
#:kivy 1.10.0
#:import hex kivy.utils.get_color_from_hex
#:import ListAdapter kivy.adapters.listadapter.ListAdapter
#:import ListItemButton kivy.uix.listview.ListItemButton
#:import ScrollView kivy.uix.scrollview
<GroupTemplateScreen>:
rows: 2
spacing: 10
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size_hint_y: None
height: 50
spacing: 10
pos: root.x, (root.height-50)
# Make the background for the toolbar blue
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
on_press: root.manager.current = 'homescreen_1'
background_normal: 'icons/home.png'
size_hint_x: None
width:50
BoxLayout:
size_hint_y: None
height: 500
spacing: 50
padding: 20
pos: root.x, (root.height-560)
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size_hint_y: None
orientation: "horizontal"
height: 50
spacing: 10
padding: 0
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
size_hint_x: None
size_hint_y: None
height: 50
width:100
text: 'Back'
on_release: root.manager.current = 'edit_device_groups_screen_5'
Button:
size_hint_x: None
size_hint_y: None
height: 50
width:100
text: 'Remove'
#have to change first argument, for now assume switch is on
on_press: root.remove()
on_release:root.manager.current = 'edit_device_groups_screen_5'
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#ffffff')
Rectangle:
pos: self.pos
size: self.size
#Devices connected
BoxLayout:
orientation: "horizontal"
canvas:
Color:
rgba: hex('#98a6b3')
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'Name:'
font_size: 24
#TODO
Label:
text: 'Group 1'
font_size: 24
#Devices connected
BoxLayout:
orientation: "horizontal"
canvas:
Color:
rgba: hex('#98a6b3')
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'Devices:'
font_size: 24
#TODO
Label:
text: '1,2,3,4'
font_size: 24
<HomeScreen>:
rows: 2
spacing: 10
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
size_hint_y: None
height: 50
spacing: 10
pos: root.x, (root.height-50)
# Make the background for the toolbar blue
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
on_press: root.manager.current = 'homescreen_1'
background_normal: 'icons/home.png'
size_hint_x: None
width:50
BoxLayout:
size_hint_y: None
height: 500
spacing: 50
padding: 50
pos: root.x, (root.height-560)
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
text: "Start"
font_size: 20
Button:
text: "Device Tester"
font_size: 20
Button:
text: "Connect Devices"
font_size: 20
Button:
text: "Edit Device Groups"
font_size: 20
on_press: root.manager.current = 'edit_device_groups_screen_5'
Button:
text: "Edit Group Behavior"
font_size: 20
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Label:
text: "Connected Devices"
font_size: 30
<EditDeviceGroupsScreen>:
rows: 2
spacing: 10
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
#ToolBar
BoxLayout:
size_hint_y: None
height: 50
spacing: 10
pos: root.x, (root.height-50)
# Make the background for the toolbar blue
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
on_press: root.manager.current = 'homescreen_1'
background_normal: 'icons/home.png'
size_hint_x: None
width:50
BoxLayout:
size_hint_y: None
height: 500
spacing: 50
padding: 20
pos: root.x, (root.height-560)
BoxLayout:
orientation: "vertical"
spacing: 10
padding: 10
canvas:
Color:
rgba: hex('#0099cc')
Rectangle:
pos: self.pos
size: self.size
Button:
size_hint_x: None
size_hint_y: None
height: 50
size: self.size
text: 'create group'
on_press: root.create_group()
on_release: root.manager.current = 'group_template_screen_11'
#Groups holder
BoxLayout:
orientation: "vertical"
spacing: 5
padding: 5
canvas:
Color:
rgba: hex('#ffffff')
Rectangle:
pos: self.pos
size: self.size
#Adds Scrollability
ScrollView:
do_scroll_x:False
do_scroll_Y:True
BoxLayout:
id: glayout2
orientation: 'vertical'
spacing: 5
size_hint_y: None
height: self.minimum_height
<Manager>:
id: screen_manager
home_screen: home_screen
edit_device_groups_screen: edit_device_groups_screen
group_template_screen: group_template_screen
HomeScreen:
id: home_screen
name: 'homescreen_1'
manager: screen_manager
EditDeviceGroupsScreen:
id: edit_device_groups_screen
name: 'edit_device_groups_screen_5'
manager: screen_manager
GroupTemplateScreen:
id: group_template_screen
name: 'group_template_screen_11'
manager: screen_manager
#Output#
Related
I try to build a simple layout of widgets.
The BoxLayout includes three widgets: label and two anchorlayouts.
BoxLayout has a spacing parameter that is specified as "vertical".
So ... I don't see any spaces between the widgets inside the boxlayout.
enter image description here
There is kv:
<WellcomeScreen>:
wellcome_label: wellcome_user
BoxLayout:
orientation: 'vertical'
pos: self.parent.pos
size: self.parent.size
padding: [30, 30]
spacing: [10,10]
canvas.before:
Rectangle:
source: 'Data\game background.png'
pos: self.pos
size: self.size
ShadowLabel:
canvas.before:
Color:
rgba: .5,1,.7,1
Rectangle:
pos: self.pos
size: self.size
size_hint: (1, .6)
id: wellcome_user
font_size: self.height/16
text_size: self.width-10, None
markup: True
color: 'purple'
halign: "center"
AnchorLayout:
size_hint: (1,.1)
canvas.before:
Color:
rgba: 1,.5,0,1
Rectangle:
pos: self.pos
size: self.size
Button:
pos_hint: {"center_x":0.5,"center_y":0.5}
size_hint: (None,None)
size: (self.parent.height-5,self.parent.height-5)
background_normal: "Data\Button_sound.png"
AnchorLayout:
size_hint: (1,.3)
canvas.before:
Color:
rgba: .2,.7,.7,1
Rectangle:
pos: self.pos
size: self.size
Button:
canvas.before:
Rectangle:
source: "Data\Button_start.png"
pos: self.pos
size: self.size
pos_hint: {"center_x":0.5,"center_y":0.5}
size_hint: (None, None)
size: (self.parent.height/4*3, self.parent.height/4*3)
background_color: (1,1,1,0)
font_size: self.height/4
text_size: self.width, None
text: "START"
color: "yellow"
bold: True
halign: 'center'
on_press:
root.manager.transition.direction = 'left'
root.manager.current = 'login'
According to the documentation, the spacing is a number, not a list of numbers. Try changing:
spacing: [10,10]
to:
spacing: 10
Am having a hard time trying to align my widgets to the top of window in kivy, here is the pictorial represenation. My expectation is that adding an empty widget at the bottom would push the widgets to the top of the screen but it only gets to the middle of the screen. Any guidance will be helpful.
Layout
.kv file
#:import utils kivy.utils
#: import Window kivy.core.window.Window
<FlatButton#ButtonBehavior+Label>:
font_size: 14
<PosWindow>:
id: main_win
orientation: 'vertical'
canvas.before:
Color:
rgb: utils.get_color_from_hex('#ffffff')
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
spacing: 0
orientation: 'vertical'
#THE BEGINNNING OF THE TOP BAR
BoxLayout:
size_hint_y: None
height: 30
Label:
text: 'HEADER POSITION'
size_hint_x: .6
bold: True
color: (1,1,1,1)
canvas.before:
Color:
rgb: utils.get_color_from_hex('#5B2437')
Rectangle:
size: self.size
pos: self.pos
Label:
text: 'The Name'
size_hint_x: .4
height: 30
bold: True
color: (1,1,1,1)
canvas.before:
Color:
rgb: utils.get_color_from_hex('#5B2437')
Rectangle:
size: self.size
pos: self.pos
# THE END OF THE TOP BAR
#THE BEGINNING OF THE SECOND WAVE FULL
BoxLayout:
id: current
size_hint_y: None
height: 50
canvas.before:
Color:
rgba: (1,1,1, 1)
Rectangle:
size: self.size
pos: self.pos
Button:
text: 'Current Item:'
background_normal: ''
background_color: (.06, .32, .32, 1)
size_hint_x: .4
Button:
id: cur_product
text: 'Default Product'
background_normal: ''
background_color: (.06, .4, .4, 1)
Button:
id: cur_price
text: '0.00'
background_normal: ''
background_color: (.06, .65, .65, 1)
size_hint_x: .2
Widget: #To place everything at the top of the screen
#THE END OF THE SECOND WAVE FULL
#SEARCH FOR A PRODUCT
BoxLayout:
id: current
orientation: 'horizontal'
size_hint_y: None
height: 50
canvas.before:
Color:
rgba: (1,1,1, 1)
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
size_hint_x: .4
id: searchBox
canvas.before:
Color:
rgba: (.06, .75, .35, 1)
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
size_hint_x: .6
id: previewBox
canvas.before:
Color:
rgba: (.06, .35, .85, 1)
Rectangle:
size: self.size
pos: self.pos
Widget:
I found that as a little, I had to remove the first BoxLayout first the empty widgets except for the last boxlayout.
#:import utils kivy.utils
#: import Window kivy.core.window.Window
<FlatButton#ButtonBehavior+Label>:
font_size: 14
:
id: main_win
orientation: 'vertical'
canvas.before:
Color:
rgb: utils.get_color_from_hex('#ffffff')
Rectangle:
size: self.size
pos: self.pos
#THE BEGINNNING OF THE TOP BAR
BoxLayout:
size_hint_y: None
height: 30
Label:
text: 'HEADER POSITION'
size_hint_x: .6
bold: True
color: (1,1,1,1)
canvas.before:
Color:
rgb: utils.get_color_from_hex('#5B2437')
Rectangle:
size: self.size
pos: self.pos
Label:
text: 'The Name'
size_hint_x: .4
height: 30
bold: True
color: (1,1,1,1)
canvas.before:
Color:
rgb: utils.get_color_from_hex('#5B2437')
Rectangle:
size: self.size
pos: self.pos
#THE BEGINNING OF THE SECOND WAVE FULL
BoxLayout:
id: current
size_hint_y: None
height: 50
canvas.before:
Color:
rgba: (1,1,1, 1)
Rectangle:
size: self.size
pos: self.pos
Button:
text: 'Current Item:'
background_normal: ''
background_color: (.06, .32, .32, 1)
size_hint_x: .4
Button:
id: cur_product
text: 'Default Product'
background_normal: ''
background_color: (.06, .4, .4, 1)
Button:
id: cur_price
text: '0.00'
background_normal: ''
background_color: (.06, .65, .65, 1)
size_hint_x: .2
#THE END OF THE SECOND WAVE FULL
#SEARCH FOR A PRODUCT
BoxLayout:
id: current
orientation: 'horizontal'
size_hint_y: None
height: 50
canvas.before:
Color:
rgba: (1,1,1, 1)
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
size_hint_x: .4
id: searchBox
canvas.before:
Color:
rgba: (.06, .75, .35, 1)
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
size_hint_x: .6
id: previewBox
canvas.before:
Color:
rgba: (.06, .35, .85, 1)
Rectangle:
size: self.size
pos: self.pos
Widget:
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]
How can I dynamically add a button to a particular screen of my .kv file's screenmanager? The problem is, the buttons that get created show up on all screens.
I have a program I am working on that will allow the user to input there particular conditions and calculate ACFM. I am currently trying to set up a file screen that allows the user to create multiple files. I am trying to allow one main button called "create new file" creates more buttons on the first screen. The problem is, the buttons that get created show up on all screens.
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
import Functions as f1
from kivy.uix.button import Button
Window.size = (300,625)
window_x = Window.width
window_y = Window.height
Builder.load_file("main.kv")
class counter:
def __init__(self):
self.count = 2
def increase(self):
self.count += 1
b = counter()
class Calc(FloatLayout):
def testbutton(self,instance):
btn = Button(text = "testing123", color = (1,1,1,1),
size_hint =(.5,.05),
pos = (0,(window_y-(window_y*(.07+(b.count*.05)))))
)
b.increase()
self.add_widget(btn)
# define the multiplication of a function
def calculate(self, instance):
f = float(self.flow.text)
p = float(self.pressure.text)
t = float(self.temperature.text)
sg = float(self.specific_gravity.text)
self.acfm.text = str(f) #str(f1.ACFM(f,p,t,sg)) I commented this out for the purpose of posting this in my questions.
class test(App):
def build(self):
return Calc()
if __name__=="__main__":
test().run()
main.kv
<calc>:
flow: _flow
pressure: _pressure
temperature: _temperature
specific_gravity: _specific_gravity
acfm: _acfm
FloatLayout:
ScreenManager:
id: screen_manager
size: self.size
Screen:
name: "filescreen"
FloatLayout:
canvas:
Color:
rgb: 1,1,1
Rectangle:
size: self.width, self.height
pos: self.pos
AnchorLayout:
anchor_x: 'center'
anchor_y: 'top'
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.size
pos: self.pos
Color:
rgb: 0,0,0
Line:
width: 1
rectangle: self.x,self.y,self.width,self.height
size_hint: 1,.07
FilescreenButton:
text: "Create New File"
pos: 0,root.height-(root.height*(.07+.05))
on_press: root.testbutton(*args)
Screen:
name: "solverscreen"
GridLayout:
cols:1
size_hint: 1,1
ScrollView:
FloatLayout:
canvas:
Color:
rgb: 1,1,1
Rectangle:
size: self.width, self.height
pos: self.pos
size_hint: 1,1.5
GridLayout:
cols:1
spacing: 1
pos_hint: {"top": (1),"left": 1}
size_hint: .4, None
row_default_height: root.height*.05
Label:
text: "Inputs"
color: 0,0,0,1
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
CustomLabel:
text: "Flow"
CustomLabel:
text: "Pressure"
CustomLabel:
text: "Temperature"
CustomLabel:
text: "Specific Gravity"
Label:
text: "Results"
color: 0,0,0,1
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
Button:
text: "Calculate"
on_press: root.calculate(*args)
GridLayout:
cols:1
spacing: 1
pos_hint: {"top": (1),"right": .7}
size_hint: .3, None
row_default_height: root.height*.05
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
TextInput:
text: "0"
id: _flow
TextInput:
text: "0"
id: _pressure
TextInput:
text: "0"
id: _temperature
TextInput:
text: ".6"
id: _specific_gravity
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
Label:
id: _acfm
color: 0,0,0,1
canvas.before:
Color:
rgba: .7,.7,.7,1
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: 1,1,1
Line:
width: 1
rectangle: self.x,self.y,self.width,self.height
GridLayout:
cols:1
spacing: 1
pos_hint: {"top": (1),"right": 1}
size_hint: .3, None
row_default_height: root.height*.05
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height/2
pos: self.pos
CustomButton:
text: "MMSCFD"
CustomButton:
text: "PSIG"
CustomButton:
text: "F"
CustomButton:
text: "SG"
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
CustomButton:
text: "ACFM"
AnchorLayout:
anchor_x: 'left'
anchor_y: 'bottom'
GridLayout:
cols: 4
size_hint: 1,.05
canvas:
Color:
rgb: 1,1,1
Rectangle:
size: self.size
pos: self.pos
Button:
text: "Files"
on_press: screen_manager.current = 'filescreen'
Button:
text: "Solver"
on_press: screen_manager.current = 'solverscreen'
<CustomLabel#Label>:
#font_size: (((self.width*5)+(self.height))/6)*.12
canvas.before:
Color:
rgb: 0,0,0
Rectangle:
size: self.size
pos: self.pos
<CustomButton#Button>:
background_color: .25,.25,.25,1
canvas.before:
Color:
rgba: .25,.25,.25,1
Rectangle:
size: self.size
pos: self.pos
Line:
width: .09
rectangle: self.x,self.y,self.width,self.height
<FilescreenButton#Button>:
size_hint: .5,.05
color: 0,0,0,1
background_color: 1,1,1,0
canvas:
Color:
rgb: 0,0,0
Line:
width: 1
rectangle: self.x,self.y,self.width,self.height
I am not getting any error messages. I just want the dynamically created buttons from the .py file to only go to one screen of the .kv file.
You are currently adding it to the Calc FloatLayout which is the root and each screen is a child of this root. If you want to add it to just one screen, than you need to call the add_widgets function of that specific screen.
One option is via an id
change kv file like below
Screen:
id: id_solverscreen
name: "solverscreen"
change py file like below
self.ids.id_solverscreen.add_widget(btn)
python file
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
Window.size = (300,625)
window_x = Window.width
window_y = Window.height
Builder.load_file("main.kv")
class counter:
def __init__(self):
self.count = 2
def increase(self):
self.count += 1
b = counter()
class Calc(FloatLayout):
def testbutton(self,instance):
btn = Button(text = "testing123", color = (1,1,1,1),
size_hint =(.5,.05),
pos = (0,(window_y-(window_y*(.07+(b.count*.05)))))
)
b.increase()
self.ids.id_solverscreen.add_widget(btn)
# define the multiplication of a function
def calculate(self, instance):
f = float(self.flow.text)
p = float(self.pressure.text)
t = float(self.temperature.text)
sg = float(self.specific_gravity.text)
self.acfm.text = str(f) #str(f1.ACFM(f,p,t,sg)) I commented this out for the purpose of posting this in my questions.
class test(App):
def build(self):
return Calc()
if __name__=="__main__":
test().run()
kv file
<calc>:
flow: _flow
pressure: _pressure
temperature: _temperature
specific_gravity: _specific_gravity
acfm: _acfm
FloatLayout:
ScreenManager:
id: screen_manager
size: self.size
Screen:
name: "filescreen"
FloatLayout:
canvas:
Color:
rgb: 1,1,1
Rectangle:
size: self.width, self.height
pos: self.pos
AnchorLayout:
anchor_x: 'center'
anchor_y: 'top'
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.size
pos: self.pos
Color:
rgb: 0,0,0
Line:
width: 1
rectangle: self.x,self.y,self.width,self.height
size_hint: 1,.07
FilescreenButton:
text: "Create New File"
pos: 0,root.height-(root.height*(.07+.05))
on_press: root.testbutton(*args)
Screen:
id: id_solverscreen
name: "solverscreen"
GridLayout:
cols:1
size_hint: 1,1
ScrollView:
FloatLayout:
canvas:
Color:
rgb: 1,1,1
Rectangle:
size: self.width, self.height
pos: self.pos
size_hint: 1,1.5
GridLayout:
cols:1
spacing: 1
pos_hint: {"top": (1),"left": 1}
size_hint: .4, None
row_default_height: root.height*.05
Label:
text: "Inputs"
color: 0,0,0,1
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
CustomLabel:
text: "Flow"
CustomLabel:
text: "Pressure"
CustomLabel:
text: "Temperature"
CustomLabel:
text: "Specific Gravity"
Label:
text: "Results"
color: 0,0,0,1
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
Button:
text: "Calculate"
on_press: root.calculate(*args)
GridLayout:
cols:1
spacing: 1
pos_hint: {"top": (1),"right": .7}
size_hint: .3, None
row_default_height: root.height*.05
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
TextInput:
text: "0"
id: _flow
TextInput:
text: "0"
id: _pressure
TextInput:
text: "0"
id: _temperature
TextInput:
text: ".6"
id: _specific_gravity
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
Label:
id: _acfm
color: 0,0,0,1
canvas.before:
Color:
rgba: .7,.7,.7,1
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: 1,1,1
Line:
width: 1
rectangle: self.x,self.y,self.width,self.height
GridLayout:
cols:1
spacing: 1
pos_hint: {"top": (1),"right": 1}
size_hint: .3, None
row_default_height: root.height*.05
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height/2
pos: self.pos
CustomButton:
text: "MMSCFD"
CustomButton:
text: "PSIG"
CustomButton:
text: "F"
CustomButton:
text: "SG"
Label:
canvas.before:
Color:
rgb: 1,1,1
Rectangle:
size: self.width,self.height
pos: self.pos
CustomButton:
text: "ACFM"
AnchorLayout:
anchor_x: 'left'
anchor_y: 'bottom'
GridLayout:
cols: 4
size_hint: 1,.05
canvas:
Color:
rgb: 1,1,1
Rectangle:
size: self.size
pos: self.pos
Button:
text: "Files"
on_press: screen_manager.current = 'filescreen'
Button:
text: "Solver"
on_press: screen_manager.current = 'solverscreen'
<CustomLabel#Label>:
#font_size: (((self.width*5)+(self.height))/6)*.12
canvas.before:
Color:
rgb: 0,0,0
Rectangle:
size: self.size
pos: self.pos
<CustomButton#Button>:
background_color: .25,.25,.25,1
canvas.before:
Color:
rgba: .25,.25,.25,1
Rectangle:
size: self.size
pos: self.pos
Line:
width: .09
rectangle: self.x,self.y,self.width,self.height
<FilescreenButton#Button>:
size_hint: .5,.05
color: 0,0,0,1
background_color: 1,1,1,0
canvas:
Color:
rgb: 0,0,0
Line:
width: 1
rectangle: self.x,self.y,self.width,self.height
I have two file test.py and test.kv .
i run test.py then shows show button.
When i click on show button then def abc call.Can someone tell me how to show array in dynamic label and value(Item1=5000.Item2=1000).
Item1 5000
Item2 1000
I am using array
arr = ({'Item1': 5000},{'Item2': 1000})
test.py
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty, NumericProperty
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (600, 600)
class Invoice(Screen):
def __init__(self, **kwargs):
super(Invoice, self).__init__(**kwargs)
def abc(self):
#fetching from database
arr = ({'Item1': 5000},{'Item2': 1000})
print(arr)
class Test(App):
def build(self):
self.root = Builder.load_file('test.kv')
return self.root
if __name__ == '__main__':
Test().run()
test.kv
<Button#Button>:
font_size: 15
font_name: 'Verdana'
size_hint_y:None
height: 30
<Label#Label>:
font_size: 15
font_name: 'Verdana'
size_hint_y:None
height: 30
Invoice:
BoxLayout:
orientation: "vertical"
padding : 15, 15
BoxLayout:
orientation: "vertical"
padding : 5, 5
size_hint: .6, None
pos_hint: {'x': .18,}
BoxLayout:
orientation: "horizontal"
padding : 5, 5
spacing: 10, 10
size: 800, 40
size_hint: 1, None
Button:
text: "Show"
size_hint_x: .05
spacing_x: 30
on_press:root.abc()
BoxLayout:
orientation: "horizontal"
size_hint: 1, 1
BoxLayout:
orientation: "vertical"
size_hint: .5, 1
padding : 0, 15
spacing: 10, 10
size: 500, 30
Button:
text: "Invoice"
text_size: self.size
halign: 'center'
valign: 'middle'
GridLayout:
cols: 2
#orientation: "horizontal"
padding : 5, 0
spacing: 10, 0
#size: 500, 30
size_hint: 1, 1
pos: self.pos
size: self.size
Label:
size_hint_x: .35
text: "Item1"
text_size: self.size
halign: 'left'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Label:
size_hint_x: .15
text: "5000"
text_size: self.size
halign: 'right'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
In your abc() method you can create labels and add them to your layout. In order to do that, I made a few change to your code. I added an id to your GridLayout and changed your custom label class to MyLabel and added it to the py file, so that I could create them in Python. Here is the modified Python file:
from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (600, 600)
class MyLabel(Label):
pass
class Invoice(Screen):
def __init__(self, **kwargs):
super(Invoice, self).__init__(**kwargs)
def abc(self):
#fetching from database
arr = ({'Item1': 5000},{'Item2': 1000})
layout = self.ids['invoices']
for invoice in arr:
for key,val in invoice.items():
lab1 = MyLabel(text=str(key),size_hint_x=.35, halign='left' )
lab2 = MyLabel(text=str(val),size_hint_x=.15, halign='right' )
layout.add_widget(lab1)
layout.add_widget(lab2)
class Test(App):
def build(self):
self.root = Builder.load_file('test.kv')
return self.root
And changes to the kv file included changing Label to MyLabel, moving as much as possible to the MyLabel class, and removing your example labels:
<Button#Button>:
font_size: 15
font_name: 'Verdana'
size_hint_y:None
height: 30
<MyLabel>:
font_size: 15
font_name: 'Verdana'
size_hint_y:None
height: 30
text_size: self.size
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Invoice:
BoxLayout:
orientation: "vertical"
padding : 15, 15
BoxLayout:
orientation: "vertical"
padding : 5, 5
size_hint: .6, None
pos_hint: {'x': .18,}
BoxLayout:
orientation: "horizontal"
padding : 5, 5
spacing: 10, 10
size: 800, 40
size_hint: 1, None
Button:
text: "Show"
size_hint_x: .05
spacing_x: 30
on_press:root.abc()
BoxLayout:
orientation: "horizontal"
size_hint: 1, 1
BoxLayout:
orientation: "vertical"
size_hint: .5, 1
padding : 0, 15
spacing: 10, 10
size: 500, 30
Button:
text: "Invoice"
text_size: self.size
halign: 'center'
valign: 'middle'
GridLayout:
id: invoices
cols: 2
#orientation: "horizontal"
padding : 5, 0
spacing: 10, 0
#size: 500, 30
size_hint: 1, 1
pos: self.pos
size: self.size
Although the option to iterate over the data and generate the widget dynamically is an option, the truth is that it is unbeatable in the long term. If you have structured information it is appropriate to use a design pattern and kivy offers to use a RecycleView for these cases, this implements the MVC pattern, so we just need to pass the data and establish a view where an appropriate adapter can be provided.
In your case it is enough to design a widget that is what is shown in each row:
<Item#GridLayout>:
cols: 2
text: "" # new property
value: 0 # new property
padding : 5, 0
spacing: 10, 0
Label:
size_hint_x: .35
text: root.text
halign: 'left'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Label:
size_hint_x: .15
text: str(root.value)
halign: 'right'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
And then replace the GridLayout with the RecycleView:
RecycleView:
id: rv
viewclass: 'Item'
RecycleBoxLayout:
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
And in the event of the button assign the data, in this case you must convert your data to a list of dictionaries where the fields will be the text and value attribute of Item:
def convert_data(data):
l = []
for item in data:
for key, value in item.items():
l.append({'text': key, 'value': value})
return l
class Invoice(Screen):
def abc(self):
#fetching from database
arr = ({'Item1': 5000},{'Item2': 1000})
# convert to [{'text': 'Item1', 'value': 5000}, {'text': 'Item2', 'value': 1000}]
self.rv.data = convert_data(arr)
Complete Code:
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
def convert_data(data):
l = []
for item in data:
for key, value in item.items():
l.append({'text': key, 'value': value})
return l
class Invoice(Screen):
def abc(self):
#fetching from database
arr = ({'Item1': 5000},{'Item2': 1000})
# convert to [{'text': 'Item1', 'value': 5000}, {'text': 'Item2', 'value': 1000}]
self.rv.data = convert_data(arr)
class MyApp(App):
def build(self):
return Builder.load_file('test.kv')
if __name__ == '__main__':
MyApp().run()
test.kv
<Button#Button>:
font_size: 15
size_hint_y:None
height: 30
<Label#Label>:
font_size: 15
size_hint_y:None
height: 30
<Item#GridLayout>:
cols: 2
text: ""
value: 0
padding : 5, 0
spacing: 10, 0
Label:
size_hint_x: .35
text: root.text
halign: 'left'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Label:
size_hint_x: .15
text: str(root.value)
halign: 'right'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Invoice:
rv: rv
BoxLayout:
orientation: "vertical"
padding : 15, 15
BoxLayout:
orientation: "vertical"
padding : 5, 5
size_hint: .6, None
pos_hint: {'x': .18,}
BoxLayout:
orientation: "horizontal"
padding : 5, 5
spacing: 10, 10
size: 800, 40
size_hint: 1, None
Button:
text: "Show"
size_hint_x: .05
spacing_x: 30
on_press:root.abc()
BoxLayout:
orientation: "horizontal"
size_hint: 1, 1
BoxLayout:
orientation: "vertical"
size_hint: .5, 1
padding : 0, 15
spacing: 10, 10
size: 500, 30
Button:
text: "Invoice"
text_size: self.size
halign: 'center'
valign: 'middle'
BoxLayout:
RecycleView:
id: rv
viewclass: 'Item'
RecycleBoxLayout:
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'