在 kivy 中嵌套小部件

2022-01-15 00:00:00 python kivy user-interface widget

问题描述

我正在尝试在 kivy 中创建一个界面,我认为对于自定义小部件以及如何对它们进行分层,即使在完成本教程之后,我仍然不了解一些基本的东西.我认为我有更多的盒子模型 html 思维方式,所以小部件嵌套在原生 GUI 中的方式对我来说仍然有点陌生.

I'm trying to make an interface in kivy and I think there are some fundamental things I don't understand about custom widgets and how to hierarchy them, even after going through the tutorial. I think I have more of a box-model html mindset, so the way widgets are nested in native GUIs are still kind of foreign to me.

一些背景:

  1. 我咨询了这篇文章关于如何添加背景(它回答问题:如何将背景图像/颜色/视频/...添加到布局中",我相信我正在使用 _update_rect 方法进行复制.

  1. I consulted this entry on how to add a background (It answers the question: "How to add a background image/color/video/... to a Layout", which I believe I was replicating with the _update_rect methods).

这个有一个 on_touch_down 事件.

This one that has an on_touch_down event.

K,我正在尝试让 MyApp 看起来像这样......

K, I'm trying to get MyApp to look like this...

据我了解,这里是我需要的东西:

As I understand it, here are the things I'd need for that:

  1. 您有一个应用.
  2. 应用有根.
  3. 根有背景,比如说背景是白色的.
  4. 背景包含一个支架,假设支架距离背景有一点边距并且是灰色的.我确实希望这是一个小部件,而不仅仅是一个非小部件画布,因为我希望持有者也对点击事件做出反应.单击时它会变成随机颜色.(只是为了表明它正在做某事.)
  5. 支架包含两个自定义小部件.这些是带有标签的圆圈,每个都是绿色的.单击时它们会变成随机颜色(只是为了表明它们正在做某事).

这是我的代码,它不再崩溃,但不显示任何颜色或任何类型的对象.

Here's my code, which doesn't crash anymore, but doesn't display any colors or objects of any kind.

#!/usr/bin/kivy
import kivy
kivy.require('1.7.2')

from random import random
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Color, Ellipse, Rectangle

class MyApp(App):
    title = 'My App'
    def build(self):
        root = RootWidget()
        root.bind(
            size=self._update_rect,
            pos=self._update_rect)
    def _update_rect(self, instance, value):
        self.rect.pos = instance.pos
        self.rect.size = instance.size

class RootWidget(FloatLayout):
    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(**kwargs)
        with self.canvas.before:
            Color(1, 0, 0, 1) # This RED does not display.
            self.rect = Rectangle(
                                    size=self.size,
                                    pos=self.pos,
                                    text="some junk!")  # This label does not display.
        mybackground = Background()
        self.add_widget(mybackground)
    def _update_rect(self, instance, value):
        self.rect.pos = instance.pos
        self.rect.size = instance.size

class Background(Widget):
    def __init__(self, **kwargs):
        super(Background, self).__init__(**kwargs)
        with self.canvas.before:    
            Color(1, 1, 1, 1)       # This WHITE does not display
            self.rect = Rectangle(
                                    size=self.size,
                                    pos=self.pos,
                                    text="More stuff!")  # This label does not display.
        myholder = Holder()
        self.add_widget(myholder)
    def _update_rect(self, instance, value):
        self.rect.pos = instance.pos
        self.rect.size = instance.size

class Holder(Widget):
    def __init__(self, **kwargs):
        super(Holder, self).__init__(**kwargs)
        with self.canvas.before:    
            Color(0.25, 0.25, 0.25, 1) # This GRAY does not display
            self.rect = Rectangle(
                                    size=self.size,
                                    pos=self.pos,
                                    text="More stuff!")  # This does not display.
        c1 = Circley(label="Label 1")  # I see I'd need to do some size/pos math here to center
        c2 = Circley(label="Label 2")  # but since everything isn't working, I've tabled this.
        self.add_widget(c1)
        self.add_widget(c2)
    def _update_rect(self, instance, value):
        self.rect.pos = instance.pos
        self.rect.size = instance.size
    def on_touch_down(self, touch):
        rcolor = Color(random(), random(), random(), 1)
        with self.canvas:
            self.color = rcolor

class Circley(Widget):
    def __init__(self, label='label', **kwargs):
        super(Circley, self).__init__(**kwargs)
        with self.canvas.before:    
            Color(0, 1, 0, 1) # This GREEN does not display
            self.circ = Ellipse(
                        size=self.size,
                        pos=self.pos,
                        text=label
            )
    def _update_circ(self, instance, value):
        self.circ.pos = instance.pos
        self.circ.size = instance.size
    def on_touch_down(self, touch):
        rcolor = Color(random(), random(), random(), 1)
        with self.canvas:
            self.color = rcolor

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

关于我做错了什么以及如何正确嵌套这些小部件的任何指针?

Any pointers on what I'm doing wrong and how to nest these widgets correctly?


解决方案

你得到一个空白屏幕的原因是你的应用程序的 build() 方法没有返回任何东西.无论它返回什么都是根小部件,但即使您制作了一些小部件,您也不会返回一个,因此不会显示任何内容.

The reason you get a blank screen is that your app's build() method does not return anything. Whatever it returns would be the root widget, but even though you make some widgets you don't return one so nothing is displayed.

如果您解决了这个问题,您可以再次运行该应用程序,但您会立即收到类似 MyApp has no attribute rect 的错误.这是因为您的根小部件会立即调整大小并定位以填充窗口,这(根据您的 root.bind 行)触发 MyApp._update_rect.但是,这种方法尝试修改 MyApp.rect.pos...但是应用程序没有 self.rect!您大概打算绑定到 root._update_rect,而不是应用程序的方法.(我改为绑定到 root._update_rect,现在至少出现了红色背景和绿色圆圈.)

If you fix this, you can run the app again but you'll immediately get an error something like MyApp has no attribute rect. This is because your root widget is immediately sized and positioned to fill the window, which (as per your root.bind line) triggers MyApp._update_rect. However, this method try to modify MyApp.rect.pos...but the app doesn't have a self.rect! You presumably intended to bind to root._update_rect, not the app's method. ( I bound to root._update_rect instead and now at least the red background and green circle do appear.)

顺便说一句,使用 kv 语言会更容易,它可以自动处理许多小部件的创建、矩形绑定等.

And as a side note, this would be a lot easier using the kv language, which could automatically take care of a lot of the widget creation, rectangle binding etc.

我现在没有时间解决它,但也许这两个问题可以帮助您解决整体流程.如果没有其他人有,我稍后会尝试发布更完整的答案.

I don't have time to fix it all right now, but perhaps these two problems can help you fix the overall flow. I'll try to post a more complete answer later if nobody else has.

根据评论,这是一个更新的 MyApp.

Here's an updated MyApp, as per the comments.

class MyApp(App):
    title = 'My App'
    def build(self):
        root = RootWidget()
        root.bind(
            size=root._update_rect,
            pos=root._update_rect)

相关文章