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

时间:2013-07-24 11:39:30

标签: python oop python-2.7 kivy

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

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

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()

这有效,但我不喜欢它,尤其是try … except部分。必须有更好的方法来做到这一点。 任何想法?

谢谢!

更新1:

@toto_tico为我提供了几个绕过try ... except部分的解决方案。

  1. 使用if代替例外:

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

    虽然在阅读了python的ifs与例外的效率之后,我不确定我是否应该尝试消除try ... except

  2. 在一开始就初始化now_playing_object。为此,我使用了:

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

    这样可行,但感觉有点不可靠(奇怪的是,如果文件名错过扩展名,Kivy会抱怨,但如果根本没有文件则不会抱怨)。 然而,我想我会用这个。

  3. 更新2:

    事实证明,解决方案就在我面前。我只是看不到它。实际上不需要@classmethod

    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_tico。

2 个答案:

答案 0 :(得分:2)

您只需检查now_playing_object是否为None

而不是:

try:
    if cls.now_playing_object.state != 'stop':
       cls.now_playing_object.stop()
except AttributeError:
    pass
...

你应该可以这样做:

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

希望它有所帮助。

答案 1 :(得分:1)

取出if条件:

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

而不是:

now_playing_object = None

您应该使用以下内容对其进行初始化:

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

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

取出@classmethod

我发现您使用@classmethod时感到很有趣。我在Python中已经有几年了,说实话,这对我来说是全新的。根据您的尝试,有几个答案。我将假设您清楚了解Oriented-Object Programming中classinstance之间的区别。

如果(1)您只有Gumb类的实例(2)您有几个Gumb类的实例但是所有它们控制着相同的soundfilesoundnow_playing_object

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

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()

这与你想要的更相似吗?