KIVY - 当按钮被按住时继续执行的Python方法

4

我目前正在尝试为IOS/Android制作一个平台游戏,但我卡在了一个问题上。我已经创建了两个按钮和一个角色。我希望角色保持移动直到按钮被释放。也就是说:当按钮被按下时,我可以移动角色一次,但我希望它保持移动直到按钮被释放。

我尝试过多种解决方案,例如使用Python的时间模块:

class Level1(Screen):
    posx = NumericProperty(0)
    posy = NumericProperty(0)
    moving = True
    i = 0
    def __init__(self, **kwargs):
        super(Level1, self).__init__(**kwargs)

    def rightmove(self):
        self.posx = self.posx+1
        time.sleep(10)

    def goright(self):
        while self.moving == True:
            self.rightmove()
            i += 1
            if i == 10:
                break


    def stopright(self):
        self.moving == False

但是它不起作用。我认为它以某种方式陷入了无限循环,因为当我按下按钮时,应用程序停止工作(“应用程序已停止运行…”错误)。

我几乎不知道如何解决这个问题。我已经尝试了几个小时,但还没有找到解决方案。 这是我的.py文件:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition,         SlideTransition
from kivy.config import Config
from kivy.core.window import Window
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, NumericProperty
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
import time
Config.set('graphics','resizable',0) #don't make the app re-sizeable
#Graphics fix
 #this fixes drawing issues on some phones
Window.clearcolor = (0,0,0,1.) 

language = "english"
curr1msg = 1

class HomeScreen(Screen):
    pass  

class OptionsScreen(Screen):
    pass

class GameScreen(Screen):
    pass

class LevelScreen(Screen):
    pass

class Level1intro(Screen):
    global language
    global curr1msg
    if language == "english" and curr1msg == 1:
        pName = "Pedro"
        msg1 = """Hello my friend!
My name is Pedro and I have a problem. Will you help me?
My spanish studens have a spanish test tomorrow, but I lost the exams!
You are the only one who can help me!"""
        cont = "Press anywhere to continue..."
    elif language == "swedish" and curr1msg == 1:
        pName = "Pedro"
        msg1 = """Hejsan!
Jag är Pedro och jag har ett problem. Kan du hjälpa mig?
Mina spanska-elever har ett spanskaprov imorgon men jag har tappat bort     proven!
Du är den enda som kan hjälpa mig!"""
        cont = "Tryck på skärmen för att fortsätta..."

class Level1(Screen):
        posx = NumericProperty(0)
        posy = NumericProperty(0)
        moving = True
        i = 0
        def __init__(self, **kwargs):
            super(Level1, self).__init__(**kwargs)

        def rightmove(self):
            self.posx = self.posx+1
            time.sleep(10)

        def goright(self):
            while self.moving == True:
                self.rightmove()
                i += 1
                if i == 10:
                    break


        def stopright(self):
            self.moving == False


class ScreenManagement(ScreenManager):
    pass


presentation = Builder.load_file("main.kv")

class MainApp(App):
    def build(self):
        return presentation

if __name__ == "__main__":
    MainApp().run()

这是我的.kv文件:

#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#: import SlideTransition kivy.uix.screenmanager.SlideTransition
ScreenManagement:
    transition: FadeTransition()
    HomeScreen:
    OptionsScreen:
    LevelScreen:
    Level1intro:
    Level1:

<HomeScreen>:
    name: 'home'

    FloatLayout:
        canvas:
            Rectangle:
                source:"images/home_background.jpg"
                size: self.size
        Image:
            source:"images/logo.png"
            allow_stretch: False
            keep_ratio: False
            opacity: 1.0
            size_hint: 0.7, 0.8
            pos_hint: {'center_x': 0.5, 'center_y': 0.9}
        Button:
            size_hint: 0.32,0.32
            pos_hint: {"x":0.34, "y":0.4}
            on_press:
                app.root.transition = SlideTransition(direction="left")
                app.root.current = "level"
            background_normal: "images/play_button.png"
            allow_stretch: False
        Button:
            size_hint: 0.25,0.25
            pos_hint: {"x":0.38, "y":0.15}
            on_press:
                app.root.transition = SlideTransition(direction="left")
                app.root.current = 'options'
            background_normal: "images/settings_button.png"

<OptionsScreen>:
    name: 'options'

<LevelScreen>
    name: "level"

    FloatLayout:
        canvas:
            Rectangle:
                source:"images/home_background.jpg"
                size: self.size
        Label:
            text: "[b]Choose Level[/b]"
            markup: 1
            font_size: 40
            color: 1,0.5,0,1
            pos: 0,250
        Button:
            size_hint: 0.1,0.1
            pos_hint: {"x": 0.1, "y": 0.8}
            on_press:
                app.root.current = "level1intro"
            Image:
                source:"images/level1.png"
                allow_stretch: True
                y: self.parent.y + self.parent.height - 70
                x: self.parent.x
                height: 80
                width: 80

        Button:
            background_normal: "images/menu_button.png"
            pos_hint: {"x": 0.4, "y": 0}
            size_hint: 0.3,0.3
            pos_hint: {"x": 0.35}
            on_press:
                app.root.transition = SlideTransition(direction="right")
                app.root.current = "home"

<Level1intro>
    name: "level1intro"

    canvas:
        Rectangle:
            source: "images/background.png"
            size: self.size
    Image:
        source: "images/dialog.png"
        pos_hint: {"y": -0.35}
        size_hint: 0.7,1.0
    Label:
        font_size: 20
        color: 1,1,1,1
        pos_hint: {"x": -0.385, "y": -0.285}
        text: root.pName
    Label:
        font_size: 15
        color: 1,1,1,1
        pos_hint: {"x": -0.15, "y": -0.4}
        text: root.msg1
    Label:
        font_size: 15
        color: 0.7,0.8,1,1
        pos_hint: {"x": 0.025, "y": -0.449}
        text: root.cont
        on_touch_down: 
            app.root.transition = FadeTransition()
            app.root.current = "level1"

<Level1>
    name: "level1"
    canvas:
        Rectangle:
            source: "images/background.png"
            size: self.size

    Button:
        text: ">"
        size_hint: 0.1,0.1
        pos_hint: {"x":0.9, "y":0.0}
        on_press:
            root.goright()
        on_release:
            root.stopright()
    Button:
        text: "<"
        size_hint: 0.1,0.1
        pos_hint: {"x": 0.0, "y": 0.0}
        on_press:
            root.posx = root.posx-1

    Image:
        id: char
        source: "images/idle1.png"
        size: self.size
        pos: root.posx,root.posy

感谢您的时间和帮助。 GryTrean
//我将“i”更改为“self.i”,但它没有解决问题。
4个回答

4

我为您创建了一个简单的示例,演示如何通过按按钮来移动角色(在这种情况下是精灵战士1级):

#!/usr/bin/env python3.5
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import mainthread, Clock

gui = '''
Root:
    orientation: 'vertical'

    arena: arena
    control_button: control_button

    Arena:
        id: arena

    Button
        id: control_button
        size_hint_y: None
        height: dp(50)
        text: 'move'


<Arena@FloatLayout>:
    player: player

    Button:
        id: player
        pos: 150, 300
        text: 'elf warrior\\nlevel 1'
        size_hint: None, None
        size: 100, 100
'''


class Root(BoxLayout):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        @mainthread
        def job():
            self.control_button.bind(on_press=self._on_press)
            self.control_button.bind(on_release=self._on_release)

        job()

    def _on_press(self, button):
        self.arena.start_movement()

    def _on_release(self, button):
        self.arena.stop_movement()


class Arena(FloatLayout):

    def start_movement(self):
        Clock.schedule_interval(self._move_right, 0.01)

    def stop_movement(self):
        Clock.unschedule(self._move_right)

    def _move_right(self, dt):
        self.player.x += 1


class Test(App):

    def build(self):
        return Builder.load_string(gui)


Test().run()

4
根据Kivy API,一旦按钮被按下,您可以创建一个事件。例如,

Kivy API

my_event = Clock.schedule_interval(rightmove, 0.5)

这个事件每0.5秒会调用rightmove()函数。创建完事件后,你可以通过-VariableName-()来调用它,所以在这种情况下我们会写my_event()
现在,如果我们松开按钮,我们需要停止循环,因此你可以将按钮的on_release属性绑定到事件上执行cancel()函数。代码如下:
my_event.cancel()

或者

Button(on_release=my_event.cancel)

注意:您需要将my_event设置为全局变量,以便开始和结束函数可以访问它。

0

这里是kivy中的按钮API。适用于您的问题的两个绑定是on_presson_release绑定。您可以使用这些绑定与Button.bind()方法一起使用。绑定函数到按钮绑定的示例可在此处找到。


我知道,但我想要一直将value添加到root.posx,直到按钮被释放。我该怎么做?感谢您的帮助/尝试帮助 :) - GryTrean
你还没有发布展示如何附加到按钮绑定的代码,这就是为什么我提到了按钮API。请发布展示你的按钮绑定的代码。 - TypeKazt
我没有使用bind。按钮是在.kv文件中创建的,这也是代码存在的原因(看看第二个按钮,它将图像向左移动== self.posx-1)。我希望在客户端停止按住按钮之前不断添加/删除此值。我正在制作一个移动平台游戏,我不知道你是否知道我的意思,但在大多数平台游戏中,你有一个按钮,当你按住它时,你会继续朝一个方向移动(例如向右)。我希望你知道我的意思:D - GryTrean
当然可以,但我在你的代码中没有看到 Level1 类被实例化。听起来你可能会尝试去解引用一个不存在于全局范围内的变量,因此根指针指向空值并导致了段错误。 - TypeKazt
我想创建一个类似于while循环的东西,当按钮被按下时运行,并在按钮释放时停止运行。 - GryTrean
显示剩余3条评论

0
这让我花了一天时间才弄明白:
假设你想要一个按钮,既可以进行短按(事件1),也可以进行长按(事件2)。
# Button:
#                 id: btn6
#                 text:'Button6\nDown\nHold 3s'
#                 size_hint: (None,None)
#                 size:self.size
#                 on_press: root.hold_press(self)
#                 on_release: scrn_mnger.transition.direction = 'down'
#                 on_release: scrn_mnger.current = 'scrn_media'

在Python文件中:
from kivy.clock import Clock

def hold_press(self,inst):
    if inst.state == 'down':
        Clock.schedule_once(lambda x: self.long_press(),1)

def long_press(self):
    if self.ids.btn6.state == 'down':
        self.ids.scrn_mnger.transition.direction = 'left'
        self.ids.scrn_mnger.current = 'scrn_media'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接