Kivy: 在我的应用中添加工具栏

0 投票
1 回答
1416 浏览
提问于 2025-04-18 18:47

我正在尝试实现一个简单的工具栏。目前我在基础部分遇到了困难。

我现在的目标:

制作一个位于应用侧边的工具栏,让用户可以往主界面添加小部件。

我目前的进展:

我已经有了一个白色的矩形,位置也对了。还有一些按钮可以添加正确的小部件。不过,我需要把这些按钮放在正确的位置。

看起来我理想中想用某种布局(比如Box)。但是我不知道怎么把这个布局只嵌入到这个矩形里。

我也不明白为什么到目前为止我做的这些不管用。

当前代码:

我会附上一个能运行的代码片段,但按钮的位置不对。如果需要,我也可以提供更简洁的代码(只要问我就行 :))。

Python代码:

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 文件:

#: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

谢谢你的耐心!

1 个回答

1

如果我理解得没错的话,你只是想把那些按钮放到白色的矩形上。这可以很简单地通过使用 BoxLayout 来实现,正如你提到的。只需要把这一行

class GraphToolBar(Widget):

改成这一行

class GraphToolBar(BoxLayout):

撰写回答