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
Related
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
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?
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.
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.
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.