How to remove widget in Kivy and then create again? - kivy

I am using Kivy and Kivymd.
There is a class Detail in my code where I am creating a card. I need to remove any card before creating a new card.
I have a func remove. I can use it to remove any cards, then create a new card. But I need to implement removing and creating in one func add.
My py file
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.label import MDLabel
from kivymd.uix.card import MDCard
class WindowManager(ScreenManager):
pass
class Detail(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def add(self, instance):
# self.remove()
article = self.ids.article.text
card = MDCard(size_hint=(None, None), padding=20)
my_label = MDLabel(text=article)
card.add_widget(my_label)
self.ids.box.add_widget(card)
def remove(self):
self.ids.boxlayout_1.clear_widgets()
self.add_widget(Detail())
class Container(Screen):
pass
class App(MDApp):
def build(self):
return WindowManager()
App().run()
my app.kv
<WindowManager>:
Container:
name: 'container'
Detail:
name: 'detail'
<Container>
MDRaisedButton:
pos_hint: {'center_x': .5, 'center_y': .5}
text: 'Go To Detail'
on_release: root.manager.current = 'detail'
<Detail>
BoxLayout:
id: boxlayout_1
orientation: 'vertical'
MDToolbar:
title: 'App'
Button:
id: btn
text: 'Remove Cards'
on_release: root.remove()
MDTextField:
id: article
hint_text: 'Input article'
helper_text_mode: "on_focus"
mode: 'rectangle'
MDRaisedButton:
id: me
text: 'add'
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: root.add(root)
ScrollView:
MDList:
spacing: 5
id: box

Your remove() method is removing more than just the cards. Try changing it to just remove the Widgets in the MDList, like this:
def remove(self):
self.ids.box.clear_widgets()
Then the add() method can just call remove(), like this:
def add(self, instance):
self.remove()
article = self.ids.article.text
card = MDCard(size_hint=(None, None), padding=20)
my_label = MDLabel(text=article)
card.add_widget(my_label)
self.ids.box.add_widget(card)

Related

How can I modify the title of the MDTopAppBar when a button, located in a separate class, is clicked?

How can I modify the title of the MDTopAppBar in response to a user selecting a button within the NavigationDrawer, such as updating the title to match the selected button's label (e.g. "Coffee")?
main.py
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.app import MDApp
from kivymd.uix.scrollview import MDScrollView
class MainLayout(Screen):
pass
class NavigationDrawer(MDScrollView):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class App(MDApp):
def build(self):
self.theme_cls.primary_palette = "Green"
self.theme_cls.theme_style = "Light"
kivy_layout = Builder.load_file("layout.kv")
return MainLayout()
if __name__ == "__main__":
App().run()
layout.kv
<NavigationDrawer>
MDNavigationDrawerMenu:
MDNavigationDrawerHeader:
title: "MyApp"
text: "Bottom Text"
source: "icon64.png"
spacing: "10dp"
MDNavigationDrawerDivider:
MDNavigationDrawerItem:
text: "Coffee"
icon: "coffee"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 1"
MDNavigationDrawerItem:
text: "History"
icon: "history"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 2"
MDNavigationDrawerItem:
text: "Settings"
icon: "cog"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "scr 3"
MDNavigationDrawerDivider:
<MainLayout>
MDScreen:
MDTopAppBar:
id: top
title: "Title"
pos_hint: {"top": 1}
elevation: 4
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
MDScreenManager:
id: screen_manager
MDScreen:
name: "scr 1"
MDLabel:
text: "Coffee"
halign: "center"
MDScreen:
name: "scr 2"
MDLabel:
text: "History"
halign: "center"
MDScreen:
name: "scr 3"
MDLabel:
text: "Settings"
halign: "center"
MDNavigationDrawer:
id: nav_drawer
radius: (0, 16, 16, 0)
NavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
After attempting to resolve the issue on my own through various means, including attempting to create a solution and conducting research online for potential solutions, I was unable to find a satisfactory resolution. As a result, I have determined it necessary to seek assistance through this platform.
You need to add an id to the MDNavigationDrawerHeader so you can access it from the class.
MDNavigationDrawerHeader:
id: drawer_header
title: "MyApp"
text: "Bottom Text"
source: "icon64.png"
spacing: "10dp"
Then you need to add a method to the NaviagtionDrawer class to make the update. You will access your NavDrawer Title by calling self.ids.drawer_header.title
class NavigationDrawer(MDScrollView):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
def set_title(self, title):
self.ids.drawer_header.title = title
Finally, you need to tell each button to call the set_title method that I created to actually do the update.
MDNavigationDrawerItem:
text: "Coffee"
icon: "coffee"
on_press:
root.nav_drawer.set_state("close")
root.set_title(self.text)
root.screen_manager.current = "scr 1"
To change the MDTopAppBar title add the following line to each of the on_press events:
app.change_title(self.text) # call method in main app
ie.
on_press:
root.nav_drawer.set_state("close")
root.set_title(self.text)
root.screen_manager.current = "scr 1"
app.change_title(self.text) # call method in main app
Then add the following method to your main app.
def change_title(self, title):
self.root.ids.top.title = title
ie.
def build(self):
self.theme_cls.primary_palette = "Green"
self.theme_cls.theme_style = "Light"
Builder.load_file("layout.kv")
return MainLayout()
def change_title(self, title):
self.root.ids.top.title = title

Change toolbar title when changing screens in kivymd

My program contains two screens. I wish to change the toolbar title whenever I change screens.
This is my main.py file
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.boxlayout import MDBoxLayout
from kivy.properties import ObjectProperty, StringProperty
from kivymd.uix.navigationdrawer import MDNavigationDrawer
from kivymd.uix.navigationdrawer import MDNavigationLayout
from kivy.uix.screenmanager import Screen
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager
from kivymd.uix.toolbar import MDToolbar
Window.size = (360,640)
global sm
class DashboardScreen(Screen):
def __init__(self, **kwargs):
super(DashboardScreen, self).__init__(**kwargs)
class DigitalGoldScreen(Screen):
def __init__(self, **kwargs):
super(DigitalGoldScreen, self).__init__(**kwargs)
class ContentNavigationDrawer(MDBoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
# Create the screen manager
sm = ScreenManager()
sm.add_widget(DashboardScreen(name='screen-dash'))
sm.add_widget(DigitalGoldScreen(name='screen-dgld'))
class MoneyManagerCopiedApp(MDApp):
def __init__(self, **kwargs):
super(MoneyManagerCopiedApp, self).__init__(**kwargs)
def set_toolbartitle(self, screen):
self.title = screen
return self.title
def get_toolbartitle(self):
screentitle = self.title
if screentitle == "screen-dash":
self.settitle = "Dashboard"
elif screentitle == "screen-dgld":
self.settitle = "Digital Gold"
else:
pass
return (self.settitle)
def build(self):
pass
print(sm.current)
if __name__ == "__main__":
MoneyManagerCopiedApp().run()
Here is my .kv file
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: "Digital Gold"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "screen-dgld"
OneLineListItem:
text: "Dashboard"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "screen-dash"
MDScreen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "dashboard"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
ScreenManager:
id: screen_manager
DashboardScreen:
DigitalGoldScreen:
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
<DashboardScreen>:
name: 'screen-dash'
MDRectangleFlatButton:
text: "You're in dashboard screen"
pos_hint: {'center_x':0.5,'center_y':0.6}
on_press: root.manager.current = 'screen-dgld'
<DigitalGoldScreen>:
name: 'screen-dgld'
MDLabel:
text: "You're in digigold screen"
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'screen-dash'
I found somewhere something related to binding a callback function but I didn't understand that. How do I create such a function and place it inside the toolbar property of MDToolbar in .kv file?
You can just include the change in your on_press for the Button that changes the Screen. Like this:
<DashboardScreen>:
name: 'screen-dash'
MDRectangleFlatButton:
text: "You're in dashboard screen"
pos_hint: {'center_x':0.5,'center_y':0.6}
on_press:
root.manager.current = 'screen-dgld'
app.root.ids.toolbar.title = 'Digital Gold'
<DigitalGoldScreen>:
name: 'screen-dgld'
MDLabel:
text: "You're in digigold screen"
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press:
root.manager.current = 'screen-dash'
app.root.ids.toolbar.title = 'Dashboard'

Expansion panel is not expanding in kivymd

My MDExpansionPanel in kivymd is not expanding I have written that code in .kv file
here's my .py file
from kivymd.app import MDApp
class TtApp(MDApp):
pass
TtApp().run()
and here's my .kv file
Screen:
BoxLayout:
orientation: "vertical"
MDExpansionPanelTwoLine:
text: ''
secondary_text: 'email: xxxxx#gmail.com'
IconLeftWidget:
icon: "email"
TwoLineAvatarListItem:
text: ''
secondary_text: 'to: #gmail.com'
IconLeftWidget:
icon: "email"
I got it to work, I think mirroring the way you had it set up.
It seems that you have to build MDExpansionPanel via Python rather than KV language. Also, you have to define content and panel_cls for the MDExpansionPanel object.
Here is you new main.py:
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelTwoLine
from kivymd import images_path
class Content(MDBoxLayout):
pass
class TtApp(MDApp):
def on_start(self):
self.root.ids.box.add_widget(
MDExpansionPanel(
icon=f"{images_path}folder.png", # need an email .png here
content=Content(),
panel_cls=MDExpansionPanelTwoLine(
text='',
secondary_text='email: xxxxx#gmail.com',
)
)
)
TtApp().run()
Here is your new tt.kv:
<Content>:
adaptive_height: True
TwoLineAvatarListItem:
text: ''
secondary_text: 'to: #gmail.com'
IconLeftWidget:
icon: "email"
Screen:
BoxLayout:
orientation: "vertical"
id: box

Reusing code in kivy

how can I reuse such code in kv lang?
<Search_type_panel>:
max1_optn: max1
max2_optn: max2
both_optn: both
BoxLayout:
padding: "10dp"
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
CheckBox:
id: max1
active: True
group: 'search_type'
Label:
text: "[ref=max1]max1[/ref]"
size_hint_x: "10"
markup: True
on_ref_press: root.setstatus(max1)
CheckBox:
id: max2
active: False
group: 'search_type'
Label:
text: "[ref=max2]max2[/ref]"
size_hint_x: "10"
markup: True
on_ref_press: root.setstatus(max2)
CheckBox:
id: both
active: False
group: 'search_type'
Label:
text: "[ref=both]both[/both]"
size_hint_x: "10"
markup: True
on_ref_press: root.setstatus(max2)
As you can see Label and Checkbox can be a grouped composite panel and I have to just supply different parameters to each one, so on the long run maintenance is simple enough, but How do I pass new parameters to these guys? While I know I can group them as:
<custom>:
CheckBox:
id: both
active: False
group: 'search_type'
Label:
text: "[ref=both]both[/both]"
size_hint_x: "10"
markup: True
on_ref_press: root.setstatus(max2)
I don't really know how to pass new parameters to the widgets and reuse the kv lang code.
Reuse code in kv File
Dynamic Classes
Since each CheckBox has a different active value (True or False), it is not recommended to create a dynamic class containing both CheckBox and Label because it is difficult to reference the CheckBox and assign different id and active value to each of them when it is instantiated as a children.
Snippets
<Custom>:
CheckBox:
active: False
group: 'search_type'
Label:
size_hint_x: "10"
markup: True
on_ref_press: app.root.setstatus(self)
Example
The example below creates two dynamice classes for CheckBox and Label respectively.
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
class Search_type_panel(BoxLayout):
max1_optn = ObjectProperty(None)
max2_optn = ObjectProperty(None)
both_optn = ObjectProperty(None)
def setstatus(self, label):
print("\nsetstatus:")
for key in label.refs:
print("\tUser clicked on refs =", key)
class TestApp(App):
def build(self):
return Search_type_panel()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
<CustomCheckBox#CheckBox>:
active: False
group: 'search_type'
<CustomLabel#Label>:
size_hint_x: "10"
markup: True
on_ref_press: app.root.setstatus(self)
<Search_type_panel>:
max1_optn: max1
max2_optn: max2
both_optn: both
BoxLayout:
padding: "10dp"
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
CustomCheckBox:
id: max1
active: True
CustomLabel:
text: "[ref=max1]max1[/ref]"
CustomCheckBox:
id: max2
CustomLabel:
text: "[ref=max2]max2[/ref]"
CustomCheckBox:
id: both
CustomLabel:
text: "[ref=both]both[/ref]"
Output

Cannot access class variable outside class - kivy

Anytime i try to run my app i get error.
Error: AttributeError: 'NoneType' object has no attribute 'text'
See code snippet below
class Login(Screen):
email_obj = ObjectProperty()
password_obj = ObjectProperty()
conn = lite.connect("database/nairamanager.db")
cursor = conn.cursor()
def init(self, **kwargs):
super().__init__(**kwargs)
# Display an alert on login validation
def login_alert(self):
p = LoginAlert()
p.open()
# Get user from database
def login_nm_user(self):
self.cursor.execute("SELECT * FROM user_tb WHERE email_db = ? AND password_db = ?;", (self.email_obj.text, self.password_obj.text))
if self.cursor.fetchall():
self.manager.current = "news_feed_screen"
else:
return self.login_alert()
self.conn.commit()
# self.conn.close
# Get user email
def get_user_email(self):
self.cursor.execute("SELECT * FROM user_tb WHERE email_db = ?;", (self.email_obj.text))
self.user_email = self.cursor.fetchone()[0]
self.conn.commit()
print(self.user_email)
login = Login()
login.get_user_email()
print(login.user_email)
.kv code
Below is the Login class in the kivy lang file
<Login>:
email_obj: email_text
password_obj: password_text
GridLayout:
size: root.size
rows: 2
rows_minimum: {0:0,1:root.height*.7}
spacing: 50
GridLayout:
rows: 2
row_default_minimum: True
rows_minimum: {0:root.height*.1,1:root.height*.01}
BoxLayout:
orientation: "horizontal"
BackButton:
text: "L"
size_hint: (.1,1)
halign: "center"
markup: True
on_press:
root.manager.current = "login_signup_screen"
root.manager.transition.direction = "left"
TopLabel:
text: "[b]Lets get you logged in...[/b]"
size_hint: (.9,1)
BorderLabel:
# Login form
BoxLayout:
orientation: "vertical"
spacing: 20
UniTextInput:
id: email_text
hint_text: "Email"
size_hint: (.6,1)
pos_hint: {"center_x":.5}
UniTextInput:
id: password_text
hint_text: "Password"
password: True
size_hint: (.6,1)
pos_hint: {"center_x":.5}
GreenButton
id: main_login_button
text: "Login"
size_hint: (.6,1)
pos_hint: {"center_x":.5}
on_press:
root.login_nm_user()
root.get_user_email()
# Fake widget for spacing
Widget:
Widget:
Widget:
Please, how do i fix this?
To share a variable to all screens, you can make it an attribute of the screenmanager.
Like this:
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang import Builder
from kivy.properties import StringProperty
class MyScreenManager(ScreenManager):
user_email = StringProperty()
class Login(Screen):
def login(self, user, pw):
# login
# if pass
self.manager.user_email = user
self.manager.current = "screen2"
root = Builder.load_string('''
MyScreenManager:
Login:
name: "login"
id: login
BoxLayout:
orientation: 'vertical'
TextInput:
id: email_input
TextInput:
id: password_input
Button:
text: "Login"
on_release: login.login(email_input.text, password_input.text)
Screen:
name: "screen2"
BoxLayout:
orientation: "vertical"
Label:
text: "Wellcome {}!".format(root.user_email)
Button:
text: "Go to screen 3"
on_release: root.current = "screen3"
Screen:
name: "screen3"
BoxLayout:
orientation: "vertical"
Label:
text: "Wellcome {}!".format(root.user_email)
Button:
text: "Go back to screen 2"
on_release: root.current = "screen2"
''')
class MyApp(App):
def build(self):
return root
MyApp().run()
That said, I see you define a init method. If you really want to overite the __init__, remember the underscores.
In this particular example, you dont really do anything in the init. super().__init__() gets executed anyways when you create a new Screen

Resources