【问题标题】:kivy binding attributes (variables) from other class attribute (import class attribute)来自其他类属性(导入类属性)的kivy绑定属性(变量)
【发布时间】:2020-06-12 07:20:32
【问题描述】:

我正在尝试将实例化的导入外部类中的属性绑定到 kivy 类,但它不起作用,我在 kivy 文档或其他地方找不到我的特定案例场景。

我做了一个示例 kivy gui,左侧的标签绑定到 kivy 类属性,当计数器启动时,左侧的标签会相应更新,但右侧的标签绑定到其他类的属性在 kivy "on_start" 方法中进行实例化,当右侧的计数器启动时,您可以在控制台中看到右侧实例化类的属性正在发生变化,但右侧的标签没有变化。

这里的代码很简单,“开始计数器”按钮调用是一个方法,该方法又使用线程调用计数器方法以避免冻结 gui,计数器方法使用 while 循环递增一个数字,因为数字会发生变化左边的标签。带“external”关键字的方法是指右边的导入类计数器,所以和左边的一样,右边的“启动计数器”按钮调用“start_external_counter”方法,在实例化的实例中启动计数器导入的类,导入的类中的数字属性是递增的,但是kivy中的绑定对导入的类属性不起作用,这是我希望解决的问题,如何从外部类或导入的模块中绑定属性到kivy环境。

注意:我可以通过使用带有轮询循环的时钟来更新右侧的标签,每个间隔调用外部类属性,但我认为这不是正确的方法。

提前感谢您的帮助。

from kivy.app import App 
from kivy.lang import Builder
from kivy.properties import ObjectProperty, NumericProperty

import time
import threading

kv = '''
BoxLayout:
    padding: 40
    spacing: 50
    # -- INTERNAL CLASS COUNTER --#
    BoxLayout:
        orientation: 'vertical'
        spacing: 50
        Label:
            text: 'Kivy class attribute binding'
            font_size: 30
            size: self.texture_size
            size_hint_y: .2
        Label:
            id: counter_label
            text: '0'
            font_size: 200
            size: self.texture_size
        BoxLayout:
            size_hint_y: None
            height: 80
            Button:
                text: 'Start Counter'
                font_size: 30
                on_release: app.get_running_app().start_counter()
            Button:
                text: 'Stop Counter'
                font_size: 30
                on_release: app.get_running_app().stop_counter()

    # -- EXTERNAL CLASS COUNTER -- #
    BoxLayout:
        orientation: 'vertical'
        spacing: 50
        Label:
            text: 'External class attribute binding'
            font_size: 30
            size: self.texture_size
            size_hint_y: .2
        Label:
            id: external_counter_label
            text: '0'
            font_size: 200
            size: self.texture_size
        BoxLayout:
            size_hint_y: None
            height: 80
            Button:
                text: 'Start Counter'
                font_size: 30
                on_release: app.get_running_app().start_external_counter()
            Button:
                text: 'Stop Counter'
                font_size: 30
                on_release: app.get_running_app().stop_external_counter()
'''

class MyClass:
    number = 0
    stop = False

    def count(self):
        while not self.stop:
            self.number += 1
            print 'External counter: %s' % self.number 
            time.sleep(1)
        self.stop = False



class main(App):
    number = NumericProperty(0)
    external_number = NumericProperty(0)
    external_counter = ObjectProperty()
    stop = False



    def build(self, *args):
        layout = Builder.load_string(kv)
        return layout

    def on_start(self):
        root = self.root_window
        self.layout = root.children[0]
        self.counter_label = self.layout.ids['counter_label']
        self.bind(number=self.update_label)

        ## -- Trying to bind a property 
        ## -- from other non kivy class
        self.external_counter_label = self.layout.ids['external_counter_label'] 
        self.external_counter = MyClass()
        self.external_number = self.external_counter.number
        self.bind(external_number=self.update_external_label)

    def update_label(self, *args):
        self.counter_label.text = str(self.number)

    def start_counter(self):
        ''' using a thread to start counter
            without freezing gui
        '''
        t = threading.Thread(target=self.count)
        t.setDaemon(True)
        t.start()

    def count(self):
        while not self.stop:
            self.number += 1
            time.sleep(1)
        self.stop = False

    def stop_counter(self):
        self.stop = True


    ## --- CALLING THE EXTERNAL CLASS METHODS -- ##
    def update_external_label(self):
        self.external_counter_label.text = self.external_number

    def start_external_counter(self):
        ''' using a thread to start counter
            without freezing gui
        '''
        t = threading.Thread(target=self.external_count)
        t.setDaemon(True)
        t.start()

    def external_count(self):
        self.external_counter.count()

    def stop_external_counter(self):
        self.external_counter.stop = True



if __name__ == '__main__':
    main().run()

【问题讨论】:

    标签: python attributes kivy bind


    【解决方案1】:

    我找到了解决方案,诀窍是使用 kivy "EventDispatcher"。

    包含要在kivy中绑定的属性的模块或类需要具有kivy绑定功能,因此要绑定其属性的导入模块或外部类必须继承自kivy的“EventDispatcher”类,但由于我们不想以任何方式更改外部类或模块,最好的解决方案是使用继承自外部类和 kivy“EventDispatcher”类的自定义类覆盖外部类,这样我们可以在 kivy 中使用新的自定义类并绑定它的任何属性。

    这里我做了一个小例子。

    注意: 如果有人知道更好的方法,请发表评论并发布一个简短的示例,谢谢。

    from kivy.app import App 
    from kivy.lang import Builder 
    from kivy.properties import NumericProperty, ObjectProperty
    from kivy.event import EventDispatcher
    
    import time 
    import threading 
    
    class Count:
        """ This is the external class we want to bind 
            attributes from, this could be an imported 
            module also.
        """
        number = 0
    
        def counter(self):
            while True:
                self.increase()
                time.sleep(1)
    
        def increase(self):
            self.number += 1
    
    
    
    
    class MyClass(Count, EventDispatcher):
        """ Custom override class.
            This class contains the kivy 
            bindings functionality
        """
        number = NumericProperty(0)  # <-- turns number attribute into a kivy object, so it can be binded in kivy
    
    
    
    kv = '''
    
    AnchorLayout:
        BoxLayout:
            spacing: 40
            size_hint: None, None
            size: 300, 200 
            orientation: 'vertical'
            Label:
                id: counter_label
                text: '0'
                font_size: 100
            BoxLayout:
                Button:
                    text: 'Add 1'
                    on_release: app.get_running_app().change()
                Button:
                    text: 'Start Counter'
                    on_release: app.get_running_app().start_counter()
    '''
    
    class main(App):
        ''' Updating a label through binding an attribute
            from an external class "MyClass".
        '''
    
        def build(self):
            return Builder.load_string(kv)
    
        def on_start(self):
            self.counter_label = self.root.ids['counter_label']
            self.my_class = MyClass()                        # <-- here we instanciate the custom class
            self.my_class.bind(number=self.update_label)     # <-- here we bind the number attribute from the custom class
    
        def update_label(self, *args):
            """ when the "number" attribute from the external
                class changes this method is called and updates
                the label accordingly.
            """ 
            self.counter_label.text = str(self.my_class.number)
    
        def change(self):
            """ Use a thread here to avoid locking the 
                gui's main loop
            """
            t = threading.Thread(target=self.increase)
            t.setDaemon(True)
            t.start()
            
        def increase(self):
            """ Calls the increase method in the custom
                class and increases the "number" attribute
            """
            self.my_class.increase()
    
        def start_counter(self):
            t = threading.Thread(target=self.count)
            t.setDaemon(True)
            t.start()
    
        def count(self):
            self.my_class.counter()
    
    
    if __name__ == '__main__':
        main().run()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-22
      • 2011-08-08
      • 1970-01-01
      • 1970-01-01
      • 2018-05-30
      • 2011-09-09
      • 2014-09-21
      • 1970-01-01
      相关资源
      最近更新 更多