来自其他 class 属性的 kivy 绑定属性(变量)(导入 class 属性)
kivy binding attributes (variables) from other class attribute (import class attribute)
我正在尝试将属性从实例化导入的外部 class 绑定到 kivy class,但它不起作用,我无法在 kivy 文档或其他地方找到我的具体案例场景。
我制作了一个 kivy gui 示例,左侧的标签绑定到 kivy class 属性,当计数器启动时,左侧的标签相应更新,但右侧的标签绑定到来自其他 class 的属性在 kivy "on_start" 方法中实例化,当右侧的计数器启动时,您可以在控制台中看到右侧实例化 class 的属性正在改变但不是右边的标签。
这里的代码很简单,"Start counter"按钮调用了一个方法,该方法又使用线程调用计数器方法以避免冻结gui,计数器方法使用while循环递增一个数字,作为数字左侧的标签也会发生变化。带"external"关键字的方法引用右边导入的class计数器,所以和左边一样,右边的"start counter"按钮调用"start_external_counter"方法并在实例化的导入class中启动计数器,导入class中的数字属性递增,但kivy中的绑定不适用于导入的class属性,并且是我希望解决的问题,How to bind an attribute from an external class or imported module to kivy environment.
注意:我可以使用带有轮询循环的时钟更新右侧的标签,每个时间间隔调用外部 class 属性,但我认为这不是正确的方法。
提前感谢您的帮助。
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()
我找到了解决方案,诀窍是使用 kivy“EventDispatcher”。
包含你想在kivy中绑定的属性的模块或class需要有kivy绑定功能,所以导入的模块或你想绑定的外部class是来自的属性,必须继承自 kivy 的“EventDispatcher”class,但由于我们不想以任何方式更改外部 class 或模块,因此最好的解决方案是覆盖外部 class使用继承自外部 class 和 kivy“EventDispatcher”class 的自定义 class,这样我们就可以在 kivy 中使用新的自定义 class 并绑定其中的任何一个属性。
这里我做了一个小例子。
注意:
如果有人知道更好的方法,请评论并 post 一个简短的例子,谢谢。
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()
我正在尝试将属性从实例化导入的外部 class 绑定到 kivy class,但它不起作用,我无法在 kivy 文档或其他地方找到我的具体案例场景。
我制作了一个 kivy gui 示例,左侧的标签绑定到 kivy class 属性,当计数器启动时,左侧的标签相应更新,但右侧的标签绑定到来自其他 class 的属性在 kivy "on_start" 方法中实例化,当右侧的计数器启动时,您可以在控制台中看到右侧实例化 class 的属性正在改变但不是右边的标签。
这里的代码很简单,"Start counter"按钮调用了一个方法,该方法又使用线程调用计数器方法以避免冻结gui,计数器方法使用while循环递增一个数字,作为数字左侧的标签也会发生变化。带"external"关键字的方法引用右边导入的class计数器,所以和左边一样,右边的"start counter"按钮调用"start_external_counter"方法并在实例化的导入class中启动计数器,导入class中的数字属性递增,但kivy中的绑定不适用于导入的class属性,并且是我希望解决的问题,How to bind an attribute from an external class or imported module to kivy environment.
注意:我可以使用带有轮询循环的时钟更新右侧的标签,每个时间间隔调用外部 class 属性,但我认为这不是正确的方法。
提前感谢您的帮助。
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()
我找到了解决方案,诀窍是使用 kivy“EventDispatcher”。
包含你想在kivy中绑定的属性的模块或class需要有kivy绑定功能,所以导入的模块或你想绑定的外部class是来自的属性,必须继承自 kivy 的“EventDispatcher”class,但由于我们不想以任何方式更改外部 class 或模块,因此最好的解决方案是覆盖外部 class使用继承自外部 class 和 kivy“EventDispatcher”class 的自定义 class,这样我们就可以在 kivy 中使用新的自定义 class 并绑定其中的任何一个属性。
这里我做了一个小例子。
注意: 如果有人知道更好的方法,请评论并 post 一个简短的例子,谢谢。
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()