title: Python kivy attribute error when trying to use multiple custom widgets
body: <p>In trying to create a widget to mimic the functions of a multiple choice quiz, I have run into an error that I can't find any solution for. I read <a href="https://stackoverflow.com/questions/58591221/kivy-attribute-error-object-has-no-attribute-trying-to-connect-widgets-in-kv">this post</a> and didn't find anything that applied to my specific situation.</p>
  
  <p>I am trying to load a group of settings from a .JSON file and use them as the main properties for my KV objects. For example, loading the "canvas_color" setting from the file and assigning that value to the canvas color of my widget.</p>
  
  <p>When I try and do this I get the error:</p>
  
  <p><code>AttributeError: 'MultipleChoiceT' object has no attribute 'settings'</code></p>
  
  <p><a href="https://github.com/luckyowl20/kivy_demo_app" rel="nofollow noreferrer">My code on github</a></p>
  
  <p>My .py file containing the "missing" attribute:</p>
  
  <pre><code>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) &lt; 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()
  </code></pre>
  
  <p>And my .kv file (yes, I am aware the indentation is off, that is only in this post):</p>
  
  <pre><code>&lt;MultipleChoiceT@BoxLayout&gt;:
  
  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'
  
  &lt;Test@BoxLayout&gt;:
      orientation: 'horizontal'
      MultipleChoiceT:
      MultipleChoiceT:
  </code></pre>
  
  <p>Whenever the MultipleChoice_text.py file is run, I get an error saying on line 11 of MultipleChoice_text.kv that the MultipleChoiceT object has no attribute 'settings' Maybe it's just my understanding of how/what attributes are. But when I ran the file on its own, without trying to have multiple the MultipleChoiceT widgets in one layout, it ran perfectly.</p>
  
  <p>My current idea is that the settings attribute is made on the instance level and is only created when the instance is created. And the instance is not created until after <code>root.settings[whatever]</code> is called. I'm not sure how I can fix this issue.</p>
  
  <p>I have tried to move the creation of the settings attribute around and into different classes to see if it can be initialized before it is called, but I had no luck with that. I also tried importing the settings attribute into the kv file but that didn't do anything either. My closest attempt has been running the <code>MultipleChoiceT</code> widget on its own and having it be the only thing in the app. That works when the code is as follows:</p>
  
  <pre><code>class MainApp(App):
      def build(self):
          return MultipleChoiceT()
  
  MainApp().run()
  </code></pre>
  
  <p>Any help or pointers would be appreciated as this is for a school project due in 2 weeks :)
  I can post the full error message if needed but the only thing I found useful in it was the line stating the missing attribute.</p>
  
  <p>As a side note, Is PyQt5 a better option for GUI development in python? I looked into it and chose not to go that route because I did not find a way for it to support scalable applications. And, the forum posts were sparse</p>