Kivy:用户触摸和拖动裁剪功能
Kivy: user touch and drag for cropping function
我正在尝试裁剪图像。基本思想是用户将鼠标拖到图像上进行裁剪。为此,当您单击 "Crop" 时,用户可以 select 左下角点,然后将矩形向上拖动到右上角。矩形应该遮蔽外部区域而不是 selected。
我一直在研究拖动行为和更简单的方法,例如 this answer(在下面尝试过),但收效甚微。作为奖励,是否可以限制用户可以触摸和拖动的区域?
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.graphics import Color
from kivy.graphics import Point
from kivy.properties import NumericProperty, ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MyScreenManager>:
ThirdScreen:
id: third_screen
<ThirdScreen>:
name: '_third_screen_'
id: third_screen
BoxLayout:
orientation: "vertical"
id: third_screen_boxlayout
Label:
id: main_title
text: "Title"
size_hint: (1, 0.1)
BoxLayout:
id: image_box_layout
Image:
id: main_image
source: "C:/Users/OneDrive/0. T2/6. Kivy/4/claymore.jpg"
BoxLayout:
id: button_boxlayout
orientation: "horizontal"
padding: 10
size_hint: (1, 0.15)
Button:
id: accept_button
text: "Okay"
size_hint: (0.33, 1)
on_press: root.image_accepted_by_user(root.image_address)
Button:
id: crop_button
text: "Crop"
size_hint: (0.33, 1)
on_press: root.enable_cropping()
Button:
id: cancel_button
text: "Cancel"
size_hint: (0.33, 1)
on_press: root.manager.current = '_first_screen_'
""")
class MyScreenManager(ScreenManager):
pass
class ThirdScreen(Screen):
def enable_cropping(self):
print("\nThirdScreen:")
print(self.ids.main_image.pos)
print(self.ids.main_image.size)
print("\tAbsolute size=", self.ids.main_image.norm_image_size)
print("\tAbsolute pos_x=", self.ids.main_image.center_x - self.ids.main_image.norm_image_size[0] / 2.)
print("\tAbsolute pos_y=", self.ids.main_image.center_y - self.ids.main_image.norm_image_size[1] / 2.)
self.ids.image_box_layout.add_widget(DrawInput(size_hint=(0.00000000000001, 0.00000000000001)))
class DrawInput(Widget):
# for cropping
draw = True
rect_box = ObjectProperty(None)
t_x = NumericProperty(0.0)
t_y = NumericProperty(0.0)
x1 = y1 = x2 = y2 = 0.0
def on_touch_down(self, touch):
if self.draw:
color = (1, 1, 1, 0.4) # red, 40% shaded
win = self.get_parent_window()
touch.ud['group'] = str(touch.uid)
with self.canvas:
# variable drag
Color(*color, mode='hsv')
self.x1 = touch.x
self.y1 = touch.y
self.t_x = touch.x
self.t_y = touch.y
self.rect_box = [Rectangle(pos=(0, self.y1),
size=(win.width, -self.y1),
group=touch.ud['group']),
Rectangle(pos=(0, self.y1),
size=(self.x1, win.height),
group=touch.ud['group']),
Point(points=(self.x1, self.y1),
source='particle.png',
group=touch.ud['group']),
Rectangle(pos=(self.t_x, self.y1),
size=(win.width - self.t_x, win.height - self.y1),
group=touch.ud['group']),
Rectangle(pos=(self.t_x, self.t_y),
size=(self.x1 - self.t_x, win.height - touch.y),
group=touch.ud['group']),
Point(points=(self.t_x, self.t_y),
source='particle.png',
group=touch.ud['group'])]
touch.grab(self)
print(self.x1, self.y1)
def on_touch_move(self, touch):
if touch.grab_current is self:
# not working
self.t_x = touch.x
self.t_y = touch.y
print(self.t_x, self.t_y)
def on_touch_up(self, touch):
if touch.grab_current is self:
# only 1 draw
self.draw = False
# final position
self.x2 = touch.x
self.y2 = touch.y
print(self.x2, self.y2)
class MyApp(App):
def build(self):
return MyScreenManager()
if __name__ == '__main__':
MyApp().run()
解决方案是删除 DrawInput class 并将 on_touch_down、on_touch_move 和 on_touch_up 集成到屏幕中。 NumericProperty() 和 ObjectProperty() 允许对作为小部件 canvas 写入 kv 文件的矩形进行操作。
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.graphics import Color
from kivy.graphics import Point
from kivy.properties import NumericProperty, ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MyScreenManager>:
ThirdScreen:
id: third_screen
<ThirdScreen>:
name: '_third_screen_'
id: third_screen
BoxLayout:
orientation: "vertical"
id: third_screen_boxlayout
Label:
id: main_title
text: "Title"
size_hint: (1, 0.1)
BoxLayout:
id: image_box_layout
Image:
id: main_image
source: "C:/Users/Mark/OneDrive/0. T2/6. Kivy/4/claymore.jpg"
Widget:
id: image_canvas
size_hint: (0.0000001, 0.0000001)
canvas:
Rectangle:
id: root.rect_box
pos: (root.x1, root.y1)
size: (root.t_x, root.t_y)
BoxLayout:
id: button_boxlayout
orientation: "horizontal"
padding: 10
size_hint: (1, 0.15)
Button:
id: accept_button
text: "Okay"
size_hint: (0.33, 1)
on_press: root.image_accepted_by_user(root.image_address)
Button:
id: crop_button
text: "Crop"
size_hint: (0.33, 1)
on_press: root.enable_cropping()
Button:
id: cancel_button
text: "Cancel"
size_hint: (0.33, 1)
on_press: root.manager.current = '_first_screen_'
""")
class MyScreenManager(ScreenManager):
pass
class ThirdScreen(Screen):
rect_box = ObjectProperty(None)
t_x = NumericProperty(0.0)
t_y = NumericProperty(0.0)
x1 = y1 = x2 = y2 = NumericProperty(0.0)
def enable_cropping(self):
print("\nThirdScreen:")
print(self.ids.main_image.pos)
print(self.ids.main_image.size)
print("\tAbsolute size=", self.ids.main_image.norm_image_size)
print("\tAbsolute pos_x=", self.ids.main_image.center_x - self.ids.main_image.norm_image_size[0] / 2.)
print("\tAbsolute pos_y=", self.ids.main_image.center_y - self.ids.main_image.norm_image_size[1] / 2.)
def on_touch_down(self, touch):
self.x1 = touch.x
self.y1 = touch.y
self.t_x = touch.x
self.t_y = touch.y
touch.grab(self)
print(self.x1, self.y1)
def on_touch_move(self, touch):
if touch.grab_current is self:
# not working
self.t_x = touch.x
self.t_y = touch.y
print(self.t_x, self.t_y)
def on_touch_up(self, touch):
if touch.grab_current is self:
# final position
self.x2 = touch.x
self.y2 = touch.y
print(self.x2, self.y2)
class MyApp(App):
def build(self):
return MyScreenManager()
if __name__ == '__main__':
MyApp().run()
我正在尝试裁剪图像。基本思想是用户将鼠标拖到图像上进行裁剪。为此,当您单击 "Crop" 时,用户可以 select 左下角点,然后将矩形向上拖动到右上角。矩形应该遮蔽外部区域而不是 selected。
我一直在研究拖动行为和更简单的方法,例如 this answer(在下面尝试过),但收效甚微。作为奖励,是否可以限制用户可以触摸和拖动的区域?
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.graphics import Color
from kivy.graphics import Point
from kivy.properties import NumericProperty, ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MyScreenManager>:
ThirdScreen:
id: third_screen
<ThirdScreen>:
name: '_third_screen_'
id: third_screen
BoxLayout:
orientation: "vertical"
id: third_screen_boxlayout
Label:
id: main_title
text: "Title"
size_hint: (1, 0.1)
BoxLayout:
id: image_box_layout
Image:
id: main_image
source: "C:/Users/OneDrive/0. T2/6. Kivy/4/claymore.jpg"
BoxLayout:
id: button_boxlayout
orientation: "horizontal"
padding: 10
size_hint: (1, 0.15)
Button:
id: accept_button
text: "Okay"
size_hint: (0.33, 1)
on_press: root.image_accepted_by_user(root.image_address)
Button:
id: crop_button
text: "Crop"
size_hint: (0.33, 1)
on_press: root.enable_cropping()
Button:
id: cancel_button
text: "Cancel"
size_hint: (0.33, 1)
on_press: root.manager.current = '_first_screen_'
""")
class MyScreenManager(ScreenManager):
pass
class ThirdScreen(Screen):
def enable_cropping(self):
print("\nThirdScreen:")
print(self.ids.main_image.pos)
print(self.ids.main_image.size)
print("\tAbsolute size=", self.ids.main_image.norm_image_size)
print("\tAbsolute pos_x=", self.ids.main_image.center_x - self.ids.main_image.norm_image_size[0] / 2.)
print("\tAbsolute pos_y=", self.ids.main_image.center_y - self.ids.main_image.norm_image_size[1] / 2.)
self.ids.image_box_layout.add_widget(DrawInput(size_hint=(0.00000000000001, 0.00000000000001)))
class DrawInput(Widget):
# for cropping
draw = True
rect_box = ObjectProperty(None)
t_x = NumericProperty(0.0)
t_y = NumericProperty(0.0)
x1 = y1 = x2 = y2 = 0.0
def on_touch_down(self, touch):
if self.draw:
color = (1, 1, 1, 0.4) # red, 40% shaded
win = self.get_parent_window()
touch.ud['group'] = str(touch.uid)
with self.canvas:
# variable drag
Color(*color, mode='hsv')
self.x1 = touch.x
self.y1 = touch.y
self.t_x = touch.x
self.t_y = touch.y
self.rect_box = [Rectangle(pos=(0, self.y1),
size=(win.width, -self.y1),
group=touch.ud['group']),
Rectangle(pos=(0, self.y1),
size=(self.x1, win.height),
group=touch.ud['group']),
Point(points=(self.x1, self.y1),
source='particle.png',
group=touch.ud['group']),
Rectangle(pos=(self.t_x, self.y1),
size=(win.width - self.t_x, win.height - self.y1),
group=touch.ud['group']),
Rectangle(pos=(self.t_x, self.t_y),
size=(self.x1 - self.t_x, win.height - touch.y),
group=touch.ud['group']),
Point(points=(self.t_x, self.t_y),
source='particle.png',
group=touch.ud['group'])]
touch.grab(self)
print(self.x1, self.y1)
def on_touch_move(self, touch):
if touch.grab_current is self:
# not working
self.t_x = touch.x
self.t_y = touch.y
print(self.t_x, self.t_y)
def on_touch_up(self, touch):
if touch.grab_current is self:
# only 1 draw
self.draw = False
# final position
self.x2 = touch.x
self.y2 = touch.y
print(self.x2, self.y2)
class MyApp(App):
def build(self):
return MyScreenManager()
if __name__ == '__main__':
MyApp().run()
解决方案是删除 DrawInput class 并将 on_touch_down、on_touch_move 和 on_touch_up 集成到屏幕中。 NumericProperty() 和 ObjectProperty() 允许对作为小部件 canvas 写入 kv 文件的矩形进行操作。
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.graphics import Color
from kivy.graphics import Point
from kivy.properties import NumericProperty, ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MyScreenManager>:
ThirdScreen:
id: third_screen
<ThirdScreen>:
name: '_third_screen_'
id: third_screen
BoxLayout:
orientation: "vertical"
id: third_screen_boxlayout
Label:
id: main_title
text: "Title"
size_hint: (1, 0.1)
BoxLayout:
id: image_box_layout
Image:
id: main_image
source: "C:/Users/Mark/OneDrive/0. T2/6. Kivy/4/claymore.jpg"
Widget:
id: image_canvas
size_hint: (0.0000001, 0.0000001)
canvas:
Rectangle:
id: root.rect_box
pos: (root.x1, root.y1)
size: (root.t_x, root.t_y)
BoxLayout:
id: button_boxlayout
orientation: "horizontal"
padding: 10
size_hint: (1, 0.15)
Button:
id: accept_button
text: "Okay"
size_hint: (0.33, 1)
on_press: root.image_accepted_by_user(root.image_address)
Button:
id: crop_button
text: "Crop"
size_hint: (0.33, 1)
on_press: root.enable_cropping()
Button:
id: cancel_button
text: "Cancel"
size_hint: (0.33, 1)
on_press: root.manager.current = '_first_screen_'
""")
class MyScreenManager(ScreenManager):
pass
class ThirdScreen(Screen):
rect_box = ObjectProperty(None)
t_x = NumericProperty(0.0)
t_y = NumericProperty(0.0)
x1 = y1 = x2 = y2 = NumericProperty(0.0)
def enable_cropping(self):
print("\nThirdScreen:")
print(self.ids.main_image.pos)
print(self.ids.main_image.size)
print("\tAbsolute size=", self.ids.main_image.norm_image_size)
print("\tAbsolute pos_x=", self.ids.main_image.center_x - self.ids.main_image.norm_image_size[0] / 2.)
print("\tAbsolute pos_y=", self.ids.main_image.center_y - self.ids.main_image.norm_image_size[1] / 2.)
def on_touch_down(self, touch):
self.x1 = touch.x
self.y1 = touch.y
self.t_x = touch.x
self.t_y = touch.y
touch.grab(self)
print(self.x1, self.y1)
def on_touch_move(self, touch):
if touch.grab_current is self:
# not working
self.t_x = touch.x
self.t_y = touch.y
print(self.t_x, self.t_y)
def on_touch_up(self, touch):
if touch.grab_current is self:
# final position
self.x2 = touch.x
self.y2 = touch.y
print(self.x2, self.y2)
class MyApp(App):
def build(self):
return MyScreenManager()
if __name__ == '__main__':
MyApp().run()