【问题标题】:Python kivy attribute error when trying to use multiple custom widgets尝试使用多个自定义小部件时出现 Python kivy 属性错误
【发布时间】:2020-02-08 08:13:43
【问题描述】:

在尝试创建一个小部件来模拟多项选择测验的功能时,我遇到了一个我找不到任何解决方案的错误。我阅读了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 小部件,它运行得很好。

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

我试图将设置属性的创建移动到不同的类中,以查看是否可以在调用它之前对其进行初始化,但我没有运气。我也尝试将设置属性导入到 kv 文件中,但这也没有做任何事情。我最接近的尝试是自己运行MultipleChoiceT 小部件,并让它成为应用程序中唯一的东西。当代码如下时有效:

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

MainApp().run()

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

作为旁注,PyQt5 是在 python 中进行 GUI 开发的更好选择吗?我研究了它并选择不走那条路,因为我没有找到支持可扩展应用程序的方法。而且,论坛帖子很少

【问题讨论】:

    标签: python kivy


    【解决方案1】:
    <MultipleChoiceT@BoxLayout>:
    

    这是错误的语法,它创建了一个名为 MultipleChoiceT 的新小部件类,除了您在 Python 中定义的那个。你想要&lt;MultipleChoiceT&gt;

    【讨论】:

    • 谢谢!它完美地工作。对于我要创建的任何自定义小部件,这是真的吗?我是否必须在 python 中定义它并继承到我需要的任何 kivy 对象,并将其保留为类名,没有@标签?
    猜你喜欢
    • 1970-01-01
    • 2014-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-24
    • 1970-01-01
    • 1970-01-01
    • 2014-05-12
    相关资源
    最近更新 更多