关于python:一个对象实例如何停止其他实例正在播放的声音?

How can one object instance stop sound that other instance is playing?

我对编程有些陌生。我正在使用Kivy框架构建一个简单的应用程序。这个应用程序有15个按钮。当你按下按钮,它开始播放一段旋律。如果此时正在播放任何其他按钮,则该按钮应停止,即不应同时播放多个声音(来自不同实例)。

这就是我所做的-我创建了一个处理停止和播放声音的类方法。按下时,按钮实例将声音对象传递给这个类方法。然而,第一次按下,由于声音对象还不存在,我的应用程序崩溃。因此,try … except AttributeError部分的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Gumb(Button):
    soundfile = StringProperty(None)
    sound = ObjectProperty(None)
    now_playing_object = None

    def on_soundfile(self, instance, value):
        self.sound = SoundLoader.load(value)

    def on_press(self):
        if self.sound:
            self.__class__.play_sound(self.sound)

    @classmethod
    def play_sound(cls, new_sound_object):
        try:
            if cls.now_playing_object.state != 'stop':
                cls.now_playing_object.stop()
        except AttributeError:
            pass
        cls.now_playing_object = new_sound_object
        cls.now_playing_object.play()

但我不喜欢这个,尤其是thetry … except部分。必须有更好的方法来做到这一点。有什么想法吗?

谢谢!

更新1:

@托托·提科为我提供了一些解决方案,以避开try ... except部分。

  • 使用if代替exception:

    1
    2
    if cls.now_playing_object != None and cls.now_playing_object.state != 'stop':
        cls.now_playing_object.stop()

    尽管,在阅读了关于Python的IFS和异常的效率之后,我不确定是否应该尝试消除try ... except

  • 从一开始就初始化now_playing_object。为此,我使用了:

    1
    now_playing_object = SoundLoader.load(‘non_existant_file.wav)

    这是可行的,但感觉有点不可靠(奇怪的是,如果文件名缺少扩展名,Kivy会抱怨,但如果根本没有文件,就不会抱怨)。不过,我想我还是要用这个。

  • 更新2:

    结果发现解决方案就在我面前。我就是看不见。确实不需要@classmethod

    1
    2
    3
    4
    5
    6
    7
    class Gumb(Button):
    ...
        def play_sound(self):
            if self.__class__.now_playing_object.state is not 'stop':
                self.__class__.now_playing_object.stop()
            self.__class__.now_playing_object = self.sound
            self.__class__.now_playing_object.play()

    我已经接受了答案,但真正的答案是在评论中。多谢@toto'u tico。


    你只要检查一下now_playing_object是否是None

    而不是:

    1
    2
    3
    4
    5
    6
    try:
        if cls.now_playing_object.state != 'stop':
           cls.now_playing_object.stop()
    except AttributeError:
        pass
    ...

    您应该能够做到:

    1
    2
    3
    if cls.now_playing_object != None and cls.now_playing_object.state != 'stop':
        cls.now_playing_object.stop()
    ...

    希望它有帮助。


    要取出if条件:

    如果不想检查,那么应该从一开始就初始化变量。我不知道您用于now_playing_object的类,但我们称它为PlayingObject

    而不是:

    1
    now_playing_object = None

    您应该用以下方法初始化它:

    1
    2
    #maybe needs parameters or simple use now_playing_object = ObjectProperty(None)
    now_playing_object = Sound()

    然后你可以去掉我第一个答案(cls.now_playing_object != None的条件),因为对象总是初始化的。

    取出@classmethod

    我发现你在使用@classmethod很有趣。我在Python工作了几年,老实说,这对我来说是全新的。有几个答案取决于你想做什么。我假设您对面向对象编程中类和实例之间的区别很清楚。

    如果(1)您只有一个Gumb类的实例,或者(2)您有几个Gumb类的实例,但所有这些实例都控制同一个soundfilesoundnow_playing_object,则当前代码可以工作。

    现在,如果您希望15个Gumb按钮控制不同的soundfilesoundnow_playing_object,那么您不应该使用@classmethod,也不应该使用声明属性的方式(因为您将它们声明为类属性,并且需要它们作为实例的一部分)。代码看起来更像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Gumb(Button):
        # Constructor.
        def __init__(self,  **kwargs):
            super(Gump, self).__init__(**kwargs)
            #This became attributes of the
            self.soundfile = StringProperty(None)\
            self.sound = ObjectProperty(None)
            self.now_playing_object = ObjectProperty(None)

        def on_soundfile(self, instance, value):
            self.sound = SoundLoader.load(value)

        def on_press(self):
            if self.sound:
                self.play_sound()

        def play_sound(self):
            if self.now_playing_object.state != 'stop':
                self.now_playing_object.stop()

            # self.sound was your new_playing_object, since it is part of the instance
            # you don't need to send it as parameter
            self.now_playing_object = self.sound
            self.now_playing_object.play()

    这更像你想要的吗?