Concurrently running a loop with a kivy window - python

I am trying to build a simple messaging app, that uses rabbitmq as its message broker and kivy as its UI. So to receive incoming messages I have a receive function that is a loop, But when I try to multi-process the app and run it, kivy seems to be opening multiple windows. Kindly advise how I can tackle this issue.
.py file
import pika
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.card import MDCard
from database import Database as D
from kivy.core.window import Window
from multiprocessing import Process
from kivymd.uix.label import MDLabel
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDRectangleFlatButton
from kivy.uix.screenmanager import ScreenManager,Screen
Window.size = (400,700)
global logged_in_user
logged_in_user = ""
def receive():
CREDENTIALS = pika.PlainCredentials('redbarker', 'Redbarker#20-21')
PARAMETERS = pika.ConnectionParameters(credentials=CREDENTIALS)
connection = pika.BlockingConnection(PARAMETERS)
channel = connection.channel()
channel.exchange_declare(exchange='system_exchange', exchange_type='topic', durable=True)
result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queue
channel.queue_bind(exchange='system_exchange', queue=queue_name, routing_key="command.#")
def callback(ch, method, properties, body):
print(body)
channel.basic_consume(queue=queue_name, on_message_callback=callback, auto_ack=True)
channel.start_consuming()
class LoginScreen(Screen):
def validate_user(self):
uname = self.ids.uname_input.text
pword = self.ids.pword_input.text
is_found = D().login(uname,pword)
self.ids.uname_input.text = ""
self.ids.pword_input.text = ""
if is_found:
logged_in_user = uname
self.parent.current = "users_screen"
class SignupScreen(Screen):
def signin(self):
uname = self.ids.uname_input.text
pword1 = self.ids.pword_input1.text
pword2 = self.ids.pword_input2.text
D().signup(uname,pword1,pword2)
self.parent.current = "login_screen"
class UsersScreen(Screen):
def to_inbox(self,text):
InboxScreen().change_header(text)
self.parent.current = 'inbox_screen'
def add_user(self,name):
container = self.ids.user_field
button = MDRectangleFlatButton(
text = name,
font_style = "H6",
text_color = (.95,.44,.49,1),
line_color = (.95,.44,.49,1),
pos_hint = {"center_x": .5, "center_y": .5},
on_press = self.to_inbox(self.text),
)
container.add_widget(button)
class CreateGroupScreen(Screen):
def add_group(self):
container = self.ids.user_field
name = self.ids.username.text
if len(name) > 0:
card = MDCard(
size_hint_y = None,
height = 50,
line_color = (.95,.44,.49,1),
radius = 10,
padding = 10
)
label = MDLabel(
halign = "left",
text = name,
theme_text_color = "Custom",
text_color = (.95,.44,.49,1)
)
card.add_widget(label)
container.add_widget(card)
self.ids.username.text = ""
def remove_all_users(self):
for child in [child for child in self.ids.user_field.children]:
self.ids.user_field.remove_widget(child)
class AddUserScreen(Screen):
users = []
def add_user(self):
for name in self.users:
pass
def append_user(self):
name = self.ids.username.text
if len(name) > 0:
container = self.ids.user_field
card = MDCard(
size_hint_y = None,
height = 50,
line_color = (.95,.44,.49,1),
radius = 10,
padding = 10
)
label = MDLabel(
halign = "left",
text = name,
theme_text_color = "Custom",
text_color = (.95,.44,.49,1)
)
card.add_widget(label)
container.add_widget(card)
self.users.append(name)
self.ids.username.text = ""
def remove_all_users(self):
for child in [child for child in self.ids.user_field.children]:
self.ids.user_field.remove_widget(child)
class InboxScreen(Screen):
def change_header(self, text):
self.ids.header.text = text
def build_widget(self,message):
container = self.ids.text_field
layout = MDBoxLayout(
size_hint_y = None,
height = 50,
padding = (10,0),
)
card = MDCard(
radius = (0,10,0,10),
padding = 10
)
place_holder = MDBoxLayout()
label = MDLabel(
halign = "right",
text = message,
font_style = "H6",
theme_text_color = "Custom",
text_color = (.95,.44,.49,1)
)
card.add_widget(label)
layout.add_widget(place_holder)
layout.add_widget(card)
container.add_widget(layout)
def send(self):
message = self.ids.message_input.text
if len(message) > 0:
CREDENTIALS = pika.PlainCredentials('redbarker', 'Redbarker#20-21')
PARAMETERS = pika.ConnectionParameters(credentials=CREDENTIALS)
consumer = self.ids.header.text
connection = pika.BlockingConnection(PARAMETERS)
channel = connection.channel()
channel.exchange_declare(exchange = 'system_exchange', exchange_type='topic', durable= True)
channel.basic_publish(exchange='system_exchange', routing_key=f"Temesgen.{consumer}", body=message)
self.build_widget(message)
class WindowManager(ScreenManager):
pass
class ChatApp(MDApp):
def build(self):
return Builder.load_file('test.kv')
#if __name__ == "__main__":
# ChatApp().run()
if __name__ == '__main__':
ChatApp().run()
p1 = Process(target=receive)
p1.start()
.kv file
WindowManager:
LoginScreen:
SignupScreen:
AddUserScreen:
UsersScreen:
CreateGroupScreen:
InboxScreen:
<LoginScreen>:
name: "login_screen"
MDBoxLayout:
orientation: "vertical"
padding: 0,1
MDCard:
size_hint_y: .1
line_color: (.95,.44,.49,1)
spacing: 20
padding: 20,10
Widget:
size_hint_y: .33
MDLabel:
halign: "center"
text: "Login"
font_style: "H4"
theme_text_color: "Custom"
text_color: (.95,.44,.49,1)
Widget:
size_hint_y: .33
MDBoxLayout:
orientation: "vertical"
size_hint_y: .4
spacing: 20
MDTextField:
id: uname_input
pos_hint: {"center_x": .5}
size_hint_x: None
width: 250
hint_text: 'Username'
mode: 'rectangle'
color: (0,1,1,1)
line_color_normal: (0,1,0,1)
text_color: (.95,.44,.49,1)
MDTextField:
id: pword_input
pos_hint: {"center_x": .5}
size_hint_x: None
width: 250
hint_text: 'Password'
mode: 'rectangle'
color: (0,1,1,1)
line_color_normal: (0,1,0,1)
text_color: (.95,.44,.49,1)
MDTextButton:
text: 'Sign Up'
underline: True
theme_text_color: "Custom"
text_color: (.95,.44,.49,1)
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
on_press: app.root.current = "signup_screen"
MDBoxLayout:
orientation: "vertical"
size_hint_y: .5
Widget:
size_hint_y: .33
MDRectangleFlatButton:
text: "Login"
font_size: 20
text_color: (.95,.44,.49,1)
line_color: (.95,.44,.49,1)
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.validate_user()
Widget:
size_hint_y: .6
<SignupScreen>:
name: "signup_screen"
MDBoxLayout:
orientation: "vertical"
padding: 0,1
MDCard:
size_hint_y: .1
line_color: (.95,.44,.49,1)
spacing: 20
padding: 20,10
MDBoxLayout:
size_hint_x: .25
MDIconButton:
size_hint_x: .25
icon: "keyboard-backspace"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: app.root.current = "login_screen"
MDLabel:
size_hint_x: .5
halign: "center"
text: "Signin"
font_style: "H4"
theme_text_color: "Custom"
text_color: (.95,.44,.49,1)
MDBoxLayout:
size_hint_x: .25
Widget:
size_hint_y: .25
MDBoxLayout:
orientation: "vertical"
size_hint_y: .4
spacing: 20
MDTextField:
id: uname_input
pos_hint: {"center_x": .5}
size_hint_x: None
width: 250
hint_text: 'Username'
mode: 'rectangle'
color: (0,1,1,1)
line_color_normal: (0,1,0,1)
text_color: (.95,.44,.49,1)
MDTextField:
id: pword_input1
pos_hint: {"center_x": .5}
size_hint_x: None
width: 250
hint_text: 'Password'
mode: 'rectangle'
color: (0,1,1,1)
line_color_normal: (0,1,0,1)
text_color: (.95,.44,.49,1)
MDTextField:
id: pword_input2
pos_hint: {"center_x": .5}
size_hint_x: None
width: 250
hint_text: 'Confirm Password'
mode: 'rectangle'
color: (0,1,1,1)
line_color_normal: (0,1,0,1)
text_color: (.95,.44,.49,1)
MDBoxLayout:
orientation: "vertical"
size_hint_y: .5
Widget:
size_hint_y: .33
MDRectangleFlatButton:
text: "Signin"
font_size: 20
text_color: (.95,.44,.49,1)
line_color: (.95,.44,.49,1)
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.signin()
Widget:
size_hint_y: .6
<UsersScreen>:
name: "users_screen"
MDBoxLayout:
orientation: "vertical"
padding: 0,1
MDCard:
size_hint_y: .1
line_color: (.95,.44,.49,1)
spacing: 20
padding: 20,10
MDIconButton:
size_hint_x: .3
icon: "logout"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: app.root.current = "login_screen"
MDLabel:
halign: "center"
text: "Users"
font_style: "H4"
theme_text_color: "Custom"
text_color: (.95,.44,.49,1)
MDBoxLayout:
size_hint_x: .4
MDIconButton:
icon: "account-plus"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: app.root.current = "add_user_screen"
MDIconButton:
icon: "account-multiple-plus"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: app.root.current = "create_group_screen"
MDBoxLayout:
orientation: "vertical"
size_hint_y: .9
ScrollView:
size: self.size
pos: self.pos
MDList:
id: user_field
spacing: 10
padding: 10
MDRectangleFlatButton:
text: 'User-1 '
font_style: "H6"
text_color: (.95,.44,.49,1)
line_color: (.95,.44,.49,1)
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.to_inbox(self.text)
MDRectangleFlatButton:
text: 'User-2 '
font_style: "H6"
text_color: (.95,.44,.49,1)
line_color: (.95,.44,.49,1)
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.to_inbox(self.text)
MDRectangleFlatButton:
text: 'User-3 '
font_style: "H6"
text_color: (.95,.44,.49,1)
line_color: (.95,.44,.49,1)
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.to_inbox(self.text)
<CreateGroupScreen>:
name: "create_group_screen"
MDBoxLayout:
orientation: "vertical"
padding: 0,1
MDCard:
size_hint_y: .1
line_color: (.95,.44,.49,1)
spacing: 20
padding: 20,10
MDIconButton:
id: remove_button
size_hint_x: .2
icon: "keyboard-backspace"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: app.root.current = "users_screen"
MDLabel:
halign: "center"
text: "Create Group"
font_style: "H4"
theme_text_color: "Custom"
text_color: (.95,.44,.49,1)
MDBoxLayout:
orientation: "vertical"
size_hint_y: .9
padding: 20
spacing: 40
MDCard:
size_hint_y: .1
line_color: (.95,.44,.49,1)
radius: 10
padding: 25,3
MDTextField:
id: username
pos_hint: {"center_x": .5, "center_y": .4}
size_hint_x: None
width: 250
hint_text: 'Username'
MDIconButton:
id: add_button
size_hint_x: .2
icon: "account-multiple-plus"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.add_user()
MDIconButton:
id: remove_button
size_hint_x: .2
icon: "account-multiple-remove"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.remove_all_users()
MDCard:
orientation: "vertical"
size_hint_y: .6
line_color: (.95,.44,.49,1)
radius: 10
MDLabel:
size_hint_y: .1
halign: "center"
text: "Add Users"
theme_text_color: "Custom"
text_color: (.95,.44,.49,1)
MDBoxLayout:
size_hint_y: .9
ScrollView:
size: self.size
pos: self.pos
MDList:
id: user_field
spacing: 10
padding: 10
MDBoxLayout:
orientation: "vertical"
size_hint_y: .3
Widget:
size_hint_y: .33
MDRectangleFlatButton:
text: "Create Group"
text_color: (.95,.44,.49,1)
line_color: (.95,.44,.49,1)
pos_hint: {"center_x": .5, "center_y": .5}
Widget:
size_hint_y: .33
<AddUserScreen>:
name: "add_user_screen"
MDBoxLayout:
orientation: "vertical"
padding: 0,1
MDCard:
size_hint_y: .1
line_color: (.95,.44,.49,1)
spacing: 20
padding: 20,10
MDIconButton:
size_hint_x: .33
icon: "keyboard-backspace"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: app.root.current = "users_screen"
MDLabel:
halign: "center"
text: "Add Users"
font_style: "H4"
theme_text_color: "Custom"
text_color: (.95,.44,.49,1)
Widget:
size_hint_x: .33
MDBoxLayout:
orientation: "vertical"
size_hint_y: .9
padding: 20
spacing: 40
MDCard:
size_hint_y: .1
line_color: (.95,.44,.49,1)
radius: 10
padding: 25,3
MDTextField:
id: username
pos_hint: {"center_x": .5, "center_y": .4}
size_hint_x: None
width: 250
hint_text: 'Username'
MDIconButton:
id: add_button
size_hint_x: .2
icon: "account-multiple-plus"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.append_user()
MDIconButton:
id: remove_button
size_hint_x: .2
icon: "account-multiple-remove"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.remove_all_users()
MDCard:
orientation: "vertical"
size_hint_y: .6
line_color: (.95,.44,.49,1)
radius: 10
MDLabel:
size_hint_y: .1
halign: "center"
text: "Add Users"
theme_text_color: "Custom"
text_color: (.95,.44,.49,1)
MDBoxLayout:
size_hint_y: .9
ScrollView:
size: self.size
pos: self.pos
MDList:
id: user_field
spacing: 10
padding: 10
MDBoxLayout:
orientation: "vertical"
size_hint_y: .3
Widget:
size_hint_y: .33
MDRectangleFlatButton:
text: "Add Users"
text_color: (.95,.44,.49,1)
line_color: (.95,.44,.49,1)
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.add_user()
Widget:
size_hint_y: .33
<InboxScreen>:
name: "inbox_screen"
MDBoxLayout:
orientation: "vertical"
padding: 0,1
MDCard:
size_hint_y: .1
line_color: (.95,.44,.49,1)
spacing: 20
padding: 20,10
MDIconButton:
size_hint_x: .2
icon: "keyboard-backspace"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: app.root.current = "users_screen"
MDLabel:
id: header
halign: "center"
text: ""
font_style: "H4"
theme_text_color: "Custom"
text_color: (.95,.44,.49,1)
MDBoxLayout:
orientation: "vertical"
size_hint_y: .9
ScrollView:
size: self.size
pos: self.pos
MDList:
id: text_field
spacing: 10
MDCard:
size_hint_y: .075
line_color: (.95,.44,.49,1)
padding: 10,0
MDTextField:
id: message_input
size_hint: None,None
height: 100
width: 325
hint_text: 'Write'
color: (0,1,1,1)
line_color_normal: (.95,.44,.49,1)
text_color: (.95,.44,.49,1)
pos_hint: {"center_x": .5, "center_y": .3}
MDIconButton:
id: send_button
icon: "send"
pos_hint: {"center_x": .5, "center_y": .5}
on_press: root.send()

What you are pretending to do is to run kivy concurrently. The best choice for async events in kivy is to use asyncio (as the oficcial documentation suggest). First at all you have to be sure your app runs from asyncio.run() instead of App.run(). To do this, you have to import asyncio and also you have to add a method to your App class. See the example below:
import asyncio
######## MAIN APP ########
class ExampleApp(App):
def build(self):
#Your app stuff here
async def kivyCoro(self): #This is the method that's gonna launch your kivy app
await self.async_run(async_lib='asyncio')
print('Kivy async app finished...')
# This func will start all the "tasks", in this case the only task is the kivy app
async def base(self):
(done, pending) = await asyncio.wait({self.kivyCoro()},
return_when='FIRST_COMPLETED')
if __name__ == '__main__':
instanceApp = ExampleApp() #You have to instanciate your App class
asyncio.run(instanciaApp.base()) # Run in async mode
With the code above you'll be able to run tour kivy app as a task (concurrently).
So far, we just have run the kivy app in async mode (concurrently). To add other task inside the Kivy running loop:
import asyncio
######## MAIN APP ########
class ExampleApp(App):
def build(self):
#Your app stuff here
async def kivyCoro(self): #This is the method that's gonna launch your kivy app
await self.async_run(async_lib='asyncio')
print('Kivy async app finished...')
async def task2InsideKivyLoop(self): #Here you declare the other task
print('Another task running inside the kivy loop')
await asyncio.sleep(1)
# This func will start all the "tasks", in this case the only task is the kivy app
async def base(self):
(done, pending) = await asyncio.wait({self.kivyCoro(), task2InsideKivyLoop()},
return_when='FIRST_COMPLETED')
if __name__ == '__main__':
instanceApp = ExampleApp() #You have to instanciate your App class
asyncio.run(instanciaApp.base()) # Run in async mode
As you can see, to add another task inside the KivyLoop we just have to declare it as a async method of the App class, then just add it to asyncio.wait()
If you want to run the other task outside the kivyLoop you have to do the fllowing:
import asyncio
######## MAIN APP ########
class ExampleApp(App):
def build(self):
#Your app stuff here
async def kivyCoro(self): #This is the method that's gonna launch your kivy app
await self.async_run(async_lib='asyncio')
print('Kivy async app finished...')
# This func will start all the "tasks", in this case the only task is the kivy app
async def base(self):
(done, pending) = await asyncio.wait({self.kivyCoro()},
return_when='FIRST_COMPLETED')
######### GLOBAL COROUTINE #######
# Here you can import functions from other python files or just declare a global func
async def GlobalTask():
for i in range(10):
print('Other concurrently global task... ',i)
await asyncio.sleep(1)
if __name__ == '__main__':
async def mainThread():
instanceApp = ExampleApp() #Instanciate your App class
a = asyncio.create_task(instanceApp.base()) #Run kivyApp as a task
b = asyncio.create_task(GlobalTask()) #Run Global func as a task
(done, pending) = await asyncio.wait({a}, return_when='FIRST_COMPLETED')
asyncio.run(mainThread())
The examples above would let you do any async task during the kv loop wheter inside the kvLoop or not

The code:
if __name__ == '__main__':
ChatApp().run()
p1 = Process(target=receive)
p1.start()
will run your ChatApp, and then after you close the ChatApp, it will run the receive process. They will not run at the same time because the ChatApp().run() will not return until the ChatApp closes. Try changing the order to:
if __name__ == '__main__':
p1 = Process(target=receive)
p1.start()
ChatApp().run()

Related

How do I turn the text that is inputed by a user into a kivyMD text box to a variable in python file?

Im relatively new to Kivy/KivyMD. I cant figure out how to take the values from the username and password text fields and turn them into variable in my python file when the sign up button is pressed.
Currently when the Signup button is clicked it just prints "clicked".
Any help would be greatly appreciated!
Below is my python file:
from kivymd.app import MDApp
from kivymd.uix.relativelayout import MDRelativeLayout
from kivy.lang import Builder
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen, SwapTransition
Builder.load_file('ALevelNEA.kv')
##Below are the classes for my different screens
class SignupScreen(Screen):
pass
class MenuScreen(Screen):
pass
class SigninScreen(Screen):
pass
## Below are the classes for my custom buttons, widgets and text fields
class PasswordField(MDRelativeLayout):
text = StringProperty()
hint_text = StringProperty()
pass
class EmailField(MDRelativeLayout):
text = StringProperty()
hint_text = StringProperty()
#email_field = ObjectProperty(None)
##Below is the code for my main app
class MainApp(MDApp):
def build(self):
sm = ScreenManager(transition= SwapTransition())
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SigninScreen(name = 'signin'))
sm.add_widget(SignupScreen(name='signup'))
return sm
def RecordCheck(self):
print("clicked")
def AddToRecord(self):
print("clicked")
if __name__ == '__main__':
MainApp().run()
Below is my KV file:
#Below are my different screens
#Below is the Menu Screen
<MenuScreen>:
Screen:
Image:
source: "Logonb.png"
size_hint_x: 0.4
size_hint_y: 0.6
allow_stretch: True
pos_hint: {"center_x": 0.5, "center_y": 0.6 }
MDFillRoundFlatButton:
id: SignIn
text: "Sign in with email"
pos_hint: {"center_x": 0.5, "center_y": 0.2}
md_bg_color: 0.3607, 0.3882, 1
size_hint: 0.225, 0
on_release:
root.manager.current = "signin"
MDRoundFlatButton:
text: "Sign up"
pos_hint: {"center_x": 0.5, "center_y": 0.12}
text_color: 0.3607, 0.3882, 1
line_color: 0, 0, 0
on_release:
root.manager.current = "signup"
MDLabel:
text: "by continuing you agree to the"
font_size: 15
halign: 'center'
pos_hint: {"center_y": 0.07}
MDTextButton:
text: "Terms and Conditions"
font_size: 15
pos_hint: {"center_x": 0.5, "center_y": 0.0525}
theme_text_color: "Custom"
text_color: 0.3607, 0.3882, 1
# Below is my sign in Screen
<SigninScreen>:
Screen:
MDIconButton:
icon: "keyboard-backspace"
pos_hint: {"center_x": 0.35, "center_y": 0.95}
on_release:
root.manager.current = "menu"
Image:
source: "LoginImage.png"
size_hint_x: 0.4
size_hint_y: 0.6
allow_stretch: True
pos_hint: {"center_x": 0.5, "center_y": 0.7 }
MDLabel:
text: "Sign in"
halign: "center"
pos_hint: {"center_x": 0.387, "center_y": 0.45}
font_size: 30
font_style: "H5"
EmailField:
hint_text: "Email ID"
pos_hint: {"center_x": 0.5, "center_y": 0.375}
size_hint_x: None
width: "250dp"
PasswordField:
size_hint_x: None
width: "250dp"
hint_text: "Password"
pos_hint: {"center_x": 0.5, "center_y": 0.3}
MDTextButton:
text: "Forgot password?"
font_size: 15
pos_hint: {"center_x": 0.62, "center_y": 0.255}
theme_text_color: "Custom"
text_color: 0.3607, 0.3882, 1
MDFillRoundFlatButton:
text: "Sign in"
pos_hint: {"center_x": 0.5, "center_y": 0.2}
md_bg_color: 0.3607, 0.3882, 1
size_hint: 0.225, 0
on_release: app.RecordCheck()
MDLabel:
text: "by continuing you agree to the"
font_size: 15
halign: 'center'
pos_hint: {"center_y": 0.15}
MDTextButton:
text: "Terms and Conditions"
font_size: 15
pos_hint: {"center_x": 0.5, "center_y": 0.1325}
theme_text_color: "Custom"
text_color: 0.3607, 0.3882, 1
#Below is the sign up screen
<SignupScreen>:
Screen:
EmailField:
hint_text: "Email"
pos_hint: {"center_x": 0.5, "center_y": 0.375}
size_hint_x: None
width: "250dp"
on_text: root.email = self.text
PasswordField:
size_hint_x: None
width: "250dp"
hint_text: "Password"
pos_hint: {"center_x": 0.5, "center_y": 0.3}
on_text: root.password = self.text
MDRoundFlatButton:
text: "Sign up"
pos_hint: {"center_x": 0.5, "center_y": 0.12}
text_color: 0.3607, 0.3882, 1
line_color: 0, 0, 0
on_release: app.AddToRecord()
#Custom buttons and text fields are located below
<PasswordField>:
size_hint_y: None
height: text_field.height
MDTextField:
id: text_field
hint_text: root.hint_text
text: root.text
password: True
icon_left: "lock"
MDIconButton:
icon: "eye-off"
pos_hint: {"center_y": .5}
pos: text_field.width - self.width + dp(8), 0
theme_text_color: "Hint"
on_release:
self.icon = "eye" if self.icon == "eye-off" else "eye-off"
text_field.password = False if text_field.password is True else True
<EmailField>:
size_hint_y: None
height: email_field.height
MDTextField:
id: email_field
hint_text: root.hint_text
text: root.text
icon_left: "email"
Add id attribute both to email and password widgets:
EmailField:
id: email_widget
PasswordField:
id: pass_widget
And you can pass any additional parameters to your method, for example replace:
on_release: app.AddToRecord()
with
on_release: app.AddToRecord(root.ids.email_widget.ids.email_field.text, root.ids.pass_widget.ids.text_field.text)
then change your AddToRecord method definition to:
def AddToRecord(self, email, passw):
print("clicked")
print(f'email: {email}, password: {passw}')
For the email, you can use the get_screen() method of ScreenManager and some ids to access the MDTextField that contains the text. First, add an id to the EmailField:
EmailField:
id: emailfield # added id
hint_text: "Email ID"
pos_hint: {"center_x": 0.5, "center_y": 0.375}
size_hint_x: None
width: "250dp"
Then use that new id and get_screen() to access the MDTextField in your RecordCheck() method:
def RecordCheck(self):
print("clicked")
signinScreen = self.root.get_screen('signin')
email = signinScreen.ids.emailfield.ids.email_field.text
print('email:', email)
Very similar approach can be used to access the password.

Change screen to MainScreen after a countdown(inactivity) in kivy MDApp

I have an App with 3 screens managed by a ScreenManager: MainScreen, ScanScreen, PinScreen. From MainScreen I have 2 buttons, 1 to go to ScanScreen, 1 to go to PinScreen. In ScanScreen I have a button to go into PinScreen and in PinScreen I have a button to go in ScanScreen. I want to go to MainScreen after some interval of time. I managed to make it works only if I go to secondary screens from MainScreen. If I go from MainScreen to ScanScreen and from ScanScreen to PinScreen then it wont work. I get this error:
line 16, in timeout
self.parent.current = 'mainScreen'
AttributeError: 'NoneType' object has no attribute 'current'
This is what I got so far:
kv file
ScreenManagement:
id:'screenManager'
MainScreen:
ScanScreen:
PinScreen:
FinalScreen:
<MainScreen>:
name: "mainScreen"
MDCard:
radius: [36, ]
size_hint: .8, .9
pos_hint: {"center_x": 0.5, "center_y": 0.5}
elevation: 10
padding: [20, 50, 20, 150]
spacing: 50
orientation: 'vertical'
MDIcon:
icon: "face-recognition"
font_size: 120
halign: 'center'
size_hint: 1, .5
MDFillRoundFlatButton:
text: "Scan Face"
font_size: 32
pos_hint: {"center_x": 0.5}
#size_hint: 1, .25
on_release:
root.manager.current = 'scanScreen'
root.manager.transition.direction = 'left'
MDFillRoundFlatButton:
text: "Access with PIN"
font_size: 32
pos_hint: {"center_x": 0.5}
#size_hint: 1, .25
on_release:
root.manager.current = 'pinScreen'
root.manager.transition.direction = 'left'
<ScanScreen>:
on_pre_enter: app.title = 'Scan Screen'
name: "scanScreen"
#on_enter: TODO timer function
MDCard:
radius: [36, ]
size_hint: .9, .95
pos_hint: {"center_x": 0.5, "center_y": 0.5}
elevation: 10
padding: [20, 50, 20, 150]
spacing: 50
orientation: 'vertical'
Image:
id: "frame_feed"
size_hint: 1, .8
MDFillRoundFlatButton:
text: "Access with PIN"
font_size: 32
pos_hint: {"center_x": 0.5}
on_release:
root.manager.current = 'pinScreen'
root.manager.transition.direction = 'left'
<PinScreen>:
on_pre_enter: app.title = 'PIN Screen'
name: "pinScreen"
MDBoxLayout:
orientation: 'vertical'
MDBoxLayout:
orientation: 'vertical'
size_hint: (1, .5)
MDIconButton:
icon: "arrow-left-drop-circle"
#icon_size: '32sp' this should be the right way but its not working
user_font_size: '32sp' #this should be deprecated but it's working
on_release:
root.manager.current = 'scanScreen'
root.manager.transition.direction = 'right'
MDIcon:
icon: "account-lock"
font_size: 60
halign: 'center'
MDLabel:
text: "Enter your passcode"
halign: "center"
font_style: "Body1"
MDGridLayout:
size_hint: (.5, 1)
pos_hint: {"center_x": .5}
cols: 6
#padding: [80,0,80,0]
#halign: "center"
MDIcon:
icon:"checkbox-blank-circle-outline"
MDIcon:
icon:"checkbox-blank-circle-outline"
MDIcon:
icon:"checkbox-blank-circle-outline"
MDIcon:
icon:"checkbox-blank-circle-outline"
MDIcon:
icon:"checkbox-blank-circle-outline"
MDIcon:
icon:"checkbox-blank-circle-outline"
Widget:
size_hint_y: None
height: 100
MDBoxLayout:
orientation: 'vertical'
size_hint: (1, .5)
MDGridLayout:
size_hint: (.5, 1)
pos_hint: {"center_x": .385}
cols:3
spacing: 20
MDFillRoundFlatButton:
text: "1"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatButton:
text: "2"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatButton:
text: "3"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatButton:
text: "4"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatButton:
text: "5"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatButton:
text: "6"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatButton:
text: "7"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatButton:
text: "8"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatButton:
text: "9"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatButton:
text: ""
md_bg_color: 1,0,0,0
MDFillRoundFlatButton:
text: "0"
text_color: .95,.953,.956,1
font_size: 20
font_style: 'H4'
md_bg_color: .4, .4, .4, 1
MDFillRoundFlatIconButton:
icon: "backspace"
padding: [40,0,0,0]
font_style: 'H4'
icon_color: .827,.827,.827,1
md_bg_color: 1,0,0,0
<FinalScreen>:
on_pre_enter: app.title = 'Final Screen'
name: "finalScreen"
python file
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.clock import Clock
Window.size = (414, 736) #This is the viewport of iPhone 6 Plus/6S Plus/7 Plus/8 Plus
import cv2
class MainScreen(Screen):
pass
class ScanScreen(Screen):
def timeout(self, *args):
self.parent.current = 'mainScreen'
def on_enter(self, *args):
Clock.schedule_once(self.timeout, 2)
class PinScreen(Screen):
def timeout(self, *args):
self.parent.current = 'mainScreen'
def on_enter(self, *args):
Clock.schedule_once(self.timeout, 2)
class FinalScreen(Screen):
def timeout(self, *args):
self.parent.current = 'mainScreen'
def on_enter(self, *args):
Clock.schedule_once(self.timeout, 2)
class ScreenManagement(ScreenManager):
pass
class MainApp(MDApp):
def __init__(self, **kwargs):
self.title = "Main Screen"
super().__init__(**kwargs)
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Green"
self.theme_cls.accent_palette = "Teal"
return Builder.load_file('main.kv')
def return_to_main_screen(self):
pass
if __name__ == "__main__":
MainApp().run()
I also tried this:
class ScanScreen(Screen):
def timeout(self, *args):
MDApp.get_running_app().root.manager.current = 'mainScreen'
#self.parent.current = 'mainScreen'
def on_enter(self, *args):
Clock.schedule_once(self.timeout, 2)
but I get this error:
MDApp.get_running_app().root.manager.current = 'mainScreen'
AttributeError: 'ScreenManagement' object has no attribute 'manager'
So to clarify, I managed to do what I wanted by using get_running_app() method and accessing the root, which in my case is the ScreenManager. I made some changes to my code so it would reset the countdown after activity. This is what I have in each of the screen that I want to return to MainScreen after 30s of inactivity:
class ScanScreen(Screen):
def timeout(self, *args):
MDApp.get_running_app().root.current = 'mainScreen'
MDApp.get_running_app().root.transition.direction = 'right'
def reset_timeout(self, *args):
Clock.unschedule(self.timeout)
Clock.schedule_once(self.timeout, 30)
def on_enter(self, *args):
Clock.schedule_once(self.timeout, 30)
def on_leave(self, *args):
Clock.unschedule(self.timeout)
def on_touch_down(self, touch):
self.reset_timeout()
return super().on_touch_down(touch)
def on_touch_move(self, touch):
self.reset_timeout()
return super().on_touch_move(touch)

How should i call a new screen?

I have a ScreenManager problem (Lack of knowledge).
I can't call another screen from a button on my RecycleView.
Note that I have 2 ScreenManager, on the first I login and I am directed to other screens of another ScreenManager.
Note in the line that contains "# <<<< HERE" the way I call the new screen.
import json
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.uix.relativelayout import RelativeLayout
from random import sample, choice
from string import ascii_lowercase
from kivy.uix.screenmanager import Screen
from kivy.uix.boxlayout import BoxLayout
sample_images = [
"https://cdn.neemo.com.br/uploads/settings_webdelivery/logo/5591/No-Image.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/5/5f/SL_Bundala_NP_asv2020-01_img08.jpg/640px-SL_Bundala_NP_asv2020-01_img08.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Palaeobranchiostoma_hamatotergum.jpg/482px-Palaeobranchiostoma_hamatotergum.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/No_image_available.svg/1024px-No_image_available.svg.png",
]
KV = '''
<ContentNavigationDrawer>:
orientation: 'vertical'
FloatLayout:
size_hint_y: None
height: "200dp"
BoxLayout:
id: box_image
x: root.x
pos_hint: {"top": 1}
FloatLayout:
# Imagem de fundo
FitImage:
pos_hint: {'center_x': .5, 'center_y': .5}
source: "./images/menu.png"
# Imagem do usuário
FitImage:
pos_hint: {'center_x': .5, 'center_y': .5}
source: "./images/user.png"
size_hint: None, None
width: dp(150)
height: dp(150)
radius: [99, 99, 99, 99]
MDLabel:
text: "Coletor de dados Postgres"
size_hint_y: None
height: self.texture_size[1]
# Define a posição do Label
x: root.x + 10
y: root.height - box_image.height + dp(10)
ScrollView:
MDList:
OneLineIconListItem:
text: "Tela Inicial"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = 'tela_item'
IconLeftWidget:
icon: "view-list"
## Tamanho do ícone user_font_size: "72sp"
OneLineIconListItem:
text: "Outra Tela"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = 'tela_signup'
IconLeftWidget:
icon: "database-export-outline"
OneLineIconListItem:
text: "Imagem maior"
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = 'tela_login'
IconLeftWidget:
icon: "database-export-outline"
<MainScreen>:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "Pesquise e compre."
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
right_action_items: [['lightbulb-outline', lambda x: app.color()], ['cart', lambda x: app.compras()]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
ScreenRecycleView
TelaLogin
TelaSingUp
TelaItem
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
<MyImageCard>:
name: 'my_image_card'
AsyncImage:
source: root.source
Label:
size_hint_y: None
height: dp(48)
text: root.text
color: 'black'
canvas:
Color:
rgba: 0, 0, 0, .3
Rectangle:
size: self.size
MDFlatButton:
text: "New Screen Here"
increment_width: "164dp"
#app.root.ids.sm.get_screen('main').ids.screenmanager.current = 'new_screen'
on_release:
root.screen_manager.current = 'tela_item' # <<<< HERE
<TelaItem>:
name: 'tela_item'
FloatLayout:
orientation: "vertical"
id: body_tela_item
FitImage:
pos_hint: {'center_x': .5, 'center_y': .85}
source: "./images/eu.jpeg"
size_hint: None, None
width: dp(90)
height: dp(90)
radius: [99, 99, 99, 99]
MDRaisedButton:
text: 'Add'
size_hint: .9, None
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: root.singup()
MDRaisedButton:
text: 'Back'
size_hint: .9, None
pos_hint: {'center_x': .5, 'center_y': .3}
on_release: root.singup()
<ScreenRecycleView>:
name: 'recycle_view'
FloatLayout:
MDBoxLayout:
pos_hint: {'center_x': 0.5, 'center_y': 0.4}
RecycleView:
id: rv
viewclass: "MyImageCard"
RecycleGridLayout:
cols: 2
row_default_height: (self.width - self.cols*self.spacing[0]) / self.cols
padding: dp(4), dp(4)
spacing: dp(4)
size_hint_y: None
height: self.minimum_height
default_size_hint: 1, None
default_size: None, dp(500)
<TelaLogin>:
name: 'tela_login'
FloatLayout:
orientation: "vertical"
id: body_tela_login
FitImage:
pos_hint: {'center_x': .5, 'center_y': .85}
source: "./images/eu.jpeg"
size_hint: None, None
width: dp(90)
height: dp(90)
radius: [99, 99, 99, 99]
MDRaisedButton:
text: 'Login'
size_hint: .9, None
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: root.login()
<TelaSingUp>:
name: 'tela_singup'
id: body_tela_singup
FloatLayout:
MDIconButton:
icon: "arrow-left-bold"
pos_hint: {"center_x": .1, "center_y": .9}
on_release: app.root.current = 'tela_login'
FitImage:
pos_hint: {'center_x': .5, 'center_y': .8}
source: "./images/user.png"
size_hint: None, None
width: dp(150)
height: dp(150)
radius: [99, 99, 99, 99]
MDRaisedButton:
text: 'Cadastrar'
size_hint: .9, None
pos_hint: {'center_x': .5, 'center_y': .1}
on_release: root.singup()
ScreenManager:
id: sm:
TelaLogin:
name: 'tela_login'
MainScreen:
name: 'main'
'''
class ContentNavigationDrawer(BoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class MainScreen(Screen):
pass
class TelaSingUp(Screen):
pass
class TelaItem(Screen):
pass
class TelaLogin(Screen):
def login(self):
self.manager.current = 'main'
class MyImageCard(RelativeLayout):
text = StringProperty()
source = StringProperty()
class ScreenRecycleView(Screen):
def on_kv_post(self, base_widget):
self.ids.rv.data = [
{
'source': choice(sample_images),
'text': ''.join(sample(ascii_lowercase, 6))
}
for x in range(50)
]
class MyApp(MDApp):
def build(self):
root = Builder.load_string(KV)
return root
MyApp().run()

Python Kivymd - AttributeError object has no attribute

Not sure where I'm going wrong, When changing the value of MDSlider it should update a label but I receive the error
the kv is loaded as a string within my py file, if I test out the slider by simply printing the value to the console it works. but when I try to update a label get the following error??
AttributeError: 'Level' object has no attribute 'training_level'
here's my code
from kivy.lang import Builder
from kivymd.uix.screen import MDScreen
from kivymd.app import MDApp
KV = """
ScreenManager:
Profile:
Level:
Routine:
Start:
<Profile>:
name: "Profile"
canvas.before:
Color:
rgba: (1,1,1,1)
Rectangle:
source: "bg.jpg"
size: root.width, root.height
pos: self.pos
MDLabel:
text: "Select a Profile"
font_size: "32"
pos_hint: {"center_x": .5, "center_y": .95}
MDRectangleFlatButton:
text: "Guest"
font_size: "32"
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.root.current = "Level"
MDBottomAppBar:
MDToolbar:
title: "HIIT"
icon: "account-cog"
type: "bottom"
left_action_items: [["menu", lambda x: x]]
mode: "center"
<Level>:
name: "Level"
canvas.before:
Color:
rgba: (1,1,1,1)
Rectangle:
source: "bg.jpg"
size: root.width, root.height
pos: self.pos
MDLabel:
text: "What is your Level of training?"
font_size: "32"
pos_hint: {"center_x": .5, "center_y": .95}
MDLabel:
id: "training_level"
theme_text_color: "Custom"
text_color: 1,1,1,1
halign: "center"
text: "2"
font_size: "128"
pos_hint: {"center_x": .5, "center_y": .65}
MDSlider:
min: 1
max: 3
value: 2
size: 500, 50
size_hint: None, None
hint: False
step: 1
orientation: "horizontal"
pos_hint: {"center_x": .5, "center_y": .40}
on_value: root.change_height(*args)
MDRectangleFlatButton:
text: "Next"
font_size: "32"
pos_hint: {"center_x": .92, "center_y": .18}
on_release: app.root.current = "Routine"
MDBottomAppBar:
MDToolbar:
title: "HIIT"
icon: "account-cog"
type: "bottom"
left_action_items: [["menu", lambda x: x]]
mode: "center"
<Routine>:
name: "Routine"
canvas.before:
Color:
rgba: (1,1,1,1)
Rectangle:
source: "bg.jpg"
size: root.width, root.height
pos: self.pos
MDLabel:
text: "Select a Routine"
font_size: "32"
pos_hint: {"center_x": .5, "center_y": .95}
MDRectangleFlatButton:
text: "10 Minute Beginner"
font_size: "32"
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.root.current = "Start"
MDBottomAppBar:
MDToolbar:
title: "HIIT"
icon: "account-cog"
type: "bottom"
left_action_items: [["menu", lambda x: x]]
mode: "center"
<Start>:
name: "Start"
canvas.before:
Color:
rgba: (1,1,1,1)
Rectangle:
source: "bg.jpg"
size: root.width, root.height
pos: self.pos
MDRectangleFlatButton:
text: "Stop"
font_size: "32"
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.root.current = "Profile"
MDBottomAppBar:
MDToolbar:
title: "HIIT"
icon: "account-cog"
type: "bottom"
left_action_items: [["menu", lambda x: x]]
mode: "center"
"""
class Profile(MDScreen):
pass
class Level(MDScreen):
def change_height(self, *args):
#print(str(round(args[1],1)))
self.training_level.text = str(round(args[1],1))
pass
class Routine(MDScreen):
pass
class Start(MDScreen):
pass
class MainApp(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Blue"
return Builder.load_string(KV)
MainApp().run()

KivyMD Remove a Expansion Panel

I'm having trouble finding the solution in how to remove an expansion panel which inside of it it contains TwoLineAvatarListItem with a IconLeftWidget, and below that is a MDCard with two MDlabels for text. I've tried to use self.root.ids.remove_widget() but it does not do anything. I've tried many ways and have not found the solution. Here is the code please feel free to view. Thanks
kv. file
<Content>:
adaptive_height: True
#size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
TwoLineAvatarListItem:
text: "Message"
secondary_text: 'to: #gmail.com'
IconLeftWidget:
icon: 'email'
MDCard:
orientation: "vertical"
padding: "8dp"
size_hint: None, None
size: "280dp", "180dp"
pos_hint: {"center_x": .5, "center_y": .5}
MDLabel:
text: "Title"
theme_text_color: "Secondary"
size_hint_y: None
height: self.texture_size[1]
MDSeparator:
height: "1dp"
MDLabel:
text: "Body"
ScreenManager:
Screen:
id: messages
name: 'messages'
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: "Messages Center"
elevation:8
MDIconButton:
icon: 'arrow-left'
on_press: screen_manager.current = "main_app_screen"
theme_text_color: 'Custom'
md_bg_color: app.theme_cls.primary_color
ScrollView:
GridLayout:
cols: 1
size_hint_y: None
height: self.minimum_height
id: box
.py python file
class DemoApp(MDApp):
def on_start(self):
for i in range(10):
panel = MDExpansionPanel(
icon=f"{images_path}folder.png",
content=Content(),
panel_cls=MDExpansionPanelTwoLine(
text='',
secondary_text=str(i) + ' email: xxxxx#gmail.com',))
self.root.ids.box.add_widget(panel)
DemoApp().run()
<Content>:
[...]
MDCard:
[...]
MDRaisedButton:
text: "REMOVE"
on_release: app.remove(root)
[...]
class DemoApp(MDApp):
[...]
def remove(self, content):
self.root.ids.box.remove_widget(content.parent)

Categories