Kivy Access ids from .kv file to .py file - python

.
I'm new into kivy and I want to make an android app. I almost finish GUI, the front-end part, but I have a very big problem. I've searched all over the internet but without answer. I don't know how to access ids from .kv to use them into .py functions.
I've tried all of what I've found on the internet, but didn't work. I want to access ids from .kv file to work with them. For exemple, I have a profile screen, where user write his first name, and last name, and in the next page I want to show his first and last name by using a function.
Here's the .kv profile page:
<Profile>
FloatLayout:
canvas.before:
Color:
rgba:(1,1,1,1)
Rectangle:
source:"CreateProfileImg.png"
size: root.width, root.height
pos: self.pos
Label:
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .1
text:"Create your profile"
font_size: 65
font_name:"FreeSansBoldOblique-BYJ3.otf"
color: rgba(247,251,246,255)
id: profile_label
Label:
text: "First Name: "
font_size: 45
color: rgba(247,251,246,255)
size_hint: 0.1, 0.1
pos_hint: {"x":0.20, "top":0.8}
TextInput:
id: name
multiline: False
size_hint: 0.5, 0.1
pos_hint: {"x": 0.35, "top": 0.8}
Label:
text: "Last Name: "
font_size: 45
color: rgba(247,251,246,255)
size_hint: 0.1, 0.1
pos_hint: {"x":0.16, "top":0.7}
TextInput:
id: prenume
multiline: False
size_hint: 0.5, 0.1
pos_hint: {"x": 0.35, "top": 0.7}
Label:
text: "Currency: "
font_size: 45
color: rgba(247,251,246,255)
size_hint: 0.1, 0.1
pos_hint: {"x":0.18, "top":0.6}
Spinner:
id: moneda
text:"Select currency"
color: 0, 0, 0 ,1
background_normal:"MoneyButton.png"
size_hint: 0.5, 0.1
pos_hint: {"x":0.35, "top":0.6}
values: ['Ron', 'Euro', 'Dolar','Lira Sterlina']
sync_height: True
#on_text: root.currency_clicked(moneda.text)
GridLayout:
rows:1
pos_hint:{"top": .2, "left": 1}
size_hint: 1, .2
ImageButton:
source:"Next_Button_On_Press.png"
on_press:
self.source = "Next_Button_On_Release.png"
app.printname()
on_release:
self.source = "Next_Button_On_Press.png"
app.change_screen("page1")
I have more .kv files with lots of ids, but I think if I learn how to work with thise two, next will be easier.
I want to specify that I have a 'main.kv' which contains: 1. name
2.ids
from all over my .kv files. I use those ids to navigate between pages.
Here's the code:
#:include homescreen.kv
#:include page1.kv
#:include profile.kv
GridLayout:
cols: 1
ScreenManager:
id: screen_manager
HomeScreen:
name: "home_screen"
id: home_screen
Profile:
name: "profile"
id: profile
Page1:
name: "page1"
id: page1
Let me explain for the last time what I want to do, maybe will be usefull for you to understand and trying to help me. As you see, into 'profile' I have 3 ids. When get TextInput from user, those ids store the information. I want to use those information into next page, where I want to say [[[" Hello " + ids]]].
So, please help me!! Make me to understand!

I also had trouble with this as well when I was first leaning Kivy a couple years back, but I finally figured out the way. there is a bit of boilier-plate required to maintain the connection.
in this example my_label is a kivy id and I am connecting it to a Python object of the same name. this is done with the line: my_label: my_label
<Screen2>:
# Python: KIVY id(s)
my_label: my_label
BoxLayout:
Label:
text: "Screen2"
MDLabel:
id: my_label
text: "-"
and in the Python code matching that object there is a line in the class definitionl my_label: MDLabel
which is providing a type hint. If you are using an IDE such as PyCharm this can help you to have auto-complete in your code according to the object type.
class Screen2(Screen):
my_label: MDLabel
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.my_label.text = "test"
and the line self.my_label.text = "test" is of course to show how you can use the object.
you can also do more complicated things such as put multiple items from a kv layout into a list or even a dictionary.
kivy code:
# a list of spinners kv_spinner_list is a ListProperty
kv_spinner_list: [kv_spinner_0, kv_spinner_1, kv_spinner_2,]
and this can be a more organized way to bring multiple items into the Python code.

Related

Trying to call a layout class within a screen layout - kivy

Essentially, I have multiple screens, being controlled by the screen manager (primarily for logins), however on the main screen, I have it broken up with a header, nav bar, and a section for the body. In the body section is where I want to be able to call different layouts that I have classes for, and add and remove these as buttons on the nav bar are pressed.
Im using kivy to control all aspects of the gui, but am rather new to it. At the moment I have:
Builder.load_string('''
<MainMenu>:
FloatLayout:
canvas.before:
Color:
rgba: 0.235, 0.271, 0.302, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
rows: 4
Label:
size_hint: 1,0.12
text: "ProductName"
text_size: self.size
halign: 'left'
valign: 'middle'
font_size: 32
color: [0.114,0.18,0.224,1]
StackLayout:
size_hint: 1,0.10
orientation: 'lr-tb'
#Purchase Order System
Button:
id: butpos
text: "Purchase Order"
size_hint: 0.166,1
on_press: spaceholder.add_widget(pos)
#Asset Assignment System
Button:
id: butaas
text: "Asset Assignment"
size_hint: 0.166,1
#Review and Revise System
Button:
id: butrrs
text: "Review"
size_hint: 0.166,1
#Administrative System
Button:
id: butadm
text: "Administration"
size_hint: 0.166,1
#Analytics and Reporting System
Button:
id: butars
text: "Analytics"
size_hint: 0.166,1
#Placeholder, possibly documentation
Button:
id: butbut
text: "Placeholder"
size_hint: 0.166,1
Label:
canvas.before:
Color:
rgba: 0.259, 0.643, 0.937, 1
Rectangle:
pos: self.pos
size: self.size
size_hint: 1,0.005
Widget:
id: spaceholder
<PurchaseOrder>
id: pos
orientation: 'lr-tb'
#Asset Details
Label:
BoxLayout:
orientation: 'vertical'
Label:
text: "Asset Requirements"
Label:
text: "Asset1: "
TextInput:
id: as1count
Label:
text: "Asset2: "
TextInput:
id: as2count
''')
class MainMenu(Screen):
pass
class PurchaseOrder(StackLayout):
pass
class MainApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainMenu(name='mainmenu'))
return sm
if __name__ == '__main__':
MainApp().run()
PurchaseOrder is a class that Id like to be added or removed, much like classes I intend to set up for the other navigation options. Ive tried a number of methods, but I think im missing something. Im either being thrown with 'pos' is not defined, or that 'spaceholder' has wrong attributes when ive tried different combinations.
You can make that work by modifying the Purchase Order Button:
on_press: spaceholder.add_widget(Factory.PurchaseOrder())
That requires an addition to your kv to import Factory:
#:import Factory kivy.factory.Factory
I would recommend using a Layout for your spaceholder to take advantage of their sizing and positioning capabilities. For example, using an AnchorLayout automatically centers its child (but is limited to a single child). Like this:
AnchorLayout:
id: spaceholder

Kivy - Referencing IDs of Subclasses (Nested IDs)

I'm creating a kivy app right now. I'm quite new to Kivy and kv lang and there doesn't seam to be much rumour about it although i find it great to seperate code logic and layout, especially after some pygame development.
So to my actuall problem: I have a wiki-style screen for screenmanager, consisting out of a BoxLayout:
Title as Label
Scrollable Label for the Text (later , there shall be displayed a nested kv file)
Buttons for Navigation (scroll up and go back to main screen)
Now I'm recreating the Navigation buttons to be floating type as known from many webpages and apps. Problem is, I suddendly cant reference my ScrollView anymore. Any help or suggestions?
<Wiki>:
name: "wiki"
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: "picture.jpg"
BoxLayout:
id: box
orientation: "vertical"
padding: 10
spacing: 10
Label:
font_size: 20
size_hint_y: .1
text: root.country_text
ScrollView:
id: scrlvw
BackgroundLabel:
background_color: 220,220,220, 0.5
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
halign: "left"
valign: "top"
text: root.wiki_text
FloatLayout:
size_hint_y: .1
Button:
size_hint_x: .2
pos_hint: {"center_x": .25, "center_y": .5}
text: "Back"
on_release:
app.root.current = "main"
root.manager.transition.direction = "right"
FloatButton:
size_hint_x: .2
pos_hint: {"center_x": .75, "center_y": .5}
text: "Up"
on_release:
widget = BoxLayout()
widget.ids.scrlvw.scroll_y = 0
Before, when it worked, it was:
BoxLayout:
FloatLayout:
size_hint_y: .1
Button:
size_hint_x: .2
pos_hint: {"center_x": .25, "center_y": .5}
text: "Back"
on_release:
app.root.current = "main"
root.manager.transition.direction = "right"
Button:
size_hint_x: .2
pos_hint: {"center_x": .75, "center_y": .5}
text: "Up"
on_release:
scrlvw.scroll_y = 0
Well as its just a design question, I guess i temporary have to dismiss the floating design. But I would be so pleased if you could help me understand this language better.
As long as the 'kv' code described as "when it worked" is still in the same <Wiki> rule, it should still work. The newer kv code will never work as you are trying to create a new BoxLayout and reference an id in it. That has two problems:
The newly created BoxLayout is not the BoxLayout instance that appears in your GUI, so any changes to it will have no effect on what appears in the display.
Unless you have a <BoxLayout> rule in your kv, the newly created BoxLayout will not have a scrlvw id.
The ids defined within a kv rule are available for use only within that rule. See the documntation.

Kivy can't get text input using ScreenManager

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

KivyMD Dynamic Tab Management with different 'iterations' of tabs

I've used the code from KivyMD documents about 'Dynamic Tab Management' so users can add / delete tabs. However, each of these tabs that are created are obviously identical and therefore the widgets that I put in it are too. This means that if I'm trying to take the id of said widget from e.g Tab 3, there isn't a way to do so as it is the same id as the widget in e.g Tab 1. Here is the code:
.py file
def on_start(self):
self.add_tab()
def get_tab_list(self):
print(self.root.ids.addworkouts.ids.tabs.get_tab_list())
def add_tab(self):
self.index += 1
self.root.ids.addworkouts.ids.tabs.add_widget(Tab(text=f"Exercise {self.index}"))
def remove_tab(self):
self.index -= 1
self.root.ids.addworkouts.ids.tabs.remove_widget(
self.root.ids.addworkouts.ids.tabs.get_tab_list()[0]
)
.kv file
<AddWorkouts>
name: 'AddWorkouts'
tabs: tabs
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: ' '#app.getWorkoutName()
type: 'top'
left_action_items: [['keyboard-backspace', lambda x: app.goBacktoMyWorkouts()]]
#md_bg_color: app.theme_cls.accent_color
elevation: 10
FloatLayout:
canvas:
Color:
rgba: 0, 0, 0.5, 0.9
Rectangle:
pos: self.pos
size: self.size
MDTabs:
id: tabs
FloatLayout:
canvas:
Color:
rgba: 1, 1, 1, 1
Rectangle:
size: self.size
pos: self.pos
pos_hint: {'center_x': 0.5, 'y': 0.1}
size_hint: 0.8, 0.6
MDTextField:
pos_hint: {'x': 0.05, 'y': 0.8}
size_hint: 0.6, None
hint_text: 'Exercise Name'
helper_text_mode: 'on_focus'
required: 'True'
multiline: False
<Tab>
MDList:
MDBoxLayout:
adaptive_height: True
md_bg_color: 1, 1, 1, 1
MDFlatButton:
text: "ADD EXERCISE"
text_color: 16/255, 167/255, 249/255, 1
on_release: app.add_tab()
MDFlatButton:
text: "REMOVE LAST EXERCISE"
text_color: 16/255, 167/255, 249/255, 1
on_release: app.remove_tab()
Tab 1, where the input in MDTextField is 'Hi':
Tab2, where the input in MDTextField has duplicated from Tab:
Is there anyway of still having the users being able to add and delete Tabs, but make it so all the widgets (like the MDTextField in the .kv file) have different ids so I can access the user input from them? Thank you in advance, if this question has been worded badly please just ask for any more info!
The ids are only populated for widgets created in a kv rule. So any Tab created outside of the .kv file will not be entered into the ids dictionary. However, you can hack them into the ids by modifying your add_tab() method:
import weakref
def add_tab(self):
self.index += 1
new_tab = Tab(text=f"Exercise {self.index}")
new_id = 'tab_' + str(self.index)
self.root.ids.addworkouts.ids.tabs.add_widget(new_tab)
self.root.ids.addworkouts.ids.tabs.ids[new_id] = weakref.ref(new_tab)
This adds the new_id to the ids in the MDTabs.
To make the MDTextField part of the Tab, just add it to the <Tab> rule. Perhaps like this:
<Tab>
MDList:
MDTextField:
size_hint: 0.6, None
hint_text: 'Exercise Name'
helper_text_mode: 'on_focus'
required: 'True'
multiline: False
MDBoxLayout:
adaptive_height: True
md_bg_color: 1, 1, 1, 1
MDFlatButton:
text: "ADD EXERCISE"
text_color: 16/255, 167/255, 249/255, 1
on_release: app.add_tab()
MDFlatButton:
text: "REMOVE LAST EXERCISE"
text_color: 16/255, 167/255, 249/255, 1
on_release: app.remove_tab()
Of course, you would remove the MDTextField (and its enclosing FloatLayout) from the <AddWorkouts> rule.

Trying to make Kivy button pop-up a text box

Similar/relevant links that didn't help much:
How to get a text input box to display with Kivy?
https://kivy.org/doc/stable/api-kivy.uix.textinput.html
Getting started with Kivy: getting user input using Kivy
I have been at this for several hours and I have found questions similar to mine but nothing has actually worked.
Here's what I'm trying to do: Create a button that when pressed, pulls up a text input box and then displays whatever you type on the button after a short string.
E.G.
Button starts like: "LP: "
You enter text: "4000"
Button now shows: "LP: 4000"
How would I accomplish this? If that's not totally possible I'd also be okay with just getting the input after hitting the button. I can't even seem to get that far. Very new to Kivy and fairly new to Python.
Button code (KV File):
<FloatLayout>:
Button:
name: 'LP'
id: LP
text: "LP: 4000"
size_hint: 0.14, 0.15
pos_hint: {"left": 1, "top": 0.8105}
Class (Py File):
class LPInput(Widget):
pass
Code for the input (KV File):
<LPInput>:
size_hint: 0.14, 0.15
pos_hint: {"left": 1, "top": 0.8105}
TextInput:
id: lifepoint
text: ""
Label:
id: currlp #not sure this is doing anything
text: "LP: "
I've written some other coding pieces to attempt to create this via different methods but in my frustration I saved over my file that was holding those so this is all I have at the moment.
The solution below is assuming LPInput is the root widget.
<LPInput>:
size_hint: 0.14, 0.15
pos_hint: {"left": 1, "top": 0.8105}
TextInput:
id: lifepoint
text: ""
Label:
id: currlp #not sure this is doing anything
text: "LP: "
<FloatLayout>:
Button:
name: 'LP'
id: LP
text: "LP: "
size_hint: 0.14, 0.15
pos_hint: {"left": 1, "top": 0.8105}
on_release:
self.text = "LP: " + app.root.ids.lifepoint.text
Kv language » Rule context
There are three keywords specific to Kv language:
app: always refers to the instance of your application.
root: refers to the base widget/template in the current rule
self: always refer to the current widget
Example
main.py
from kivy.lang import Builder
from kivy.base import runTouchApp
runTouchApp(Builder.load_string('''
#:kivy 1.11.0
FloatLayout:
TextInput:
id: lifepoint
text: ""
size_hint: 0.14, 0.15
pos_hint: {"left": 1, "top": 0.8105}
Button:
name: 'LP'
id: LP
text: "LP: "
size_hint: 0.14, 0.15
pos_hint: {"right": 1, "top": 0.8105}
on_release:
self.text = "LP: " + root.ids.lifepoint.text
'''))
Output

Categories

Resources