尝试使用多个自定义窗口小部件时发生Python Kivy属性错误

时间:2020-02-08 08:13:43

标签: python kivy

在尝试创建小部件来模仿多项选择测验的功能时,遇到了一个我找不到任何解决方案的错误。我读了this post,但没有发现适合我具体情况的任何内容。

我正在尝试从.JSON文件加载一组设置,并将它们用作我的KV对象的主要属性。例如,从文件中加载“ canvas_color”设置,并将该值分配给我的小部件的画布颜色。

当我尝试执行此操作时,出现错误:

AttributeError: 'MultipleChoiceT' object has no attribute 'settings'

My code on github

我的.py文件包含“缺少”属性:

import kivy
import json
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.app import App

kivy.require("1.11.1")

Builder.load_file("MultipleChoice_text.kv")


class MultipleChoiceT(BoxLayout):

    # collects info from json file and assigns it to settings var
    with open('widget_settings.json', 'r') as file:
        settings = json.load(file)["MultipleChoice_settings"][0]

    print(settings)

    test = 'hello'

    def __init__(self, **kwargs):
        super(MultipleChoiceT, self).__init__(**kwargs)
        self.app = App.get_running_app()
        print(self.app)
        self.w_width = 0.5
        self.w_height = 1
        self.data = {'pressed_btn': 0, 'btn_text': None}

    # the function called for when the toggle button is on_state
    def pressed(self, instance):
        self.data['pressed_btn'] = self.get_id(instance)
        self.data['btn_text'] = instance.text
        print(self.data)

    # changes the bottom label when submit button is pressed
    def submit(self):
        self.bottom_label.text = f"ID: {self.data['pressed_btn']} TEXT: {self.data['btn_text']}"

    # returns the id of the given instance
    @staticmethod
    def get_id(instance):
        for id, widget in instance.parent.ids.items():
            if widget.__self__ == instance:
                return id

    # null method for now, not implemented
    def change_question(self, new_text):
        self.question_label.text = str(new_text)

    # prints text when the next question button is pressed
    def next_question(self):
        print('next question pressed')

    # returns the dimensions for the toggle buttons based on their text length and font size defined in settings json
    def get_dimension(self, instance):
        if len(instance.text) < 25:
            width = ((len(instance.text) / 2) * instance.font_size) * 1.1
        else:
            width = (len(instance.text) / 2) * instance.font_size

        height = instance.font_size * 1.35
        return width * self.settings["btn_scalar"], height * self.settings["btn_scalar"]


class Test(BoxLayout):
    pass


class MainApp(App):
    def build(self):
        return Test()


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

还有我的.kv文件(是的,我知道缩进已关闭,仅在本文中如此):

<MultipleChoiceT@BoxLayout>:

orientation: 'vertical'
padding: 10
id: main_layout
bottom_label: bottom_label

canvas:

    Color:
        rgba: root.settings["canvas_color"]
    Rectangle:
        pos: self.x + 5, self.y + 5
        size: self.width - 10, self.height - 10

Label:
    id: question_label
    font_size: root.settings["font_size"]
    text: 'Sample question'
    text_size: self.size[0] + 5, self.size[1]
    valign: 'middle'
    height: self.font_size * 1.33
    size_hint: 1, None
    color: root.settings["label_text_color"]

Label:
    size_hint_y: None
    height: 10

ToggleButton:
    id: btn1
    background_color: root.settings["button_bg_color"]
    color: root.settings["button_text_color"]
    font_size: root.settings["font_size"]
    text: 'Option1'
    group: 'MultChoice'
    on_state: root.pressed(self)
    size: root.get_dimension(self)
    size_hint: None, None
    halign: 'center'
    valign: 'center'

Label:
    size_hint_y: 0.1

ToggleButton:
    id: btn2
    background_color: root.settings["button_bg_color"]
    color: root.settings["button_text_color"]
    font_size: root.settings["font_size"]
    text: '4785839409fjvojrvn eijofgj4389'
    group: 'MultChoice'
    on_state: root.pressed(self)
    size: root.get_dimension(self)
    size_hint: None, None
    halign: 'center'
    valign: 'center'

Label:
    size_hint_y: 0.1

ToggleButton:
    id: btn3
    background_color: root.settings["button_bg_color"]
    color: root.settings["button_text_color"]
    font_size: root.settings["font_size"]
    text: 'Option3'
    group: 'MultChoice'
    on_state: root.pressed(self)
    size: root.get_dimension(self)
    size_hint: None, None
    halign: 'center'
    valign: 'center'

Label:
    size_hint_y: 0.1

ToggleButton:
    id: btn4
    background_color: root.settings["button_bg_color"]
    font_size: root.settings["font_size"]
    color: root.settings["button_text_color"]
    text: 'A sample question type'
    group: 'MultChoice'
    on_state: root.pressed(self)
    size: root.get_dimension(self)
    size_hint: None, None
    halign: 'center'
    valign: 'center'

Label:
    size_hint: 1, 0.5

GridLayout:
    id: bottom_layout
    cols: 4

    size_hint_y: None

    canvas:
        Color:
            rgba: root.settings["second_canvas_color"]
        Rectangle:
            pos: self.x, self.y
            size: self.width, self.height


    Button:
        id: submit_btn
        text: 'Press to submit'
        on_press: root.submit()
        font_size: root.settings["font_size"]
        background_color: root.settings["second_btn_bg_color"]
        color: root.settings["second_btn_txt_color"]

        # for text wrapping
        size: root.get_dimension(self)
        text_size: self.size
        halign: 'center'
        valign: 'center'

    Label:
        id: bottom_label
        text: 'answer'
        font_size: root.settings["font_size"]
        color: root.settings["second_lbl_txt_color"]
        size_hint_x: main_layout.size_hint_x * 2

        # for text wrapping
        size: root.get_dimension(self)
        text_size: self.size
        halign: 'center'
        valign: 'center'

    Button:
        id: next_btn
        text: 'Next question'
        font_size: root.settings["font_size"]
        background_color: root.settings["second_btn_bg_color"]
        color: root.settings["second_btn_txt_color"]
        on_press: root.next_question()

        # for text wrapping
        size: self.texture_size
        text_size: self.size
        halign: 'center'
        valign: 'center'

<Test@BoxLayout>:
    orientation: 'horizontal'
    MultipleChoiceT:
    MultipleChoiceT:

无论何时运行MultipleChoice_text.py文件,我都会在MultipleChoice_text.kv的第11行收到一条错误消息,提示MultipleChoiceT对象没有属性“设置”,也许这只是我对属性的了解。但是,当我自己运行文件时,如果没有尝试在一个布局中包含多个MultipleChoiceT小部件,它就可以完美运行。

我当前的想法是settings属性是在实例级别创建的,并且仅在创建实例时创建。并且直到调用root.settings[whatever]之后才创建实例。我不确定如何解决此问题。

我尝试将settings属性的创建移至不同的类中,以查看是否可以在调用它之前对其进行初始化,但是我对此并不走运。我还尝试将settings属性导入到kv文件中,但也没有执行任何操作。我最接近的尝试是独自运行MultipleChoiceT小部件,并使它成为应用程序中唯一的东西。当代码如下时,该方法起作用:

class MainApp(App):
    def build(self):
        return MultipleChoiceT()

MainApp().run()

任何帮助或指点将不胜感激,因为这是为期2周的学校项目:) 如果需要,我可以发布完整的错误消息,但我发现唯一有用的是声明缺少属性的行。

作为旁注,PyQt5是python GUI开发的更好选择吗?我调查了一下,选择不走这条路,因为我没有找到支持可扩展应用程序的方法。而且,论坛帖子稀疏

1 个答案:

答案 0 :(得分:0)

<MultipleChoiceT@BoxLayout>:

这是错误的语法,它会创建一个名为MultipleChoiceT的新窗口小部件类,除了您在Python中定义的类之外,还 。您需要<MultipleChoiceT>