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.
Related
.
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.
Python kivy 2.1.0
In the code a popup opens when you press the button set credentials
Main layout
In the popup I want the user to type details and when they press set credentials I want to dismiss the popup and execute a funcction inside the on_release event of the popup or maybe pass those details to mainLayout somehow. The problem is I think kivy doesn't relate the popup box to the mainLayout. But I need those details in the mainLayout so that so that I can use them there (pressing button or showing the details in the log may be)
pop layout
Here's my .py file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
#set screen ratio
from kivy.core.window import Window
Window.size = (480/1.8, 800/1.8)
Builder.load_file('ui.kv')
class FaucetLayout(Widget):
def set_cred(self, cred):
print(cred)
pass
class DollarApp(App):
def build(self):
return FaucetLayout()
if __name__ == '__main__':
DollarApp().run()
Heres my .kv file:
#: import Factory kivy.factory.Factory
<CredPopup#Popup>
auto_dismiss: False
title: "Type your Credentials"
size_hint: 1, .5
pos_hint: {'center_x': .5, 'top': .95}
on_dismiss: root.set_cred({'username':username.text, 'password':password.text})
GridLayout:
cols:1
padding: 10
spacing: 15
size: self.width, self.height
TextInput:
hint_text: "type your username"
hint_text_color: 0, 0, 0, .5
id: username
multiline: False
size_hint: 1, .1
focus: True
TextInput:
hint_text: "type your password"
hint_text_color: 0, 0, 0, .5
id: password
multiline: False
size_hint: 1, .1
Button:
text: "Set Credentials"
size_hint: .4, .1
on_press: root.dismiss()
<FaucetLayout>
GridLayout:
size: root.width, root.height
cols: 1
spacing: 10
Label:
id: faucet_log
text: '[color=000]log will appear here[/color]'
markup: True
background_color: 1, 1, 1, 1
size_hint: .5, None
height: root.height/2
canvas.before:
Color:
rgba: self.background_color
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
orientation: 'vertical'
size: root.width, self.height
padding: 30
spacing: 25
Button:
text: 'Set Credentials'
size_hint: .6, None
height: 40
pos_hint: {'center_x': .5}
on_release: Factory.CredPopup().open()
Button:
text: 'Start'
size_hint: .6, None
height: 40
pos_hint: {'center_x': .5, 'center_y':.9 }
Getting this error:
File "t:\Codes\Kivy\dollar\ui.kv", line 9, in
on_dismiss: root.set_cred({'username':username.text, 'password':password.text}) File "kivy\weakproxy.pyx", line 32, in
kivy.weakproxy.WeakProxy.getattr AttributeError: 'CredPopup'
object has no attribute 'set_cred'
So sorry if I couldn't express myself. I am new to asking questions here. I'm not a pro python programmer, just try to solve some daily personal task with python by following tutorials on the internet.
Got answer from discord.
root refers to the base of the rule it is in, you could use app.root in this case instead to use to refer to the app's root widget .
<CredPopup#Popup>
auto_dismiss: False
title: "Type your Credentials"
size_hint: 1, .5
pos_hint: {'center_x': .5, 'top': .95}
on_dismiss: app.root.set_cred({'username':username.text, 'password':password.text})
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
Good Morning,
I work on my cross-platform app project. I've got a trouble with size management in KivyMD.
I would like to change the size and position of my widgets as well as this I want to set size_hint into (None, None) in order to have a right working on cross-platform devices. I try to change the size with size parameter but this only works on the y-axis. X-axis is still in the beginning position. I would like to ask about another widgets like icons, FloatLayout layers how to set size_hint to None and also change the size in order to have a right working on cross-platform devices.
#: import utils kivy.utils
MDScreen:
name: "LoginPage"
on_enter:
app.animationBack1(back1)
app.animationBack2(back2)
app.animationIcon(userIcon)
app.animationLoginLabel(loginLabel)
MDFloatLayout:
MDFloatLayout:
id: back1
size_hint_y: .3
pos_hint: {'center_y': 1.5}
canvas.before:
Color:
rgb: utils.get_color_from_hex('#74A3FC')
Rectangle:
size: self.size
pos: self.pos
MDFloatLayout:
id: back2
size_hint_y: .6
pos_hint: {'center_y': 1.5}
canvas.before:
Color:
rgb: utils.get_color_from_hex('#74A3FC') # most
Ellipse:
size: self.size
pos: self.pos
MDIcon:
id: userIcon
halign: 'center'
icon: 'account-circle'
font_size: '70sp'
pos_hint: {'center_x': .5, 'center_y': .75}
opacity: 0 # visibility
MDLabel:
id: loginLabel
text: '[size=60][b]Login Page[/b][/size]'
markup: True
halign: 'center'
pos_hint: {'center_y': .8}
opacity: 0 # visibility
MDTextField:
id: email
hint_text: 'Enter Email'
required: True
helper_text_mode: 'on_error'
helper_text: 'Please, enter Your Email.'
color_mode: 'custom'
line_color_focus: utils.get_color_from_hex('#74A3FC')
current_hint_text_color: utils.get_color_from_hex('#74A3FC') # can be, otherwise after running app, hint_text will be grey
# after error rasing hint_text will be also customized
size: 800, 1
size_hint: None, None
pos_hint: {'center_x': .5, 'center_y': .45}
MDTextField:
id: password
hint_text: 'Enter Password'
password: True
required: True
helper_text_mode: 'on_error'
helper_text: 'Please, enter Your Password.'
pos_hint: {'center_x': .5, 'center_y': .3}
size: 800, 1
size_hint: (None, None)
line_color_focus: utils.get_color_from_hex('#74A3FC')
current_hint_text_color: utils.get_color_from_hex('#74A3FC')
color_mode: 'custom' # must be, otherwise on the second click line color
will customized
MDFillRoundFlatButton:
text: 'Login'
pos_hint: {'center_x': .5, 'center_y': 0.05}
size: 800, 100
size_hint: (None, None) #text never leaves the button-I don't know why
# theme_text_color: 'Error' #['Primary', 'Secondary', 'Hint', 'Error', 'Custom', 'ContrastParentBackground'] - only change text color
md_bg_color: utils.get_color_from_hex('#74A3FC')
try to use
root.width*i, root.height*j ##when dealing with widget size and pos it will get u what u want
I'm trying to do a video on top of a graph, with two buttons controlling the graph. I'd like one button to be at the left of the window, the other one at the right, both on the same row, and my problem is placing the - and + buttons.
I have the following kv file, the concerned buttons are the two Control class:
#:kivy 1.0.9
<Control>
canvas:
Rectangle:
size: 5, 5
pos: self.pos
<VideoWidget>:
video_player: video_handler
graph: graph_handler
data_plus: ctr_plus_handler
data_minus: ctr_minus_handler
BoxLayout:
orientation: 'vertical'
VideoHandler:
id: video_handler
state: 'play'
options: {'allow_stretch': True}
size_hint: 1, 0.45
on_touch_down: root.test()
on_position: root.move()
BoxLayout:
size_hint: 1, 0.1
orientation: 'horizontal'
Control:
id: ctr_minus_handler
size_hint: 0.1, 1
pos_hint: {'left': 0.1}
Control:
id: ctr_plus_handler
size_hint: 0.1, 1
pos_hint: {'right': 0.1}
GraphWidget:
id: graph_handler
size_hint: 1, 0.45
But both Control are taking half the width of the row, whatever I change... Any ideas ?
You can use an empty Widget to fill the blank space between the Controls like this:
BoxLayout:
size_hint: 1, 0.1
orientation: 'horizontal'
Control:
id: ctr_minus_handler
size_hint: 0.1, 1
Widget:
size_hint: 0.8, 1
Control:
id: ctr_plus_handler
size_hint: 0.1, 1
You have to use FloatLayout instead of BoxLayout with correct pos_hint values. Below is the snippet for the button widget part:
FloatLayout:
size_hint: 1, 0.1
orientation: 'horizontal'
Control:
id: ctr_minus_handler
size_hint: 0.1, 1
pos_hint: {'left': 1}
Control:
id: ctr_plus_handler
size_hint: 0.1, 1
pos_hint: {'right': 1}
Hope this would solve your issue.