user124784
user124784

Reputation: 956

Kivy: Adding a toolbar to my app

I'm trying to implement a simple toolbar. Right now I am stuck on the basics.

My current goal:

A toolbar which sits on the side of the app, and allows the user to add widgets to the main gui.

My current progress:

A white rectangle that sits in the correct location. Buttons which add the correct widgets. I need to position the buttons correctly.

It looks like ideally I'd like to use some kind of layout (Box?). However I can't work out how to embed this just on the rectangle.

I also don't know why what I've done so far doesn't work.

Current Code:

I'll attach a working piece of code, where the buttons aren't positioned correctly. I will happily provide minimal code if this is more helpful (just ask :) ).

Python code:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import Property, NumericProperty, ReferenceListProperty,\
    ObjectProperty
from kivy.graphics import Color, Ellipse, Line
from kivy.clock import Clock
import math

class GraphToolBar(Widget):

    def add_buttons(self, game):
        createNodeButton = Button(text = 'CreateNode', pos = (self.x,game.height))
        createEdgeButton = Button(text = 'CreateEdge', pos = (self.x,0.8*game.height))

        self.add_widget(createNodeButton)
        self.add_widget(createEdgeButton)

        def createNode(instance):
            newNode = GraphNode()
            game.add_widget(newNode)
            print "Node Created"

        def createEdge(instance):
            newEdge = GraphEdge()
            game.add_widget(newEdge)
            print "Edge Created"

        createNodeButton.bind(on_press=createNode)
        createEdgeButton.bind(on_press=createEdge)
    pass



class GraphInterface(Widget): 
    node = ObjectProperty(None)
    toolbar = ObjectProperty(None)

    def update(self, dt):
        for widget in self.children:
            if isinstance(widget, GraphEdge) and widget.collide_widget(self):
                widget.check_connection()

    def construct_toolbar(self):
        self.toolbar.add_buttons(self)

class GraphNode(Widget):
    r = NumericProperty(1.0)

    def __init__(self, **kwargs):
        self.size= [50,50]
        self.pos = [175,125]
        self.r = 1.0
        super(GraphNode, self).__init__(**kwargs)

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            if touch.grab_current == None:
                self.r = 0.6
                touch.grab(self)             
                return True                
        return super(GraphNode, self).on_touch_down(touch)

    def on_touch_move(self, touch):
        if touch.grab_current is self:
            self.pos=[touch.x-25,touch.y-25]
        for widget in self.parent.children:
            if isinstance(widget, GraphEdge) and widget.collide_widget(self):
                widget.snap_to_node(self)


    def on_touch_up(self, touch):
        if touch.grab_current is self:
            touch.ungrab(self)
            self.r = 1.0
            # and finish up here

    pass

class GraphEdge(Widget):
    r = NumericProperty(1.0)
    connected_point_0 = Property(False)
    connected_point_1 = Property(False)
    connected_node_0 = Widget()
    connected_node_1 = Widget()

    def __init__(self, **kwargs):
        super(GraphEdge, self).__init__(**kwargs)
        with self.canvas:
            Color(self.r, 1, 1, 1)
            self.line = Line(points=[100, 200, 200, 200], width = 2.0, close = True)
            self.center = ((self.line.points[0]+self.line.points[2])/2,(self.line.points[1]+self.line.points[3])/2)


    def snap_to_node(self, node):
        if self.collide_widget(node):
            distance_from_0 = [math.sqrt(((self.line.points[0]-node.center[0])**2 + (self.line.points[1]-node.center[1])**2))]*2
            distance_from_1 = [math.sqrt(((self.line.points[2]-node.center[0])**2 + (self.line.points[3]-node.center[1])**2))]*2

            if distance_from_0 < distance_from_1:
                if (self.connected_point_0 is False):
                    print "collision"                
                    if node is not self.connected_node_1:
                        self.connected_point_0 = True
                        self.connected_node_0 = node
                        self.line.points = node.center + self.line.points[2:]
                        self.size = [math.sqrt(((self.line.points[0]-self.line.points[2])**2 + (self.line.points[1]-self.line.points[3])**2))]*2
                        self.center = ((self.line.points[0]+self.line.points[2])/2,(self.line.points[1]+self.line.points[3])/2)
                return True

            elif distance_from_1 < distance_from_0:
                if (self.connected_point_1 is False):
                    print "collision"
                    if node is not self.connected_node_0:
                        self.connected_point_1 = True
                        self.connected_node_1 = node
                        self.line.points =  self.line.points[:-2] + node.center
                        self.size = [math.sqrt(((self.line.points[0]-self.line.points[2])**2 + (self.line.points[1]-self.line.points[3])**2))]*2
                        self.center = ((self.line.points[0]+self.line.points[2])/2,(self.line.points[1]+self.line.points[3])/2)
                    return True
        pass

    def check_connection(self):
        if self.connected_point_0:
            self.line.points = self.connected_node_0.center + self.line.points[2:] 
            self.size = [math.sqrt(((self.line.points[0]-self.line.points[2])**2 + (self.line.points[1]-self.line.points[3])**2))]*2
            self.center = ((self.line.points[0]+self.line.points[2])/2,(self.line.points[1]+self.line.points[3])/2)
            self.r = self.connected_node_1.r

        if self.connected_point_1:
            self.line.points = self.line.points[:2] + self.connected_node_1.center
            self.size = [math.sqrt(((self.line.points[0]-self.line.points[2])**2 + (self.line.points[1]-self.line.points[3])**2))]*2
            self.center = ((self.line.points[0]+self.line.points[2])/2,(self.line.points[1]+self.line.points[3])/2)
            self.r = self.connected_node_1.r

class GraphApp(App):

    def build(self):
        game = GraphInterface()

        game.construct_toolbar()

        Clock.schedule_interval(game.update, 1.0/20.0)  
        return game

if __name__ == '__main__':

    GraphApp().run()

.kv file:

#:kivy 1.0.9

<GraphInterface>:
    node: graph_node
    toolbar: graph_toolbar

    GraphNode:
        id: graph_node
        center: self.parent.center  

    GraphToolBar:
        id: graph_toolbar
        size: root.width * 2/10, root.height
        x: root.width * 8/10
        y: 0

<GraphToolBar>:
    size: 10,100

    canvas:
        Color:
            rgba: (1,1,1,1)
        Rectangle:
            size: self.size
            pos: self.pos

<GraphNode>:
    size: 50, 50
    canvas:
        Color:
            rgba: (root.r,1,1,1)
        Ellipse:
            pos: self.pos
            size: self.size


<GraphEdge>:
    size: self.size
    center: self.center
    canvas:
        Color:
            rgba: (root.r,1,1,1)
        Line:
            width: 2.0
            close: True

Thanks for your patience!

Upvotes: 0

Views: 1440

Answers (1)

Tatarkow
Tatarkow

Reputation: 529

If I understand you correctly, you just want to get those buttons onto the white rectangle. That can be easily done by using BoxLayout, as you have mentioned. Just change this line

class GraphToolBar(Widget):

to this line

class GraphToolBar(BoxLayout):

Upvotes: 1

Related Questions