Python - Kivy: 尝试获取self.ids时出现属性错误:'super'对象没有'__getattr__'属性。

9

我写了一段代码用于实现一种Android锁定功能,但是每当我尝试使用ID获取特定的ClickableImage时,就会出现以下错误:

AttributeError: 'super' object has no attribute '__getattr__'

我花了数小时尝试寻找这个问题的解决方案,我查看了其他遇到相同问题的人的建议,他们告诉他们需要改变生成器的网站,因为它需要先调用ids属性或者类似的东西,但是每当我移动生成器时,都会出现"未定义的类"错误。有什么线索吗?

下面是我的代码:

from kivy.app import App
from kivy.config import Config
from kivy.lang import Builder
from kivy.graphics import Line
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors import ButtonBehavior

#Variables
cords = ()
bld = Builder.load_file('conf.kv')

class Manager(ScreenManager): pass
class Principal(Screen): pass

class ClickableImage(ButtonBehavior, Image):
    def on_press(self):
        self.source = 'button_press.png'

    def on_release(self):
        self.source = 'button.png'
        self.ids.uno.source = 'button_press.png'

class canva(Widget):
    def on_touch_down(self, touch):
        global cords
        with self.canvas:
            touch.ud['line'] = Line(points=(touch.x, touch.y), width=1.5)
        cords = (touch.x, touch.y)

    def on_touch_move(self,touch):
        global cords
         touch.ud['line'].points = cords + (touch.x, touch.y)

    def on_touch_up(self,touch):
        self.canvas.clear()

class Api(App):    
    def build(self):
        return bld

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

这是我的.kv文件:

# conf to file:  test.py

<Manager>:
    Principal:

<Principal>:
    GridLayout:
        size_hint_x: 0.5
        size_hint_y: 0.6
        width: self.minimum_width
        cols: 3
        ClickableImage:
            id: 'uno'
            size: 10,10
            source: 'button.png'
            allow_strech: True
        ClickableImage:
            id: 'dos'
            size: 30,30
            source: 'button.png'
            allow_strech: True
    canva:

1
请使用self.parent.parent.ids,因为self.ids不存在。 - Peter Badida
我不知道为什么没有答案说出来。id:'dos' 在你的self.ids键中意味着"'dos'"。只需使用id:dos即可。 - Vishesh Mangla
3个回答

9
让我们看一下输出结果:
'super' object has no attribute '__getattr__'

在kv语言中,id是以一种特殊的方式设置的(目前最高版本为1.9.2),它的值不是字符串,因为它不是普通的变量。你不能使用<widget>.id来访问它。
我认为这类似于canvas,它不是一个小部件,但它可能看起来像一个小部件(这就是为什么我对你的代码感到困惑的原因 :P)。你已经注意到something: <some object>就像Python的something = <object>,这(至少我认为)是id的值不是字符串的整个意义,这对某些人来说可能很奇怪。如果id是一个字符串,可能需要检查一下,以排除它从普通分配值中被排除的情况。也许这是出于性能或简单性的考虑。
因此,让我们说id是未来关键字的关键字。实际上,它确实是,因为分配给id的字符将成为一个字符串键,其值为从WeakProxy获取的对象,该对象指向WeakProxy指向的对象。或者更好地说:
id: value

变成

<some_root_widget>.ids[str(value)] = weakref.proxy(value)

在这里value被转换成了一个对象(调用print(self)会返回的结果)。

我怀疑(不确定)如果你使用字符串作为id的值,你最终会得到指向字符串的weakref/ WeakProxy。我使用“point”一词是因为它让我想起了指针,不要将其与C指针混淆。

现在,如果你再次查看输出:

  • super让你可以访问从中继承的类

  • print('string id'.__getattr__)将会给你同样的错误,但是'super'被替换成了真正的值,因为......它没有__getattr__方法

因此,如果你将一个字符串赋给id,你就会陷入这种情况:

<some_root_widget>.ids[str('value')] = weakref.proxy('value')  # + little bit of magic

虽然str('value')并不一定是错误的,但默认情况下你不能为字符串创建弱引用代理。我不确定Kivy如何处理WeakProxies,但如果你将一个字符串赋值给id,大致上你会得到这样的结果。

(如果我有误,请纠正我)


我成功地获取到了对象的链接,在主类中通过Principal().ids.uno调用它(我获得了'uno'对象),返回值是<__main__.ClickableImage object at 0x7f394cb428d8>,我甚至可以通过Principal().ids.uno.source获取到图像的源文件,它返回的是button.png(即源文件)。但是当我尝试将源文件设置为Button_press.png时,它只在本地更改了(我可以打印新的值,但屏幕上没有发生变化)... 我应该如何改变这个值?(我正在尝试在任何地方按下时改变特定按钮,而不仅仅在按钮本身)谢谢 :) - gramsch
3
如果按照您在评论中所写的方式(Class().ids.something)执行,那么每次调用都会创建一个新的实例,而不是修改您想要的那个实例。从一开始就很容易混淆,如果您来自非面向对象编程的话就更加困惑。无论如何,为了更好地解释它:当您在kv文件中放置<Principal>:时,类似于class Principal ...:,但是当您执行Principal:(注意没有括号)时,它会创建一个实例,并成为Manager的子元素。您需要访问要更改属性的实例,例如在“dos”按钮中:on_release: uno.source=<path> - Peter Badida
或者在ClickableImage类中:self.parent.parent.ids.uno.source=<路径> - Peter Badida
太好了!非常感谢!!我终于用测试使它运行起来了。 还有最后一件事:当我在kv上调用“Principal”时,我创建了Manager()子类的Principal()实例。我怎样才能从外部访问该实例?例如,如果我想从“Canva”类调用它,我该怎么做?最后这个对象的名字是什么。 换句话说,在kv中,“Principal:”就像执行“x = Principal(Manager)”。那么“x”的实际名称是什么? 谢谢您的帮助 :) - gramsch
@gramsch 其实应该是Principal:,但你说得对。这个答案应该可以帮助你解决这个问题。 :) - Peter Badida

3

在定义id时不要使用引号。

id: uno

替代

id: 'uno'

0

我在代码的两个部分中发现了一个不必要的名称:name,删除它们后代码正常工作了。


目前你的回答不够清晰。请编辑并添加更多细节,以帮助其他人理解它如何回答所提出的问题。你可以在帮助中心找到有关如何撰写好答案的更多信息。 - Community
你的回答目前写得不够清楚。请编辑以添加更多细节,帮助其他人理解这如何回答所提出的问题。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接