Monday, July 7, 2014

46. Widget Selection


A widget is selected by touch by transversing the list of children.




A ball shaped widget is drawn here. The root widget is based on RelativeLayout. Besides the App, we need two graphic classes, since we will be dynamically adding code to highlight the selected child. Three Kivy Properties are used. The ObjectProperty will be used to point to the selected child. In the BooleanProperty, we will hold the current state of whether a child has been selected. The Numeric Property will hold the counter indicating the number of Ball widgets that have been added.




The Ball Widget holds only the counter NumericProperty. The root, based on RelativeLayout, also has a NumericProperty with that name. This does not create any problem, however, it clarifies that, they will, be the same number, most of the time. The variable selected is an ObjectProperty and will be used to point to the selected child. The BooleanProperty will indicate a child has been selected.




The on_touch_down function is run whenever a touch or click has been detected. We first test, if the touch coordinates, are inside the lower-left region, of size of, 100 by 100. This is where ToggleButtons will be placed by the kv file. The two toggle buttons will be 'Draw' and 'Select'. If the touch occurs over that place, we return to super, so the ToggleButtons can handle this event. Next, we test if the select ToggleButton is in the on (or 'down') state.




If the condition is true and we are in Select mode, we look at children list. Since the root is based on RelativeLayout, the children will include anything inside the RelativeLayout. This includes the 2 ToggleButtons and any Balls added. If a child's coordinates, include the touch coordinate, the selected ObjectProperty, points to this child and sel becomes True.




Next, there are drawing instructions. A black color is selected and then a dashed rectangle is drawn. We use the user data dictionary (ud) in the touch variable. After a child has been selected, we break out of the for loop.




The on_touch_move will be called when there is dragging motion. First the select ToggleButton is checked, and also the sel variable. If so, the last selection rectangle is removed. Next, a local variable child is used to point to variable in selected. We change the child's center position to the touch position.




Next, a new selection rectangle is drawn. This one will be updated to reflect the new touch position.




Finally, once the dragging is finished, the on_touch_up function, is called. The if statement, checks if mode is Select, and the sel is true. If so the last select rectangle is removed, and now the sel becomes False. Next, if the coordinates are inside the ToggleButtons, we return.




If the draw ToggleButton, is in the 'down' state, the counter is incremented and a new Ball Widget is created. The Ball's counter is updated as well. Finally, the widget is added to the Layout.




The app class must exist for the main file. The title of the main window is 'Widget Selection'.


# ex46.py

from kivy.uix.widget import Widget
from kivy.uix.relativelayout import RelativeLayout
from kivy.app import App
from kivy.graphics import Line, Color
from kivy.properties import (ObjectProperty, BooleanProperty,
                             NumericProperty)

class Ball(Widget):
    counter = NumericProperty()

class Ex46(RelativeLayout):
    counter = NumericProperty(0)
    selected = ObjectProperty(None)
    sel = BooleanProperty(False)
    
    def on_touch_down(self,touch):
        if touch.x<100 and touch.y<100:
            return super(Ex46, self).on_touch_down(touch)
        if self.select.state == 'down':
            for child in self.children:
                if child.collide_point(*touch.pos):
                    self.selected = child
                    self.sel = True
                    with self.canvas:
                        Color(0,0,0)
                        touch.ud['line']=Line(rectangle=(child.x-5,
                            child.y-5,child.width+10,child.height+10),
                            dash_length=5,dash_offset=2)
                    break

    def on_touch_move(self,touch):
        if self.select.state == 'down' and self.sel == True:
            self.canvas.remove(touch.ud['line'])
            child = self.selected
            child.center = touch.pos
            with self.canvas:
                touch.ud['line']=Line(rectangle=(child.x-5,child.y-5,
                             child.width+10,child.height+10),width=1,
                             dash_length=5,dash_offset=2)

    def on_touch_up(self,touch):
        if self.select.state == 'down' and self.sel == True:
            self.canvas.remove(touch.ud['line'])
            self.sel = False
        if touch.x<100 and touch.y<100:
            return super(Ex46, self).on_touch_down(touch)
        if self.draw.state == 'down':
            self.counter += 1
            ball = Ball()
            ball.center = touch.pos
            ball.counter = self.counter
            self.add_widget(ball)
       
class Ex46App(App):
    def build(self):
        self.title = 'Widget Selection'
        return Ex46()

if __name__=='__main__':
    Ex46App().run()



In the kv file, first we define the Ball, as 50 by 50 pixels and colored cyan with a background image of 'kivy.png'. We had used this image earlier.




On each Ball, we have a Label indicating the current counter number.




The background is green. The draw and select attributes are created. These point to the two ToggleButtons.




This is the first ToggleButton, for the 'Draw' mode. Its id is draw.




The other ToggleButton is for 'Select' and has the id 'select'. We had used these ids in the attribute definitions.


# ex46.kv

<Ball>:
    size_hint: None, None
    size: 50,50
    canvas:
        Color:
            rgb: 0,1,1
        Ellipse:
            pos: self.pos
            size: self.size
            source: 'kivy.png'
    Label:
        size: 50,10
        pos: root.x,root.y+35
        text: str(root.counter)
            
<Ex46>:
    draw: draw
    select: select
    canvas:
        Color:
            rgb: 0,1,0
        Rectangle:
            size: self.size
            pos: self.pos
    ToggleButton:
        id: draw
        size_hint: None, None
        size: 100,50
        pos: 0,0
        state: 'down'
        text: 'Draw'
        group: 'mode'
    ToggleButton:
        id: select
        size_hint: None, None
        size: 100,50
        pos: 0,50
        text: 'Select'
        group: 'mode'


Demo of App on Youtube.

1 comment:

  1. Python:

    Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.

    https://www.emexotechnologies.com/online-courses/python-training-in-electronic-city/

    ReplyDelete