来自其他类属性(导入类属性)的绑定绑定属性(变量)

问题描述 投票:0回答:1

我正在尝试将实例化的导入的外部类的属性绑定到kivy类,但是它不起作用,我在kivy doc或其他地方找不到我的特殊情况。

[我制作了一个示例kivy gui,左侧的标签绑定到kivy类属性,并且当计数器启动左侧的标签时,标签相应地进行更新,但是右侧的标签绑定到其他类的属性在kivy的“ on_start”方法中实例化,当启动右侧的计数器时,您可以在控制台中看到右侧的实例化类的属性正在更改,但右侧的标签未更改。

这里的代码很简单,“ Start counter”按钮调用是一种方法,该方法依次使用线程调用counter方法以避免冻结gui,counter方法使用while循环递增一个数字,因为数字改变了,所以左边的标签。带有“ external”关键字的方法引用右侧的已导入类计数器,因此与左侧的方法相同,右侧的“ start counter”按钮调用“ start_external_counter”方法并在实例化时启动计数器导入的类,导入的类中的number属性增加,但是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个回答
0
投票

我找到了解决方案,诀窍是使用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.
    """
    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()
© www.soinside.com 2019 - 2024. All rights reserved.