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
相关产品推荐
相关产品推荐

