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)