Kivy - Binding a button in a popup - python-2.7

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.

Related

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?

kivy - clear screen after saving image?

Using kivy 1.9.1/2
What I would like to achieve :
Saving the image when clicking 'OK' then clearing the screen. So the next time I enter the Signature screen, the screen doesn't contain anything but the 'OK' button.
My Issue:
canvas.clear doesn't work. I probably integrated it the wrong way.
Can someone help ?
I'm a beginner and have been stuck on this for days.
Thanks in advance.
#*** PYTHON FILE
from kivy.app import App
from kivy.lang import Builder
from kivy.config import Config
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.graphics import Line
import datetime
Config.set('graphics', 'width', '1024')
Config.set('graphics', 'height', '768')
class Other(Screen):
pass
class Painter(Widget):
def __init__(self,**kwargs):
super(Painter, self).__init__(**kwargs)
self.canvas.clear()
def on_touch_down(self, touch):
with self.canvas:
touch.ud['line'] = Line(points=(touch.x, touch.y))
def on_touch_move(self, touch):
touch.ud['line'].points += [touch.x, touch.y]
class Signature(Screen):
def Save_image(self):
from PIL import Image, ImageFont, ImageDraw
self.export_to_png("test.png")
img=Image.open("test.png")
draw=ImageDraw.Draw(img)
font=ImageFont.truetype("/usr/share/fonts/truetype/noto/NotoSans-Bold.ttf",24, encoding="unic")
draw.text((0,0),str("{:%d %b %y-%Hh%Mm%Ss}".format(datetime.datetime.now())), (255,255,255), font=font)
img.save("test.png")
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("appdessin.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == '__main__':
MainApp().run()
#***KV FILE
#:import datetime datetime
#:kivy 1.9.1
ScreenManagement:
Other:
Signature:
<Other>:
name: "other"
id: otherScreen
BoxLayout:
Button:
text: "OK"
on_release:
root.manager.transition.direction = 'right'
app.root.current = "signature"
<Signature>:
name: "signature"
id: signatureScreen
BoxLayout:
Painter
Button:
background_color: 0,0,1,1
font_size: 32
size_hint: (0.1, 0.1)
text: "OK"
pos_hint: {"right":1, "bottom":1}
on_release:
root.Save_image()
root.manager.transition.direction = 'right'
app.root.current = "other"
As suggested by niavlys on kivy's google group, the answer is to clear the canvas everytime it is loaded. Obvious, but to achieve this, one has to declare in the kv file, in the screen:
on_pre_enter:
sign.canvas.clear()
BoxLayout:
Painter
id: sign

Kivy BoxLayout.orientation

I am new to kivy.
There is something about the orientation property of the BoxLayout class which is confusing me: if i set the orientation to vertical the child widgets are actually set up horizontally and vice versa. What am i understanding wrong? Why is that decided to be so and not other way around which is more intuitive?
Here are my codes:
# main.py
from kivy.app import App
class LernApp(App):
pass
if __name__ == "__main__":
LernApp().run()
# lern.kv
BoxLayout
orientation: "vertical"
Button
text: "Button1"
Button
text: "Button2"
This produces this window:
The widgets are stacked vertically.
So the meaning of orientation is how to stack widgets inside the BoxLayout.
I don't understand what you want exactly, but I think you want the buttons horizontally
try this :
from kivy.app import App
class LernApp(App):
# lern.kv
return BoxLayout("""
orientation: "horizontal"
Button:
text: "Button1"
Button:
text: "Button2"
"""
if __name__ == "__main__":
LernApp().run()
Or try this
from kivy.app import App
class LernApp(App):
# lern.kv
return BoxLayout("""
orientation: "horizontal"
Button:
text: "Button1"
Button:
text: "Button2"
"""
if __name__ == "__main__":
LernApp().run()

Getting Kivy to update Async image with Clock

I have several screens run by ScreenManager, and on one I have an image that is loaded with an image taken from a URL (for weather). My issue is, I can't seem to quite get the URL & image updated periodically:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
import pyowm
class Weather(Screen):
addr = StringProperty()
def wx_forecast(self):
owm = pyowm.OWM('******************')
observation = owm.weather_at_place('London,uk')
w = observation.get_weather()
i = w.get_weather_icon_name()
addr = str("http://openweathermap.org/img/w/" + i + ".png")
print(addr)
return addr
def update(self):
Clock.schedule_interval(self.ids.wxlabel.source, 45)
#def update(self):
#self.wx_forecast()
#Clock.schedule_interval(lambda dt: self.ids.wxlabel, 30)
class ScreenManagement(ScreenManager):
pass
smsettings = Builder.load_string( """
ScreenManagement:
Weather:
<Weather>:
name: 'weather'
FloatLayout:
BoxLayout:
AsyncImage:
id: wxlabel
source: root.wx_forecast()
BoxLayout:
size_hint_y: None
height: 30
Button:
text: 'Change Location'
on_press:
Button:
text: 'More Details'
on_release:
""" )
class HomeUtilities(App):
def build(self):
return smsettings
if __name__=='__main__':
HomeUtilities().run()
Where the def update is where I am having issues; I have tried calling the function wx_forecast again, even trying another function outside the Weather class, but all it says is str: has no attribute 'wx_forecast'
So I believe I am on the right lines? Or I could be completely mistaken, but nothing I try seems to work. Even putting self.ids.wxlabel.source.sunny.png brings up str: has no attribute image but the file is there.

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.