Kivy:如何停止分散小部件重叠选项卡 headers

Kivy: How do I stop a scatter widget overlapping tab headers

Scatter 小部件位于 TabbedPanelItem 内容中。移动散点图 object 时,它会移动到所有选项卡 headers 上。我如何确保它移动到选项卡 headers 下?我知道小部件深度索引遵循添加小部件的顺序,但不明白如何在 Tab 小部件之前添加 Scatter 以避免这种情况?

问题图片:

分散小部件重叠选项卡headers

说明问题的代码(分散项为红色,在选项卡 1 上):

from  kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.base import runTouchApp

Builder.load_string("""

<TabbedTestScreen>:
    TabbedPanel:
        id: tab_panel
        do_default_tab: False
        tab_pos: 'left_top'
        tab_height: 90
        tab_width: 90

        TabbedPanelItem:
            text: '1'
            BoxLayout:
                size: self.size
                pos: self.pos

                Scatter:
                    canvas.after:
                        Color: 
                            rgba: 1,0,0,0.5
                        Rectangle:
                            size: self.size
                            pos: self.pos
                    auto_bring_to_front: False     # this doesn't make any difference
                    center: self.parent.center
                    size: self.parent.size
                    do_rotation: False
                    do_translation: True
                    do_scale: True
                    Label:
                        text: 'Tab 1 scatter widget'
                        font_size: 20
                        center: self.parent.center
                        size: self.parent.size

        TabbedPanelItem:
            text: '2'
            Label:
                text: '2'
        TabbedPanelItem:
            text: '3' 
            id: home_tab
            Label:
                text: '3'                    

""")

class TabbedTestScreen(Screen):

    def __init__(self, **kwargs):
        super(TabbedTestScreen, self).__init__(**kwargs)

runTouchApp(TabbedTestScreen())

这不是一个完整的答案,但对于可以更进一步的人来说可能是一个开始。如果您使用自定义 Scatter,例如:

class MyScatter(Scatter):

    def on_pos(self, *args):
        parent = self.parent
        gparent = parent.parent
        if gparent is None:
            return
        ggparent = gparent.parent
        if ggparent is None:
            return
        num_children = len(ggparent.children)
        ggparent.remove_widget(gparent)
        ggparent.add_widget(gparent, num_children)

非常难看,还有一个我不明白的副作用(标签出现在右边)。但是 MyScatter 对象留在选项卡后面。

我认为问题与小部件排序有关。

Kivy 使用 widget 插入树的顺序为 z-index,(你总是默认添加 "on top"),布局和复杂的 widgets 也使用这个顺序作为位置指示,大多数时候这一切都很好,但有时不是,然后你有时需要奇怪的解决方法。

这就是这里发生的事情,TabbedPanel 有点神奇,headers 实际上都是 "before"(在下面)容器,并且由于您的内容可以超出预期区域,效果出乎你的意料。

我在这里建议的解决方案是使用 Stencil 实际上防止内容在其位置之外绘制任何东西。

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.base import runTouchApp

Builder.load_string("""

<TabbedTestScreen>:
    TabbedPanel:
        id: tab_panel
        do_default_tab: False
        tab_pos: 'left_top'
        tab_height: 90
        tab_width: 90

        TabbedPanelItem:
            text: '1'
            BoxLayout:
                size: self.size
                pos: self.pos

                canvas.before:
                    StencilPush
                    Rectangle:
                        pos: self.pos
                        size: self.size
                    StencilUse

                canvas.after:
                    StencilUnUse
                    Rectangle:
                        pos: self.pos
                        size: self.size
                    StencilPop

                Scatter:
                    canvas.after:
                        Color: 
                            rgba: 1,0,0,0.5
                        Rectangle:
                            size: self.size
                            pos: self.pos
                    auto_bring_to_front: False     # this doesn't make any difference
                    center: self.parent.center
                    size: self.parent.size
                    do_rotation: False
                    do_translation: True
                    do_scale: True
                    Label:
                        text: 'Tab 1 scatter widget'
                        font_size: 20
                        center: self.parent.width / 2, self.parent.height / 2
                        size: self.parent.size

        TabbedPanelItem:
            text: '2'
            Label:
                text: '2'
        TabbedPanelItem:
            text: '3' 
            id: home_tab
            Label:
                text: '3'

""")

class TabbedTestScreen(Screen):

    def __init__(self, **kwargs):
        super(TabbedTestScreen, self).__init__(**kwargs)

runTouchApp(TabbedTestScreen())

但是你仍然有一个问题,虽然它在相关地方正确地不可见,但散点图仍然处于活动状态并且可以被抓取,这使得界面混乱。

因此您还希望 BoxLayout 不关心其位置之外的触摸。

人们会认为像 StencilView 这样的东西可以做到这一点,但事实并非如此,所以您需要做一些工作。

from  kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.base import runTouchApp
from kivy.uix.stencilview import StencilView
from kivy.uix.boxlayout import BoxLayout


class StencilBox(StencilView, BoxLayout):
    def on_touch_down(self, touch):
        if not self.collide_point(*touch.pos):
            return
        return super(StencilBox, self).on_touch_down(touch)

    def on_touch_move(self, touch):
        if not self.collide_point(*touch.pos):
            return
        return super(StencilBox, self).on_touch_move(touch)

    def on_touch_up(self, touch):
        if not self.collide_point(*touch.pos):
            return
        return super(StencilBox, self).on_touch_up(touch)


Builder.load_string("""

<TabbedTestScreen>:
    TabbedPanel:
        id: tab_panel
        do_default_tab: False
        tab_pos: 'left_top'
        tab_height: 90
        tab_width: 90

        TabbedPanelItem:
            text: '1'
            StencilBox:
                size: self.size
                pos: self.pos

                Scatter:
                    canvas.after:
                        Color: 
                            rgba: 1,0,0,0.5
                        Rectangle:
                            size: self.size
                            pos: self.pos
                    auto_bring_to_front: False     # this doesn't make any difference
                    center: self.parent.center
                    size: self.parent.size
                    do_rotation: False
                    do_translation: True
                    do_scale: True
                    Label:
                        text: 'Tab 1 scatter widget'
                        font_size: 20
                        center: self.parent.width / 2, self.parent.height / 2
                        size: self.parent.size

        TabbedPanelItem:
            text: '2'
            Label:
                text: '2'
        TabbedPanelItem:
            text: '3' 
            id: home_tab
            Label:
                text: '3'

""")

class TabbedTestScreen(Screen):

    def __init__(self, **kwargs):
        super(TabbedTestScreen, self).__init__(**kwargs)

runTouchApp(TabbedTestScreen())

(我还修复了一个标签位置不是相对的小问题,因为你在一个散点中,你需要使用相对于散点的坐标,而不是整个屏幕)。

希望对您有所帮助。