kivy touch events bubbling and overriding on_ methods - kivy

The pictures example in /share/kivy-examples/demo/pictures places Image widget in Scatter. I'd like to extend the example and replace Image with <ImageButton#ButtonBehavior+Image>. However, the touch events are not implemented correctly. The ImageButtons are press-able but the drag functionality from the original example is lost.
At first I simply changed Image to <ImageButton#ButtonBehavior+Image> in the pictures.kv file. I see in the documentation that I may need to (re)implement on-press or on_touch_down. To that end, I've added these methods in the Picture class:
class Picture(Scatter):
source = StringProperty(None)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
print('picture touch down')
else:
print('no collide in picture')
super(Picture, self).on_touch_down(touch)
def on_touch_move(self, touch):
if self.collide_point(*touch.pos):
print('touch move')
super(Picture, self).on_touch_move(touch)
I see the print statements in the terminal. But the ImageButtons are still consuming the on_press, and I know this from a print statement in that event handler. I tried to re-implement on_press and just not do anything with pass, and then I had an idea of calling self.parent.on_touch_down, but I don't have a touch object to pass into it.
One idea would be to
class ImageButton(ButtonBehavior, Image):
'''
def on_press(self):
print("button pressed")
print()
return super(ImageButton, self).on_press()
'''
def on_press(self):
pass
So, say I want the ImageButton to only register a double-clicks, and otherwise, for the widgets to behave just like in the example. How would I achieve that? Somewhat related

While there may be a way of distinguishing between a quick touch_down followed immediately by a touch_up, it is easier to show the desired functionality by letting an ImageButton press be activated with a double_tap:
class ImageButton(ButtonBehavior, Image):
def __init__(self, **kwargs):
super(ImageButton, self).__init__(**kwargs)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and touch.is_double_tap:
self.on_press(touch)
return True
return False
def on_press(self,touch):
#whatever else you want to happen
return True

Related

self.manager.current only works in def on_enter(self):

I want to go to the next screen after finishing a time consuming calculation. Therefore, I wnat to define at the end of a function the screen that has to be shown. However, when I try the following:
def gotoscreen1(self):
print self.manager (prints None)
self.manager.current = 'screen2'
I have the error = AttributeError: 'NoneType' object has no attribute 'current'
When I try:
def on_enter(self):
print self.manager (prints <__main__.ScreenManagement object at 0x1227C688>)
self.manager.current = 'screen2'
I can jump imediatly to screen 2, but that is not what I want ...
Many thanks in advance for your help!
After a hort night sleep (thank you son) I woke up and knew the solution: I needed to pass the correct arguments when calling the function in the clock schedule
Clock.schedule_interval(partial(self.gotoscreen1, self), 0.5)

best tool for rendering short items of text in wxPython and Kivi

I have a multi-platform app running on desktop (wxPython) and mobile (kivy). In it I want to render small areas of variable text in a window in the app. The text will depend on the state of the app. I am happy to use rtf, html or reStructuredText. I need to use the same source for the text on each platform.
A typical example of a text snippet would be:
Heading
=======
1. With 24 widgets pull a **long** one;
2. with fewer, push a **wide** one.
Which would render as:
Heading
With 24 widgets pull a long one;
with fewer, push a wide one.
My question is: which format should I use?
My preference would be reStructuredText. There appears to be a kivy widget to support this but nothing in wxPython
One solution is to use the docutils package.
This will take reStructuredText and output it as html. I can then use the wxPython wx.html control to display the output.
import wx
import wx.html as wxhtml
from docutils.core import publish_string
class MainFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = MainPanel(self)
sizer = wx.BoxSizer()
sizer.Add(self.panel)
self.SetSizerAndFit(sizer)
self.Show()
input_string = ("Heading\n"
"=======\n"
"\n"
"1. With 24 widgets pull a **long** one;\n"
"2. with fewer, push a **wide** one.\n")
self.display_rst(input_string)
def display_rst(self, rst):
html = publish_string(rst, writer_name='html')
self.panel.html.SetPage(html)
class MainPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
wx.Panel.__init__(self, parent, *args, **kwargs)
display_style = wx.VSCROLL|wx.HSCROLL|wx.TE_READONLY|wx.BORDER_SIMPLE
self.html = wxhtml.HtmlWindow(self, -1, size=(300, 200),
style=display_style)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.html)
self.SetSizer(sizer)
if __name__ == "__main__":
SCREEN_APP = wx.App()
MAIN_FRAME = MainFrame(None, title="Display HTML")
SCREEN_APP.MainLoop()

what is in kivy "instance" ? like on_pressed(self, instance, pos)

I would like to know what does the word instance mean in kivy?
class CustomBtn(Widget):
pressed = ListProperty([0, 0])
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.pressed = touch.pos
# we consumed the touch. return False here to propagate
# the touch further to the children.
return True
return super(CustomBtn, self).on_touch_down(touch)
def on_pressed(self, instance, pos):
print ('pressed at {pos}'.format(pos=pos))
print ('My callback is call from', instance)
'instance' is the name and reference to the object instance of the Class CustomBnt when you press the button. It does not have to be named 'instance'. You may also call it 'obj' or 'btn' or whatever makes sense to you.
You use it to gather information about the pressed Button. instance.text would be the text on the Button e.g.
Use print type(instance) to find out what kind of object 'instance' is.
instance does not have a special meaning. This argument is used to convey to the method which object triggered an event. Consider an event handler attached to an event from another class:
class MyLabel(Label):
def pressed_handler(self, instance, pos):
print ('pressed at {pos}'.format(pos=pos))
print ('My callback is call from', instance)
a = CustomBtn()
b = MyLabel()
a.bind(on_pressed=b.pressed_handler)
then pressed_handler will know which object sent the event, through the instance argument.

Why do I need to create a new instance of a Line instead of simply update or add and remove it in Kivy

I am attempting to draw a line with the mouse by dragging from one point to another point of the window. I also want to represent the line while I am dragging. Like drawing a line in an old MS PaintBrush.
My problem is that I have only been able to achieve this by constantly removing the old Line and adding a new Vertex Instruction to the canvas. However, I cannot update existing instructions. Not even adding and removing the same instruction. It has to be a new instance of Line. You can see the result that I want by running the following code. If you try to run it with the commented lines it doesn't work any more.
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.graphics import Line
class MyCanvas(RelativeLayout):
def on_touch_down(self, touch):
with self.canvas:
self.line = Line(points=[touch.x,touch.y,touch.x+1,touch.y+1])
self.bind(on_touch_move=self.update_line, on_touch_up=self.end_line)
return True
def update_line(self, instance, touch):
self.line.points[2] = touch.x
self.line.points[3] = touch.y
self.canvas.remove(self.line)
# self.canvas.add(self.line) # - this doesn't work
# self.canvas.ask_update() # - not even using this
with self.canvas:
self.line = Line(points=self.line.points) # this works
def end_line(self, instance, touch):
self.unbind(on_touch_move=self.update_line)
self.unbind(on_touch_up=self.end_line)
self.line.points[2] = touch.x
self.line.points[3] = touch.y
self.canvas.remove(self.line)
# self.canvas.add(self.line) # - this doesn't work
# self.canvas.ask_update() #- not even using this
self.canvas.add(Line(points=self.line.points)) # this way works
class ExampleApp(App):
def build(self):
return MyCanvas()
ExampleApp().run()
I also tried using Kivy properties as suggested in this other question with the Color instruction. It didn't work and there is another question related to it.
I am struggling with the same problem. I started from the 6_button.py example from the kivy/guide/firstwidget directory
I found something that works (using pop twice to remove the last x,y pair from points) But I think it is very awkward, see my code below. I hope someone can tel us how to 'update' properly.
based on 6_button.py
from random import random
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.graphics import Color, Ellipse, Line
class MyPaintWidget(Widget):
def on_touch_down(self, touch):
color = (random(), 1, 1)
with self.canvas:
Color(*color, mode='hsv')
d = 10.
Ellipse(pos=(touch.x - d / 2, touch.y - d / 2), size=(d, d))
touch.ud['line'] = Line(points=(touch.x, touch.y, touch.x+30, touch.y))
#print(dir(touch.ud['line']))
def on_touch_move(self, touch):
#touch.ud['line'].points += [touch.x, touch.y]
touch.ud['line'].points.pop() #
touch.ud['line'].points.pop() # works but is awkward
touch.ud['line'].points += [touch.x, touch.y] #
#touch.ud['line'].points[2:4] = [touch.x, touch.y]
#self.canvas.ask_update() # no error but didnt work
#touch.ud['line'].ask_update() # didnt work
#print(touch.ud['line'].points)
#touch.ud['line'].needs_redraw() # error 'bool not callable'
#touch.ud['line'].needs_redraw = True # error 'not writable'
#touch.ud['line'].needs_redraw #no error but doesnt work
class MyPaintApp(App):
def build(self):
parent = Widget()
painter = MyPaintWidget()
clearbtn = Button(text='Clear')
parent.add_widget(painter)
parent.add_widget(clearbtn)
def clear_canvas(obj):
painter.canvas.clear()
clearbtn.bind(on_release=clear_canvas)
return parent
if __name__ == '__main__':
MyPaintApp().run()

on_touch_up event recieved by canvas. Not sure why

Not sure why on_touch_up is being fired when the button is released. The other two events, on_touch_down and on_touch_move are not fired.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
class MyPaintWidget(Widget):
def on_touch_down(self, touch):
print "on_touch_down"
def on_touch_move(self, touch):
print "on_touch_move"
def on_touch_up(self, touch):
print "on_touch_up"
class MyPaintApp(App):
def build(self):
parent = Widget()
painter = MyPaintWidget()
btn = Button(text='Click Me')
parent.add_widget(painter)
parent.add_widget(btn)
return parent
if __name__ == '__main__':
MyPaintApp().run()
You've overloaded the up, down and move methods of MyPainterWidget and they execute as defined when clicking on the widget.
A uix.Button doesn't have a on_touch_up method so the event propagates up the widget tree. You can investigate this a little further by changing the order of
parent.add_widget(painter)
parent.add_widget(btn)
to
parent.add_widget(btn)
parent.add_widget(painter)
We now see that the both "on_touch_up" and "on_touch_down" are printed to the console, even when clicking the uix.Button, rather than just "on_touch_up".
These are facets of how kivy handles events, the details of which can be found here

Resources