Problem about kivy.uix.widget.WidgetException - python

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()

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()

Updating Created Custom KivyMD Property

I'm hoping there's a way to do this but not entirely sure. I want a way to update all of my labels after a button press. This is how I'm currently doing it which throws an error but I don't quite understand it.
At the moment I'm trying to add an id to the custom label and then call it but that doesn't work.
Python Code:
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Builder.load_file("main.kv")
def build(self):
self.theme_cls.theme_style = "Dark"
return self.screen
def button_press(self):
self.root.ids.custom_label.text = "Two"
if __name__ == '__main__':
MainApp().run()
Kivy File:
<MDLabel>
id: custom_label
text: "One"
halign: "center"
BoxLayout:
orientation: "vertical"
MDLabel
MDLabel
MDRoundFlatButton:
text: "Press"
pos_hint: {"center_x": 0.5, "center_y": 0.5}
on_release: app.button_press()
First things first you can't name a custom label, MDLabel.
restructure your code like so. Starting with your .kv file
<CustomLabel#MDLabel>
text: "One"
halign: "center"
BoxLayout:
orientation: "vertical"
CustomLabel:
id: custom_label_1
CustomLabel:
id: custom_label_2
MDRoundFlatButton:
text: "Press"
pos_hint: {"center_x": 0.5, "center_y": 0.5}
on_release: app.button_press()
The in your .py file
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Builder.load_file("main.kv")
def build(self):
self.theme_cls.theme_style = "Dark"
return self.screen
def button_press(self):
self.root.ids.custom_label_1.text = "Two"
self.root.ids.custom_label_2.text = "Two"
if __name__ == '__main__':
MainApp().run()
Hope that works. you can't have 2 widgets using the same id. so if you wanna change the the text of 2 custom labels you gonna have to reference them individually

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.

Problems with user's input in KivyMD

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()

Kivy ActionBar back Button to switch to previous screen

I am trying to run this type of code on android but when i tap mobile back button, inspite of coming to the previous screen app is closed,
i used this piece of code to stop the App for being closed:
def __init__(self, **kwargs):
super(MyApp, self).__init__(**kwargs)
Window.bind(on_keyboard=self.BackButton)
def BackButton(self, window, key, *args):
if key==27:
return True
But how to return to the previous screen. and how to use ActionPrevious Button to come at previous screen?
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MyAppGUI>:
orientation:'vertical'
id: box
ActionBar:
ActionView:
ActionPrevious:
title: 'How to go back using this button???'
with_previous: True
ScreenManager:
id: screenmanger
Screen:
name: 's1'
Button:
text: 'goto screen 2'
on_release: screenmanger.current='s2'
Screen:
name: 's2'
Button:
text: 'goto screen 3'
on_release: screenmanger.current='s3'
Screen:
name: 's3'
Button:
text: 'goto main screen'
on_release: screenmanger.current='s1'
""")
class MyAppGUI(BoxLayout):
pass
class MyApp(App):
def build(self):
return MyAppGUI()
if __name__=='__main__':
MyApp().run()
To return to the previous window using ActionPrevious button you just need to bind its on_release event to a function that uses current property to set the previous screen using the name returned by ScreenManager.previous () method or use a list that serves as a history of the windows visited. To use the Back key on Android, your code is correct in principle, at least on my device it works without problems:
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<MyAppGUI>:
sm: screenmanger
orientation:'vertical'
id: box
ActionBar:
ActionView:
ActionPrevious:
title: 'How to go back using this button???'
with_previous: True
on_release: root.set_previous_screen()
ScreenManager:
id: screenmanger
Screen:
name: 's1'
Button:
text: 'goto screen 2'
on_release:
screenmanger.transition.direction = 'right'
screenmanger.current='s2'
Screen:
name: 's2'
Button:
text: 'goto screen 3'
on_release:
screenmanger.transition.direction = 'right'
screenmanger.current='s3'
Screen:
name: 's3'
Button:
text: 'goto main screen'
on_release:
screenmanger.transition.direction = 'right'
screenmanger.current='s1'
""")
class MyAppGUI(BoxLayout):
sm = ObjectProperty()
def __init__(self, **kwargs):
super(MyAppGUI, self).__init__(**kwargs)
Window.bind(on_keyboard=self._key_handler)
def _key_handler(self, instance, key, *args):
if key is 27:
self.set_previous_screen()
return True
def set_previous_screen(self):
if self.sm.current != 's1':
self.sm.transition.direction = 'left'
self.sm.current = self.sm.previous()
class MyApp(App):
def build(self):
return MyAppGUI()
if __name__ == '__main__':
MyApp().run()

Categories