1

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.

Community
  • 1
  • 1
toto_tico
  • 17,977
  • 9
  • 97
  • 116
  • I dig into the code with `ipdb` and I realize that the there is a Line'attribute called needs_redraw. It is always False in the current Line and the variable is write protected. – toto_tico Jun 16 '13 at 05:33

1 Answers1

1

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()
Smitje
  • 26
  • 1
  • It is not that awkward. It doesn't explain why the direct assign doesn't work `self.line.points[2] = touch.x` though. I was started to think that the only way creating a new instance but you found another way. I can tell you that this will fail if you are updating `pos` for an `Ellipse` because it is a `tuple` and there is no `pop` method. Look at this [question](http://stackoverflow.com/questions/17058098/how-do-i-update-the-color-of-a-dynamically-added-ellipse-not-using-builder) that was answered by a Kivy developer. Creating a new Line might not be that wrong after all. – toto_tico Aug 22 '13 at 02:11