Updating Created Custom KivyMD Property - python

I'm hoping there's a way to do this but not entirely sure. I want a way to update all of my labels after a button press. This is how I'm currently doing it which throws an error but I don't quite understand it.
At the moment I'm trying to add an id to the custom label and then call it but that doesn't work.
Python Code:
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Builder.load_file("main.kv")
def build(self):
self.theme_cls.theme_style = "Dark"
return self.screen
def button_press(self):
self.root.ids.custom_label.text = "Two"
if __name__ == '__main__':
MainApp().run()
Kivy File:
<MDLabel>
id: custom_label
text: "One"
halign: "center"
BoxLayout:
orientation: "vertical"
MDLabel
MDLabel
MDRoundFlatButton:
text: "Press"
pos_hint: {"center_x": 0.5, "center_y": 0.5}
on_release: app.button_press()

First things first you can't name a custom label, MDLabel.
restructure your code like so. Starting with your .kv file
<CustomLabel#MDLabel>
text: "One"
halign: "center"
BoxLayout:
orientation: "vertical"
CustomLabel:
id: custom_label_1
CustomLabel:
id: custom_label_2
MDRoundFlatButton:
text: "Press"
pos_hint: {"center_x": 0.5, "center_y": 0.5}
on_release: app.button_press()
The in your .py file
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Builder.load_file("main.kv")
def build(self):
self.theme_cls.theme_style = "Dark"
return self.screen
def button_press(self):
self.root.ids.custom_label_1.text = "Two"
self.root.ids.custom_label_2.text = "Two"
if __name__ == '__main__':
MainApp().run()
Hope that works. you can't have 2 widgets using the same id. so if you wanna change the the text of 2 custom labels you gonna have to reference them individually

Related

Get Values between Kivy screens on button click

I have an Kivy app that has 2 screens. Screen 1 (ProblemWindow) will get the user input and screen 2 (StepsWindow) will show some images based on screen1 input. However, I need to pass one of the values (Spinner id: problem_id) from screen 1 (ProblemWindow) into screen 2 (StepsWindow) and also use the value in the python file for additional logic.
I can pass the value between the screens but I am not able to use it in python. I need to get the updated Label Text of the StepsWindow into my python code every time I change it in my ProblemWindow and press the button "ShowSteps".
Could anyone please guide me how to achieve this?
This is my python code:
# importing dependencies
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class WindowManager(ScreenManager):
pass
class ProblemWindow(Screen):
def selected_problem(self, value):
self.ids.click_label.text = f'selected problem: {value}'
return value
class StepsWindow(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# self.get_problem_name()
def get_problem_name(self, *args):
self.problem_name = self.ids.problem_name.text
print(self.problem_name)
kv = Builder.load_file('main.kv')
class main(App):
def build(self):
return kv
if __name__ == '__main__':
main().run()
This is my kv file for reference:
WindowManager:
id: window_manager
ProblemWindow:
id: pw
StepsWindow:
id: sw
<ProblemWindow>:
name: "problem_window"
GridLayout:
rows: 4
Label:
text: "TEST"
font_size: 24
Label:
id: click_label
text: "Select a problem"
Spinner:
id: problem_id
text: "Problems List (Click here)"
font_size: 24
values: ["Problem_1", "Problem_2"]
on_text: root.selected_problem(problem_id.text)
size_hint: 0.1, 0.1
width: 300
Button:
text: "Show Steps"
font_size: 28
size_hint: 0.2, 0.2
pos_hint: {"center_x": 0.5, "center_y": 0.5}
on_release:
app.root.current = "steps_window"
root.manager.transition.direction = "left"
<StepsWindow>:
name: "steps_window"
GridLayout:
rows: 3
size: root.width, root.height
Label:
id: problem_name
text: root.manager.ids.pw.ids.problem_id.text
font_size: '42'
size_hint_y: None
height: 50
Button:
text: "Back"
on_release:
app.root.current = "problem_window"
root.manager.transition.direction = 'right'
Something like this?
# importing dependencies
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class WindowManager(ScreenManager):
pass
class ProblemWindow(Screen):
def selected_problem(self, value):
self.value = value
self.ids.click_label.text = f'selected problem: {value}'
return value
#logic here
def logic_here(self):
if self.value == "Problem_1":
print(f'The solution for "Problem_1" is:')
else:
print(f'The solution for "Problem_2" is:')
class StepsWindow(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# self.get_problem_name()
def get_problem_name(self, *args):
self.problem_name = self.ids.problem_name.text
print(self.problem_name)
kv = Builder.load_file('main.kv')
class main(App):
def build(self):
return kv
if __name__ == '__main__':
main().run()
And for the <ProblemWindow> button in the .kv file add this:
root.logic_here()

Apply screen transition direction to an element card inside an expansion panel and screen kivy/kivymd

I am a new programmer, working on a Kivy app and I need to use screens (screen manager), the issue is that I am using element cards as but buttons, and it is supposed that when using on_release it will change the screen, however if I include root.manager.transition.direction = "left" I get the error AttributeError: 'MyContentComunes' object has no attribute 'screen'. I don't know if anyone could help me with this.
Here is a piece of code.
from kivy.app import App
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivymd.uix.expansionpanel import MDExpansionPanel, MDExpansionPanelTwoLine
from kivy.core.window import Window
from kivymd.color_definitions import colors
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.screen import MDScreen
KV = '''
WindowManager:
id: screen_manager
PrincipalWindow:
LenghtWindow:
<PrincipalWindow>:
name: "principal"
BoxLayout:
MDToolbar:
title: "Conversor"
pos_hint: {"top": 1}
ScrollView:
pos_hint: {"center_x": .5, "center_y": .5} #UbicaciĆ³n
size_hint: 0.90, 0.75
GridLayout:
halign: 'center'
cols: 1
adaptive_height: True
id: box
<LenghtWindow>:
name: "Lenght"
md_bg_color: 0.34,0.024,0.292,1
MDBoxLayout:
orientation: "vertical"
MDToolbar:
title: "Conversor"
pos_hint: {"top": 1}
Button:
text: "Back"
font_size: 32
on_release:
app.root.current = "principal"
root.manager.transition.direction = "right"
<MyContentComunes>:
orientation: 'vertical'
padding: dp(10)
spacing: dp(10)
size_hint_y: None
height: self.minimum_height
ElementCard:
image:"1490820814-13_82405.png"
text:"Longitud"
id:longitud
on_release:
app.root.current = "Lenght"
root.manager.transition.direction = "left"
<ElementCard#MDCard>:
md_bg_color: app.theme_cls.primary_color
padding: dp(15)
spacing: dp(15)
size_hint: 1, None
ripple_behavior: True
# on_release: print("worked")
image:''
text:""
items_count:""
subtext:''
orientation: "vertical"
Image:
source:root.image
halign:"center"
padding: dp (4)
spacing: dp(4)
MDBoxLayout:
orientation: "vertical"
MDLabel:
halign:"center"
text:root.text
MDLabel:
halign:"center"
font_style:"Caption"
text:root.subtext
'''
Window.size = 324, 720
class Conversor(MDApp):
def build(self):
self.theme_cls.primary_palette = "Blue"
return Builder.load_string(KV)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.list_items = [] # Dictionary where the items are stored
self.category_list1 = ['Comunes']
def on_start(self):
for category in self.category_list1:
self.root.get_screen('principal').ids.box.add_widget(
MDExpansionPanel(
icon="3.png",
content=MyContentComunes(),
panel_cls=MDExpansionPanelTwoLine(
text=category,
secondary_text="Ver las unidades"
)
)
)
class MyContentComunes(BoxLayout):
pass
class PrincipalWindow(Screen):
pass
class LenghtWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
Conversor().run()
The element card is inside an expansion panel, which is also inside a screen.
Try adding Screen to class MyContentComunes.
class MyContentComunes(BoxLayout, Screen):
pass
And I recommend you to organize your code like this. It will make your work more eaisier.
from kivy.app import App
from kivymd.app import MDApp
# import libraries ...
KV = '''
# Your KV File
'''
Window.size = 324, 720
class MyContentComunes(BoxLayout, Screen):
pass
class PrincipalWindow(Screen):
pass
class LenghtWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class Conversor(MDApp):
def build(self):
self.theme_cls.primary_palette = "Blue"
return Builder.load_string(KV)
def __init__(self, **kwargs):
# Code
.
.
.
def on_start(self):
# Code
.
.
.
Conversor().run()

KivyMD: Change font_name of MDLabel

I would like to change font_name property of MDLabel. But I want to add this component dynamically, in .py file, not .kv.
So when I write something like this:
main.py
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
class MainApp(MDApp):
def build(self):
Builder.load_string(KV)
otherLabel = MDLabel(
text="one more text",
halign="center",
font_name="18328",
font_size="50sp"
)
screen.add_widget(
otherLabel
)
return screen
MainApp().run()
it does not help. How can I implement font applying dynamically?
KV string
KV = '''
Screen:
MDCard:
id: box
orientation: "vertical"
padding: "8dp"
size_hint: None, None
size: "280dp", "180dp"
pos_hint: {"center_x": .5, "center_y": .5}
MDLabel:
text: "Hello"
theme_text_color: "Secondary"
size_hint_y: None
height: self.texture_size[1]
font_name: '18328.ttf'
MDSeparator:
height: "1dp"
MDLabel:
text: "Body"
MDLabel:
text: "Hello"
font_name: 'BebasNeue-Regular.otf'
font_size: "50sp"
'''
Create a reference in the screen itself (also you don't need to use .ttf, the library adds it automatically, just the filename without extension is fine)
class MyScreen(Screen):
my_MD_Label = None
def on_kv_post(self, instance):
self.my_MD_Label = MDLabel(
text="one more text"
font_name="18328"
)
self.ids.box.add_widget(self.my_MD_Label)
And whenever you want to change it, you can simply do this
self.my_MD_Label.font_name = "new_font" # When in same screen
self.manager.get_screen('MyScreen').my_MD_Label.font_name = "new_font" #When in other screen

Problem about kivy.uix.widget.WidgetException

I am new to python and kivymd and I am trying to develop a program for data entry. However, when I create a drop-down menu for a selection, an error occurred and I can't update the value of the text field after I select an item.
Here is the python code:
from kivymd.app import MDApp
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.properties import ObjectProperty
from kivymd.uix.menu import MDDropdownMenu
class AMRMenu(Screen):
def GIbutton(self):
sm.current = 'GI'
class GIWindow(Screen):
weather = ObjectProperty(None)
menu_weather_items = [{"text":"Sunny"},{"text":"Cloudy"},{"text":"Raining"}]
menu_FeedResponse_items=[{"text":"High"},{"text":"Medium"},{"text":"Low"}]
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.menu = MDDropdownMenu(
items=self.menu_weather_items,
width_mult=4,
caller = self.weather,
callback=self.set_item)
def set_item(self, instance):
def set_item(interval):
self.weather.text = instance.text
self.menu.dismiss()
Clock.schedule_once(set_item, 0.5)
class WindowManager(ScreenManager):
pass
sm = WindowManager()
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
kv = Builder.load_file("FR.kv")
def build(self):
screens = [AMRMenu(name = "menu"), GIWindow(name = "GI")]
for screen in screens:
sm.add_widget(screen)
sm.current = "menu"
return sm
if __name__ == "__main__":
MainApp().run()
And here is the kv. file:
<AMRMenu>:
name:"menu"
BoxLayout:
orientation: "vertical"
MDToolbar:
title: "Main Menu"
MDList:
OneLineListItem:
text: "General Information"
on_press: root.GIbutton()
OneLineListItem:
text: "Water Temperature"
OneLineListItem
text: "Feeding Amount"
OneLineListItem:
text: "Residue and Feeding response"
OneLineListItem:
text: "Dead fish"
OneLineListItem:
text: "Sell/Use"
<GIWindow>
name: "GI"
weather: weather
ScrollView:
id: screen
MDBoxLayout:
orientation: 'vertical'
adaptive_height: True
MDTextField:
id: weather
pos_hint: {'center_x': .5, 'center_y': .5}
hint_text: "Weather"
icon_right: "arrow-down-drop-circle-outline"
input_filter: lambda text, from_undo: text[:5 - len(self.text)]
on_focus: root.menu.open()
Here is the error message:
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/kivy/core/window/__init__.py", line 1297, in add_widget
(widget, widget.parent)
kivy.uix.widget.WidgetException: Cannot add <kivymd.uix.menu.MDDropdownMenu object at 0x7fd2fa31a6e0> to window, it already has a parent <kivy.core.window.window_sdl2.WindowSDL object at 0x7fd2f65826e0>
I don't know why I made this happen. It helps a lot if someone figures it out. Thank you for reading this question
The problem is that your kv line:
on_focus: root.menu.open()
is opening the menu every time the focus changes. So, it tries to open the menu even when the focus becomes false. An easy fix is to just open the menu when focus is True`:
on_focus: if self.focus: root.menu.open()

Use On_Press Event to Change Screen without KV Language for Dynamically Created Buttons

Question:
How do you use an On-Press event to change screen for dynamically created buttons in python, and without using KV language?
Goal:
Be able to navigate to new screen from clicking on dynamically created button,
[without needing to create button in Kivy, and still getting to use Screenmanager in both Python and Kivy (not sure if you have to stick with either Python or Kivy throughout entire program?]
Things I've already tried:
Using button_share.bind(on_press = self.changer), then this:
def changer(self,*args):
ScreenManager()
screenmanager.current = 'MainScreen'
But I get the error ScreenManagerException: No Screen with name "MainScreen".
Suspicion:
I think this is because I'm creating a new instance of ScreenManager, instead of referencing the existing one. To combat this issue, I considered instantiating Screenmanager() in the App class, then referencing that instantiation in the my button def changer(self, *args) method, but that's useless if it's not the same ScreenManager I'm actually using for all my screens. And those are all defined in KV language. I wouldn't be able to switch them all over without a substantial amount of effort.
Using:
button_share.bind(on_press=partial(app.sm.setter('current'), (app.sm, "MainScreen")))`
But the error I get here is ValueError: ScreenManager.current accept only str
Below is a fully runnable example:
Note: In this example, I want to click 'Continue Editing' button, then click on 'Test 1', 'Test 2' or 'Test 3' button and have it take me to another screen.
Python Code:
from kivy.app import App
# kivy.require("1.10.0")
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.uix.widget import Widget
#from kivy.base import runTouchApp
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from functools import partial
class ScrollableLabelDataEntryInstructions(BoxLayout):
pass
class NewGarageScreen(Screen):
pass
class ContinueEditingScreen(Screen):
pass
class GarageNameBoxLayout(BoxLayout):
box_share2 = ObjectProperty()
sm = ScreenManager()
def __init__(self, **kwargs):
super(GarageNameBoxLayout, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 5)
def create_button(self, *args):
self.box_share2.clear_widgets()
app = App.get_running_app()
#put GarageNameStartList data into app class, then pull from it in this class
top_button_share = 1.1
color = (.4, .4, .4, 1)
for i in range(len(app.GarageNameStartList)):
top_button_share -= .4
id_ = app.GarageNameStartList[i]
button_share = Button(background_normal='',
background_color = color,
id = id_,
pos_hint = {"x": 0, "top": top_button_share},
size_hint_y = None,
height = 60,
font_size = 30,
text = app.GarageNameStartList[i])
button_share.bind(on_press = self.changer)
#button_share.bind(on_press=partial(app.sm.setter('current'), (app.sm, "MainScreen")))
self.box_share2.add_widget(button_share)
def changer(self,*args):
ScreenManager()
#app = App.get_running_app()
screenmanager.current = 'MainScreen'
class BackHomeWidget(Widget):
pass
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("example_on_press.kv")
class MainApp(App):
GarageNameStartList = ["Test1", "Test2", "Test3"]
def Update_GarageNameStartList(self, *args):
self.GarageNameStartList = ["Test1", "Test2", "Test3"]
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
KV Code:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
AnotherScreen:
NewGarageScreen:
ContinueEditingScreen:
<SmallNavButton#Button>:
font_size: 32
size: 125, 50
color: 0,1,0,1
<MedButton#Button>:
font_size: 30
size_hint: 0.25, 0.1
color: 0,1,0,1
<BackHomeWidget>:
SmallNavButton:
on_release: app.root.current = "main"
text: "Home"
pos: root.x, root.top - self.height
<MainScreen>:
name: "main"
FloatLayout:
MedButton:
on_release: app.root.current = "edit"
text: "Edit"
pos_hint: {"x":0.3728, "top": 0.4}
<AnotherScreen>:
name: "edit"
BackHomeWidget:
SmallNavButton:
on_release: app.root.current = "main"
text: "Back"
pos: root.x, root.top - (2.25*(self.height))
FloatLayout:
MedButton:
on_release: app.root.current = "continueediting"
text: "Continue Editing"
pos_hint: {"x":0.25, "top": 0.6}
MedButton:
on_release: app.root.current = "newgarage"
text: "Create New"
pos_hint: {"x":0.3728, "top": 0.4}
<NewGarageScreen>:
name: "newgarage"
BackHomeWidget:
SmallNavButton:
on_release: app.root.current = "edit"
text: "Back"
pos: root.x, root.top - (2.25*(self.height))
FloatLayout:
MedButton:
text: "1. Groundfloor"
pos_hint: {"x":0, "top": 0.6}
<GarageNameBoxLayout>:
box_share2: box_share2
ScrollView:
GridLayout:
id: box_share2
cols: 1
size_hint_y: None
size_hint_x: 0.5
spacing: 5
padding: 130
height: self.minimum_height
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
<ContinueEditingScreen>:
name: "continueediting"
GarageNameBoxLayout:
BackHomeWidget:
SmallNavButton:
on_release: app.root.current = "edit"
text: "Back"
pos: root.x, root.top - (2.25*(self.height))
Your code can be improved in the following things:
You do not have to create box_share2 in the .py since you're creating it in the .kv
When you use sm = ScreenManager() you are creating another ScreenManager different from the original, that is not necessary.
It is not necessary to use range and len, make your code less readable, you just have to iterate.
If we look at the structure of the .kv we see that the presentation object is the ScreenManager so you can get it via app.root.
Using the above your code the solution is:
[...]
class GarageNameBoxLayout(BoxLayout):
def __init__(self, **kwargs):
super(GarageNameBoxLayout, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 5)
def create_button(self, *args):
self.box_share2.clear_widgets()
app = App.get_running_app()
sm = app.root
#put GarageNameStartList data into app class, then pull from it in this class
top_button_share = 1.1
color = (.4, .4, .4, 1)
for text in app.GarageNameStartList:
top_button_share -= .4
id_ = text
button_share = Button(background_normal='',
background_color = color,
id = id_,
pos_hint = {"x": 0, "top": top_button_share},
size_hint_y = None,
height = 60,
font_size = 30,
text = text)
button_share.bind(on_press=lambda *args: setattr(sm, 'current', "main"))
self.box_share2.add_widget(button_share)
[...]

Categories