ListView in Kivy (.kv vs .py) - python

If I initialize an empty ListView in a .kv file,
<Panel>:
do_default_tab: False
size_hint_y: 0.7
TabbedPanelItem:
id: tab_1
text: 'VIEW'
ListView:
id: VIEWlist
adapter:
ListAdapter(
data=[],
args_converter = root.args_converter,
selection_mode='multiple',
cls=ListItemButton
)
how would I add data to it from the .py side?

You need to access VIEWlist.adapter.data property. An example:
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.lang import Builder
Builder.load_string('''
#:import ListItemButton kivy.uix.listview.ListItemButton
#:import ListAdapter kivy.adapters.listadapter.ListAdapter
<Panel>:
size_hint: .5, .5
pos_hint: {'center_x': .5, 'center_y': .5}
do_default_tab: False
TabbedPanelItem:
id: tab_1
text: 'VIEW'
BoxLayout:
orientation: 'vertical'
Button:
text: 'add'
on_press: root.add()
ListView:
id: VIEWlist
adapter:
ListAdapter(
data=[],
selection_mode='multiple',
cls=ListItemButton)
''')
class Panel(TabbedPanel):
def add(self, *args):
self.ids['VIEWlist'].adapter.data.append('txt')
class TabbedPanelApp(App):
def build(self):
return Panel()
if __name__ == '__main__':
TabbedPanelApp().run()

Related

Can't change location of widgets in KivyMD

I'm trying to move MDLabel and MDRectangleFlatButton to the center of the screen, but both pos_hint and halign are getting ignored.
app.kv:
ScreenManager:
MainScreen:
<MainScreen>
name: 'main_screen'
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'My App'
MDLabel:
text: 'Welcome again!'
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
style: 'H5'
MDRectangleFlatButton:
text: 'Enter'
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
app.py:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
class MainScreen(Screen):
pass
sm = ScreenManager()
sm.add_widget(MainScreen(name="main_screen"))
class MyApp(MDApp):
def build(self):
return Builder.load_file("app.kv")
MyApp().run()

Kivy Python: buttons and classes for multiple screens

I have a problem with my Kivy Python Code. I have 2 screens: 1st is to navigate to the second screen and on the 2nd screen there is a button to add text to a scrollview...navigating is working but it does not add any text to the scrollview...I think I need some help here! AttributeError: 'super' object has no attribute 'getattr'
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock, mainthread
from kivy.core.window import Window
from kivy.uix.scrollview import ScrollView
from kivy.effects.scroll import ScrollEffect
from kivy.lang import Builder
Builder.load_string("""
<MenuScreen>:
name: 'mainmenu'
BoxLayout:
spacing: 1
orientation: "vertical"
Label:
text: "MAIN MENU"
Button:
text: 'Go to Screen 2'
on_release:
root.manager.current = 'screen2'
root.manager.transition.direction = "left"
Button:
text: 'Quit'
on_release: root.manager.current = app.exit_software()
<Screen2>:
name: 'screen2'
BoxLayout:
spacing: 1
orientation: "vertical"
ScrollView:
id: scroll_view
always_overscroll: False
BoxLayout:
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Label:
id: label
text: "You can add some Text here by pressing the button"
size_hint: None, None
size: self.texture_size
Button:
text: 'Add text!'
size_hint_y: 0.1
on_release: app.add_text()
Button:
text: 'Back to main menu'
size_hint_y: 0.1
on_release:
root.manager.current = 'mainmenu'
root.manager.transition.direction = "right"
""")
# Declare both screens
class MenuScreen(Screen):
pass
class Screen2(Screen):
pass
class AddTextApp(App):
def __init__(self,**kwargs):
super().__init__(**kwargs)
def build(self):
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='mainmenu'))
sm.add_widget(Screen2(name='screen2'))
return sm
def add_text(self):
self.root.ids.label.text += f"Some new Text\n"
self.root.ids.scroll_view.scroll_y = 0
def exit_software(self):
App.get_running_app().stop()
if __name__ == '__main__':
AddTextApp().run()
Thank you very much in advance!
The error occurred because self.root.ids gets access to widgets located in the root widget of the main class. To access the secondary screen elements, you need to add it to the main class (in your case, in ScreenManager) and set its id. Also, you have a lot of imported excess, so that it is clearly visible, I advise you to use Pycharm or something like that.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
kv = """
<MenuScreen>:
name: 'mainmenu'
BoxLayout:
spacing: 1
orientation: "vertical"
Label:
text: "MAIN MENU"
Button:
text: 'Go to Screen 2'
on_release:
root.manager.current = 'screen2'
root.manager.transition.direction = "left"
Button:
text: 'Quit'
on_release: root.manager.current = app.exit_software()
<Screen2>:
name: 'screen2'
BoxLayout:
spacing: 1
orientation: "vertical"
ScrollView:
id: scroll_view
always_overscroll: False
BoxLayout:
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Label:
id: label
text: "You can add some Text here by pressing the button"
size_hint: None, None
size: self.texture_size
Button:
text: 'Add text!'
size_hint_y: 0.1
on_release: app.add_text()
Button:
text: 'Back to main menu'
size_hint_y: 0.1
on_release:
root.manager.current = 'mainmenu'
root.manager.transition.direction = "right"
ScreenManager:
MenuScreen:
id: menu_scr
Screen2:
id: scr_2
"""
class MenuScreen(Screen):
pass
class Screen2(Screen):
pass
class AddTextApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
return Builder.load_string(kv)
def add_text(self):
self.root.ids.scr_2.ids.label.text += f"Some new Text\n"
self.root.ids.scr_2.ids.scroll_view.scroll_y = 0
#staticmethod
def exit_software():
App.get_running_app().stop()
if __name__ == '__main__':
AddTextApp().run()

MDExpansion Panel - kivy.properties.DictProperty' object has no attribute 'panel_container'

I am currently building a mobile app with kivy. In one of my screens I am trying to include an MDExpansionPanel, although I have tried with a lot of different codes, and searched on the web for solutions, either I get an error, or simply the Expansion Panel is not rendered in my screen. I am using ScreenManager since I have 5 screens, with the possibility of increasing such number.
The relevant Python code is the following:
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.app import MDApp
from kivymd.theming import ThemeManager
from kivymd.toast import toast
from kivymd.uix.picker import MDDatePicker, MDTimePicker
from kivymd.uix.expansionpanel import MDExpansionPanel
from kivymd.uix.boxlayout import MDBoxLayout
class LoginWindow(Screen):
pass
class CreateAccountWindow(Screen):
pass
class MainWindow(Screen):
pass
class TravelManagerWindow(Screen):
pass
class IngActivWindow(Screen):
panel_container = ObjectProperty(None)
def on_pre_enter(self, *args):
self.add_panels()
def show_timepicker(self):
picker = MDTimePicker()
picker.bind(time=self.got_time)
picker.open()
def got_time(self, picker_widget, time):
self.text = str(time.hour) + ":" + str(time.minute)
self.focus = False
# selectedTime= self.text
print(self.text)
self.ids.tiempoActiv.text = self.text
def add_panels(self):
for i in range(5):
IngActivWindow.ids.panel_container.add_widget(
MDExpansionPanel(
icon="actividades.png",
content=MyContent(),
panel_cls=MDExpansionPanelOneLine(
text="Text",
)
)
)
class MyContent(MDBoxLayout):
pass
class WindowManager(ScreenManager):
ScreenManager().add_widget(LoginWindow(name='login'))
ScreenManager().add_widget(CreateAccountWindow(name='create'))
ScreenManager().add_widget(MainWindow(name='main'))
ScreenManager().add_widget(IngActivWindow(name='ingActiv'))
ScreenManager().add_widget(TravelManagerWindow(name='travelManager'))
class powerApp1(MDApp):
def build(self):
self.theme_cls.primary_palette = "Teal"
return WindowManager()
if __name__ == "__main__":
powerApp1().run()
The relevant kv code is the following:
(I have an image on the background and an Action Bar before the MDBoxLayout where I am trying to add the Expansion Panel)
<WindowManager>:
LoginWindow:
CreateAccountWindow:
MainWindow:
IngActivWindow:
TravelManagerWindow:
<IngActivWindow>:
name: 'ingActiv'
panel_container: panel_container
FloatLayout:
cols:1
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'ingActiv_background.png'
ActionBar:
pos_hint: {'top':1}
ActionView:
use_separator: True
ActionPrevious:
title: '---------'
with_previous: False
ActionButton:
icon: 'homeIcon.png'
on_press:
root.manager.current = 'main'
root.manager.transition.direction = 'right'
ActionGroup:
text: 'Gastos de Viaje'
mode: 'spinner'
size_hint:(None,None)
size: root.width/5,root.height/12
ActionButton:
text: 'Solicitar'
on_release:
root.manager.current = 'solicitud'
root.manager.transition.direction = 'left'
ActionButton:
text: 'Comprobar'
on_release:
root.manager.current = 'comprobar'
root.manager.transition.direction = 'left'
ActionGroup:
text: 'Opciones'
mode: 'spinner'
size_hint:(None,None)
size: root.width/5,root.height/12
ActionButton:
text: 'Ingresar Actividad'
on_release:
root.manager.current = 'ingActiv'
root.manager.transition.direction = 'left'
ActionButton:
text: 'Enviar Reporte'
ActionButton:
text: 'Cerrar Sesion'
on_release:
root.manager.current = 'login'
root.manager.transition.direction = 'down'
MDBoxLayout:
cols:1
size_hint: 1,0.6
pos_hint: {"center_x": 0.5, "center_y": 0.5}
ScrollView:
GridLayout:
id: panel_container
cols: 1
pos_hint: {"center_x": 0.5, "center_y": 0.5}
<MyContent>:
size_hint: 1, None
height: self.minimum_height
orientation: 'horizontal'
Button:
size_hint: None, None
Thanks a lot in advance for your support,
Have a great day.

How to switch screens from a dynamically created button in kivy recycleview

Problem
I have a screen (OrderScreen) that populates with buttons if there is data to be processed. I would like the user to click one of the buttons to be brought to another screen (MenuScreen) to process the data. While my intention is to populate the next screen with data from the button, I am currently just trying to get the ScreenManager to change to the next screen after a button press. I added a pass_data() method to the OrderButton and tried to trigger the screen manager there but self.manager.current and root.manager.current were throwing exceptions. Any help would be greatly appreciated.
recycleview_test.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.recycleview import RecycleView
from kivy.uix.screenmanager import ScreenManager, Screen
from random import randint
class MenuScreen(Screen):
def quit(self):
App.get_running_app.stop()
Window.close()
class OrderScreen(Screen):
pass
class OrderButton(Button):
def __init__(self, **kwargs):
super(OrderButton, self).__init__(**kwargs)
def pass_data(self):
print("button pushed")
class OrderScroll(RecycleView):
def __init__(self, **kwargs):
super(OrderScroll, self).__init__(**kwargs)
self.data = [{'text': str(f"Make {randint(10, 25)} items from package #{randint(1,4)}")} for x in range(12)]
class WindowManager(ScreenManager):
pass
class RecycleApp(App):
def build(self):
return WindowManager()
if __name__ == "__main__":
RecycleApp().run()
recycle.kv
#:import Factory kivy.factory.Factory
#: import ScreenManager kivy.uix.screenmanager.ScreenManager
#: import Screen kivy.uix.screenmanager.ScreenManager
#:import App kivy.app.App
<OrderScroll>:
viewclass: 'OrderButton'
manager: None
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
padding: 20
spacing: 10
<OrderButton>:
manager: None
font_size: 32
bold: True
on_release:
root.pass_data()
<WindowManager>:
id: screen_manager
OrderScreen:
id: order_screen
name: "OrderScreen"
manager: screen_manager
MenuScreen:
id: menu_screen
name: 'MenuScreen'
manager: screen_manager
<OrderScreen>:
BoxLayout:
orientation: "vertical"
Label:
text: "Data Buttons"
font_size: 64
size_hint_y: None
# pos_hint: {"x":0, "y":1}
height: 200
OrderScroll:
<MenuScreen>:
BoxLayout:
orientation: "vertical"
Label:
text: "Made it"
font_size: 64
FloatLayout:
Button:
text: 'Keep going'
font_size: 48
size_hint: .8,.5
pos_hint: {"center_x": .5, "center_y": .1}
FloatLayout:
Button:
text: 'Quit'
size_hint: .15,.3
pos_hint: {"center_x": .5, "center_y": .5}
on_release:
root.quit()
Try to create an object of screen manager:
class RecycleApp(App):
def build(self):
self.sm = WindowManager()
return self.sm
And then access it by:
App.get_running_app().sm.current = 'MenuScreen' # in Python
app.sm.current = 'MenuScreen' # in kv

Switching between kivy classes inside one screen

I'm looking for a way to change a part of a screen between 'DownPanel1' and 'DownPanel1' but I would like to avoide creating nex screen class 'ToAvoid'. Is it possible?
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
kv = '''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
SomeScreen:
ToAvoid:
<Menu#RelativeLayout>
id: main_menu
size_hint_x: None
width: 120
Button:
size_hint_y: None
pos: root.x, root.top - self.height
text: 'SomeScreen'
on_press: app.root.current = "SomeScreen"
<UpScreen>:
BoxLayout:
Button:
text: 'switch'
on_press: app.root.current = "ToAvoid"
<DownPanel1>:
Button:
text: 'DownPanel1'
#on_press:
<DownPanel2>:
Button:
text: 'DownPanel2'
#on_press:
<SomeScreen>:
name: 'SomeScreen'
BoxLayout:
orientation: 'horizontal'
Menu:
BoxLayout:
orientation: 'vertical'
UpScreen:
DownPanel1:
<ToAvoid>:
name: 'ToAvoid'
BoxLayout:
orientation: 'horizontal'
Menu:
BoxLayout:
orientation: 'vertical'
UpScreen:
DownPanel2:
'''
class DownPanel1(BoxLayout):
pass
class DownPanel2(BoxLayout):
pass
class UpScreen(Screen):
pass
class SomeScreen(Screen):
pass
class ToAvoid(Screen):
pass
class ScreenManagement(ScreenManager):
pass
sm = Builder.load_string(kv)
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
How about some inception? Just put another ScreenManager inside of the other.
Try this example:
from kivy.app import App
from kivy.lang import Builder
KV = """
ScreenManager:
Screen:
name: "1st"
Button:
text: "next"
on_release:
root.current = "2nd"
Screen:
name: "2nd"
BoxLayout:
Button:
text: "3rd to avoid"
on_release:
root.current = "3rd"
ScreenManager:
id: sm2
Screen:
name: "inner1"
Button:
text: "go to inner 2"
on_release:
sm2.current = "inner2"
Screen:
name: "inner2"
Label:
text: "This is inner 2!"
Screen:
name: "3rd"
Label:
text: "To Avoid!"
"""
class MyApp(App):
def build(self):
return Builder.load_string(KV)
if __name__ == "__main__":
MyApp().run()

Categories

Resources