为什么根小部件的窗口大小不同?

2022-01-15 00:00:00 python kivy

问题描述

我正在尝试使用带有 GridLayout 的自定义小部件,但结果始终是角落中的一个非常小的网格,而不是在整个窗口中扩展的网格.

I'm trying to use a custom widget with a GridLayout, but the result is always a really small grid in a corner, instead of a grid expanding in the whole window.

示例代码:

import kivy
kivy.require('1.5.1')

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout



class MyWidget(Widget):
    def __init__(self):
        super(MyWidget, self).__init__()

        grid_layout = GridLayout(cols=3)
        grid_layout.add_widget(Button(text='A'))
        grid_layout.add_widget(Button(text='B'))
        grid_layout.add_widget(Label(text='text'))
        grid_layout.add_widget(Label(text='other'))
        grid_layout.add_widget(Button(text='text'))
        self.add_widget(grid_layout)


class MyApp(App):
    def build(self):
        float = 
        return MyWidget()


MyApp().run()

由于 Widget 的默认 size_hint(1,1) 它应该在整个窗口中展开,并且 GridLayout 也是.为什么这没有发生?怎样才能得到我想要的结果?

Since Widget's default size_hint is (1,1) it should expand in the whole window, and the GridLayout also. Why is this not happening? How can I obtain the result I want?


解决方案

`class MyWidget(Widget):`

您的根小部件是 MyWidget,它继承自 Widget 而不是其中一种布局,因此不控制其子级的大小 如前所述 这里, "size_hint 是一个layouts 用于管理其子级大小的值的元组".

Your root widget is MyWidget which inherits from a Widget and not from one of the layouts and therefore does not control the size of it's children As mentioned here, "The size_hint is a tupple of values used by layouts to manage the size of their children".

您的根小部件确实占据了窗口的整个空间.您可以通过向 MyWidget 的画布添加一个 Rectangle 来测试这一点,如下所示 ::

Your root widget does take up the entire space of the window. You can test this by adding a Rectangle to the canvas of MyWidget like so ::

with self.canvas.before:
    Color(1,0,0,1)
    Rectangle(pos=self.pos, size=self.size)

你应该熟悉画布,canvas.before 和 canvas.after.它们基本上是指令组,前组在小部件的画布指令之前绘制,后组在之后.

You should get familiar with canvas, canvas.before and canvas.after. They are basically groups of instructions, the before group is drawn before the canvas instructions of the widget and after group after.

Kivy 中的一个关键不同之处在于,小部件的大小/布局会延迟到下一帧,因此如果您只是将上述代码段添加到您的代码中,如下所示::

One key thing different in Kivy though is that widget's sizing/layout is delayed till next frame, so if you just add the above snippet to your code like so::

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.graphics import *


class MyWidget(Widget):

    def __init__(self, **kwargs):
        # I'd make sure to pass of kwargs through to the super
        # as there are widgets's that process their initial
        # arguments through.
        super(MyWidget, self).__init__(**kwargs)

        grid_layout = GridLayout(cols=3)
        grid_layout.add_widget(Button(text='A'))
        grid_layout.add_widget(Button(text='B'))
        grid_layout.add_widget(Label(text='text'))
        grid_layout.add_widget(Label(text='other'))
        grid_layout.add_widget(Button(text='text'))
        self.add_widget(grid_layout)
        with self.canvas.before:
            Color(1,0,0,1)
            Rectangle(pos=self.pos, size=self.size)


class MyApp(App):
    def build(self):
        return MyWidget()

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

这只会在您的小部件的初始位置和大小处显示一个红色矩形,当时它的默认位置和大小分别为 (0, 0) 和 (100, 100).

This would only display a red rectangle at the initial position and size of your widget, which at the time would be it's default pos and size a.k.a (0, 0) and (100, 100) respectively.

为了使红色矩形符合小部件的大小,我们应该 绑定它的大小到小部件的大小,像这样::

To make the red rect adhere to the size of the widget we should bind it's size to the size of the widget, like so::

...
        grid_layout.add_widget(Button(text='text'))
        self.add_widget(grid_layout)
        with self.canvas.before:
            Color(1,0,0,1)
            self.rect = Rectangle(pos=self.pos, size=self.size)
        self.bind(size=self.update_rect)

    def update_rect(self, instance, value):
        self.rect.pos = self.pos
        self.rect.size = self.size

class MyApp(App):
    def build(self):
...

正如上面代码的输出所示,您的小部件占据了整个窗口的大小.但是,这并不能解决您的问题,并且子布局仍然保持原来的位置和位置.它的原始大小.这是因为小部件不能如上所述控制其子级的大小.

As the output of the above code above will show, your widget is taking up the size of the entire window. However, this doesn't solve your issue and the child layout still remains at it's original pos & of it's original size. This is cause widgets don't control the size of their children as stated above.

这里有两个选择,要么更新尺寸,要么更新尺寸.您的小部件的子/子的 pos 像您更新 rect (会很快变得复杂,有多个孩子)或使用其中一个布局作为您的根小部件.

You have two choices here, either update the size & pos of your widget's child/children like you update rect(would quickly get complicated with multiple children) or use one of the Layouts as your root widget.

这也可以像这样在 kv 中完成::

This could also be done in kv like so::

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder

Builder.load_string('''
# this is the rule for MyWidget that defines
# what MyWidget looks like i.e.drawing
# instructions and widgets etc 
<MyWidget>:
    canvas.before:
        Color:
            rgba: 1, 0, 0, 1
        Rectangle:
            # this implicitly binds the size of the
            # rect to the size of the widget
            size: self.size
            # self here still refers to the widget as Rectangle is only a
            # graphics instruction and not a widget
            pos: self.pos
    GridLayout:
        cols: 3
        # root here refers to the `MyWidget`, bind the size of the
        # GridLayout to the size of your root widget
        size: root.size
        Button:
            text: 'A'
        Button:
            text: 'B'
        Label:
            text: 'text'
        Label:
            text: 'other'
        Button:
            text: 'text'        
''')


class MyWidget(Widget):
    pass


class MyApp(App):
    def build(self):
        return MyWidget()

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

上面的例子将孩子的大小绑定到它的父窗口小部件的大小.我仍然建议使用布局作为你的根小部件,不要犹豫嵌套布局.

The example above binds the size of the child to the size of it's parent widget. I'd still recommend using a layout as your root widget, and don't be hesitant of nesting layouts.

相关文章