【问题标题】:Kivy: Why Button is losing its property in case 2?Kivy:为什么 Button 在案例 2 中失去了它的属性?
【发布时间】:2021-01-21 08:57:04
【问题描述】:

这个问题看起来很长,其实不然。刚刚添加了代码和相应的日志供查看者参考。

我正在试验 Clock.schedule_interval。

目标按下按钮后,标签文本最初为 0 将增加 10 倍。

我用两种方式写了这个。案例1工作正常。案例 2(在案例 1 代码中很少编辑)丢失按钮 on_press 属性。

案例一:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, ListProperty
from kivy.clock import Clock, _default_time as time

kv = '''
Some:
    target: target
    tick: tick
    
    id: tick
    
    BoxLayout:
        orientation: 'vertical'
        size: root.width, root.height
        
        Label:
            id: target
            text: '0'
            font_size: 32
            size_hint: 1, .5
            
        Button:
            text: 'Change'
            on_press: 
                root.chik.extend(range(int(target.text)+1, int(target.text)+ 11))
                app.con.extend([root.change] * 10)
'''

class Some(Widget):
    
    target = ObjectProperty(None)
    tick = ObjectProperty(None)
    chik = ListProperty([])
    
    def __init__(self, **kwargs):
        Widget.__init__(self, **kwargs)
    
    def change(self):
        print(f'From widget: {self.chik} and Label text: {self.target.text}')
        if self.chik:
            self.target.text = str(self.chik.pop(0))

class TestingApp(App):
    
    con = ListProperty([])
    
    def build(self):
        Clock.schedule_interval(self.consume, 1)
        return Builder.load_string(kv)
    
    def consume(self, *args):
        print(f'from app: {self.con}')
        if self.con:
            self.con.pop(0)()
    
if __name__ == "__main__":
    TestingApp().run()

日志 1:

[INFO   ] [Base        ] Start application main loop
from app: []
[INFO   ] [GL          ] NPOT texture support is available
from app: []
from app: []
from app: []
from app: []
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method 
Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and Label text: 0
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method 
Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [2, 3, 4, 5, 6, 7, 8, 9, 10] and Label text: 1
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method 
Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [3, 4, 5, 6, 7, 8, 9, 10] and Label text: 2
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method 
Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [4, 5, 6, 7, 8, 9, 10] and Label text: 3
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method 
Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [5, 6, 7, 8, 9, 10] and Label text: 4
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method 
Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [6, 7, 8, 9, 10] and Label text: 5
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method 
Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [7, 8, 9, 10] and Label text: 6
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method 
Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [8, 9, 10] and Label text: 7
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>, <bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [9, 10] and Label text: 8
from app: [<bound method Some.change of <__main__.Some object at 0x000001DFFDFA5040>>]
From widget: [10] and Label text: 9
from app: []
from app: []
from app: []
from app: []
from app: []
from app: []
from app: []
from app: []
from app: []
[INFO   ] [Base        ] Leaving application in progress...

工作正常并提供所需的输出

print(self.target.text) 的情况 2 在def change: 中产生错误

rom kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, ListProperty
from kivy.clock import Clock, _default_time as time

kv = '''
Some:
    target: target
    tick: tick
    
    id: tick
    
    BoxLayout:
        orientation: 'vertical'
        size: root.width, root.height
        
        Label:
            id: target
            text: '0'
            font_size: 32
            size_hint: 1, .5
            
        Button:
            text: 'Change'
            on_press: 
                root.chik.extend(range(int(target.text)+1, int(target.text)+ 11))
                # app.con.extend([root.change]*10) removed
'''

class Some(Widget):
    
    target = ObjectProperty(None)
    tick = ObjectProperty(None)
    chik = ListProperty([])
    
    def __init__(self, **kwargs):
        Widget.__init__(self, **kwargs)
    
    def change(self):
        print(f'From widget: {self.chik} and Label text: {self.target.text}') # Error here
        if self.chik:
            self.target.text = str(self.chik.pop(0))

class TestingApp(App):
    
    # con = ListProperty([]) deleted
        
    def build(self):
        self.Some = Some() # Change here
        Clock.schedule_interval(self.consume, 1) 
        return Builder.load_string(kv)
    
    def consume(self, *args):
        self.get_running_app().Some.change() # Change here
    
if __name__ == "__main__":
    TestingApp().run()

def change: 中使用print(self.target.text) 记录2。

[INFO   ] [Base        ] Start application main loop
[INFO   ] [Base        ] Leaving application in progress...
 Traceback (most recent call last):
   File "e:\Search_GUI\practice.py", line 58, in <module>
     TestingApp().run()
   File "E:\Search_GUI\search_env\lib\site-packages\kivy\app.py", line 950, in run
     runTouchApp()
   File "E:\Search_GUI\search_env\lib\site-packages\kivy\base.py", line 582, in runTouchApp
     EventLoop.mainloop()
   File "E:\Search_GUI\search_env\lib\site-packages\kivy\base.py", line 347, in mainloop
     self.idle()
   File "E:\Search_GUI\search_env\lib\site-packages\kivy\base.py", line 387, in idle
     Clock.tick()
   File "E:\Search_GUI\search_env\lib\site-packages\kivy\clock.py", line 733, in tick
     self.post_idle(ts, self.idle())
   File "E:\Search_GUI\search_env\lib\site-packages\kivy\clock.py", line 776, in post_idle
     self._process_events()
   File "kivy\_clock.pyx", line 616, in kivy._clock.CyClockBase._process_events
   File "kivy\_clock.pyx", line 649, in kivy._clock.CyClockBase._process_events
   File "kivy\_clock.pyx", line 645, in kivy._clock.CyClockBase._process_events
   File "kivy\_clock.pyx", line 218, in kivy._clock.ClockEvent.tick
   File "e:\Search_GUI\practice.py", line 55, in consume
     self.get_running_app().Some.change() # Change here
   File "e:\Search_GUI\practice.py", line 41, in change
     print(f'From widget: {self.chik} and Label text: {self.target.text}')
 AttributeError: 'NoneType' object has no attribute 'text'

没有print(self.target.text)的情况2:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, ListProperty
from kivy.clock import Clock, _default_time as time

kv = '''
Some:
    target: target
    tick: tick
    
    id: tick
    
    BoxLayout:
        orientation: 'vertical'
        size: root.width, root.height
        
        Label:
            id: target
            text: '0'
            font_size: 32
            size_hint: 1, .5
            
        Button:
            text: 'Change'
            on_press: 
                root.chik.extend(range(int(target.text)+1, int(target.text)+ 11))
                # app.con.extend([root.change]*10) removed
'''

class Some(Widget):
    
    target = ObjectProperty(None)
    tick = ObjectProperty(None)
    chik = ListProperty([])
    
    def __init__(self, **kwargs):
        Widget.__init__(self, **kwargs)
    
    def change(self):
        print(f'From widget: {self.chik}')
        if self.chik:
            self.target.text = str(self.chik.pop(0))

class TestingApp(App):
    
    # con = ListProperty([]) deleted
        
    def build(self):
        self.Some = Some() # Change here
        Clock.schedule_interval(self.consume, 1) 
        return Builder.load_string(kv)
    
    def consume(self, *args):
        self.get_running_app().Some.change() # Change here
    
if __name__ == "__main__":
    TestingApp().run()

没有print(self.target.text)的日志2:

[INFO   ] [Base        ] Start application main loop
From widget: []
[INFO   ] [GL          ] NPOT texture support is available
From widget: []
From widget: []
From widget: []
From widget: []
From widget: []
From widget: []
From widget: []
From widget: []
[INFO   ] [Base        ] Leaving application in progress...

对于上述情况,按下按钮时什么都没有发生

我想知道为什么会这样?如何让我的代码像案例 1 一样为案例 2 工作?

【问题讨论】:

    标签: python python-3.x kivy


    【解决方案1】:

    在您的情况 2 中,创建了两个 Some 实例。一个是按行建的:

    return Builder.load_string(kv)
    

    那是你的应用展示的根。

    该行正在创建另一个:

    self.Some = Some() # Change here
    

    而且这第二个实例不是您的应用显示的一部分。因此,当您计划调用self.consume() 时,consume() 方法会调用不属于 App 显示的Some 实例的change() 方法。此外,第二个实例没有在kv 字符串中定义的任何项目,因为kv 字符串具有根小部件定义,但不是创建Some 实例的规则。


    解决此问题的一种方法是将kv 中的根小部件定义更改为规则:

    Some:
    

    到:

    <Some>:
    

    并更改 build() 方法以使用该定义:

    def build(self):
        Builder.load_string(kv)
        self.Some = Some()  # create just one instance of Some
        Clock.schedule_interval(self.consume, 1)
        return self.Some  # use that one instance as the App root widget
    

    【讨论】:

    • 有没有办法访问.py file 中另一个小部件类中的一个小部件类的 ID?同样,我们如何在.py file 的应用类中引用小部件ID?
    • 是的,但ids 仅通过加载kv 来创建,并且它们仅出现在创建它们的根小部件或根规则中。您需要访问从根小部件或根规则创建的实例,您可以访问该实例中的ids 字典。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多