You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kivy拖动子部件时如何使其始终处于顶层显示?

问题描述

我有一个父GridLayout(名为grd),包含两个子部件flt和bxl。flt的子部件中有一个带DragBehavior的dragwid,目前可以拖动它,但希望dragwid无论拖到哪里都处于顶层显示,始终是可见的最上层部件。尝试过移除再重新添加到父部件的方法,但因为其他部件是它的“表亲”关系,这个方法无效,求解决方案。

解决方案

核心思路

将可拖动部件DragCirc从原来的子容器flt移到最顶层的grd中,让它和flt、bxl成为同级部件。只要保证它在grd的子部件列表最后(或每次拖动时重新添加到最后),就能始终处于顶层显示。同时需要处理初始位置的坐标转换,因为父容器变更后,部件的位置参考系会改变。

修改步骤

1. 调整DragCirc的父容器

在practiceApp的build方法中,把drgwid添加到grd而非flt,同时将初始位置转换为基于grd的坐标:

# 替换原flt.add_widget(drgwid)代码
drgwid.pos = flt.to_window(*drgwid.pos)  # 把flt内的局部坐标转为窗口全局坐标
grd.add_widget(drgwid)

2. 修改DragCirc的触摸事件逻辑

更新触摸事件处理,确保每次点击拖动时,将部件移到顶层容器的子部件列表末尾:

class DragCirc(DragBehavior, Label):
    def __init__(self,**kwargs):
        super(DragCirc,self).__init__(**kwargs)
    
    def on_touch_down(self,touch):
        if self.collide_point(*touch.pos):
            if self.parent:
                # 移除后重新添加到父部件最后,确保顶层显示
                self.parent.remove_widget(self)
                self.parent.add_widget(self)
        return super().on_touch_down(touch)

3. 修正原代码的颜色绘制问题

原circ类中Color和Ellipse的顺序错误,导致颜色不生效,调整顺序:

def cir_build(self):
    wid = Widget()
    with wid.canvas:
        Color(*self.clr)  # 颜色设置要在图形绘制前
        Ellipse(pos =self.posit,size = (self.rx,self.ry))
    return wid

完整修改后的practice.py代码

from kivy.app import App
from kivy.uix.behaviors import DragBehavior
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import Ellipse, Color, Rectangle
from kivy.core.window import Window
from kivy.lang import Builder

class DragCirc(DragBehavior,Label):
    def __init__(self,**kwargs):
        super(DragCirc,self).__init__(**kwargs)
    
    def on_touch_down(self,touch):
        if self.collide_point(*touch.pos):
            if self.parent:
                self.parent.remove_widget(self)
                self.parent.add_widget(self)
        return super().on_touch_down(touch)

class circ(Widget):
    def __init__(self,clr,sze,pos,**kwargs):
        super(circ,self).__init__(**kwargs)
        self.rx=sze[0]
        self.ry=sze[1]
        self.clr = clr
        self.posit = pos

    def cir_build(self):
        wid = Widget()
        with wid.canvas:
            Color(*self.clr)
            Ellipse(pos =self.posit,size = (self.rx,self.ry))
        return wid

class practiceApp(App):
    def __init__(self,**kwargs):
        super(practiceApp,self).__init__(**kwargs)

    def build(self):
        Window.size = (750,750)
        grd = GridLayout()
        grd.cols = 1
        
        bxl = BoxLayout()
        bxl.pos = (Window.size[0]/2,Window.size[1]/2)
        bxl.pos_hint = {'center_x':0.5,'center_y':0.5}
        sir = circ((.5,.7,.3,1),bxl.size,bxl.pos)
        bxl.add_widget(sir.cir_build())
        
        flt = FloatLayout()
        flt.pos = (Window.size[0]/4,Window.size[1]/4)
        flt.pos_hint = {'center_x':0.5,'center_y':0.5}
        flt.size_hint = .9,.9

        sirc=circ((1,1,0,0),flt.size,flt.pos)
        wid = sirc.cir_build()

        with wid.canvas:
            Color(0,1,.1,1)
            Rectangle(pos=(25,25), size=wid.size)
        flt.add_widget(wid)
        
        drgwid = DragCirc()
        drgwid.pos = flt.to_window(*drgwid.pos)
        grd.add_widget(flt)
        grd.add_widget(bxl)
        grd.add_widget(drgwid)
        return grd
        

if __name__ == "__main__":
    Builder.load_file('practice.kv')
    practiceApp().run()

效果说明

  • 调整后drgwid成为grd的直接子部件,和flt、bxl同级,每次触摸拖动时会被移到grd子部件列表的最后,确保不会被其他“表亲”部件遮挡。
  • 初始位置通过to_window方法转换,保证drgwid一开始就出现在原来的位置。

内容的提问来源于stack exchange,提问作者Brian Ketelboeter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 12:12:31