使用变量从 python 文件到 kv 文件的 kivy 错误

kivy error from using a variable from python file to kv file

我正在制作漫画 reader 它使用了 kivy 中的图像模块,我发现了一个错误,可以通过使用从 py 文件到 kv 文件的变量来解决。我在 App class 中有一个名为 sourceToImage 的变量,但是当我在 kv 文件中调用 sourceToImage 变量时 source: app.sourceToImage 给我一个很长的错误

py

class MyApp(App):
    sourceToImage = "Source" 

    def build(self):
        return sm

kv

<ReaderApp>:
    name: "reader"
    im: page

    canvas:
        Color:
            rgba: 1,1,1,0.17
        Rectangle:
            size: (root.width, root.height)
            pos: (0,0)
    Image:
        size: 500, 600
        pos: 0, 0
        id: page
        source: app.sourceToImage

但是当我使用 app.sourceToImage 时,它给我一个错误提示:

Traceback (most recent call last):
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 242, in create_handler
     return eval(value, idmap), bound_list
   File "D:\GameDev-Programming\Python\kivyProjects\firstKivy\my.kv", line 20, in <module>
     source: app.sourceToImage
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\parser.py", line 75, in __getattribute__
     object.__getattribute__(self, '_ensure_app')()
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\parser.py", line 70, in _ensure_app
     app.bind(on_stop=lambda instance:
 AttributeError: 'NoneType' object has no attribute 'bind'
 
 During handling of the above exception, another exception occurred:
 
 Traceback (most recent call last):
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 695, in _apply_rule
     value, bound = create_handler(
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 245, in create_handler
     raise BuilderException(rule.ctx, rule.line,
 kivy.lang.builder.BuilderException: Parser: File "D:\GameDev-Programming\Python\kivyProjects\firstKivy\my.kv", line 20:
 ...
      18:        pos: 0, 0
      19:        id: page
 >>   20:        source: app.sourceToImage
      21:
      22:
 ...
 AttributeError: 'NoneType' object has no attribute 'bind'
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 242, in create_handler
     return eval(value, idmap), bound_list
   File "D:\GameDev-Programming\Python\kivyProjects\firstKivy\my.kv", line 20, in <module>
     source: app.sourceToImage
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\parser.py", line 75, in __getattribute__
     object.__getattribute__(self, '_ensure_app')()
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\parser.py", line 70, in _ensure_app
     app.bind(on_stop=lambda instance:
 
 
 During handling of the above exception, another exception occurred:
 
 Traceback (most recent call last):
   File "D:\GameDev-Programming\Python\kivyProjects\firstKivy\main.py", line 87, in <module>
     kv = Builder.load_file("my.kv")
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 306, in load_file
     return self.load_string(data, **kwargs)
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 408, in load_string
     self._apply_rule(
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 661, in _apply_rule
     child.apply_class_lang_rules(
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\uix\widget.py", line 463, in apply_class_lang_rules
     Builder.apply(
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 541, in apply
     self._apply_rule(
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 710, in _apply_rule
     raise BuilderException(rule.ctx, rule.line,
 kivy.lang.builder.BuilderException: Parser: File "D:\GameDev-Programming\Python\kivyProjects\firstKivy\my.kv", line 20:
 ...
      18:        pos: 0, 0
      19:        id: page
 >>   20:        source: app.sourceToImage
      21:
      22:
 ...
 BuilderException: Parser: File "D:\GameDev-Programming\Python\kivyProjects\firstKivy\my.kv", line 20:
 ...
      18:        pos: 0, 0
      19:        id: page
 >>   20:        source: app.sourceToImage
      21:
      22:
 ...
 AttributeError: 'NoneType' object has no attribute 'bind'
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 242, in create_handler
     return eval(value, idmap), bound_list
   File "D:\GameDev-Programming\Python\kivyProjects\firstKivy\my.kv", line 20, in <module>
     source: app.sourceToImage
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\parser.py", line 75, in __getattribute__
     object.__getattribute__(self, '_ensure_app')()
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\parser.py", line 70, in _ensure_app
     app.bind(on_stop=lambda instance:
 
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 695, in _apply_rule
     value, bound = create_handler(
   File "C:\Users\USER\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 245, in create_handler
     raise BuilderException(rule.ctx, rule.line,

完整的 kv 文件:

WindowManager:
    SelectingApp:
    ReaderApp:


<ReaderApp>:
    name: "reader"
    im: page

    canvas:
        Color:
            rgba: 1,1,1,0.17
        Rectangle:
            size: (root.width, root.height)
            pos: (0,0)
    Image:
        size: 500, 600
        pos: 0, 0
        id: page
        source: ""



<SelectingApp>
    name: "selecting"
    listSpin: spinnerId
    canvas:
        Color:
            rgba: 1,1,1,0.17
        Rectangle:
            size: (root.width, root.height)
            pos: (0,0)
    FloatLayout:
        Label:
            text: app.sourceToImage
            size_hint: 0.1,0.1
            pos_hint: {"x": 0.4, "y": 0.5}
        Button:
            text: "Go to reader"
            on_press: root.Change()
            size_hint: 0.5, 0.05
            pos_hint: {"x": 0.2}
        Spinner:
            id: spinnerId
            text: "Choose Manga"
            values: ["hey", "ey"]
            size_hint: 0.3, 0.1
            pos_hint: {"x":0.3, "y":0.4}
        Button:
            text: "refresh list"
            size_hint: 0.5, 0.05
            pos_hint: {"x": 0.2, "y": 0.2}
            on_press: root.Refresh()

完整的py文件:

import os
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.config import Config
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import StringProperty


width = 360
height = 640    # How does this make the resolution 641

Config.set('graphics', 'width', width)
Config.set('graphics', 'height', height)

Manga = "Seirei Gensouki - Konna Sekai De Deaeta Kimi Ni"
page = 0
arr = os.listdir(f"Manga/{Manga}")
MangaList = os.listdir("Manga")
fileCount = len(arr)


class WindowManager(ScreenManager):
    pass


class ReaderApp(Screen):
    im = ObjectProperty(None)

    def NextPage(self):
        global page
        if fileCount > page:
            page += 1
            if arr[0] == "1.jpg":
                self.im.source = f"Manga/{Manga}/{page}.jpg"
            else:
                self.im.source = f"Manga/{Manga}/{page}.png"
            print("next page")

    def PreviousPage(self):
        global page
        if page > 1:
            page -= 1
            if arr[0] == "1.jpg":
                self.im.source = f"Manga/{Manga}/{page}.jpg"
            else:
                self.im.source = f"Manga/{Manga}/{page}.png"
            print("previous page")

    def on_touch_down(self, touch):
        global page
        xPos, yPos, = touch.pos
        xPos = int(xPos)
        yPos = int(yPos)
        print(f"X: {xPos}\nY: {yPos}")
        if xPos > 300 and yPos > 600:
            sm.current = "selecting"
            f = open("Records", "r+")
            f.writelines(f"{Manga};{page}")
            f.close()
        elif self.width * .30 > xPos:
            self.PreviousPage()
        elif xPos > self.width * .70:
            self.NextPage()


class SelectingApp(Screen):
    listSpin = ObjectProperty(None)

    def Refresh(self):
        self.listSpin.values = MangaList

    def Change(self):
        global page
        f = open("Records", "r")
        records = f.read().split("\n")
        print(records)
        for lines in records:
            print(lines)
            mangaTitle, mangaPage = lines.split(";")
            print(mangaTitle, mangaPage)
            if mangaTitle == Manga:
                page = int(mangaPage)
        sm.current = "reader"


kv = Builder.load_file("my.kv")

sm = WindowManager()
screens = [SelectingApp(name="selecting"), ReaderApp(name="reader")]
for screen in screens:
    sm.add_widget(screen)


class MyApp(App):
    sourceToImage = StringProperty(f"Manga/{Manga}/{page}.jpg")

    def build(self):
        return sm


if __name__ == "__main__":
    MyApp().run()

您的代码有几个问题:

  • 您的 kv 代码引用了 app,但是 kv 在创建 App 之前加载。这就是为什么您会收到有关 NoneType 的消息(appNone 的原因)。您可以通过将 kv = Builder.load_file("my.kv") 移动到 Appbuild() 方法中来解决这个问题。但是请看下面的注释。

  • 您正在构建 App 小部件树两次。一次使用 kv = Builder.load_file("my.kv") 行,再次使用代码:

     sm = WindowManager()
     screens = [SelectingApp(name="selecting"), ReaderApp(name="reader")]
     for screen in screens:
         sm.add_widget(screen)
    

以上代码和Builder.load_file()代码都可以去掉。见下文。

  • 因为你将kv文件命名为my.kv,它会被kivy自动加载,所以你不需要以上任何一种技术来构建App小部件树。

所以你的 App class 可以简单地是:

class MyApp(App):
    sourceToImage = StringProperty(f"Manga/{Manga}/{page}.jpg")

根本没有 build() 方法。然后,在您引用 sm.current 的方法中,您可以使用 self.manager.current.