I'm relatively new to Kivy/KivyMD. I'm currently trying to figure out how to set up a scroll view for one of my screens. As far as I'm aware in order to use a scroll view I need to use a grid layout. Whenever I do this, all the items move to the left of the screen and some of them disappear completely.
Below is an image of the screen I need more space for:
Not enough space
Below is my python file for what I attempted:
import sqlite3
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
from kivy.uix.scrollview import ScrollView
Builder.load_file('ALevelNEA.kv')
##Below are the classes for my different screens
class SignupScreen(Screen):
pass
class MenuScreen(Screen):
pass
class SigninScreen(Screen, ScrollView):
pass
## Below are the classes for my custom buttons, widgets and text fields
class NameField(MDRelativeLayout):
text = StringProperty()
hint_text = StringProperty()
pass
class ConfirmPasswordField(MDRelativeLayout):
text = StringProperty()
hint_text = StringProperty()
pass
class NameField(MDRelativeLayout):
text = StringProperty()
hint_text = StringProperty()
pass
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, email, passw):
NewEmail = email
NewPassword = passw
# Connect to or create a new database
conn = sqlite3.connect('example.db')
# Create a cursor object to execute SQL commands
cursor = conn.cursor()
# Create a table to store the records
cursor.execute('''CREATE TABLE IF NOT EXISTS users (email TEXT, password TEXT)''')
# Insert the user's information into the table
cursor.execute("INSERT INTO users (email, password) VALUES (?, ?)", (NewEmail, NewPassword))
# Committing the changes and closing the connection to the database file
conn.commit()
conn.close()
if __name__ == '__main__':
MainApp().run()
Below is the section of my KV file which is relevant for what I attempted:
<SignupScreen>:
ScrollView:
GridLayout:
cols: 1
size_hint_y: None
height: self.minimum_height
padding: dp(10)
spacing: dp(10)
Image:
source: "Images/RegisterImage.png"
size_hint_x: 0.4
size_hint_y: 0.6
allow_stretch: True
pos_hint: {"center_x": 0.5, "center_y": 0.8 }
MDIconButton:
icon: "keyboard-backspace"
pos_hint: {"center_x": 0.35, "center_y": 0.95}
on_release:
root.manager.current = "menu"
EmailField:
id: email_widget
hint_text: "Email"
pos_hint: {"center_x": 0.5, "center_y": 0.55}
size_hint_x: None
width: "250dp"
NameField:
id: name_widget
hint_text: "Name"
pos_hint: {"center_x": 0.5, "center_y": 0.45}
size_hint_x: None
width: "250dp"
PasswordField:
id: passw_widget
size_hint_x: None
width: "250dp"
hint_text: "Password"
pos_hint: {"center_x": 0.5, "center_y": 0.35}
ConfirmPasswordField:
id: Cpass_widget
size_hint_x: None
width: "250dp"
hint_text: "Confirm Password"
pos_hint: {"center_x": 0.5, "center_y": 0.25}
MDRoundFlatButton:
text: "Sign up"
pos_hint: {"center_x": 0.5, "center_y": 0.15}
text_color: 0.3607, 0.3882, 1
line_color: 0, 0, 0
#on_release: app.AddToRecord()
on_release:
app.AddToRecord(root.ids.email_widget.ids.email_field.text, root.ids.passw_widget.ids.text_field.text)
MDLabel:
text: "by continuing you agree to the"
font_size: 15
halign: 'center'
pos_hint: {"center_y": 0.1}
MDTextButton:
text: "Terms and Conditions"
font_size: 15
pos_hint: {"center_x": 0.5, "center_y": 0.0825}
theme_text_color: "Custom"
text_color: 0.3607, 0.3882, 1
#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
<ConfirmPasswordField>:
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"
<NameField>:
size_hint_y: None
height: name_field.height
MDTextField:
id: name_field
hint_text: root.hint_text
text: root.text
icon_left: "account"
Below is the result:
Failed attempt (it does scroll but the layout is wrong)
Any help would be greatly appreciated, thanks!
A GridLayout is not required. Try using a BoxLayout, like this:
<SignupScreen>:
ScrollView:
BoxLayout:
orientation: 'vertical'
# cols: 1
Related
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.
from kivy.app import App
from kivy.graphics import Line
from kivy.metrics import dp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView
from kivy.uix.stacklayout import StackLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivymd.app import MDApp
class ScrollViewExample(ScrollView):
pass
class FirstWindow(Screen):
def set_text(self):
my_title_input = self.ids.note_title
my_details = self.ids.note_details
fo = open("notes.txt", "a")
fo.write('\n'+'\n'+my_title_input.text)
fo.write('\n'+my_details.text)
fo.close()
class SecondWindow(Screen):
pass
class ThirdWindow(Screen):
def on_toggle_button_state(self):
dark_mode = self.ids.dark_mode
dark_mode.text = 'Feature not available'
pass
class FourthWindow(Screen):
#This class displays notes from a file in buttons
#works but only the notes showup
def build(self):
root = FourthWindow()
root.add_widget(FourthWindow(name='StackLayout'))
class StackLayoutExample(StackLayout):
class ScrollView(ScrollView):
pass
def __init__(self, **kwargs):
super().__init__(**kwargs)
layout = StackLayout(orientation='lr-tb')
with open('notes.txt', 'r') as fp:
data = fp.read()
splat = data.split("\n\n")
notes = []
for i, note in enumerate(splat, 0):
notes.append(note)
for i in range(0, len(notes)):
b = Button(text=notes[i], size_hint=(.25, .4))
self.add_widget(b)
class WindowManager(ScreenManager):
pass
class RelativeLayoutExample(RelativeLayout):
pass
class ScreeManager(ScreenManager):
pass
class LineMaker(Widget):
pass
class Noteit(App):
pass
Noteit().run()
Noteit.kv <---The kv file begins here
WindowManager:
FirstWindow:
SecondWindow:
ThirdWindow:
FourthWindow:
<FirstWindow>:
id: first_window
name: "Notes"
RelativeLayout:
Label:
text: "Notes"
size_hint: None,None
pos_hint: {'top':1,'center_x':.5}
font_size: 35
Button:
text: 'Account'
size_hint: None,None
pos_hint: {'top':1,'right':1}
on_release:
app.root.current = "Account"
root.manager.transition.direction= "left"
Button:
text: 'Preferences'
size_hint: None,None
pos_hint: {'top':1,'left':1}
on_release:
app.root.current = "Preferences"
root.manager.transition.direction= "right"
Label:
text: "Title:"
size_hint: None,None
pos_hint: {'top':.85,'center_x':.5}
font_size: 20
TextInput:
id: note_title
size_hint: .45,.08
pos_hint: {'top':.75,'center_x':.5}
multiline: False
Label:
text: "Details:"
size_hint: None,None
pos_hint: {'top':.65,'center_x':.5}
font_size: 20
TextInput:
id: note_details
size_hint: .45,.3
pos_hint: {'top':.55,'center_x':.5}
multiline: True
Button:
text: "Submit"
size_hint: .25,.1
length: "100dp"
pos_hint: {'top':.25,'center_x':.5}
on_release:
root.set_text()
LineMaker:
canvas:
Color:
rgba: 1, 1, 1, 1
Line:
width: 2.
points: (self.width,self.height*.83,0,self.height*.83)
Button:
text: 'View All Notes'
size_hint:.25,.15
pos_hint: {'bottom':1,'center_x':.5}
on_release:
app.root.current = "View"
root.manager.transition.direction= "up"
<SecondWindow>:
name: "Account"
RelativeLayout:
Label:
text: "Account"
size_hint: None,None
pos_hint: {'top':1,'center_x':.5}
font_size: 35
Button:
text: 'Notes'
size_hint: None,None
pos_hint: {'top':1,'left':1}
on_release:
app.root.current = "Notes"
root.manager.transition.direction= "right"
Button:
text: 'Preferences'
size_hint: None,None
pos_hint: {'top':1,'right':1}
on_release:
app.root.current = "Preferences"
root.manager.transition.direction= "left"
Label:
text: "Username:"
size_hint: None,None
pos_hint: {'top':.85}
font_size: 20
TextInput:
size_hint: .5,.1
pos_hint: {'top':.55,'left':.3}
multiline: False
Label:
text: "Password:"
size_hint: None,None
pos_hint: {'top':.65}
font_size: 20
TextInput:
size_hint: .5,.1
pos_hint: {'top':.75,'left':.3}
multiline: False
Button:
text: "Log In"
size_hint: .25,.1
length: "100dp"
pos_hint: {'top':.45}
Button:
text: "Sign Up"
size_hint: .25,.1
length: "100dp"
pos_hint: {'top':.45,'center_x':.375}
LineMaker:
canvas:
Color:
rgba: 1, 1, 1, 1
Line:
width: 2.
points: (self.width,self.height*.83,0,self.height*.83)
<ThirdWindow>:
name: "Preferences"
RelativeLayout:
Label:
text: "Preferences"
size_hint: None,None
pos_hint: {'top':1,'center_x':.5}
font_size: 35
Button:
text: 'Notes'
size_hint: None,None
pos_hint: {'top':1,'right':1}
on_release:
app.root.current = "Notes"
root.manager.transition.direction= "left"
Button:
text: 'Account'
size_hint: None,None
pos_hint: {'top':1,'left':1}
on_release:
app.root.current = "Account"
root.manager.transition.direction= "right"
ToggleButton:
text: 'Dark Mode'
size_hint: .25,.1
pos_hint: {'top':.75,'center_x':.25}
on_state: root.on_toggle_button_state()
Label:
id: dark_mode
text: ''
LineMaker:
canvas:
Color:
rgba: 1, 1, 1, 1
Line:
width: 2.
points: (self.width,self.height*.83,0,self.height*.83)
<FourthWindow>:
name: "View"
RelativeLayout:
Button:
text: 'Note Submission'
size_hint:.25,.15
pos_hint: {'top':1,'center_x':.5}
on_release:
app.root.current = "Notes"
root.manager.transition.direction= "down"
Label:
text: 'All Notes'
size_hint: None,None
pos_hint: {'top':1,'right':.9}
font_size: 35
How do I make the stacklayout code showup with the kv file code? Or at least make the stacklayout part of the screen.
Right now the only option I have is either take the screen as output or the stack layout as output.
Replacing the def build(self) inside the class FourthWindow(Screen) will show the whole application. The code inside the def build(self) also works but they do not work together.
I am new to kivy and object oriented programming so I do not know much about this topic so I could be missing something really basic.
How's everyone doing? I am learning about KivyMD and ScreenManager and I have a little problem (and a big one). I was writing a program (like most beginners do) which would allow a user to log in or sign up (I was also practicing about sqlite3, but I don't have a problem with that).
First, I've tried to do everything in only one screen and it worked perfectly!
But now, when I try to use any function, I get:
_username = self.root.ids.my_username.text
AttributeError: 'super' object has no attribute '__getattr__'
The py file is:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
import sqlite3
class MainMenu(Screen):
pass
class LogIn(Screen):
pass
class SignUp(Screen):
pass
class MainLogSignApp(MDApp):
def build(self):
# Connect to databse an create a cursor
conn = sqlite3.connect('user_pass.db')
c = conn.cursor()
c.execute("""CREATE TABLE if not exists user_name(
username text,
password text
)""")
conn.commit()
conn.close()
return Builder.load_file('mainlogsign.kv')
return sm
def submit_info(self):
'''If the info does not exist, it adds it to the table;
If it does, the function check if it is correct'''
_username = self.root.ids.my_username.text
_password = self.root.ids.my_password.text
# Connect and create cursor
conn = sqlite3.connect('user_pass.db')
c = conn.cursor()
# This should not be necesary, but it is here just in case I delete the table using the DELETE button.
c.execute("""CREATE TABLE if not exists user_name(
username text,
password text
)""")
c.execute("SELECT * FROM user_name WHERE username = (?)", (_username,))
already_in = c.fetchone()
if already_in and already_in[0]:
self.root.ids.my_message.text = already_in[0] + '\nUser already exists'
else:
c.execute("INSERT INTO user_name VALUES (?,?)", (_username, _password))
self.root.ids.my_message.text = 'User added successfully'
conn.commit()
conn.close()
self.root.ids.my_username.text = ''
self.root.ids.my_password.text = ''
# THERE ARE MORE FUNCTIONS, BUT THEY ALL HAVE THE SAME PROBLEM.
if __name__ == '__main__':
MainLogSignApp().run()
And the kivy file is:
ScreenManager:
MainMenu:
LogIn:
SignUp:
<MyTextField#MDTextFieldRound>:
font_size: 12
size_hint_x: 0.6
pos_hint: {'center_x': 0.5}
halign: 'center'
<MainMenu>:
name: 'main_menu'
BoxLayout:
orientation: 'vertical'
Label:
size_hint: .1, .3
MDRoundFlatButton:
text: 'LOG IN'
pos_hint: {'center_x': .5}
on_press:
root.manager.current = 'login'
root.manager.transition.direction = 'left'
Label:
size_hint: .1, .1
MDRoundFlatButton:
text: 'SIGN UP'
pos_hint: {'center_x': .5}
on_press:
root.manager.current = 'signup'
root.manager.transition.direction = 'left'
Label:
size_hint: .1, .3
<LogIn>:
name: 'login'
BoxLayout:
orientation: 'vertical'
MDLabel:
text: 'LOGIN'
halign: 'center'
Button:
id: my_label_l
text: 'DELETE'
size_hint: 0.6, 0.2
pos_hint: {'center_x': .5}
halign: 'center'
on_press: app.delete_record()
MyTextField:
id: my_username_l
hint_text: 'Enter your username'
icon_right: 'account'
MyTextField:
id: my_password_l
hint_text: 'Enter your password'
icon_right: 'eye-outline'
Button:
id: log_in
text: 'LOG IN'
size_hint: 0.6, 0.2
pos_hint: {'center_x': .5}
on_press: app.log_in()
Button:
id: show_button_l
text: 'Nothing'
size_hint: 0.6, 0.2
pos_hint: {'center_x': .5}
MDLabel:
id: my_message_l
text: ''
halign: 'center'
MDRectangleFlatIconButton:
icon: 'backspace-outline'
on_press:
root.manager.current = 'main_menu'
root.manager.transition.direction = 'right'
<SignUp>:
name: 'signup'
BoxLayout:
orientation: 'vertical'
MDLabel:
text: 'SIGNUP'
halign: 'center'
Button:
id: my_label
text: 'DELETE'
size_hint: 0.6, 0.2
pos_hint: {'center_x': .5}
halign: 'center'
on_press: app.delete_record()
MyTextField:
id: my_username
hint_text: 'Enter your username'
icon_right: 'account'
MyTextField:
id: my_password
hint_text: 'Enter your password'
icon_right: 'eye-outline'
Button:
id: submit_button
text: 'Submit info'
size_hint: 0.6, 0.2
pos_hint: {'center_x': .5}
on_press: app.submit_info()
Button:
id: show_button
text: 'Show records'
size_hint: 0.6, 0.2
pos_hint: {'center_x': .5}
on_press: app.show_info()
MDLabel:
id: my_message
text: ''
halign: 'center'
MDRectangleFlatIconButton:
icon: 'backspace-outline'
on_press:
root.manager.current = 'main_menu'
root.manager.transition.direction = 'right'
None of the functions work, so I'm guessing it has something to do with 'app.function()' or the ids, but it did work the first time. Maybe I have to use a different path, but I don't know.
As a small problem, when I write on the TextInput the hint text doesn't disappear. Any idea why?
Thanks!
BTW, you may notice the button 'DELETE' and 'SHOW RECORDS' and think they have nothing to do with a Login or SignUp program. You couldn't be more right!! So don't pay attention to them. I only added those button to work with sqlite3, but on a real app or site they wouldn't be there. Still, the problem is the same with any button.
Ok, I've solved the problem. I wrote the function submit_info into the SignUp class and change the app.submit_info() to root.submit_info() (in the kv file). Also, I had to remove every "root." in the function.
I hope this works if anybody gets the same problem.
So I'm working on making a fully functional login screen for my first serious kivy app and I want to be able to change windows/screens when the user presses on a button. I know normally in the KV file i'd just use on release but I want the on release to call to a method to verify the users credentials, and if those credentials are correct then change screens. So how in python itself would I be able to call to screen manager to change the screen?
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
class LoginLayout(Screen):
def login(self, **kwargs):
print("Login function working")
userEmail = self.ids.username.text
userPassword = self.ids.password.text
print(userEmail)
print(userPassword)
ScreenManager.current('emailWindow')
class EmailWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file('loginScreen.kv')
class LoginScreen(App):
def build(self):
return kv
app = LoginScreen()
app.run()
KV
ScreenManager:
LoginLayout:
EmailWindow:
<LoginLayout>:
name: 'loginWindow'
canvas.before:
Color:
rgba: (28/255, 102/255, 137/255, 1)
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
size: root.width, root.height
Label:
text: 'Username'
font_name: 'Framd.ttf'
font_size: 20
TextInput:
id: username
multiline: False
size_hint: (.5, .3)
pos_hint: {'center_x' : .5}
Label:
text: 'Password'
font_name: 'Framd.ttf'
font_size: 20
TextInput:
id: password
multiline: False
size_hint: (.5, .3)
pos_hint: {'center_x' : .5}
Button:
text: 'Login'
size_hint: (.2, .8)
pos_hint: {'center_x' : 0.5}
font_name: 'Framd.ttf'
on_release: root.login()
Button:
text: 'Create Account'
size_hint: (.2, .8)
pos_hint: {'center_x' : 0.5}
font_name: 'Framd.ttf'
Button:
text: 'Forgot login Info'
size_hint: (.2, .8)
pos_hint: {'center_x' : 0.5}
font_name: 'Framd.ttf'
<EmailWindow>:
name: 'emailWindow'
canvas.before:
Color:
rgba: (28/255, 102/255, 137/255, 1)
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
size: root.width, root.height
Label:
text: 'To:'
font_name: 'Framd.ttf'
TextInput:
multiline: False
pos_hint: {'center_x': 0.5}
size_hint: (.5, .3)
font_name: 'Framd.ttf'
Label:
text: 'Subject'
TextInput:
multiline: False
pos_hint: {'center_x': 0.5}
size_hint: (.5, .3)
font_name: 'Framd.ttf'
Label:
text: 'Body'
font_name: 'Framd.ttf'
TextInput:
size_hint: (.5, .7)
pos_hint: {'center_x': 0.5}
multiline: True
Button:
text: 'send'
size_hint: (.2, .8)
pos_hint: {'center_x' : 0.5}
font_name: 'Framd.ttf'
When a Screen gets added to a ScreenManager, it gets a manager attribute set to the ScreenManager. So in your login() method you can do:
self.manager.current = 'emailWindow'
instead of:
ScreenManager.current('emailWindow')
Note that using ScreenManager, as you do above, references the ScreenManager class, not the ScreenManager instance that is in your App.
Please I keep getting an attribute error when I try to get the text a user input into an MDTextField, tried everything I could think of, researched and couldn't get anything. Please I need your help.
I try to get the text with this argument of self.root.ids.MyId.text
This is the error I get:
File "C:/Users/user/Documents/KmD/Webma/main_webma.py", line 120, in login
email_addr = self.root.ids.email.text
File "kivy\properties.pyx", line 864, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
And this is my main.py code
from kivymd.app import MDApp
from kivy.clock import Clock
from kivy.lang import Builder
from home_page_strings import toolbar
import sqlite3
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.picker import MDThemePicker
from kivy.uix.popup import Popup
from kivymd.uix.textfield import MDTextField
# calling of screen classes and addition to the screen_manager
class Friend1(Screen):
pass
class WelcomeScreen(Screen):
def __init__(self, **kwargs):
super(WelcomeScreen, self).__init__(**kwargs)
Clock.schedule_once(self.callNext, 5)
def callNext(self, dt):
self.manager.current = 'account'
class AccountScreen(Screen):
pass
class HomeScreen(Screen):
pass
class RoomScreen(Screen):
pass
class ContactsScreen(Screen):
pass
class CallsScreen(Screen):
pass
class SettingsScreen(Screen):
pass
class FaqScreen(Screen):
pass
class InviteScreen(Screen):
pass
manager = ScreenManager()
manager.add_widget(Friend1(name='friend1'))
manager.add_widget(HomeScreen(name='home'))
manager.add_widget(WelcomeScreen(name='welcome'))
manager.add_widget(RoomScreen(name='room'))
manager.add_widget(ContactsScreen(name='contacts'))
manager.add_widget(CallsScreen(name='calls'))
manager.add_widget(SettingsScreen(name='settings'))
manager.add_widget(FaqScreen(name='faq'))
manager.add_widget(InviteScreen(name='invite'))
manager.add_widget(AccountScreen(name='account'))
# end of call of screen classes and addition to screenmanager
class EMC_WebmaApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# -------------------------- CODE FOR THE DATABASE ---------------------------------- #
conn = sqlite3.connect('webma_database.db')
cursor = conn.cursor()
# cursor.execute('''CREATE TABLE users (
# email text,
# password integer
# )''')
def sign_up(self, cursor):
cursor.execute('INSERT INTO users VALUES('', '')')
conn.commit()
conn.close()
# -------------------------- END OF CODE FOR THE DATABASE ---------------------------------- #
# BUILDER FUNCTION AND SCREEN
def build(self):
screen = Screen()
# theme settings
self.theme_cls.primary_palette = 'DeepPurple'
self.theme_cls.theme_style = 'Dark'
# end of theme declaration
# loaded strings
navigation_bar = Builder.load_string(toolbar)
# end of loaded strings
# addition of loaded strings to screen + return screen
screen.add_widget(navigation_bar)
return screen
# end of addition
# END OF BUILDER FUNCTION
# USER VALIDATION FUNCTION
def login(self):
email_addr = self.root.ids.email.text
if email_addr == str(''):
print('Invalid Email Address')
# END OF USER VALIDATION
def change(self, dt):
self.root.manager.current = 'home'
# App theme manager
def change_theme(self):
picker = MDThemePicker()
picker.open()
# End of app theme manager
# home page search bar
def search_bar(self, dt):
searchbar = Popup(
title='Search',
content=MDTextField(
helper_text='Input values to be searched for',
helper_text_mode='on_focus'
),
auto_dismiss=True,
# size_hint=(None, None),
# size=(400, 98)
size_hint=(0.7, 0.17),
pos_hint={'center_x': 0.5, 'center_y': 0.8}
)
searchbar.open()
# APP RUN
if __name__ == '__main__':
EMC_WebmaApp().run()
# WELL... THIS IS THE END OF EVERYTHING
Then my string code where I try to get my text from
toolbar = '''
ScreenManager:
WelcomeScreen:
HomeScreen:
RoomScreen:
ContactsScreen
CallsScreen:
SettingsScreen:
FaqScreen:
InviteScreen:
AccountScreen:
Friend1:
<WelcomeScreen>:
name: 'welcome'
Image:
source: 'logo.png'
size_hint: 0.4, 0.4
pos_hint: {'center_x': 0.5, 'center_y':0.6}
MDLabel:
text: 'Webma'
halign: 'center'
font_style: 'H3'
color: (1, 1, 1, 1)
pos_hint: {'center_x': 0.5, 'center_y':0.8}
MDLabel:
text: 'Powered by emc_codes'
halign: 'center'
bold: 'True'
italic: True
pos_hint: {'center_x': 0.5, 'center_y':0.1}
<HomeScreen>:
name: 'home'
NavigationLayout:
ScreenManager:
Screen:
BoxLayout:
orientation: 'vertical'
spacing: '8dp'
MDToolbar:
title: 'EMC_Webma'
left_action_items: [['menu', lambda x: navigation.set_state('open')]]
right_action_items: [['card-search', app.search_bar]]
elevation: 10
ScrollView:
MDList:
TwoLineAvatarIconListItem:
text: 'Friend 1'
ImageLeftWidget:
source: 'einstein.jpg'
IconRightWidget:
icon: 'dots-vertical'
MDNavigationDrawer:
id: navigation
BoxLayout:
orientation: 'vertical'
spacing: '8dp'
padding: '8dp'
Image:
source: 'einstein.jpg'
MDLabel:
text: 'Enwerem Melvin Confidence'
font_style: 'Subtitle2'
size_hint_y: None
height: self.texture_size[1]
MDLabel:
text: 'enweremmelvinconfidence#gmail.com'
font_style: 'Caption'
size_hint_y: None
height: self.texture_size[1]
ScrollView:
MDList:
OneLineIconListItem:
text: 'Room'
on_release: root.manager.current = 'room'
IconLeftWidget:
icon: 'theater'
on_release: root.manager.current = 'room'
OneLineIconListItem:
text: 'Contacts'
on_release: root.manager.current = 'contacts'
IconLeftWidget:
icon: 'contacts'
on_release: root.manager.current = 'contacts'
OneLineIconListItem:
text: 'Calls'
on_release: root.manager.current = 'calls'
IconLeftWidget:
icon: 'cellphone-android'
on_release: root.manager.current = 'calls'
OneLineIconListItem:
text: 'Settings'
on_release: root.manager.current = 'settings'
IconLeftWidget:
icon: 'settings'
on_release: root.manager.current = 'settings'
OneLineIconListItem:
text: 'FAQ'
on_release: root.manager.current = 'faq'
IconLeftWidget:
icon: 'frequently-asked-questions'
on_release: root.manager.current = 'faq'
OneLineIconListItem:
text: 'Invite Friends'
on_release: root.manager.current = 'invite'
IconLeftWidget:
icon: 'account-plus'
on_release: root.manager.current = 'invite'
OneLineIconListItem:
text: 'Theme Manager'
on_release:
app.change_theme()
IconLeftWidget:
icon: 'theme-light-dark'
<RoomScreen>:
name: 'room'
MDLabel:
text: 'Welcome to the Room Screen'
halign: 'center'
bold: True
font_size: 50
MDIconButton:
icon: 'keyboard-backspace'
pos_hint: {'center_x': 0.06, 'center_y': 0.96}
on_release: root.manager.current = 'home'
size_hint: None, None
<ContactsScreen>:
name: 'contacts'
MDLabel:
text: 'Welcome to the Contacts Screen'
halign: 'center'
bold: True
font_size: 50
MDIconButton:
icon: 'keyboard-backspace'
pos_hint: {'center_x': 0.06, 'center_y': 0.96}
on_release: root.manager.current = 'home'
size_hint: None, None
<CallsScreen>:
name: 'calls'
MDLabel:
text: 'Welcome to the Calls Screen'
halign: 'center'
bold: True
font_size: 50
MDIconButton:
icon: 'keyboard-backspace'
pos_hint: {'center_x': 0.06, 'center_y': 0.96}
on_release: root.manager.current = 'home'
size_hint: None, None
<SettingsScreen>:
name: 'settings'
MDLabel:
text: 'Welcome to the Settings Screen'
halign: 'center'
bold: True
font_size: 50
MDIconButton:
icon: 'keyboard-backspace'
pos_hint: {'center_x': 0.06, 'center_y': 0.96}
on_release: root.manager.current = 'home'
size_hint: None, None
<FaqScreen>:
name: 'faq'
MDLabel:
text: 'Welcome to the Faq Screen'
halign: 'center'
bold: True
font_size: 50
MDIconButton:
icon: 'keyboard-backspace'
pos_hint: {'center_x': 0.06, 'center_y': 0.96}
on_release: root.manager.current = 'home'
size_hint: None, None
<InviteScreen>:
name: 'invite'
MDLabel:
text: 'Welcome to the Invite Screen'
halign: 'center'
bold: True
font_size: 50
MDIconButton:
icon: 'keyboard-backspace'
pos_hint: {'center_x': 0.06, 'center_y': 0.96}
on_release: root.manager.current = 'home'
size_hint: None, None
<AccountScreen>:
name: 'account'
BoxLayout:
MDLabel:
text: 'Account Login'
pos_hint: {'center_x': 0.6, 'center_y': 0.9}
color: (1, 1, 1, 1)
font_size: 40
MDTextFieldRound:
id: email
hint_text: 'Email Address'
icon_right: 'email'
icon_right_color: app.theme_cls.primary_color
pos_hint: {'center_x': 0.5, 'center_y': 0.7}
size_hint_x: None
width: 300
MDTextFieldRound:
id: password
hint_text: 'Enter Password'
icon_right: 'lock'
icon_right_color: app.theme_cls.primary_color
password: True
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
size_hint_x: None
width: 300
MDRaisedButton:
text: 'Login'
md_bg_color: app.theme_cls.primary_color
pos_hint: {'center_x': 0.5, 'center_y': 0.2}
on_release: app.login() # root.manager.current = 'home'
<Friend1>
name: 'friend1'
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Friend 1'
left_action_items: [['keyboard-backspace', app.change]]
right_action_items: [['phone', lambda x: print('go ahead and make a call')], ['video', lambda x: print('Video')], ['dots-vertical', lambda x: print('dots')]]
MDLabel:
text: 'This is a test screen for our Chat'
halign: 'center'
'''
Please guys I really appreciate the help. Thanks in advance...
The email id only appears in the ids for the AccountScreen. Any id appearing in a kv gets inserted into the ids of the widget that is the root of the rule that contains the id. So, you must access the AccountScreen instance to get to the email id:
# BUILDER FUNCTION AND SCREEN
def build(self):
screen = Screen()
# theme settings
self.theme_cls.primary_palette = 'DeepPurple'
self.theme_cls.theme_style = 'Dark'
# end of theme declaration
# loaded strings
self.navigation_bar = Builder.load_string(toolbar) # save a reference to the loaded ScreenManager
# end of loaded strings
# addition of loaded strings to screen + return screen
screen.add_widget(self.navigation_bar)
return screen
# end of addition
# END OF BUILDER FUNCTION
# USER VALIDATION FUNCTION
def login(self):
# use the saved reference to access the email address
email_addr = self.navigation_bar.get_screen('account').ids.email.text
if email_addr == str(''):
print('Invalid Email Address')
# END OF USER VALIDATION
value = self.root.get_screen('nameofscreen').ids.username.text
print("What you typed is: ", value)
syntax = self.root.get_screen('name').ids.id.text