Inagnikai Inagnikai - 1 month ago 7
Python Question

Adding variable number of controls to DropDown - weakly-referenced object no longer exists

I have a dropdown with a list of the months in it. When the Month is selected, I'm trying to dynamically populate buttons in a second dropdown with the correct number of days. When I do so, I get:

ReferenceError: weakly-referenced object no longer exists


Here are my files for reference:

main.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

globIDs = {}

class appScreen(BoxLayout):

def dayDropPop(self, num):
globIDs['dayDropDown'].populate(num)


class ExtDropDown(BoxLayout):
heldValue = ''

def setID(self, key):
globIDs[key] = self

def changeValue(self, sentText, parent):
self.heldValue = sentText
parent.text = sentText

class PriorityDropDown(ExtDropDown):
pass

class MonthDropDown(ExtDropDown):

def __init__(self, **kwargs):
super(MonthDropDown, self).__init__(**kwargs)
self.setID('monthDropDown')

def monthSelect(self, month):
monthDay = {'Jan': 31, 'Feb': 29, 'Mar': 31, 'Apr': 30, 'May': 31, 'Jun': 30, 'Jul': 31, 'Aug': 31, 'Sep': 30,
'Oct': 31, 'Nov': 30, 'Dec': 31}

numOfDays = monthDay[month]
appScreen().dayDropPop(numOfDays)

def testingFurther(self):
print()

class DayDropDown(ExtDropDown):

def __init__(self, **kwargs):
super(DayDropDown, self).__init__(**kwargs)
self.setID('dayDropDown')

def populate(self, num):

for i in range(0, num):
newButt = Button(text=str(num + 1))
self.ids.drop.add_widget(newButt)

class schedulerApp(App):

def build(self):
return appScreen()

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


scheduler.kv:

<PriorityDropDown>:
Button:
id: ddRoot
text: 'Priority'
on_release: drop.open(ddRoot)
size_hint_y: None
height: root.height

DropDown:
id: drop
on_parent: self.dismiss()
on_select: root.changeValue(args[1], ddRoot)

Button:
text: 'Top'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'High'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Medium'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Low'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)

<MonthDropDown>:
Button:
id: ddRoot
text: 'Month'
on_release: drop.open(ddRoot)
size_hint_y: None
height: root.height

DropDown:
id: drop
on_parent: self.dismiss()
on_select: root.monthSelect(args[1])

Button:
text: 'Jan'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Feb'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Mar'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Apr'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'May'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Jun'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Jul'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Aug'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Sep'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Oct'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Nov'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)
Button:
text: 'Dec'
size_hint_y: None
height: root.height
on_release: drop.select(self.text)

<DayDropDown>:
height: root.height
Button:
id: ddRoot
text: 'Day'
on_release: drop.open(ddRoot)
size_hint_y: None
height: root.height

DropDown:
id: drop
on_parent: self.dismiss()
on_select: root.changeValue(args[1], ddRoot)

<appScreen>:
orientation: 'vertical'
Label:
size_hint_y: .1
text: 'Hello World'
GridLayout:
size_hint_y:.1
width: root.width
cols: 3
Button:
Button:
Button:
ScrollView:
canvas.before:
Color:
rgba: .3, .3, .3, 5
Rectangle:
pos: self.pos
size: self.size
GridLayout:
cols: 3
Label:
id: textReceiver
text: 'Words'
text_size: self.size
halign: 'left'
valign: 'top'
Label:
Label:
BoxLayout:
size_hint_y: .125
TextInput:
size_hint_x: .7
PriorityDropDown:
size_hint_x: .3
BoxLayout:
size_hint_y: .125
MonthDropDown:
size_hint_x: .35
DayDropDown:
id: 'dayDrop'
size_hint_y: 1
size_hint_x: .2
TextInput:
size_hint_x: .45


I think the issue stems from the controls in question being created in Kivy code, rather than in Python. What testing I've done leads me to believe that I'm referencing my DayDropDown widget incorrectly. However, I don't know how else I would do so. With that in mind, how would I go about referencing my DayDropDown using what I already have? If that isn't my issue, what else might be causing the ReferenceError to be thrown?

Edit:

Messed with my code a little bit. I created a new class "globAddable" with methods "getID" - a simple return self - and put setID in there instead. I then set my setID now assigns self.getID() to a variable, then uses that variable as the object to be added to the globObjects (formerly globIDs) dictionary.

I also created a new class for my DropDown object, called ExtDropArray, which lives in my DayDropDown. I moved the populate() method to this new class so that it can be called directly by the Dropdown, rather than its parent BoxLayout. I made the ExtDropArray and ExtDropDown inherit from globAddable to expose the setID (and implicitly getID) methods.

The net result of all this is exactly the same. I still don't see my day DropDown when clicking the button on DayDropDown, and after testing with different values on the MonthDropDown, again I get the 'ReferenceError: weakly-referenced object no longer exists' error. However, I am noticing that the offending line is actually the method that opens the dropdown (drop.open(ddRoot), which is called on line 114 of my .kv file). This still doesn't quite give me enough information to know which part of the process is causing the error, be it the adding of the buttons to the DropDown or simply the calling of the open method. Given this new information, is anyone able to deduce what needs to change?

Answer

Alright, I finally figured this one out.

My ReferenceError was not a result of my methods, but rather a fundamental misunderstanding on my part of the implementation of DropDown objects. The fix was simply

<DayDropDown>:
    drop: drop.__self__
    ...

This took me one heck of a long time to find, but came in the form of this documentation. Seeing as no other posts mentioning these ReferenceErrors makes any mention to this document, I'm leaving it here for others to use in case they run into a similar issue.