引用.kv文件中的类属性

时间:2017-06-06 22:39:11

标签: python-2.7 kivy

我的.py和.kv文件中有以下相关部分。

class DrillScreen(Screen):
    def __init__(self, **kwargs):
        super(DrillScreen, self).__init__(**kwargs)
        self.thedrill = drill(kwargs['options'])
        self.thedrill.bind(on_answer = self.printtest)


    def printtest(self,*args):
        self.ids.answerlabel.text = self.thedrill.answer
        print(self.ids.answerlabel.text)

    def startdrill(self):
        Process(target=self.thedrill.run).start()
<DrillScreen>:
    FloatLayout:
        FloatLayout:
            size_hint: 1.0, None
            height: self.width
            pos_hint: {'center_x':0.5,'center_y':0.5}
            id: wheelbox
            background_color: 1,1,1,1
            pos: 0, 200
            Label:
                id: answerlabel
                pos_hint: {'center_x':0.5, 'center_y':0.5}
                text: root.thedrill.answer
        Button:
            text: 'Start'
            size_hint: 0.5, 0.1
            on_release: root.startdrill()
        Button:
            text: 'Back'
            size_hint: 0.5, 0.1
            pos_hint: {'x':0.5}

这给了我一个错误AttributeError: 'DrillScreen' object has no attribute 'thedrill'。如果我将标签文本中的root.thedrill.answer更改为某个文字字符串,它运行正常,并从printtest()的输出中看到它确实在内部更改,但其显示不会更新。我想从.kv文件中访问class属性,或者从.py文件中设置标签显示。谢谢。

修改 按照Yoav Glazner的建议,我尝试添加一个按钮来绑定printtest()函数以查看发生了什么,我很困惑地看到来自不同绑定的调用产生不同的输出。这是来自.py:

的修改过的类
class DrillScreen(Screen):
    answer = StringProperty('alo')
    def __init__(self, **kwargs):
        self.thedrill = drill(kwargs['options'])
        self.thedrill.bind(on_answer = self.printtest)
        super(DrillScreen, self).__init__(**kwargs)
        self.answer = self.thedrill.answer

        b = Button(text = 'Test',
            size_hint = [0.1,0.1],
            pos_hint = {'top':1,'right':1})
        b.bind(on_release = self.printtest)

        self.ids.wheelbox.add_widget(b)

    def printtest(self,*args):
        self.answer = self.thedrill.answer
        print('This is printtest(): ', self.ids.answerlabel.text)

    def startdrill(self):
        Process(target=self.thedrill.run).start()

.kv文件的唯一更改是标签文本指向root.answer。当我运行它时,(并单击.kv中调用startdrill())的按钮,thedrill定期调度其on_answer个事件,我也会手动点击新创建的按钮同时调用printtest,在终端中产生这样的输出:

('This is printtest(): ', 'ti')
('This is printtest(): ', 're')
('This is printtest(): ', 'Default')
('This is printtest(): ', 'Default')
('This is printtest(): ', 'so')
('This is printtest(): ', 'Default')
('This is printtest(): ', 'le')
('This is printtest(): ', 'Default')

带有音节的行是on_answer thedrill个调度的结果,而“默认”的行来自按钮点击。我无法想象会有什么影响这种差异。在检查drill类时,我认为没有理由让它立即恢复到之前的值(它实际上在行设置sleep(1)之后的行上.answer并调度事件)。也许我错过了一些东西,如果没有更合理的解释,我可以发布代码。

EDIT2: 这是一个可运行的例子。 .kv文件:

<DrillScreen>:
    FloatLayout:
        FloatLayout:
            id: wheelbox
            size_hint: 1.0, None
            height: self.width
            pos_hint: {'center_x':0.5,'center_y':0.5}
            background_color: 1,1,1,1
            pos: 0, 200
            Label:
                id: answerlabel
                pos_hint: {'center_x':0.5, 'center_y':0.5}
                text: root.answer
        Button:
            text: 'Start'
            size_hint: 0.5, 0.1
            on_release: root.startdrill()

和.py:

from kivy.app import App
from random import choice
from time import sleep
from kivy.event import EventDispatcher
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.properties import StringProperty
from multiprocessing import Process

class DrillScreen(Screen):
    answer = StringProperty('Initialized.')
    def __init__(self, **kwargs):
        self.thedrill = drill()
        self.thedrill.bind(on_answer = self.printtest)
        super(DrillScreen, self).__init__(**kwargs)
        self.answer = self.thedrill.answer

        b = Button(
                text = 'printtest()',
                size_hint = [ 0.1, 0.1 ],
                pos_hint = {'center_x':0.75,'center_y':0.5},
                )
        b.bind(on_release = self.printtest)
        print(self.ids)
        self.ids.wheelbox.add_widget(b)

    def printtest(self,*args):
        self.answer = self.thedrill.answer
        print('This is printtest(): ', self.ids.answerlabel.text)

    def startdrill(self):
        Process(target=self.thedrill.run).start()

class drill(EventDispatcher):
    def __init__(self):
        self.register_event_type('on_answer')
        self.answer = 'Default'

    def on_answer(self,*args):
        pass

    def run(self):

        while True:

            self.answer = str(choice(range(100)))
            self.dispatch('on_answer', self.answer)
            sleep(1)

class Manager(ScreenManager):
    pass

class TestingApp(App):
    def build(self):
        sm = Manager()
        s = DrillScreen(name='ds')
        sm.add_widget(s)
        sm.current = 'ds'
        return sm

TestingApp().run()

单击“开始”按钮会在其自己的进程中启动drrun.run,每秒将随机数吐出到终端,当它运行时,单击printtest()按钮会输出“Default”作为标签文本值。样本输出:

('This is printtest(): ', '8')
('This is printtest(): ', '60')
('This is printtest(): ', '6')
('This is printtest(): ', 'Default')
('This is printtest(): ', '66')
('This is printtest(): ', '68')
('This is printtest(): ', 'Default')
('This is printtest(): ', '89')
('This is printtest(): ', '69')

1 个答案:

答案 0 :(得分:0)

您需要更改 __ init __ 功能中的顺序:

thedrill = ObjectProperty(None) # not really needed ... 

def __init__(self, **kwargs):
    self.thedrill = drill(kwargs['options'])
    self.thedrill.bind(on_answer = self.printtest)
    super(DrillScreen, self).__init__(**kwargs) # this will trigger the kv file section that exploded before ... 

也就是说,使用进程不适用于您的示例,因为每个进程都有内存,无法直接处理 UI 。您应该使用进程队列或移动到线程。只记得从主线程更新 UI 。 (使用时钟 @mainthread

玩得开心......