Problems with user's input in KivyMD - python

I'm having some problems while trying to get user's input and use it on another screen. First of all, on the first screen, there is a TextField and a Button, when you click on the button, it takes you to another screen with a MDLabel and another button. My intention is to get the user's name using TextField on the first screen and then put it as a text in my MDLabel, on the second screen, like Hello, (user's name)
Here's my code:
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
screen_helper = """
ScreenManager:
MenuScreen:
ProfileScreen:
<MenuScreen>:
name: 'menu'
input_name: input_name
MDTextField:
id: input_name
hint_text: 'Digite seu nome ou apelido'
pos_hint:{'center_x':0.5,'center_y':0.7}
color_mode:'custom'
line_color_focus: 0.43, 0.82, 0.83, 0.6
MDRectangleFlatButton:
text: 'Entrar'
pos_hint: {'center_x':0.5,'center_y':0.4}
on_press: root.manager.current = 'profile'
<ProfileScreen>:
name: 'profile'
MDLabel:
id: label_welcome
text: "Hello,"
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'menu'
"""
class MenuScreen(Screen):
pass
class ProfileScreen(Screen):
pass
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(ProfileScreen(name='profile'))
class DemoApp(MDApp):
def build(self):
screen = Builder.load_string(screen_helper)
return screen
DemoApp().run()

You have to pair the id(s) to object properties on root widget.
It might not be the most efficient way, but it's working for me.
I changed your code below, major changes actually:
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty #<--- Object Property
screen_helper = """
MainScreenManager: # <--- changes here
label_: label_welcome
input_name: input_name
MenuScreen:
name: 'menu'
MDTextField:
id: input_name
hint_text: 'Digite seu nome ou apelido'
pos_hint:{'center_x':0.5,'center_y':0.7}
color_mode:'custom'
line_color_focus: 0.43, 0.82, 0.83, 0.6
MDRectangleFlatButton:
text: 'Entrar'
pos_hint: {'center_x':0.5,'center_y':0.4}
on_press:
root.ButtonFunction()
ProfileScreen:
name: 'profile'
MDLabel:
id: label_welcome
text: "Hello,"
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.current = 'menu'
"""
class MainScreenManager(ScreenManager): #This gonna be the root widget
label_ = ObjectProperty(None)
input_name = ObjectProperty(None)
def ButtonFunction(self):
self.current = 'profile'
self.label_.text = f'Hello, {self.input_name.text}'
class MenuScreen(Screen):
pass
class ProfileScreen(Screen):
pass
# Create the screen manager
sm = MainScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(ProfileScreen(name='profile'))
class DemoApp(MDApp):
def build(self):
screen = Builder.load_string(screen_helper)
return screen
DemoApp().run()

Related

Not able to save data instantly after clicking save button- kivy

I am trying to write in a text file after getting an input through TextInput in kivy. The problem is the data gets saved only after i close the app but i want the data to get saved as soon as I leave the specific screen and go to another screen. I am trying to input in add_staff and get it saved in the app through save() function.
import kivymd
import kivy
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
Builder.load_string('''
<MenuScreen>:
nm:nm_input
BoxLayout:
TextInput:
id:nm_input
Button:
text: 'Add New Staff'
on_press: root.manager.current = 'add_staff'
Button:
text: 'View Staff Profile'
Button:
text: 'Salary report'
<Add_new_staff>:
nam: str(name_input)
job: job_input
GridLayout:
cols: 2
Label:
text: 'Name'
TextInput:
id: name_input
multiline: False
Label:
text: 'Job'
TextInput:
id: job_input
Label:
text: 'Salary'
TextInput:
Label:
text: 'Date of Joining'
TextInput:
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
Button:
text: 'Save'
on_press: root.save(name_input.text, job_input.text);root.manager.current = 'menu'
''')
class MenuScreen(Screen):
pass
class Add_new_staff(Screen):
def save(self, name, job):
fob = open('testi.txt','a')
fob.write(name + "\n")
fob.write(job)
fob.close()
class TestApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(Add_new_staff(name='add_staff'))
return sm
def lo(self,nm):
f=open('log.txt','a')
f.write(nm)
f.close()
if __name__ == '__main__':
TestApp().run()

Error while Storing data in a JSON file in KivyMD Python

I tried to store username taken from user in a .json file. But it dosen't work properly. After the user repeatedly enters the username, it is randomly stored in a json file. Please solve the problem with this code.
Sometimes the .json file is created randomly. But not
at the first time.
.kv file
screen_helper = """
ScreenManager:
SetUpWindow:
Home:
<SetUpWindow>:
name: 'settingup'
MDTextField:
id:username_text_fied
pos_hint: {'center_x':0.5,'center_y':0.45}
size_hint: (0.7,0.1)
hint_text : 'Name your Studio'
helper_text: 'Required'
helper_text_mode: 'on_focus'
icon_right: 'account'
icon_right_color: app.theme_cls.primary_light
required : True
MDRaisedButton:
id:disabled_button
disabled: True
text:"Next"
md_bg_color:app.theme_cls.primary_color
pos_hint: {'center_x':0.9,'center_y':0.08}
font_style: 'Button'
ripple_rad_default : 40
ripple_duration_out : 1
md_bg_color: app.theme_cls.primary_dark
on_press:
root.manager.current = 'home'
root.manager.transition.direction = 'left'
<Home>:
name: 'home'
"""
.py file
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.app import MDApp
from kivy.storage.jsonstore import JsonStore
class SetUpWindow(Screen):
pass
sm = ScreenManager()
sm.add_widget(SetUpWindow(name='settingup'))
class Mode(MDApp):
def build(self):
screen = Screen()
self.screen = Builder.load_string(screen_helper)
screen.add_widget(self.screen)
self.username_text = self.screen.get_screen('settingup').ids.username_text_fied.text
return screen
self.store.put('UserInfo', name=self.username_text)
self.username_changer()
def username_changer(self):
self.screen.get_screen('home').ids.bottomappbar.title = f"Studio {self.store.get('UserInfo')['name']}"
def on_start(self):
self.store = JsonStore("std_profile.json")
try:
if self.store.get('UserInfo')['name'] != "":
self.username_changer()
self.screen.get_screen('home').manager.current = 'home'
except KeyError:
self.screen.get_screen('settingup').manager.current = 'settingup'
Mode().run()
Here is a modified version of your code that does what I think you want:
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen
from kivymd.app import MDApp
from kivy.storage.jsonstore import JsonStore
screen_helper = """
ScreenManager:
SetUpWindow:
Home:
<SetUpWindow>:
name: 'settingup'
MDTextField:
id:username_text_fied
pos_hint: {'center_x':0.5,'center_y':0.45}
size_hint: (0.7,0.1)
hint_text : 'Name your Studio'
helper_text: 'Required'
helper_text_mode: 'on_focus'
icon_right: 'account'
icon_right_color: app.theme_cls.primary_light
required : True
on_text:
disabled_button.disabled = False # enable next button
MDRaisedButton:
id:disabled_button
disabled: True
text:"Next"
md_bg_color:app.theme_cls.primary_color
pos_hint: {'center_x':0.9,'center_y':0.08}
font_style: 'Button'
ripple_rad_default : 40
ripple_duration_out : 1
md_bg_color: app.theme_cls.primary_dark
on_press:
app.save_user_name() # save user name to Json
root.manager.current = 'home'
root.manager.transition.direction = 'left'
<Home>:
name: 'home'
MDBottomAppBar:
MDToolbar:
id: bottomappbar
title: "Title"
icon: "git"
type: "bottom"
left_action_items: [["menu", lambda x: x]]
mode: "free-end"
"""
class SetUpWindow(Screen):
pass
class Home(Screen):
pass
class Mode(MDApp):
def build(self):
return Builder.load_string(screen_helper)
def save_user_name(self): # added method to actually save user name to JSon
self.username_text = self.root.get_screen('settingup').ids.username_text_fied.text
self.store.put('UserInfo', name=self.username_text)
self.username_changer()
def username_changer(self):
self.root.get_screen('home').ids.bottomappbar.title = f"Studio {self.store.get('UserInfo')['name']}"
def on_start(self):
self.store = JsonStore("std_profile.json")
try:
if self.store.get('UserInfo')['name'] != "":
self.username_changer()
self.root.get_screen('home').manager.current = 'home'
except KeyError as e:
self.root.get_screen('settingup').manager.current = 'settingup'
Mode().run()
Note the change in the build() method and the elimination of the other code that was building ScreenManager and SetUpWindow. References to self.screen have been replaced with self.root. The Next button now saves the user name.

How do I get KivyMD Navigation Drawer to call another app in a separate .py file?

I know how to write an app with kivy and I understand how to make a KivyMD app that has a navigation drawer. I can make the apps run individually but how do I combine the two?
I am new to coding and I am stuck here. Below are the codes for two mock-up apps for training purposes only. The first is a very simple app called "+5 Game" that allows the user to click a button to add 5 to the total which is then displayed in the GUI.
The second is a KivyMD app with a navigation drawer that contains two buttons. One to go to the home screen and one to go to the "+5 Game"
How do I link the button in the navigation draw to the +5 game?
And what do I do about the imports at the top of the file?
This is the code for the +5 Game:
from kivymd.app import MDApp
from kivymd.uix.screen import Screen
from kivymd.uix.label import MDLabel
from kivymd.uix.button import MDRectangleFlatButton
class ClickToAdd(MDApp):
def build(self):
self.screen = Screen()
self.num = 0
self.display_number = MDLabel(text="0", font_style="H1", halign="center",
pos_hint={'center_x': 0.5, 'center_y': 0.8})
self.add5_BTN = MDRectangleFlatButton(text="+5", pos_hint={'center_x': 0.5, 'center_y': 0.65},
on_release=self.add5_CMD)
self.screen.add_widget(self.display_number)
self.screen.add_widget(self.add5_BTN)
return self.screen
def add5_CMD(self, *args):
self.num = self.num + 5
self.display_number.text = str(self.num)
ClickToAdd().run()
This is the main GUI of the KivyMD app with a navigation drawer and a screen manager:
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
KV = '''
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: "Home"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "home"
OneLineListItem:
text: "Play +5 Game"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "plus5game"
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "Navigation Drawer Test"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "home"
MDLabel:
text: "Home Screen Stuff Goes Here"
halign: "center"
Screen:
name: "plus5game"
MDLabel:
text: "+5 Game Goes here"
halign: "center"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
'''
class ContentNavigationDrawer(BoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class TestNavigationDrawer(MDApp):
def build(self):
return Builder.load_string(KV)
TestNavigationDrawer().run()
You cannot run one App inside another App, but you can use the guts of another App inside the first. In your case, you can redefine the the Plus5 App like this:
from kivymd.app import MDApp
from kivymd.uix.screen import Screen
from kivymd.uix.label import MDLabel
from kivymd.uix.button import MDRectangleFlatButton
class Plus5Screen(Screen):
def __init__(self, **kwargs):
super(Plus5Screen, self).__init__(**kwargs)
self.num = 0
self.display_number = MDLabel(text="0", font_style="H1", halign="center",
pos_hint={'center_x': 0.5, 'center_y': 0.8})
self.add5_BTN = MDRectangleFlatButton(text="+5", pos_hint={'center_x': 0.5, 'center_y': 0.65},
on_release=self.add5_CMD)
self.add_widget(self.display_number)
self.add_widget(self.add5_BTN)
def add5_CMD(self, *args):
self.num = self.num + 5
self.display_number.text = str(self.num)
class ClickToAdd(MDApp):
def build(self):
self.screen = Plus5Screen()
return self.screen
if __name__=='__main__':
ClickToAdd().run()
and save this is a file named plus5.py.
Then in you main App, you can just reference this in the 'kv':
KV = '''
#:import Plus5Screen plus5 # import the guts of the Plus5 game
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: "Home"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "home"
OneLineListItem:
text: "Play +5 Game"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "plus5game"
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "Navigation Drawer Test"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "home"
MDLabel:
text: "Home Screen Stuff Goes Here"
halign: "center"
Plus5Screen: # this creates the guts of the Plu5 game
name: "plus5game"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
'''

How to refer to another class method in kivy

I'm trying to make an app. On button click on the bottom right of the screen there appears a dialog window(popup). On "Done" click the popup window closes (close_dialog method), and a new List Item is expected to appear.
Unfortunately the error occurs on "Done" click:
AttributeError: 'DialogContent' object has no attribute 'get_screen'
Could you please tell me why does the error occur and how can I fix it?
I suppose that it is caused by the fact that DialogContent class inherits from BoxLayout (not from Screen) but I don't know how to fix it.
Code .py:
from kivy.lang import Builder
from kivy.core.window import Window
from kivymd.app import MDApp
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
from kivymd.uix.textfield import MDTextField
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.list import TwoLineAvatarListItem
Window.size = (288, 511)
class GroupScreen(Screen):
pass
class DialogContent(BoxLayout):
pass
class MainScreen(Screen):
dialog = None
def show_dialog(self, *args):
'''
Create group creation popup
'''
if not self.dialog:
self.dialog = MDDialog(
title="Create new group",
type="custom",
content_cls=DialogContent(),
auto_dismiss=False
)
self.dialog.open()
def close_dialog(self, *args):
'''
Close popup on Done click
'''
self.dialog.dismiss()
self.new_window()
def new_window(self, *args):
'''
Create new group button
'''
mylist = TwoLineAvatarListItem(text = self.dialog.content_cls.textfield.text,
secondary_text = "1,2,3...")
self.mdlist.add_widget(mylist)
class test2App(MDApp):
def build(self):
sm = ScreenManager()
sm.add_widget(MainScreen(name='main'))
sm.add_widget(GroupScreen(name='group'))
scroll = ScrollView()
return sm
if __name__ == '__main__':
test2App().run()
Code .kv:
ScreenManager:
MainScreen:
GroupScreen:
<DialogContent>:
textfield: textfield
orientation: "vertical"
spacing: "12dp"
size_hint_y: None
height: "120dp"
MDTextField:
id: textfield
hint_text: "Group name"
MDFlatButton:
id: btn1
text: "Done"
text_color: self.theme_cls.primary_color
on_release: root.get_screen['main'].close_dialog()
<MainScreen>:
name: 'main'
mdlist: mdlist
FloatLayout:
size_hint: 1, 0.89
ScrollView:
MDList:
id: mdlist
MDFloatingActionButton:
pos_hint: {'right': 0.95, 'y': 0.05}
icon: "android"
theme_text_color: "Custom"
text_color: app.theme_cls.primary_color
on_release:
root.show_dialog()
<GroupScreen>:
name: 'group'
MDLabel:
text: 'Welcome'
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x': 0.5, 'center_y': 0.3}
on_release: root.manager.current = 'main'
Change:
on_release: root.get_screen['main'].close_dialog()
to:
on_release: app.root.get_screen('main').close_dialog()
The app.root gets you a reference to the root widget of the app, which is the ScreenManager. Then you can use get_screen('main') to access the main Screen and call its close_dialog() method.

Problem about kivy.uix.widget.WidgetException

I am new to python and kivymd and I am trying to develop a program for data entry. However, when I create a drop-down menu for a selection, an error occurred and I can't update the value of the text field after I select an item.
Here is the python code:
from kivymd.app import MDApp
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.properties import ObjectProperty
from kivymd.uix.menu import MDDropdownMenu
class AMRMenu(Screen):
def GIbutton(self):
sm.current = 'GI'
class GIWindow(Screen):
weather = ObjectProperty(None)
menu_weather_items = [{"text":"Sunny"},{"text":"Cloudy"},{"text":"Raining"}]
menu_FeedResponse_items=[{"text":"High"},{"text":"Medium"},{"text":"Low"}]
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.menu = MDDropdownMenu(
items=self.menu_weather_items,
width_mult=4,
caller = self.weather,
callback=self.set_item)
def set_item(self, instance):
def set_item(interval):
self.weather.text = instance.text
self.menu.dismiss()
Clock.schedule_once(set_item, 0.5)
class WindowManager(ScreenManager):
pass
sm = WindowManager()
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
kv = Builder.load_file("FR.kv")
def build(self):
screens = [AMRMenu(name = "menu"), GIWindow(name = "GI")]
for screen in screens:
sm.add_widget(screen)
sm.current = "menu"
return sm
if __name__ == "__main__":
MainApp().run()
And here is the kv. file:
<AMRMenu>:
name:"menu"
BoxLayout:
orientation: "vertical"
MDToolbar:
title: "Main Menu"
MDList:
OneLineListItem:
text: "General Information"
on_press: root.GIbutton()
OneLineListItem:
text: "Water Temperature"
OneLineListItem
text: "Feeding Amount"
OneLineListItem:
text: "Residue and Feeding response"
OneLineListItem:
text: "Dead fish"
OneLineListItem:
text: "Sell/Use"
<GIWindow>
name: "GI"
weather: weather
ScrollView:
id: screen
MDBoxLayout:
orientation: 'vertical'
adaptive_height: True
MDTextField:
id: weather
pos_hint: {'center_x': .5, 'center_y': .5}
hint_text: "Weather"
icon_right: "arrow-down-drop-circle-outline"
input_filter: lambda text, from_undo: text[:5 - len(self.text)]
on_focus: root.menu.open()
Here is the error message:
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/kivy/core/window/__init__.py", line 1297, in add_widget
(widget, widget.parent)
kivy.uix.widget.WidgetException: Cannot add <kivymd.uix.menu.MDDropdownMenu object at 0x7fd2fa31a6e0> to window, it already has a parent <kivy.core.window.window_sdl2.WindowSDL object at 0x7fd2f65826e0>
I don't know why I made this happen. It helps a lot if someone figures it out. Thank you for reading this question
The problem is that your kv line:
on_focus: root.menu.open()
is opening the menu every time the focus changes. So, it tries to open the menu even when the focus becomes false. An easy fix is to just open the menu when focus is True`:
on_focus: if self.focus: root.menu.open()

Categories