Kivy: Как остановить виджет разброса, перекрывающий заголовки вкладок

Виджет Scatter размещается в содержимом TabbedPanelItem. Когда точечный объект перемещается, он перемещается по всем заголовкам вкладок. Как мне убедиться, что он перемещается под заголовками вкладок? Я понимаю, что индекс глубины виджета соответствует порядку добавления виджетов, но не понимаю, как я могу добавить Scatter перед виджетом Tab, чтобы избежать этого?

Изображение проблемы:

Разбросанный виджет, перекрывающий заголовки вкладок

Код, иллюстрирующий проблему (элемент разброса красный, на вкладке 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())

person Neex    schedule 08.03.2018    source источник
comment
У меня тоже отлично работает. Я использую Python 3.6.1 с Kivy v1.10.0 под Ubuntu 16.04. Что вы используете?   -  person John Anderson    schedule 08.03.2018
comment
@JohnAnderson Вы перетащили скаттер?   -  person eyllanesc    schedule 08.03.2018
comment
@eyllanesc, нет. Ой, я неправильно прочитал ОП.   -  person John Anderson    schedule 08.03.2018
comment
@JohnAnderson Это то, что описывает автор, проблема возникает из-за того, что при его перемещении и установке в False свойство auto_bring_to_front должно отображаться за вкладкой теоретически, но это не так.   -  person eyllanesc    schedule 08.03.2018
comment
Привет, я новичок на этом форуме и хотел бы внести больше, но у меня пока нет представителя... если вы считаете, что этот вопрос и ответ полезны для сообщества, не могли бы вы проголосовать? Большое спасибо!   -  person Neex    schedule 11.03.2018


Ответы (2)


Я считаю, что проблема связана с порядком виджетов.

Kivy использует порядок вставки виджетов в дерево как z-index (вы всегда добавляете «сверху» по умолчанию), а макеты и сложные виджеты также используют этот порядок как указание позиции, большую часть времени это все хорошо и хорошо, но иногда это не так, и тогда вам иногда нужны странные обходные пути.

Это происходит здесь, TabbedPanel делает немного волшебства, и заголовки на самом деле все «перед» (то есть под) контейнером, и, поскольку ваш контент может выходить за пределы предполагаемой зоны, эффект не тот, что вы ожидаете.

Решение, которое я бы посоветовал здесь, состоит в том, чтобы фактически предотвратить отрисовку контента за пределами его позиции с помощью трафарета.

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())

(я также исправил небольшую проблему с неотносительной позицией метки, поскольку вы находитесь в разбросе, вам нужно использовать координаты относительно разброса, а не всего экрана).

Надеюсь это поможет.

person Tshirtman    schedule 11.03.2018
comment
Гвозди это ;-) Спасибо за объяснение, имеет смысл. Мне нравится инструмент Stencil View — я вообще о нем не знал — он устраняет для меня много проблем и в других местах, как и сенсорное управление. - person Neex; 11.03.2018

Не полный ответ, но, возможно, начало для тех, кто может пойти дальше. Если вы используете пользовательский 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 остается за вкладками.

person John Anderson    schedule 08.03.2018
comment
Привет, спасибо, хотя побочный эффект в этом приложении был для меня ошеломляющим, я все же кое-что узнал из вашего кода... - person Neex; 11.03.2018