How to send Only Selected value to another screen in KivyMD? - python

output KivyMD Programmers, im new in KivyMD....
on_start() list items are sucessfully showing and on_press sending a selected value too passValue() function....
but here now i wants open new Screen under passValue() function...and pass variable value's to new Screen....
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.lang import Builder
from kivymd.uix.list import OneLineListItem
#Builder String
helper_string = '''
ScreenManager:
Hello:
Bye:
<Hello>:
name: 'hello'
ScrollView:
MDList:
id: list
<Bye>:
name: 'bye'
MDLabel:
text:'Good Bye'
MDLabel:
id:'aaa'
text:""
'''
class Hello(Screen):
pass
class Bye(Screen):
pass
sm = ScreenManager()
sm.add_widget(Hello(name = 'hello'))
sm.add_widget(Bye(name = 'bye'))
class DemoApp(MDApp):
def build(self):
screen = Screen()
self.help_str = Builder.load_string(helper_string)
screen.add_widget(self.help_str)
return screen
def on_start(self):
for i in range(50):
item = OneLineListItem(text='Item ' + str(i),
on_release=lambda x, value_for_pass={i}: self.passValue(value_for_pass)
)
self.help_str.get_screen('hello').ids.list.add_widget(item)
def passValue(self, *args):
args_str = ','.join(map(str,args))
print(args_str)
self.help_str.get_screen('bye').manager.current = 'bye' #how to pass/send args_str's value to bye screen???
DemoApp().run()

Since It's not clear exactly where in your screen Bye you want some variable to be passed, let's suppose you want to pass a list's text to the MDLabel having text 'Good Bye' of the screen Bye by method passValue.
You may achieve this as follows:
First in the kvlang of screen Bye assign an id say, target to the MDLabel,
<Bye>:
name: 'bye'
MDLabel:
id: target
text:'Good Bye'
Then in your method passValue,
def passValue(self, *args):
args_str = ','.join(map(str,args))
print(args_str)
bye_screen = self.help_str.get_screen('bye')
bye_screen.manager.current = 'bye'
bye_screen.ids.target.text = args_str
As a side note you perhaps don't need (as you already defined that in kvlang) the following:
sm = ScreenManager()
sm.add_widget(Hello(name = 'hello'))
sm.add_widget(Bye(name = 'bye'))

Related

Kivy: how to access IDs created in python code

I have this code below. I've set id by python code, but I couldn't access.
def abrirReceita(self,instance):
instance.text = str(instance.ids)
I'd like to change the text with the number of the ID when I press.
Exemple: if I input the first button, change the text for '1', which is the ID I've passed.
from kivymd.app import MDApp
from kivymd.uix.boxlayout import BoxLayout
from kivymd.uix.floatlayout import FloatLayout
from kivymd.uix.list import TwoLineListItem
from kivymd.uix.textfield import MDTextField
from kivy.lang import Builder
import os
from kivy.core.window import Window
import sqlite3
KV = '''
ScreenManager:
Screen:
name: 'telaSelecionada'
BoxLayout:
orientation: 'vertical'
MDToolbar:
id: tb
title: 'Drinks'
md_bg_color: 0, 0, 0, 1
TelaSelecionada:
id: telaselecionada
<TelaSelecionada>:
ScrollView:
MDList:
id: mostraReceita
padding: '20dp'
'''
Window.softinput_mode = "below_target"
class TelaSelecionada(FloatLayout):
pass
class Aplicativo(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
i = 1
for x in range(5):
self.textLine = TwoLineListItem(text = 'change text', secondary_text = 'change text')
self.root.ids.telaselecionada.ids.mostraReceita.add_widget(self.textLine)
self.root.ids.telaselecionada.ids.mostraReceita.ids[i] = self.textLine
self.textLine.bind(on_release = self.abrirReceita)
i += 1
def abrirReceita(self,instance):
instance.text = str(instance.ids)
Aplicativo().run()
How do I access the IDs from python code?
I'd like to change the text with the number of the ID when I press.
Just pass the required args through method abrirReceita using functools.partial as,
def on_start(self):
...
self.textLine.bind(on_release = partial(self.abrirReceita, i))
i += 1
# Then in `abrirReceita` :
def abrirReceita(self, id_index, instance):
instance.text = str(id_index)
Note:
The ids are not used here at all (actually you don't need that here for this purpose) !
Update:
As, the Widget.id has been deprecated since version 2.0.0 you should use it only in kvlang not in python.
But that doesn't keep you from creating it as a dynamically added attribute. Thus you can do something like this (I modified the on_start method a little):
def on_start(self):
mostraReceita = self.root.ids.telaselecionada.ids.mostraReceita
for i, _ in enumerate(range(5), start=1):
# Or just,
# for i in range(1, 5+1):
self.textLine = TwoLineListItem(
text = 'change text',
secondary_text = 'change text',
)
self.textLine.index = i
# Or even,
# self.textLine.id = i
self.textLine.bind(on_release = self.abrirReceita)
mostraReceita.add_widget(self.textLine)
def abrirReceita(self,instance):
instance.text = str(instance.index) # or, str(instance.id)

The binding on button is confusing in Kivy

As a test, I prepared the following code,
Firstly, I set the button text from two function names add_front and add_back
Then, I get the function handle from the name and make a partial function to bind it to Buttons.
Though the binding seems ok, the results are random.
Anybody can help me out?
"""
#author:
#file:test-bind.py
#time:2022/01/3111:42
#file_desc
"""
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.clock import Clock
import random
from functools import partial
Builder.load_string("""
<MButton#Button>:
_cb:[]
<TestWin>:
inp1:inp1_
lbl1:lbl1_
btn1:btn1_
btn2:btn2_
orientation: 'vertical'
BoxLayout:
orientation:'horizontal'
TextInput:
id:inp1_
readonly: True
Label:
id:lbl1_
MButton:
id:btn1_
text: 'add_front'
MButton:
id:btn2_
text: 'add_back'
Button:
id:btn_cls_
text:'clear'
on_press:root.clear_elements()
Button:
id:btn_shuffle_
text:'Shuffle'
on_press:root.shuffle_btn()
TextInput:
multiline: True
text:'Usage: press <Shuffle> to randomize button function and set a random number in [0,9], press <add_front> or <add_back> buttons to insert to list'
""")
class Box:
def __init__(self):
self.elements =[]
def add(self,e,front=False):
if front:
self.elements.insert(0,e)
else:
self.elements.append(e)
def add_front(self,e):
print("add_front",e)
self.add(e,front=True)
def add_back(self,e):
print("add_back",e)
self.add(e,front=False)
class TestWin(BoxLayout):
inp1 = ObjectProperty()
lbl1 = ObjectProperty()
btn1 = ObjectProperty()
btn2 = ObjectProperty()
btn_bind = ObjectProperty()
def __init__(self, **kwargs):
super(TestWin, self).__init__(**kwargs)
self.box = Box()
Clock.schedule_interval(self.update_elements_display, 0.5)
def update_elements_display(self,*args):
self.lbl1.text = "%s"%str(self.box.elements)
pass
def clear_elements(self):
self.box.elements=[]
def shuffle_btn(self):
btn_txt_ = ["add_front", "add_back"]
random.shuffle(btn_txt_)
self.btn1.text = btn_txt_[0]
self.btn2.text = btn_txt_[1]
v = random.randint(0,9)
self.inp1.text= "%d"%v
# bind func
for btn in [self.btn1,self.btn2]:
# clear old bind firstly
for cb in btn._cb:
btn.funbind("on_press",cb)
btn._cb = []
# The following codes give wrong result
#foo_ = getattr(self.box, btn.text)
#foo = lambda elem, instance: foo_(elem)
#call_back_ = partial(foo, self.inp1.text)
# The following codes give correct result
if btn.text=="add_back":
call_back_ = partial(self.box.add_back, self.inp1.text)
elif btn.text =="add_front":
call_back_ = partial(self.box.add_front, self.inp1.text)
btn._cb.append(call_back_)
btn.fbind('on_press',call_back_)
print("bind to",call_back_)
class TestApp(App):
def build(self):
return TestWin()
if __name__ == '__main__':
TestApp().run()
Edit:
Modifying the following codes may give me correct result,
but I wonder why
# The following codes give wrong result
#foo_ = getattr(self.box, btn.text)
#foo = lambda elem, instance: foo_(elem)
#call_back_ = partial(foo, self.inp1.text)
# The following codes give correct result
if btn.text=="add_back":
call_back_ = partial(self.box.add_back, self.inp1.text)
elif btn.text =="add_front":
call_back_ = partial(self.box.add_front, self.inp1.text)
# The following doesn't work
# foo_ = getattr(self.box, btn.text)
# foo = lambda elem, instance: foo_(elem)
# call_back_ = partial(foo, self.inp1.text)
#this is ok
call_back_ = partial(getattr(self.box, btn.text), self.inp1.text)

Kivy: How to change the attribute's value like text of label from current screen in another screen

I tried to change the text of another screen from current screen. But it didn't work
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import ObjectProperty
from kivy.lang.builder import Builder
Builder.load_string("""
<Input_Screen>:
input: input
BoxLayout:
orientation : "vertical"
TextInput:
id: input
text: "Changed"
Button:
text: "Enter"
on_press : root.clicked()
<Display_Screen>:
nice: nice
BoxLayout:
orientation : "vertical"
Label:
id: nice
text: "NotChanged"
""")
class Input_Screen(Screen):
input = ObjectProperty(None)
def clicked(self):
Display_Screen().change()
class Display_Screen(Screen):
nice = ObjectProperty(None)
def change(self):
print(self.nice.text) #Checking the Current text of The Label
print(Input_Screen().input.text) #Checking What do we want to change
self.nice.text = Input_Screen().input.text
print(self.nice.text) #Checking if it has change or not
MyApp().sm.current = "Ds" #Changing Screen to Display_Screen
print(self.nice.text) #Checking if it has change or not
class MyApp(App):
sm = ScreenManager()
def build(self):
self.sm.add_widget(Input_Screen(name="Is"))
self.sm.add_widget(Display_Screen(name="Ds"))
return self.sm
MyApp().run()
What I get in My console:
NotChanged #Checked the Current text of The Label
Changed #Checked What do we want to change
Changed #Checked if it has change or not
Changed #Checked if it has change or not
It says that my Label's text has been changed but when it goes to the next screen it hasn't been changed.
Display_Screen
Anyone knows whats the problem? Help me please
Whenever you use a construction like ClassName(), you are creating a new instance of ClassName. You are doing this in your change() method and in your clicked() method. Whenever you do that, you are referencing that new instance, and not the instance that is in your GUI.
To fix that, change:
def clicked(self):
Display_Screen().change()
to:
def clicked(self):
self.manager.get_screen('Ds').change()
and in your change() method, replace:
Input_Screen()
with:
self.manager.get_screen('Is')
and replace"
MyApp()
with:
App.get_running_app()

KivyMD: Adding list widget inside python code, problem with "on_release" - property

Trying to add some buttons with a for-loop to a container that is already defined in the .kv file:
The code looks like this:
a_list = ["Spam", "Eggs"]
for i in range(len(a_list)):
self.ids.container.add_widget(
ThreeLineListItem(id = a_list[i], text= "Some info ",
secondary_text = "More info",
tertiary_text = "Final info",
on_release=lambda x: print_me(self.id))
)
def print_me(the_id):
print(the_id)
This just prints "None", how come? And how to fix it so the first button prints "Spam" on release and the second one "Eggs"?
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.list import OneLineListItem
KV = '''
ScrollView:
MDList:
id: box
'''
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
a_list = ["Spam", "Eggs"]
for name in a_list:
self.root.ids.box.add_widget(
OneLineListItem(text=name, on_release=self.print_item)
)
def print_item(self, instance):
print(instance, instance.text)
Test().run()

Updating Labels across Screens in Kivy (Python) - Problem with (lack of) interactions between .kv and .py files with ScreenManager

I'm trying to build a Kivy application that has 2 screens which are re-used over and over again with different text.
So I go from a FirstScreen with a Label that says "First1" to a SecondScreen with a Label that says "Second1", and then back to the FirstScreen but this time with the Label "First2", then SecondScreen and "Second2", and so on and so forth.
The code for this is pretty straightforward, but there seems to be a problem in updating the Label text without a designated update button. For some reason, my Python code manages to update the text, but it isn't updated in my .kv file. So for instance, my print statements will tell me that the Label text is "First2", but Kivy displays "First1" for me. I've illustrated this in the Screenshot below:
By adding a Button that updates the text on press, everything is updated, synced up and works, but I'd really like it to work without the extra user input. Does anybody know how I can go about this? I've scoured the docs and stackoverflow questions left and right but can't seem to find the answer to my seemingly simple problem.
Here's the code:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty, ObjectProperty
from kivy.lang import Builder
S_ID = 1 # global screen ID. I'm using this to detect what text to use.
class FirstScreen(Screen):
text = StringProperty("")
lbl = ObjectProperty(None)
def __init__(self, **kwargs):
super(FirstScreen, self).__init__(**kwargs)
global S_ID
print("\nS_ID is ", S_ID)
self.update()
def update(self):
print("FIRST - UPDATE")
if S_ID == 1:
print("FIRST1")
self.text = "FIRST1"
elif S_ID == 2:
print("FIRST2")
self.text = "FIRST2"
print("self.lbl.text", self.lbl.text)
else:
print("FIRST ELSE")
self.text = "FIRST ELSE"
def pressed(self):
sm.current = "second"
class SecondScreen(Screen):
text = StringProperty("")
def __init__(self, **kwargs):
super(SecondScreen, self).__init__(**kwargs)
self.update()
def update(self):
print("SECOND - UPDATE")
if S_ID == 1:
print("SECOND1")
self.text = "SECOND1"
elif S_ID == 2:
print("SECOND2")
self.text = "SECOND2"
else:
print("SECOND ELSE")
self.text = "SECOND ELSE"
def pressed(self):
global S_ID
S_ID += 1
FirstScreen.update(FirstScreen())
sm.current = "first"
sm = ScreenManager()
kv = Builder.load_file("test.kv")
sm.add_widget(FirstScreen(name='first'))
sm.add_widget(SecondScreen(name='second'))
sm.current = "first"
class MyApp(App):
def build(self):
return sm
if __name__ == '__main__':
MyApp().run()
and here's the .kv file:
<FirstScreen>:
name: "first"
lbl: lbl:
GridLayout:
cols:2
Label:
id: lbl
text: root.text
Button:
text: "next"
on_press: root.pressed()
<SecondScreen>:
name: "second"
GridLayout:
cols:2
Label:
text: root.text
Button:
text: "next"
on_press:
root.pressed()
The problem is your statement:
FirstScreen.update(FirstScreen())
This statement is creating a new instance of FirstScreen and updating that instance. Unfortunately, that instance is not the one shown in your GUI. You can correct that by replacing the above statement with:
first_screen = self.manager.get_screen('first')
first_screen.update()
This code gets the instance of FirstScreen from the ScreenManager and calls update() on that instance.

Categories

Resources