Exchanging Variables between Screens in kivy python - python-2.7

I tried to make an app, with two screens, one with an Textinput and the other one Label, that is displaying the Text of the TextInput.
I tried to make this by creating an StringProperty in the app class, but I had a problem with accessing the Property.
I would like to know how to access the Variable.
Here is the source code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
class Manager(ScreenManager):
pass
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
pass
root_widget = Builder.load_string('''
Manager:
FirstScreen:
SecondScreen:
<FirstScreen>:
name: 'first'
BoxLayout:
orientation: 'vertical'
TextInput:
id: my_text
font_size: 50
Button:
id: b1
text: 'Go to next Screen'
on_release: app.root.current = 'second'
<SecondScreen>:
name: 'second'
BoxLayout:
orientation: 'vertical'
Label:
id: my_Label
text: root.txt
Button
id: b2
text: 'Go back'
on_release: app.root.current = 'first'
''')
class Caption(App):
txt = StringProperty('')
def build(self):
return root_widget
Caption().run()

Screen class has an attribute called 'manager' which specifies which manager that screen belongs to. In ScreenManager class, there is an attribute called 'screens', which is a ListProperty object that holds all the screens. If you want to reach out an information about another screen, you can use this way. In your case, you need to update your your b1 id button in your kv Builder with this:
Button:
id: b1
text: 'Go to next screen'
on_release:
root.manager.screens[1].ids.my_Label.text = root.ids.my_text.text
root.manager.current = 'second'
For more complex behaviours, you can define related Property in that particular Page class and reach out from python with:
self.manager.screens[<screen_number>].ids.<widget_id>.<property_name>

Related

Calling a variable not working in kivy's kv-language

My question is this, after I set a variable on one screen how do I call it on another?
Here is the code for what I am trying to do:
main.py
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
class Manager(ScreenManager):
pass
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
pass
class ExampleApp(App):
def build(self):
return Manager()
if __name__ == '__main__':
ExampleApp().run()
example.kv
#: import sm kivy.uix.screenmanager
#: set Question 'not working'
<Manager>
transition: sm.FadeTransition()
FirstScreen:
SecondScreen:
<FirstScreen>
BoxLayout:
TextInput:
id: txt
multiline: False
Button:
text: 'Press Me'
on_release:
Question = txt.text
app.root.current = 'Next'
<SecondScreen>
name: 'Next'
Label:
text: Question
When this is run everything works as it should. With one exception. The Label on SecondScreen reads "not working" when it should read whatever I type into the text input on FirstScreen. Why can I not accomplish this task?
You have to provide id tag and use ids method or ObjectProperty to reference the variables. Please refer to the two example below for details.
Example 1 - Using ids method
An id is a weakref to the widget.
main.py
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
class Manager(ScreenManager):
pass
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
pass
class ExampleApp(App):
def build(self):
return Manager()
if __name__ == '__main__':
ExampleApp().run()
example.kv
#:kivy 1.10.0
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
#: set Question 'not working'
<Manager>
transition: FadeTransition()
FirstScreen:
SecondScreen:
id: second_screen
<FirstScreen>
BoxLayout:
TextInput:
id: txt
text: "Working!"
multiline: False
Button:
text: 'Press Me'
on_release:
root.manager.ids.second_screen.ids.label.text = txt.text
app.root.current = 'Next'
<SecondScreen>
name: 'Next'
Label:
id: label
text: Question
Output - Using ids method
Example 2 - Using ObjectProperty
The ‘best practice’ to use the ObjectProperty. This creates a direct reference, provides faster access and is more explicit.
main.py
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import ObjectProperty
class Manager(ScreenManager):
second_screen = ObjectProperty()
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
label = ObjectProperty()
class ObjectPropertyApp(App):
def build(self):
return Manager()
if __name__ == '__main__':
ObjectPropertyApp().run()
objectproperty.kv
#:kivy 1.10.0
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
#: set Question 'not working'
<Manager>
second_screen: second_screen
transition: FadeTransition()
FirstScreen:
SecondScreen:
id: second_screen
<FirstScreen>
BoxLayout:
TextInput:
id: txt
text: "Working!"
multiline: False
Button:
text: 'Press Me'
on_release:
root.manager.second_screen.label.text = txt.text
app.root.current = 'Next'
<SecondScreen>
name: 'Next'
label: label
Label:
id: label
text: Question
Output - Using ObjectProperty

How to manage multiple screen in kivy?

I am using kivy to scan cards.How do I manage multiple screens . I wanted to capture the card first and the captured card should be passed to tesseract?
from kivy.app import App
from kivy.lang import Builder
import time
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from PIL import Image, ImageEnhance, ImageFilter
import pytesseract
from kivy.uix.button import Button
Builder.load_string("""
<CameraClick>:
orientation: 'vertical'
Camera:
id: camera
resolution: (640, 480)
play: False
ToggleButton:
text: 'Play'
on_press: camera.play = not camera.play
size_hint_y: None
height: '48dp'
Button:
text: 'Capture'
size_hint_y: None
height: '48dp'
on_press: root.capture()
<SettingsScreen>:
BoxLayout:
Button:
text: 'My settings button'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
""")
class CameraClick(BoxLayout):
def capture(self):
'''
Function to capture the images and give them the names
according to their captured time and date.
'''
camera = self.ids['camera']
timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("reading.png".format(timestr))
print("Captured")
class SettingsScreen(Screen):
def card(self):
im = Image.open(r"readimg.png")
p = pytesseract.image_to_string(im, lang='eng', config='-psm 6')
print p
return Button(text=p)
# Create the screen manager
sm = ScreenManager()
sm.add_widget(CameraClick(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
I am new to kivy I wanted to know how to manage multiple screens.How to display the content of the card from tesseract in another screen?

getting input from kivy textinput

I want to use this for a later project, I just want to print user's input from the TextInput by clicking the button on the MainScreen, but when I run and click on the button with the text "Print Text" nothing happens no errors and no output.
The .kv file:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
SecondScreen:
<MainScreen>:
name: "main"
Button:
on_release: root.get_text
text: "Print Text"
font_size: 50
size_hint:0.3,0.1
TextInput:
text:"Hello World"
size_hint: 0.35,0.25
pos_hint:{"right":1, "top":1}
color:1,0,0,1
id: user_text
Button:
color: 0,1,0,1
font_size: 25
size_hint: 0.3,0.25
text: "Continue"
on_release: app.root.current = "other"
pos_hint:{"right":1, "top":0}
<SecondScreen>:
name: "other"
FloatLayout:
Button:
color: 0,1,0,1
font_size: 25
size_hint: 0.3,0.25
text: "Back Home"
on_release: app.root.current = "main"
pos_hint:{"right":1, "top":1}
The python code:
from kivy.app import App
#kivy.require("1.9.1")
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen , FadeTransition
from kivy.uix.widget import Widget
from kivy.graphics import Line
from kivy.uix.textinput import TextInput
class MainScreen(Screen):
def get_text(self,*args):
textinput= self.ids.user_text
user= TextInput.text
print(user)
class SecondScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
gui = Builder.load_file("main.kv")
class MainApp(App):
def build(self):
return gui
MainApp().run()
When you code in kv, you bind directly as if you'd call the function e.g.
on_release: do_this()
but you did it without parenthesis as if it was a casual python binding:
self.bind(on_release=do_this)
Add the parenthesis and it should print:
on_release: root.get_text()

Kivy - Binding a button in a popup

I'm currently making a simple app in Kivy involving a Popup object containing a custom widget. I want to be able to access the information from the widget (button presses, etc) from the main screen, but I'm having issues with the button not being instantiated.
A (mostly) minimal (partially) working example is given below.
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.checkbox import CheckBox
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
from kivy.lang import Builder
Builder.load_string('''
<Main>
popup: popup
BoxLayout:
id: boxlayout
Button:
text: "Open Popup"
on_release: popup.open(); root.create_popup()
Popup:
id: popup
title: "Example popup"
on_parent: if self.parent == boxlayout: boxlayout.remove_widget(self)
PopupContent:
<PopupContent>:
closer: closer
# Suppose there is a more complicated nested layout structure here
BoxLayout:
orientation: 'vertical'
CheckBox:
Button:
id: closer
text: "Close popup"
''')
class PopupContent(BoxLayout):
def __init__(self,**kwargs):
super(PopupContent, self).__init__(**kwargs)
class Main(BoxLayout):
popup = ObjectProperty()
def __init__(self,**kwargs):
super(Main, self).__init__(**kwargs)
# Callback from button in PopupContent
def closer_callback(self,*args):
print("callback called!")
self.popup.dismiss()
# Creates container widget for popup and binds button to perform callback
def create_popup(self,*args):
popup_content = PopupContent()
popup_content.closer.bind(on_release = self.closer_callback)
# Problem: This returns "<type 'kivy.weakproxy.WeakProxy'>"
print(type(popup_content.closer))
class TestApp(App):
def build(self):
return Main()
if __name__ == '__main__':
TestApp().run()
As I annotated in my code, I attempted to bind the button in the PopupContent widget to run a method in my Main screen when pressed, but the button hasn't been created yet so no such binding is made.
How can I instead bind this button?
Turns out I had made a mistake when creating my example - although I created an instance of the PopupContent widget, I never added it to the Popup widget. Fixing that, it works fine now.
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.checkbox import CheckBox
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
from kivy.lang import Builder
Builder.load_string('''
<Main>
popup: popup
BoxLayout:
id: boxlayout
Button:
text: "Open Popup"
on_release: popup.open(); root.create_popup()
Popup:
id: popup
title: "Example popup"
on_parent: if self.parent == boxlayout: boxlayout.remove_widget(self)
<PopupContent>:
closer: closer
# Suppose there is a more complicated nested layout structure here
BoxLayout:
orientation: 'vertical'
CheckBox:
Button:
id: closer
text: "Close popup"
''')
class PopupContent(BoxLayout):
def __init__(self,**kwargs):
super(PopupContent, self).__init__(**kwargs)
class Main(BoxLayout):
popup = ObjectProperty()
def __init__(self,**kwargs):
super(Main, self).__init__(**kwargs)
# Callback from button in PopupContent
def closer_callback(self,*args):
print("callback called!")
self.popup.dismiss()
# Creates container widget for popup and binds button to perform callback
def create_popup(self,*args):
popup_content = PopupContent()
self.popup.content = popup_content
popup_content.closer.bind(on_release = self.closer_callback)
# Note: This still returns "<type 'kivy.weakproxy.WeakProxy'>"
# But the button is correctly bound regardless
print(type(popup_content.closer))
class TestApp(App):
def build(self):
return Main()
if __name__ == '__main__':
TestApp().run()
My understanding is that when the instance of PopupContent is first created, for performance reasons the actual widget isn't loaded until it is displayed (presumably via some load function). Until then however, we still get "weak references" via WeakProxy, which makes the binding possible.

Button's 'on_release' event calls 'on_touch_up' event of every dynamically created label

In my application I want to call some function when one of the dynamically created labels is touched. And there is a button at the bottom which calls another function.But when I press the button, on_touch_up event called for every label:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.logger import Logger
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<Root>:
BoxLayout:
orientation: 'vertical'
size_hint: 1, 1
ScrollView:
id: scroll_view
size_hint: 1,.9
do_scroll_x: False
BoxLayout:
orientation: 'horizontal'
size_hint: 1,.1
Button:
id: bt1
text: 'Click'
size_hint: 1,1
on_release: root.btn1_click()
""")
class Root(BoxLayout):
def btn1_click(self):
Logger.info('BUTTON: button was released')
class TestApp(App):
def callback(self, instance, touch):
#if not instance.collide_point(*touch.pos):
# return
Logger.info('LABEL: ' + instance.text + 'was released')
def build(self):
root = Root()
sv = root.ids.scroll_view
grid = GridLayout(cols=1, spacing=(0,10), size_hint_y=None,
padding = [10,10,10,10],row_force_default=True, row_default_height=50)
grid.bind(minimum_height=grid.setter('height'))
sv.add_widget(grid)
for tr in [i for i in range(50)]:
lb_text = str(tr) + ' ' + 'Label'
lb = Label(text=lb_text)
lb.bind(on_touch_up=self.callback)
grid.add_widget(lb)
return root
if __name__ == '__main__':
TestApp().run()
And if I use:
#if not instance.collide_point(*touch.pos):
# return
button and label behind this button will be pressed.
How should I properly handle this touch?
This is because you are binding to on_touch_up. The on_touch_down event has already been fired, and the Button has grabbed that touch. So when you release, the Button will process the event no matter what (even if the touch is no longer over the Button).
This is why we have ButtonBehavior. If you use the behavior, then everything plays nice:
from kivy.uix.behaviors import ButtonBehavior
class TouchLabel(ButtonBehavior, Label):
pass
Then when you add the labels:
lb = TouchLabel(text=lb_text)
lb.bind(on_release=self.callback)
And finally, the signature of the event handler for on_release is different from on_touch_down:
def callback(self, instance):
Logger.info('LABEL: ' + instance.text + 'was released')
on_touch_down/on_touch_move/on_touch_up are for more advanced usage.