将图像拖到 Kivy 上的目标时消失

Image that disappears when dragging it to a target on Kivy

我正在开发一款游戏,用户必须通过首字母(西班牙语)匹配图像,以便当他们拖动到一个点(大锅)时,以正确字母开头的图像(在本例中为冰屋、印第安人和磁铁)此图像消失。Example screen

换句话说,基本上,图像会在拖动到特定点时消失。

*.kv

#:import win kivy.core.window

<Picture@Scatter>:
    source: None
    on_size: self.center = win.Window.center
    size: image.size
    size_hint: None, None
    do_scale: False
    do_rotation: False

    Image:
        id: image
        source: root.source
        size: 250, 250 / self.image_ratio

<Relaciona3x2>:
    AnchorLayout:
        Image:
            source: 'data/img/fondobosque.jpg'
            allow_stretch: True
            keep_ratio: False

        FloatLayout:
            size_hint: 1, 1

            Image:
                id: img003
                source: 'data/img/caldero.png'
                size_hint: 0.55, 0.55
                pos_hint: {"center_x": 0.5, "center_y": 0.20}

            Button:
                size_hint:.06, 0.1
                text: "Volver al menú"
                on_release: app.root.current = 'menu'

            Picture:
                id: potionscatter
                source: "data/img/letra_i/iglú.png"
                pos: 175, 680
            Picture:
                source: "data/img/letra_i/indio.png"
                pos: 835, 680
            Picture:
                source: "data/img/letra_m/moto.png"
                pos: 1495, 680
            Picture:
                source: "data/img/letra_u/uña.png"
                pos: 175, 420
            Picture:
                source: "data/img/letra_i/imán_1.png"
                pos: 835, 420
            Picture:
                source: "data/img/letra_u/urraca.png"
                pos: 1495, 420

<Relaciona4x2Screen>:
    AnchorLayout:
        Image:
            source: 'data/img/fondobosque.jpg'
            allow_stretch: True
            keep_ratio: False

        FloatLayout:
            size_hint: 1, 1

            Image:
                id: img003
                source: 'data/img/caldero.png'
                size_hint: 0.55, 0.55
                pos_hint: {"center_x": 0.5, "center_y": 0.20}

            Button:
                size_hint:.06, 0.1
                text: "Volver al menú"
                on_release: app.root.current = 'menu'

<Relaciona5x2Screen>:
    AnchorLayout:
        Image:
            source: 'data/img/fondobosque.jpg'
            allow_stretch: True
            keep_ratio: False

        FloatLayout:
            size_hint: 1, 1

            Image:
                id: img003
                source: 'data/img/caldero.png'
                size_hint: 0.55, 0.55
                pos_hint: {"center_x": 0.5, "center_y": 0.20}

            Button:
                size_hint:.06, 0.1
                text: "Volver al menú"
                on_release: app.root.current = 'menu'

relaciona.py

__all__ = ('Relaciona3x2', 'Relaciona4x2', 'Relaciona5x2')

import kivy
kivy.require('1.0.6')

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen

Builder.load_file('relaciona.kv')

class Relaciona3x2(Screen):
    pass

class Relaciona4x2(Screen):
    pass

class Relaciona5x2(Screen):
    pass

main.py

import kivy
kivy.require('1.0.6')

from kivy.uix.popup import Popup
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.animation import Animation
from kivy.uix.button import Button
from kivy.uix.scatter import Scatter
from kivy.logger import Logger
from kivy.uix.gesturesurface import GestureSurface

from letras import DrawGame, ImageEx, SoundEx, ContainerBox
from explota import BubblePop, BubbleGame
from relaciona import *
from borra import Borra
from caza import Caza
from config import Config
from kivy.core.window import Window

Builder.load_file('design.kv')

class MyScreenManager(ScreenManager):
    def __init__(self):
        super (MyScreenManager, self).__init__()
        
class HomeMenu(Screen):
    pass

class LetrasScreen(Screen):
    pass

class ExplotaScreen(Screen):
    pass

class Relaciona3x2Screen(Screen):
    pass

class Relaciona4x2Screen(Screen):
    pass

class Relaciona5x2Screen(Screen):
    pass

class BorraScreen(Screen):
    pass

class CazaScreen(Screen):
    pass

class ConfigScreen(Screen):
    pass

class myApp(App):
    def build(self):
        Window.fullscreen = 'auto'
        return MyScreenManager()
    
    def on_pause(self):
        return True
    
    def on_resume(self):
        pass
    
myApp().run()

我已经使用 DragNDropWidget 解决了这个问题。它使用起来很简单,但现在我不知道如何改变按钮的大小,我希望它们更大并且彼此分开一些。

DragNDropWidget.py

#!/usr/bin/python
# -*- coding: UTF-8 -*-
from kivy.core.window import Window
from kivy.animation import Animation
import copy
from kivy.uix.widget import Widget
from kivy.properties import (
    ListProperty, NumericProperty, BooleanProperty, ObjectProperty)


class DragNDropWidget(Widget):
    # let kivy take care of kwargs and get signals for free by using
    # properties
    droppable_zone_objects = ListProperty([])
    bound_zone_objects = ListProperty([])
    drag_opacity = NumericProperty(1.0)
    drop_func = ObjectProperty(None)
    drop_args = ListProperty([])
    remove_on_drag = BooleanProperty(True)

    def __init__(self, **kw):
        super(DragNDropWidget, self).__init__(**kw)

        self.register_event_type("on_drag_start")
        self.register_event_type("on_being_dragged")
        self.register_event_type("on_drag_finish")
        self.register_event_type("on_motion_over")
        self.register_event_type("on_motion_out")

        self._dragged = False
        self._dragable = True
        self._fired_already = False

    def set_dragable(self, value):
        self._dragable = value

    def set_remove_on_drag(self, value):
        """
        This function sets the property that determines whether the dragged widget is just copied from its parent or taken from its parent
        @param value: either True or False. If True then the widget will disappear from its parent on drag, else the widget will jsut get copied for dragging
        """
        self.remove_on_drag = value

    def set_bound_axis_positions(self):
        for obj in self.bound_zone_objects:
            try:
                if self.max_y < obj.y+obj.size[1]-self.size[1]:
                    self.max_y = obj.y+obj.size[1]-self.size[1]
            except AttributeError:
                self.max_y = obj.y+obj.size[1]-self.size[1]
            try:
                if self.max_x < obj.x+obj.size[0]-self.size[0]:
                    self.max_x = obj.x + obj.size[0]-self.size[0]
            except AttributeError:
                self.max_x = obj.x+obj.size[0]-self.size[0]
            try:
                if self.min_y > obj.y:
                    self.min_y = obj.y
            except AttributeError:
                self.min_y = obj.y
            try:
                if self.min_x > obj.x:
                    self.min_x = obj.x
            except AttributeError:
                self.min_x = obj.x

    def on_touch_down(self, touch):
        if self.collide_point(touch.x, touch.y) and self._dragable:
            # detect if the touch is short - has time and end (if not dispatch drag)
            if abs(touch.time_end - touch.time_start) > 0.2:
                self.dispatch("on_drag_start")


    def on_touch_up(self, touch):
        if self._dragable and self._dragged:
            self.short_touch = True
            self.dispatch("on_drag_finish")
            self.short_touch = False

    def on_touch_move(self, touch):
        if self._dragged and self._dragable:
            x = touch.x
            y = touch.y

            try:
                if touch.x < self.min_x:
                    x = self.min_x
                if touch.x > self.max_x:
                    x = self.max_x
                if touch.y < self.min_y:
                    y = self.min_y
                if touch.y > self.max_y:
                    y = self.max_y
            except AttributeError:
                pass
            self.pos = (x, y)

    def easy_access_dnd(self, function_to_do, function_to_do_out, arguments = [], bind_functions = []):
        """
        This function enables something that can be used instead of drag n drop
        @param function_to_do: function that is to be called when mouse_over event is fired on the widget
        @param bind_functions: what is really to be done - background function for GUI functionality
        """
        Window.bind(mouse_pos=self.on_motion)
        self.easy_access_dnd_function = function_to_do
        self.easy_access_dnd_function_out = function_to_do_out
        self.easy_access_dnd_function_aguments = arguments
        self.easy_access_dnd_function_binds = bind_functions

    def on_motion(self, etype, moutionevent):
        if self.collide_point(Window.mouse_pos[0], Window.mouse_pos[1]):
            if not self._fired_already:
                self.dispatch("on_motion_over")
        else:
            self.dispatch("on_motion_out")

    def on_motion_over(self):
        self.easy_access_dnd_function(
            self.easy_access_dnd_function_aguments,
            self.easy_access_dnd_function_binds)

        self._fired_already = True

    def on_motion_out(self):
        try:
            self.easy_access_dnd_function_out()
        except AttributeError:
            pass
        self._fired_already = False

    def on_drag_start(self):
        print ('drag start')
        self.opacity = self.drag_opacity
        self.set_bound_axis_positions()
        self._old_drag_pos = self.pos
        self._old_parent = self.parent
        self._old_index = self.parent.children.index(self)
        self._dragged = True
        if self.remove_on_drag:
            self.reparent(self)
        else:
            #create copy of object to drag
            self.reparent(self)
            # the final child class MUST implement __deepcopy__
            # IF self.remove_on_drag == False !!! In this case this is
            # met in DragableArhellModelImage class
            copy_of_self = copy.deepcopy(self)
            self._old_parent.add_widget(copy_of_self, index=self._old_index)

    def on_drag_finish(self):
        print ('drag finish')
        if self._dragged and self._dragable:
            self.opacity = 1.0
            dropped_ok = False
            for obj in self.droppable_zone_objects:
                if obj.collide_point(*self.pos):
                    dropped_ok = True
            if dropped_ok:
                self.drop_func(*self.drop_args)
                anim = Animation(opacity=0, duration=0.7, t="in_quad")
                anim.bind(on_complete=self.deparent)
                anim.start(self)
            else:
                anim = Animation(pos=self._old_drag_pos, duration=0.7, t="in_quad")
                if self.remove_on_drag:
                    anim.bind(on_complete = self.reborn)
                else:
                    anim.bind(on_complete = self.deparent)
                anim.start(self)
            self._dragged = False

    def deparent(self, widget="dumb", anim="dumb2"):
        self.get_root_window().remove_widget(self)

    def on_being_dragged(self):
        print ('being dragged')

    def reborn(self, widget, anim):
        self.deparent()
        self._old_parent.add_widget(self, index=self._old_index)

    def reparent(self, widget):
        parent = widget.parent
        orig_size = widget.size
        if parent:
            parent.remove_widget(widget)
            parent.get_root_window().add_widget(widget)
            widget.size_hint = (None, None)
            widget.size = orig_size

DragableButton.py

Created on Oct 24, 2012

@author: Pavel Kostelnik
'''


from DragNDropWidget import DragNDropWidget
from kivy.uix.button import Button


class DragableButton(Button, DragNDropWidget):
    '''
    classdocs
    '''
    def __init__(self, **kw):
        '''
        Constructor
        '''
        #Button.__init__(self, **kw)
        super(DragableButton, self).__init__(**kw)
        self.size_hint = (None, None)

    def __deepcopy__(self, dumb):
        return DragableButton(text=self.text,
                              droppable_zone_objects=self.droppable_zone_objects,
                              bound_zone_objects=self.bound_zone_objects,
                              drag_opacity=self.drag_opacity,
                              drop_func=self.drop_func,
                              remove_on_drag=self.remove_on_drag)

arrastrar.py

__all__ = ('Relaciona3x2', 'Relaciona4x2', 'Relaciona5x2')

import kivy
kivy.require('1.0.6')

from DragableButton import DragableButton
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen

Builder.load_file('arrastrar.kv')

class Relaciona3x2(Screen):
    pass

class Relaciona4x2(Screen):
    pass

class Relaciona5x2(Screen):
    pass

class ArrastraApp(App):
    def build(self):
        #Window.fullscreen = 'auto'
        return Relaciona3x2()
    
    def greet(self):
        print('Draggin done!')
    
if __name__ == "__main__":
    ArrastraApp().run()

arrastrar.kv

#:kivy 1.9.0

<Relaciona3x2>:
    canvas.before:
        Rectangle:
            pos: self.pos
            size: self.size
            source: 'data/img/fondobosque.jpg'
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            id: from_box
            DragableButton:
                canvas:
                    Rectangle:
                        pos: self.pos
                        size: self.size
                        source: 'data/img/letra_i/iglu.png'
                bound_zone_objects: [from_box, to_box ]
                droppable_zone_objects: [to_box, ]
                drop_func: app.greet
                
            DragableButton:
                canvas:
                    Rectangle:
                        pos: self.pos
                        size: self.size
                        source: 'data/img/letra_i/indio.png'
                bound_zone_objects: [from_box, to_box, ]
                droppable_zone_objects: [to_box, ]
                drop_func: app.greet

                
            DragableButton:
                canvas:
                    Rectangle:
                        pos: self.pos
                        size: self.size
                        source: 'data/img/letra_m/moto.png'
                bound_zone_objects: [from_box, to_box, ]
                #droppable_zone_objects: [to_box, ]
                drop_func: app.greet
        
        BoxLayout:
            id: from_box
            DragableButton:
                canvas:
                    Rectangle:
                        pos: self.pos
                        size: self.size
                        source: 'data/img/letra_u/una.png'
                bound_zone_objects: [from_box, to_box, ]
                #droppable_zone_objects: [to_box, ]
                drop_func: app.greet
                
            DragableButton:
                canvas:
                    Rectangle:
                        pos: self.pos
                        size: self.size
                        source: 'data/img/letra_i/iman_1.png'
                bound_zone_objects: [from_box, to_box, ]
                droppable_zone_objects: [to_box, ]
                drop_func: app.greet
                
            DragableButton:
                canvas:
                    Rectangle:
                        pos: self.pos
                        size: self.size
                        source: 'data/img/letra_u/urraca.png'
                bound_zone_objects: [from_box, to_box, ]
                #droppable_zone_objects: [to_box, ]
                drop_func: app.greet

        Image:
            id: to_box
            source: "data/img/caldero.png"
            
<Relaciona4x2>:
    AnchorLayout:
        Image:
            source: "data/img/fondobosque.jpg"
            allow_stretch: True
            keep_ratio: False
            
<Relaciona5x2>:
    AnchorLayout:
        Image:
            source: "data/img/fondobosque.jpg"
            allow_stretch: True
            keep_ratio: False