Kivy can't get text input using ScreenManager - python

I'me working on a project and I'm using kivy.
I want to crete an app and I need multiple pages so I'm using ScreenManages.
I also need to take User Input in one of the pages and save it, so I've used MDTextField for take the text and a button to save the data.
When i press the button the app should take the data from the text field and save it in a file with sqlite3, but when I press the button it give me a really strange error.
I've tried to rewrite only that page of the app without the ScreenManager and it works.
How can I make it work also with the ScreenManager ?
(How can I get the User Input using MDTextField and ScreenManager)
I will show you some lines of code to make you understand better:
This is the Kivy Code:
<AddWindow>:
name: "add"
MDTextField:
id: account_link
hint_text: "Link"
helper_text: "Insert the Link of the WebSite to enter in the website from this app"
helper_text_mode: "on_focus"
line_color_normal: app.theme_cls.accent_color
pos_hint: {"center_x": 0.5, "center_y": 0.8}
size_hint_x: None
width: 1200
This is the code to take the data from the Text Field (that part of code it's executed when the user presses the submit button):
data = self.root.ids["account_link"].text
This is the error i get when i press the button:
data = self.root.ids["account_link"].text
KeyError: 'account_link'

Note that the documentation says:
ids are added to the root widget’s ids dictionary.
Poorly worded documentation, because they elsewhere refer to "root widget" as the root of the entire GUI. But in this case "root widget" is the root of the rule where the ids are defined. In your case that might be the AddWindow rule (not 100% sure due to the indentation of your kv snippet). If that is the case, then you need a reference to the instance of AddWindow that appears in your GUI:
data = addwindow_instance.ids["account_link"].text
Without seeing more of your code, I can only guess at the appropriate method to access the instance of AddWindow.
With the addition of a complete code, I can now help you. Here is a modified version of your add_passwd() method:
def add_passwd(self):
# get a reference to the AddWindow Screen
addwindow_instance = self.root.get_screen('add')
# use that instance to access the MDTextFields
account_link = addwindow_instance.ids["account_link"].text
account_name = addwindow_instance.ids["md_account_name"].text
account_nickname = addwindow_instance.ids["md_account_nickname"].text
email = addwindow_instance.ids["md_email"].text
passwd = addwindow_instance.ids["md_passwd"].text
#TEST
print(account_link)
print(account_name)
print(account_nickname)
print(email)
print(passwd)
Note that this also requires a couple corrections to your kv. Wherever you have anything like:
id: "some_id"
it should be changed to:
id: some_id
One example is id: "md_account_name".

This is More of my code:
# Screens
class MainWindow(Screen):
pass
class AddWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
KV = """
WindowManager:
MainWindow:
AddWindow:
<MainWindow>:
name: "main"
MDRoundFlatButton:
text: "Add"
pos_hint: {"center_x": 0.5, "center_y": 0.7}
on_press:
app.root.current = "add"
root.manager.transition.direction = "left"
MDRoundFlatButton:
text: "Show"
pos_hint: {"center_x": 0.5, "center_y": 0.6}
on_press:
app.root.current = "show"
MDTextButton:
text: "Account"
pos_hint: {"center_x": 0.5, "center_y": 0.1}
on_press:
app.root.current = "settings"
root.manager.transition.direction = "up"
<AddWindow>:
name: "add"
MDRaisedButton:
text: "BACK"
md_bg_color: 0, 0, 0, 1
pos_hint: {"x": 0.01, "y": 0.93}
on_release:
app.root.current = "main"
root.manager.transition.direction = "right"
MDTextField:
id: account_link
hint_text: "Link"
helper_text: "Insert the Link of the WebSite to enter in the website from
this app"
helper_text_mode: "on_focus"
line_color_normal: app.theme_cls.accent_color
pos_hint: {"center_x": 0.5, "center_y": 0.8}
size_hint_x: None
width: 1200
MDTextField:
id: "md_account_name"
hint_text: "Account"
helper_text: "Insert the Name of the Account You Want to Save"
helper_text_mode: "on_focus"
line_color_normal: app.theme_cls.accent_color
pos_hint: {"center_x": 0.5, "center_y": 0.7}
size_hint_x: None
width: 1200
MDTextField:
id: "md_account_nickname"
hint_text: "Nickname"
helper_text: "Insert the Nickname You Have in the Account"
helper_text_mode: "on_focus"
line_color_normal: app.theme_cls.accent_color
pos_hint: {"center_x": 0.5, "center_y": 0.6}
size_hint_x: None
width: 1200
MDTextField:
id: "md_email"
hint_text: "Email"
helper_text: "Insert the Email You Created the Account with"
helper_text_mode: "on_focus"
line_color_normal: app.theme_cls.accent_color
pos_hint: {"center_x": 0.5, "center_y": 0.5}
size_hint_x: None
width: 1200
MDTextField:
id: "md_passwd"
hint_text: "Password"
helper_text: "Insert Your Password of the Account"
helper_text_mode: "on_focus"
line_color_normal: app.theme_cls.accent_color
pos_hint: {"center_x": 0.5, "center_y": 0.4}
size_hint_x: None
width: 1200
MDFillRoundFlatButton:
text: "Submit"
pos_hint: {"center_x": 0.5, "center_y": 0.1}
on_press: app.add_passwd()
"""
class App(MDApp):
def build(self):
self.title = "Safed" #The Name of the App is "Safed": "Save" + "Saved"
self.theme_cls.theme_style = "Dark" # Light
self.theme_cls.primary_palette = "Blue"
return Builder.load_string(KV)
def add_passwd(self):
account_link = AddWindow_istance.ids["account_link"].text
account_name = self.root.ids["md_account_name"].text
account_nickname = self.root.ids["md_account_nickname"].text
email = self.root.ids["md_email"].text
passwd = self.root.ids["md_passwd"].text
#TEST
print(account_link)
print(account_name)
print(account_nickname)
print(email)
print(passwd)
if __name__ == "__main__":
App().run()

Related

Python Kivy/kivyMD load second screen method in authentication page

I want to know the method of loading the second screen when authentication is passed.
I can call the second screen from the .kv file vi on_press or other methods. But I need to call from python code to check the authentication.
Can anyone help with my code?
Here is my code:
app.py
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import ObjectProperty
class my_layout(FloatLayout):
screen_mngr = ObjectProperty(None)
class myapp(MDApp):
def build(self):
self.theme_cls.theme_style = "Light"
self.theme_cls.primary_palette = "BlueGray"
return Builder.load_file("app.kv")
def logger(self):
if self.root.ids.user.text == 'admin' and self.root.ids.password.text=='admin':
self.root.ids.welcome_label.text = f'Sup {self.root.ids.user.text}!'
screen = Screen(name='screen2')
else:
self.root.ids.welcome_label.text = 'Wrong credentials'
if __name__ == "__main__":
myapp().run()
And here is my design kv file.
app.kv file:
my_layout:
screen_mngr: screen_mngr
ScreenManager:
id: screen_mngr
home: home
Screen:
id: home
name: 'home'
MDCard:
size_hint: None, None
size: 450, 600
pos_hint: {"center_x": 0.5, "center_y": 0.5}
elevation: 10
padding: 25
spacing: 25
orientation: 'vertical'
MDLabel:
id: welcome_label
text: "WELCOME"
font_size: 40
halign: 'center'
size_hint_y: None
height: self.texture_size[1]
padding_y: 15
MDTextField:
id: user
hint_text: "username"
icon_right: "account"
size_hint_x: None
width: 200
font_size: 18
pos_hint: {"center_x": 0.5}
MDTextField:
id: password
hint_text: "password"
icon_right: "eye-off"
size_hint_x: None
width: 200
font_size: 18
pos_hint: {"center_x": 0.5}
MDRoundFlatButton:
text: "LOG IN"
font_size: 12
pos_hint: {"center_x": 0.5}
on_press: screen2
Screen:
id: screen2
name: 'screen2'
MDRoundFlatButton:
text: "This is Second Screen\nGo to Screen1"
size_hint: 0.2,0.1
pos_hint: {"center_x":0.5,"y":0.5}
on_press: screen_mngr.current = "home"
A couple small problems:
First, in your kv you need to have your LOG IN Button call the code to handle the log in, like this:
MDRoundFlatButton:
text: "LOG IN"
font_size: 12
pos_hint: {"center_x": 0.5}
on_press: app.logger() # call the method that handles login
Then, in that method, you can change to the screen2 by using the current property of the ScreenManager, like this:
def logger(self):
if self.root.ids.user.text == 'admin' and self.root.ids.password.text=='admin':
self.root.ids.welcome_label.text = f'Sup {self.root.ids.user.text}!'
self.root.ids.screen_mngr.current = 'screen2' # go to screen2
# screen = Screen(name='screen2')
else:
self.root.ids.welcome_label.text = 'Wrong credentials'

Popup in Kivy Python does not get dismissed for more than one time

I have made a popup to show up when an error occurs, with a button that closes the popup when clicked. All worked well until I tried getting an error for 2 times. In the second time the popup did not close and I am wondering if there is a way to work it out.
This is the python code:
class MyPopup(Popup):
filechooser= ObjectProperty(None)
class ErrorPopup(Popup):
filechooser= ObjectProperty(None)
class Main(FloatLayout):
audio_check= ObjectProperty(None)
video_check= ObjectProperty(None)
spinner_id= ObjectProperty(None)
yt_link=ObjectProperty(None)
name=ObjectProperty(None)
error_label=ObjectProperty(None)
def submit_text(self):
self.error_popup=MyApp()
if self.audio_check.active and self.video_check.active:
try:
YouTube(self.yt_link.text).streams.filter(res=self.spinner_id.text).first().download(path, filename=self.name.text+'.mp4')
except:
self.error_popup.error()
elif self.audio_check.active and not self.video_check.active:
try:
YouTube(self.yt_link.text).streams.filter(only_audio=True).first().download(path, filename=self.name.text+'.mp3')
except:
self.error_popup.error()
elif not self.audio_check.active and self.video_check.active:
try:
YouTube(self.yt_link.text).streams.filter(res=self.spinner_id.text, only_video =True).first().download(path, filename=self.name.text+'.mp4')
except:
self.error_popup.error()
elif not self.audio_check.active and not self.video_check.active:
self.error_popup.error()
def spinner_clicked(self, value):
self.ids.spinner_id.text= value
class MyApp(App):
def build(self):
return Main()
def open_popup(self):
self.popup = MyPopup()
self.popup.open()
def dismiss_popup(self):
self.popup.dismiss()
global path
path=self.popup.filechooser.path
def error(self):
self.error_popup= ErrorPopup()
self.error_popup.open()
def try_again(self):
self.error_popup.dismiss()
print("Andrew")
if __name__ == '__main__':
MyApp().run()
This is the kivy code:
<MyPopup>:
auto_dismiss: False
title: "Select a folder"
filechooser: filechooser
FloatLayout:
FileChooserIconView:
id: filechooser
Button:
id:my_button
text: 'Save'
size_hint: (0.1, 0.1)
pos_hint:{'x': 0, 'y': 0}
on_release: app.dismiss_popup()
<ErrorPopup>:
title: "Error"
Button:
text: "Try Again"
on_release: app.try_again()
<Main>:
audio_check: audio_check
video_check: video_check
spinner_id: spinner_id
yt_link: yt_link
name: name
folder_button: folder_button
BoxLayout:
orientation:'vertical'
cols: 4
Label:
text: "YouTube Downloader"
halign: 'center'
bold: True
font_size:'50sp'
TextInput:
id: yt_link
size_hint: (.5, .2)
multiline: False
hint_text: 'Enter the link of the Youtube video you want to download.'
pos_hint: {"x": 0.25}
TextInput:
id: name
size_hint: (.5, .2)
multiline: False
hint_text: 'Enter the name you want the file to have.'
pos_hint: {"x": 0.25}
BoxLayout:
cols:4
BoxLayout:
Label:
text: "Audio:"
halign: 'center'
font_size:'20sp'
CheckBox:
id: audio_check
BoxLayout:
Label:
text: "Video:"
halign: 'center'
font_size:'20sp'
CheckBox:
id: video_check
FloatLayout:
Spinner:
id: spinner_id
text: "Quality"
values: ['144p', '240p', '360p', '480p', '720p', '1080p', '1440p', '2160p']
on_text: root.spinner_clicked(spinner_id.text)
size_hint: None, None
size: 130, 50
pos_hint: {'x': .2, 'y':.4}
FloatLayout:
Button:
id:folder_button
text: 'Folder'
on_release: app.open_popup()
size_hint: None, None
size: 130, 50
pos_hint: {'x':0.2,'y':.4}
Button:
text: "Submit"
size_hint: (.5, .2)
pos_hint: {"x": 0.25}
on_release: root.submit_text()
The problem is in your submit() method. The code:
self.error_popup=MyApp()
is creating a new instance of MyApp, then the code:
self.error_popup.error()
is calling the error() method from that new instance of MyApp. You need to be calling the methods of the App that you are running. To do that, just remove the line:
self.error_popup=MyApp()
And replace:
self.error_popup.error()
with:
App.get_running_app().error()
This makes sure that you are calling the error() method of the App that you are running.

trying to set the same task but with different data each one in kivy

how i can run the same task with different data? i tried everything but is always the same. for example i put "yes" in one textinput and "no" in another and start printing it in one thread, but if i try to erase the data of the input for start a new one with other data it will also erase it from the thread that is already running, for example again "yes" and "no" if i delete "yes" the thread will also delete it and continue printing only "no". how can i resolve this? Code is down below
.kv file
<Request>:
name: 'request'
MDTextField:
id: emailrequest
hint_text: "E-mail"
pos_hint: {"center_x": 0.5, "center_y": 0.70}
size_hint_x: None
width: 300
on_text_validate: root.calling()
MDTextField:
id: seguridad
hint_text: "At what age did you give your first kiss?"
pos_hint: {"center_x": 0.5, "center_y": 0.60}
size_hint_x: None
width: 300
on_text_validate: root.calling()
MDLabel:
text: "Password request"
pos_hint: {"center_x": 0.7, "center_y": 0.80}
size_hint_x: None
width: 500
MDRaisedButton:
text: "Request"
pos_hint: {"center_x": 0.5, "center_y": 0.45}
on_press: root.imprimir()
MDRectangleFlatButton:
text: "Go back"
pos_hint: {"center_x": 0.3, "center_y": 0.85}
on_press: root.manager.current = 'login'
.py file
class Request(Screen):
def __init__(self, **kw):
super().__init__(**kw)
def calling(self):
_thread.start_new_thread(self.imp2, ("thread", 4))
def imp2(self, *args):
Clock.schedule_interval(self.pal, 1/1)
def pal(self, *args):
palabra = self.ids.emailrequest.text
palabra2 = self.ids.seguridad.text
print(palabra, palabra2)
pass
sm = ScreenManager()
sm.add_widget(Request(name='request'))
class work(MDApp):
def build(self):
self.theme_cls.primary_palette = "Yellow"
screen = Builder.load_string(screen_helper)
return screen
work().run()

Python - Kivy: KeyError with Text Input using ScreenManager

I'm working on a project using Kivy with ScreenManager (and KivyMD).
The reason for using this is my need of making an application with multiple screens. My goal is to get User Input from an existing MDTextField by (a string) ID and then Print it out.
However, I keep getting this Error: File "<string>", line 37, in <module> File "c:/Users/admin/Desktop/myApp/Main.py", line 75, in loginFunction username = mainScreenInstance.ids["input_username"].text KeyError: 'input_username'.
I have searched for answers all over the internet and StackOverflow, but no solution seems to work for me. Maybe there's someone who can help me with my problem and make my code work.
Anyway, here's my short & simple Code:
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
import sys
screen_helper = """
ScreenManager:
MenuScreen:
name: 'menu'
SecondScreen:
name: 'second'
<MenuScreen>:
MDToolbar:
title: "Menu Screen"
pos_hint: {"top": 1}
anchor_title: "center"
md_bg_color: (0/255, 0/255, 0/255, 1)
Image:
source: "Logo_h_black.png"
pos_hint: {"center_x": 0.5, "center_y": 0.75}
size_hint_x: (0.25)
size_hint_y: (0.25)
MDTextField:
id: input_username
hint_text: "Username"
size_hint: (0.5, 0.1)
pos_hint: {"center_x": 0.5, "center_y": 0.55}
font_size: 20
mode: "rectangle"
MDTextField:
id: input_password
hint_text: "Password"
size_hint: (0.5, 0.1)
pos_hint: {"center_x": 0.5, "center_y": 0.45}
font_size: 20
mode: "rectangle"
MDFillRoundFlatButton:
text: "LOG IN"
font_size: 17
pos_hint: {"center_x": 0.5, "center_y": 0.25}
on_press: app.loginFunction()
<SecondScreen>:
MDLabel:
text: 'Profile'
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'menu'
"""
class MenuScreen(Screen):
pass
class SecondScreen(Screen):
pass
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SecondScreen(name='second'))
class myApp(MDApp):
def loginFunction(self, args=None):
if args is None:
args = sys.argv
mainScreenInstance = sm.get_screen('menu')
username = mainScreenInstance.ids["input_username"].text
password = mainScreenInstance.ids["input_password"].text
print (username)
print (password)
def build(self):
screen = Builder.load_string(screen_helper)
return screen
myApp().run()
The problem is that your loginFunction is trying to get the mainScreenInstance from sm, but sm is created before the screen_helper is loaded, so it doesn't know about the ids you have defined in your screen_helper. Also, that sm is not actually used as part of you GUI, it is just ignored (except for your reference to it in the loginFunction).
So, I recommend that you eliminate the following lines completely:
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SecondScreen(name='second'))
And change loginFunction to not use sm by replacing:
mainScreenInstance = sm.get_screen('menu')
with:
mainScreenInstance = self.root.get_screen('menu')
You're duplicating the widget definitions and then not using them in code. Finally, you're overwriting the Screen manager from the screen_helper string with a new instance. Tbh I did some of that when I started learning Kivy. The kv language is confusing at first but once you get it is beautiful. This version should work (I haven't tested it since I haven't found an easy way to install kivymd on my phone's dev env):
from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen
import sys
screen_helper = """
ScreenManager:
MenuScreen:
name: 'menu'
SecondScreen:
name: 'second'
<MenuScreen>:
MDToolbar:
title: "Menu Screen"
pos_hint: {"top": 1}
anchor_title: "center"
md_bg_color: (0, 0, 0, 1)
Image:
source: "Logo_h_black.png"
pos_hint: {"center_x": 0.5, "center_y": 0.75}
size_hint_x: (0.25)
size_hint_y: (0.25)
MDTextField:
id: input_username
hint_text: "Username"
size_hint: (0.5, 0.1)
pos_hint: {"center_x": 0.5, "center_y": 0.55}
font_size: 20
mode: "rectangle"
MDTextField:
id: input_password
hint_text: "Password"
size_hint: (0.5, 0.1)
pos_hint: {"center_x": 0.5, "center_y": 0.45}
font_size: 20
mode: "rectangle"
MDFillRoundFlatButton:
text: "LOG IN"
font_size: 17
pos_hint: {"center_x": 0.5, "center_y": 0.25}
on_press: app.loginFunction()
<SecondScreen>:
MDLabel:
text: 'Profile'
halign: 'center'
MDRectangleFlatButton:
text: 'Back'
pos_hint: {'center_x':0.5,'center_y':0.1}
on_press: root.manager.current = 'menu'
"""
class MenuScreen(Screen):
pass
class SecondScreen(Screen):
pass
class myApp(MDApp):
sm = None
def on_start():
self.sm = self.root
def loginFunction(self, args=None):
if args is None:
args = sys.argv
mainScreenInstance = self.sm.get_screen('menu')
username = mainScreenInstance.ids["input_username"].text
password = mainScreenInstance.ids["input_password"].text
print (username)
print (password)
# if self.is_login_correct(username, password):
# self.sm.current('second')
def build(self):
return Builder.load_string(screen_helper)
myApp().run()
Personally, I would move all login-related functions to the MenuScreen class, for consistency.

Can not load kivy file from kv variable

So I'm working on my first app and when learning about kivy and watching youtube videos I was seeing different ways people would load in their KV file and one was putting in in a variable and returning it from the build method. Anytime I do this, no errors are necessarily thrown but it doesn't load the window properly and it makes no sense why the KV file isnt loading. If anyone can point me in the right direction I'd appreciate it, the code is below.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
class LoginLayout(Widget):
def login(self, **kwargs):
print("Login function working")
username = self.ids.username.text
password = self.ids.password.text
print(username)
print(password)
kv = Builder.load_file('loginScreen.kv')
class LoginScreen(App):
def build(self):
return kv
app = LoginScreen()
app.run()
kv file
<LoginLayout>:
BoxLayout:
orientation: 'vertical'
size: root.width, root.height
Label:
text: 'Username'
TextInput:
id: username
multiline: False
size_hint: (.5, .3)
pos_hint: {'center_x' : .5}
Label:
text: 'Password'
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}
on_release: root.login()
Button:
text: 'Create Account'
size_hint: (.2, .8)
pos_hint: {'center_x' : 0.5}
Button:
text: 'Forgot login Info'
size_hint: (.2, .8)
pos_hint: {'center_x' : 0.5}
The problem is that your kv does not define a root widget, it only defines rules for how to build the LoginLayout. An easy fix is to remove the <> from around LoginLayout.

Categories

Resources