我正在尝试创建一些相互堆叠的不规则矩形按钮。我已经完成了大部分任务,代码如下,代码创建并保存各种形状的 .png,然后在 kivy 应用程序中使用这些形状。我按照我想要的方式调整了大小和位置,但我遇到的问题是图像小部件似乎是矩形的,因此即使形状不重叠,小部件也会重叠。由此产生的问题是 on_press() 函数并不总是触发正确的按钮,尤其是在边缘附近,如此处的图像所示。 (屏幕截图上的红色框是手绘的,以显示小部件的形状!)
我想要实现的想法是,按下形状内部将激活按钮。有什么想法可以将面积缩小到形状内部吗?
谢谢
from PIL import Image, ImageDraw
from kivy.app import App
from kivy.uix.image import Image as Img
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.floatlayout import FloatLayout
def calculate_hw(tuple_list, ht = False, wt = False):
'using the coordinates of the shapes, returns the height and/or width'
if not tuple_list:
return None
min_x = min(tuple_list, key=lambda tup: tup[0])[0]
max_x = max(tuple_list, key=lambda tup: tup[0])[0]
min_y = min(tuple_list, key=lambda tup: tup[1])[1]
max_y = max(tuple_list, key=lambda tup: tup[1])[1]
h = max_y - min_y
w = max_x - min_x
if ht and wt:
return h, w
elif ht:
return h
elif wt:
return w
def calculate_y_difference(tuple_list):
'''
returns the height of the objecy at its widest part
used to calculate the value of the pos_y of the next button
'''
sorted_by_x = sorted(tuple_list, key=lambda tup: tup[0], reverse=True)
y_diff = sorted_by_x[0][1] - sorted_by_x[1][1]
return abs(y_diff)
shapes = {
'1': [(500.0, 0.0), (500.0, 93.38888888888945), (0.0, 25.0), (0.0, 0.0), (500.0, 0.0)],
'2': [(500.0, 93.38888888888945), (500.0, 133.33333333333357), (0.0, 75.0), (0.0, 25.0), (500.0, 93.38888888888945)],
'3': [(500.0, 133.33333333333357), (500.0, 188.3333333333333), (0.0, 125.0), (0.0, 75.0), (500.0, 133.33333333333357)]}
shapes_height = {k: calculate_hw(shapes[k], ht = True) for k in shapes.keys() }
shape_pos_height = {k: calculate_y_difference(shapes[k]) for k in shapes.keys() }
shape_width = {k: calculate_hw(shapes[k], wt = True) for k in shapes.keys() }
#Loop through each shape, create it, crop it and save it as a .png
for shape_name, vertices in shapes.items():
# Create a new transparent image
width, height = 4000, 4000
background_color = (0, 0, 0, 0) # Transparent background
image_normal = Image.new("RGBA", (width, height), background_color)
image_down = Image.new("RGBA", (width, height), background_color)
# Create a drawing object
draw_normal = ImageDraw.Draw(image_normal)
draw_down = ImageDraw.Draw(image_down)
# Draw the irregular shape
draw_normal.polygon(vertices, ) #fill=(255, 0, 0, 128)) # Fill color with transparency
draw_down.polygon(vertices, fill=(0, 255, 0, 128)) # Fill color with transparency
# Find the bounding box of the polygon
min_x = min(point[0] for point in vertices)
min_y = min(point[1] for point in vertices)
max_x = max(point[0] for point in vertices)
max_y = max(point[1] for point in vertices)
#crop the image
image_normal = image_normal.crop((min_x, min_y, max_x, max_y))
image_down = image_down.crop((min_x, min_y, max_x, max_y))
# Save the image as a PNG file
image_normal.save(f"{shape_name}.png", "PNG", dpi=(2000, 2000), resolution_unit="in")
image_down.save(f"{shape_name}_down.png", "PNG", dpi=(2000, 2000), resolution_unit="in")
class MyButton(ButtonBehavior, Img):
'''
a very basic button with an image used instead of a Label
'''
def __init__(self,normal, down, **kwargs):
super(MyButton, self).__init__(**kwargs)
self.normal = normal
self.down = down
self.source = self.normal
def on_press(self):
self.source = self.down
def on_release(self):
self.source = self.normal
class Main(FloatLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
y = 300
for shape in shapes: #Loop through the shape names
y -= shape_pos_height[shape] #the y pos of the first shape, this is reduced by the width of the shape to achieve a stacking effect
#add the custom budget
self.add_widget(MyButton(
down = f'{shape}_down.png',
normal = f'{shape}.png',
size_hint = (None, None),
size = (shape_width[shape], shapes_height[shape]),
pos = (100,y) ))
class MainApp(App):
def build(self):
return Main()
MainApp().run()
您需要创建自己的函数来测试碰撞,而不是依赖 ButtonBehavior。这是一个使用圆的完整示例。我还添加了创建和调度 on_release 和 on_press 事件。
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ColorProperty, NumericProperty
from kivy.uix.label import Label
kv = """
<CircleButton>:
size_hint: None, None
size: self.radius * 2, self.radius * 2
canvas.before:
Color:
rgba: self._button_color
Ellipse:
size: self.size
pos: self.pos
AnchorLayout:
CircleButton:
text: 'Push'
radius: dp(75)
on_press: print('Button Pressed')
on_release: print('Button Released')
"""
class CircleButton(Label):
radius = NumericProperty(50)
normal_color = ColorProperty('gray')
down_color = ColorProperty('blue')
_button_color = ColorProperty('gray')
def __init__(self, **kwargs):
self.register_event_type('on_release')
self.register_event_type('on_press')
super().__init__(**kwargs)
def on_press(self):
pass
def on_release(self):
pass
def is_inside_circle(self, touch_x, touch_y):
dx = abs(touch_x - self.center_x)
dy = abs(touch_y - self.center_y)
return dx ** 2 + dy ** 2 <= self.radius ** 2
def on_touch_down(self, touch):
if self.is_inside_circle(*touch.pos):
touch.grab(self)
self._button_color = self.down_color
self.dispatch('on_press')
return True
super().on_touch_down(touch)
def on_touch_up(self, touch):
if touch.grab_current is self:
self._button_color = self.normal_color
self.dispatch('on_release')
return True
return super().on_touch_up(touch)
class CircleButtonExampleApp(App):
def build(self):
return Builder.load_string(kv)
CircleButtonExampleApp().run()