Kivy调整窗口大小时布局与图片无法自动缩放、拖拽图片边界限制问题
问题1:布局与组件无法随窗口缩放的原因
- 你手动给
DraggableArea设置了size_hint = (None, None),关闭了布局的自适应尺寸特性,默认FloatLayout的size_hint为(1,1),会自动填充父窗口 - 你在canvas中绘制的绿色棋盘矩形用了固定值
size=(200, 200)、pos=(300, 200),没有和布局的size、pos做绑定,布局尺寸变化时画布元素不会自动更新 - 棋子的初始pos是写死的固定值,且你给
MoveableImage设置的drag_rectangle是自身的坐标范围,没有和父布局绑定,也不会随布局缩放调整
问题2:限制拖拽范围的内置方法
Kivy的DragBehavior本身就提供了拖拽范围限制能力,不需要额外写碰撞检测逻辑:
- 开启
drag_clamp = True属性,拖拽时会自动把组件限制在drag_rectangle定义的范围内 - 把
drag_rectangle的值设置为父布局的坐标和尺寸范围,不要用组件自身的范围,同时绑定父布局的size、pos变化事件,实时更新drag_rectangle的取值即可
修正后的可运行代码
from kivy.config import Config Config.set('graphics', 'resizable', True) Config.write() import kivy kivy.require('2.0.0') from kivy.app import App from kivy.uix.floatlayout import FloatLayout from kivy.uix.image import Image from kivy.uix.behaviors import DragBehavior from kivy.graphics import Color, Rectangle class DraggableArea(FloatLayout): def __init__(self, **kwargs): super(DraggableArea, self).__init__(**kwargs) # 保留默认size_hint=(1,1),自动填充窗口 with self.canvas.before: Color(0, 1, 0, 1) self.rect = Rectangle(size=self.size, pos=self.pos) # 绑定布局尺寸、位置变化,更新画布棋盘和所有棋子的拖拽范围 self.bind(size=self.update_layout, pos=self.update_layout) # 添加棋子,初始位置用相对比例计算,避免固定值 self.add_widget(MoveableImage( source="./data/images/chess-pieces/WhiteQueen.png", size_hint=(1/8, 1/8), # 国际象棋为8x8棋盘,每个棋子默认占1/8大小 pos_hint={"x": 0.25, "y": 0.25} # 相对位置,自动适配缩放 )) def update_layout(self, *args): self.rect.pos = self.pos self.rect.size = self.size # 遍历所有棋子更新拖拽范围 for child in self.children: if isinstance(child, MoveableImage): child.update_drag_range() class MoveableImage(DragBehavior, Image): def __init__(self, **kwargs): super(MoveableImage, self).__init__(**kwargs) self.drag_timeout = 10000000 self.drag_distance = 0 # 开启拖拽范围限制 self.drag_clamp = True self.keep_ratio = True self.allow_stretch = True # 初始化拖拽范围 self.update_drag_range() def update_drag_range(self): # 拖拽范围设为父布局的内部区域,保证棋子不会拖出棋盘 self.drag_rectangle = [ self.parent.x, self.parent.y, self.parent.width - self.width, self.parent.height - self.height ] class egGameApp(App): def build(self): return DraggableArea() if __name__ == '__main__': egGameApp().run()
内容的提问来源于stack exchange,提问作者Sam Quintanar
相关产品推荐
相关产品推荐

