Text displayed in debug window instead of popup window of Kivy App - python

I encountered some weird behavior of Kivy when trying to place the results of some analysis in a popup window of my App. By the button activation I can get a popup window (that should display the results of the analysis but is empty) and then my results are displayed in a debug window. But I'd like to see them in a popup. No error, no traceback, just weirdness.
This is how it looks like:
This is the line that runs a popup:
show_syllsoutput_popup()
This is the line that should populate it but populates debug window instead:
try: SyllOutputPopup.screen_output_label.text = cfd_syll.tabulate()
So, the question is how to put cfd_syll.tabulate() into this popup (.kv):
<SyllOutputPopup>:
FloatLayout:
Label:
id: screen_output_label
font_size: 12
pos: 100, 120
=============================================================
Alternative test: if I try to populate output popup by this:
SyllOutputPopup.screen_output_label.text = cfd_syll.tabulate()
(without try:), I get AttributeError: type object 'SyllOutputPopup' has no attribute 'screen_output_label'
Here is the full traceback, in case it'd be helpful:
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\runpy.py", line 85, in _run_code
exec(code, run_globals)
File "C:\GUI Projects\gercort\main.py", line 190, in <module>
Gercort().run()
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\app.py", line 855, in run
runTouchApp()
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\base.py", line 504, in runTouchApp
EventLoop.window.mainloop()
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\core\window\window_sdl2.py", line 747, in mainloop
self._mainloop()
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\core\window\window_sdl2.py", line 479, in _mainloop
EventLoop.idle()
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\base.py", line 342, in idle
self.dispatch_input()
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\base.py", line 327, in dispatch_input
post_dispatch_input(*pop(0))
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\base.py", line 293, in post_dispatch_input
wid.dispatch('on_touch_up', me)
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\_event.cp37-win32.pyd", line 707, in kivy._event.EventDispatcher.dispatch
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\uix\behaviors\button.py", line 179, in on_touch_up
self.dispatch('on_release')
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\_event.cp37-win32.pyd", line 703, in kivy._event.EventDispatcher.dispatch
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\_event.cp37-win32.pyd", line 1214, in kivy._event.EventObservers.dispatch
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\_event.cp37-win32.pyd", line 1098, in kivy._event.EventObservers._dispatch
File "C:\Users\gavrk\AppData\Local\Programs\Python\Python37-32\Lib\site-packages\kivy\lang\builder.py", line 64, in custom_callback
exec(__kvlang__.co_value, idmap)
File "C:\GUI Projects\gercort\gercort.kv", line 484, in <module>
on_release: root.output_toscreen(root.filepath)
File "C:\GUI Projects\gercort\main.py", line 135, in output_toscreen
SyllOutputPopup.screen_output_label.text = cfd_syll.tabulate() # populate the popup
builtins.AttributeError: type object 'SyllOutputPopup' has no attribute 'screen_output_label'
Would be grateful for any help! I don't know what parts of code would be helpful, so I apologize in advance, any suggestions are greatly appreciated.
==================================================================
Additional info:
cfd_syll is defined in the fourth line of:
def output_toscreen(сorpus_root, *args):
corpus = PlaintextCorpusReader(args[0], '.*')
cfd_syll = nltk.ConditionalFreqDist(
(textname, num_syll)
for textname in corpus.fileids()
for num_syll in [len(w) for w in ''.join(char for char in reduce_dip(corpus.raw(fileids=textname)) if char in vowels).split()])
show_syllsoutput_popup() # run the popup
try: SyllOutputPopup.screen_output_label.text = cfd_syll.tabulate() # populate the popup
except: pass
Here is the Popup class:
class SyllOutputPopup(FloatLayout):
pass
Here is definition of show_syllsoutput_popup:
def show_syllsoutput_popup():
show = SyllOutputPopup() # Create a new instance of the LicensePopup class
SyllOutputPopupWindow = Popup(title="Output", content=show, size_hint=(None,None),size=(600,400))
# Create the popup window
SyllOutputPopupWindow.open() # show the popup
In the .kv Popup is defined as:
<SyllOutputPopup>:
FloatLayout:
Label:
id: screen_output_label
font_size: 12
pos: 100, 120

screen_output_label is not the parameter of your class, it's widget's id, so that line
SyllOutputPopup.screen_output_label.text = cfd_syll.tabulate()
is wrong, you should use:
SyllOutputPopup.ids.screen_output_label.text = cfd_syll.tabulate()
==========
Also you are creating several objects of SyllOutputPopup class. You put the text in one object:
try: SyllOutputPopup.ids.screen_output_label.text = cfd_syll.tabulate()
And then you create new object, which is blank and has empty Label, and you show it:
show = SyllOutputPopup() # Create a new instance
SyllOutputPopupWindow = Popup(title="Output", content=show, size_hint=(None,None),size=(600,400))
SyllOutputPopupWindow.open()
You should use one object - set the text there and then show exactly that object, something like:
def output_toscreen(сorpus_root, *args):
corpus = PlaintextCorpusReader(args[0], '.*')
cfd_syll = nltk.ConditionalFreqDist(
(textname, num_syll)
for textname in corpus.fileids()
for num_syll in [len(w) for w in ''.join(char for char in reduce_dip(corpus.raw(fileids=textname)) if char in vowels).split()])
# that will be your object
self.sylloutputpopup = SyllOutputPopup()
self.sylloutputpopup.ids.screen_output_label.text = cfd_syll.tabulate()
show_syllsoutput_popup() # run the popup
def show_syllsoutput_popup():
show = self.sylloutputpopup
SyllOutputPopupWindow = Popup(title="Output", content=show, size_hint=(None,None),size=(600,400))
SyllOutputPopupWindow.open()
But that will work only if both of functions above are in the same class.

Related

Str object is not callable in Kivy

I'm using Kivy in python trying to make the button "Save" run the "Save_number" function but I keep getting a "TypeError: 'str' object is not callable"-error. Below is relevant code.
class DBScreen(Screen):
def save_number(self):
conn = sqlite3.connect('database.db')
c = conn.cursor()
name = self.ids.save_name.text
number = self.ids.save_number.text
name = str(name).replace("(","").replace(")","").replace(",","").replace("'","")
number = str(number).replace("(", "").replace(")", "").replace(",", "").replace("'", "")
number = int(number)
c.execute("INSERT INTO database VALUES (?,?)", (name, number))
c.execute("SELECT * FROM database WHERE name=:name", {'name': name})
conn.commit()
conn.close()
pass
def save_process(self):
self.save_name = self.ids.save_name.text
self.save_number = self.ids.save_number.text
if self.save_number == "" and self.save_name == "":
self.disable_save()
elif self.save_number != "" and self.save_name != "":
self.enable_save()
pass
def disable_save(self):
self.ids.save_button.disabled = True
def enable_save(self):
self.ids.save_button.disabled = False
def load_number(self):
global number_list
global initial_length
conn = sqlite3.connect('database.db')
c = conn.cursor()
name = self.ids.load_name.text
if self.ids.load_name.text == "":
self.disable_submit()
else:
self.enable_submit()
c.execute("SELECT * FROM database WHERE name=:name", {'name': name})
name, number = c.fetchone()
conn.commit()
conn.close()
number_list = []
for i in number:
number_list.append(int(i))
initial_length = len(number_list)
def load_process(self):
self.load_text = self.ids.load_name.text
print(self.load_text)
if self.load_text == "":
self.disable_load()
else:
self.enable_load()
def disable_load(self):
self.ids.load_button.disabled = True
def enable_load(self):
self.ids.load_button.disabled = False
Below is the relevant part of the Kivy file
<DBScreen>:
GridLayout:
padding: 20
cols: 1
rows: 10
TextInput:
id: save_name
hint_text:'Name'
pos_hint: {'center_x': 0.5, 'center_y': 0.705}
size_hint: 0.95, 0.5
multiline: False
on_text: root.save_process()
TextInput:
id: save_number
hint_text:'Number'
pos_hint: {'center_x': 0.5, 'center_y': 0.705}
size_hint: 0.95, 0.5
multiline: False
input_filter: 'int'
on_text: root.save_process()
Button:
id: save_button
text: 'Save'
disabled: True
on_press: root.save_number()
Error code
Traceback (most recent call last):
File "C:\Users\aronf\PycharmProjects\rtn\main.py", line 184, in <module>
gui.run()
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\app.py", line 955, in run
runTouchApp()
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 574, in runTouchApp
EventLoop.mainloop()
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 339, in mainloop
self.idle()
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 383, in idle
self.dispatch_input()
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 334, in dispatch_input
post_dispatch_input(*pop(0))
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\base.py", line 263, in post_dispatch_input
listener.dispatch('on_motion', etype, me)
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\core\window\__init__.py", line 1660, in on_motion
self.dispatch('on_touch_down', me)
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\core\window\__init__.py", line 1677, in on_touch_down
if w.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\uix\screenmanager.py", line 1210, in on_touch_down
return super(ScreenManager, self).on_touch_down(touch)
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\uix\widget.py", line 589, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\uix\relativelayout.py", line 306, in on_touch_down
ret = super(RelativeLayout, self).on_touch_down(touch)
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\uix\widget.py", line 589, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\uix\widget.py", line 589, in on_touch_down
if child.dispatch('on_touch_down', touch):
File "kivy\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\uix\behaviors\button.py", line 151, in on_touch_down
self.dispatch('on_press')
File "kivy\_event.pyx", line 727, in kivy._event.EventDispatcher.dispatch
File "kivy\_event.pyx", line 1307, in kivy._event.EventObservers.dispatch
File "kivy\_event.pyx", line 1191, in kivy._event.EventObservers._dispatch
File "C:\Users\aronf\AppData\Local\Programs\Python\Python39\lib\site-packages\kivy\lang\builder.py", line 55, in custom_callback
exec(__kvlang__.co_value, idmap)
File "C:\Users\aronf\PycharmProjects\rtn\gui.kv", line 124, in <module>
on_press: root.save_number()
TypeError: 'str' object is not callable
Process finished with exit code 1
You have a function and a TextInput with the same name number and both of them are in root (DBScreen), so the interpreter doesn't understand which name you call and calls a TextInput, not a function as you expected. To fix the problem give a TextInput another name

kivy - textinput from kv to py - import data

I'm trying to get the data from a simple textinput that is in my kv file and use the information in my python file.
but i keep getting the same error.
this error happens when i press a button and the function try to run looks like its trying to get the information from the text_location widget
thank you in advance.
class ScatterTextWidget(BoxLayout):
def initialize_request(self):
''' Initial analysis and control flow of the class '''
location = self.ids.input_location
date = S.ids.input_date
time_raw = ScatterTextWidget.ids.input_time
print(f'{location} - {date} - {time_raw}')
class HistoricalApp(App):
def build(self):
return ScatterTextWidget()
if __name__ == "__main__":
HistoricalApp().run()
the kivy file called "historical.kv"
i reduce it because is kind of long
<ScatterTextWidget>:
BoxLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex('#01183f')
Rectangle:
pos: self.pos
size: self.size
size_hint_y: None
height: 40
valign: 'middle'
TextInput:
id:text_location
width: 200
id: input2
font_size: 15
size_hint_y: None
height:40
TextInput:
id:input_date
width: 200
id: input1
font_size: 15
size_hint_y: None
height: 40
TextInput:
id:input_time
width: 200
id: input1
font_size: 15
size_hint_y: None
height: 40
the error i receive is
File "kivy\properties.pyx", line 838, in
kivy.properties.ObservableDict.__getattr__
KeyError: 'text_location'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:/Users/IBKCo/Desktop/Git Programs/TimeStationHistoricalRecords/MainGui.py", line 77, in <module>
HistoricalApp().run()
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\pdEnv\lib\site-packages\kivy\app.py", line 826, in run
runTouchApp()
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\pdEnv\lib\site-packages\kivy\base.py", line 502, in runTouchApp
EventLoop.window.mainloop()
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\pdEnv\lib\site-packages\kivy\core\window\window_sdl2.py", line 727, in mainloop
self._mainloop()
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\pdEnv\lib\site-packages\kivy\core\window\window_sdl2.py", line 460, in _mainloop
EventLoop.idle()
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\pdEnv\lib\site-packages\kivy\base.py", line 340, in idle
self.dispatch_input()
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\pdEnv\lib\site-packages\kivy\base.py", line 325, in dispatch_input
post_dispatch_input(*pop(0))
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\pdEnv\lib\site-packages\kivy\base.py", line 291, in post_dispatch_input
wid.dispatch('on_touch_up', me)
File "kivy\_event.pyx", line 707, in kivy._event.EventDispatcher.dispatch
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\pdEnv\lib\site-packages\kivy\uix\behaviors\button.py", line 179, in on_touch_up
self.dispatch('on_release')
File "kivy\_event.pyx", line 703, in kivy._event.EventDispatcher.dispatch
File "kivy\_event.pyx", line 1214, in kivy._event.EventObservers.dispatch
File "kivy\_event.pyx", line 1098, in kivy._event.EventObservers._dispatch
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\pdEnv\lib\site-packages\kivy\lang\builder.py", line 64, in custom_callback
exec(__kvlang__.co_value, idmap)
File "C:\Users\IBKCo\Desktop\Git Programs\TimeStationHistoricalRecords\historical.kv", line 127, in <module>
on_release: root.initialize_request()
File "C:/Users/IBKCo/Desktop/Git Programs/TimeStationHistoricalRecords/MainGui.py", line 31, in initialize_request
location = self.ids.text_location
File "kivy\properties.pyx", line 841, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
thank you in advance
best,
First, you need to remove both of the id:input1 lines and the id:input2 line. Then change your initialize_request method to:
def initialize_request(self):
''' Initial analysis and control flow of the class '''
location = self.ids.text_location.text
date = self.ids.input_date.text
time_raw = self.ids.input_time.text
print(f'{location} - {date} - {time_raw}')
Note that self.ids.text_location is a reference to the TextInput widget, so you have to add .text to get its text. Same for the other TextInput widgets.

Kivy - How to access kivy widget in python?

I want to edit a text in a TextInput in kivy but don't know how to, I've tried this code as Ive search in the net but still no luck.
class MainApp(Screen):
def __init__(self,**kwargs):
super(MainApp,self).__init__(**kwargs)
pass
class Forms(BoxLayout):
def __init__(self, **kwargs):
super(Main,self).__init__(**kwargs)
self.ids.txtSearch.text = "new text"
class Main(App):
def build(self):
return root_widget
if __name__ == "__main__":
Main().run()
------kivy-------
<Main>:
TextInput:
id: txtSearch
this is not my whole code but I think those are what matters in the issue
this is the error:
File "C:\Users\Guest\Documents\Python\Kivy\Project\main.py", line 295, in <module>
''')
File "D:\Kivy-1.9.0-py3.4-win32-x64\kivy34\kivy\lang.py", line 1828, in load_string
self._apply_rule(widget, parser.root, parser.root)
File "D:\Kivy-1.9.0-py3.4-win32-x64\kivy34\kivy\lang.py", line 1985, in _apply_rule
self.apply(child)
File "D:\Kivy-1.9.0-py3.4-win32-x64\kivy34\kivy\lang.py", line 1872, in apply
self._apply_rule(widget, rule, rule)
File "D:\Kivy-1.9.0-py3.4-win32-x64\kivy34\kivy\lang.py", line 1986, in _apply_rule
self._apply_rule(child, crule, rootrule)
File "D:\Kivy-1.9.0-py3.4-win32-x64\kivy34\kivy\lang.py", line 1986, in _apply_rule
self._apply_rule(child, crule, rootrule)
File "D:\Kivy-1.9.0-py3.4-win32-x64\kivy34\kivy\lang.py", line 1983, in _apply_rule
child = cls(__no_builder=True)
File "C:\Users\Guest\Documents\Python\Kivy\Project\main.py", line 40, in __init__
self.ids.txtSearch.text = "new text"
File "kivy\properties.pyx", line 720, in kivy.properties.ObservableDict.__getattr__ (kivy\properties.c:10911)
AttributeError: 'super' object has no attribute '__getattr__'
To change Widgets properties in kivy you need to 'link' the widget between .py and .kv file, first in .py file:
txt_Search = ObjectProperty()
then in .kv file, in your root widget:
txt_Search: txtSearch
then assign the id to a widget (as you already did):
<Main>:
TextInput:
id: txtSearch
text: ' '
then in your .py file, you can change attributes of the widget by doing this:
self.txt_Search.text = 'New Text'
or any other attribute:
self.txt_Search.height = '30dp'
Are you sure self.ids.txtSearch exists when you try to assign a text to it? You're calling super(Main,self) one line above so I guess txtSearch is never instantiated.
Btw, it's better to init widgets in *.kv files:
<Main>:
TextInput:
id: txtSearch
text: "new text"

Kivy Scatter Image - Bounding Box - NoneType

I'm having some issues using the bounding box feature of the Scatter (FloatView). My build code is;
MapPopup = Popup(title='Map')
def openMap(self):
box = BoxLayout(orientation='vertical')
stack = StackLayout(orientation='tb-lr', padding=0, spacing=1)
grid = GridLayout(orientation='vertical', cols=1)
button = Button(markup=True,bold=True,text='B\nA\nC\nK',width=40,size_hint_x=None)
button.bind(on_press=partial(self.backButton))
stack.add_widget(button)
source = os.path.join(os.getcwd(), 'Games', str(CURRENT_GAME[0]), 'map.png')
if os.path.isfile(source):
img = PILImage.open(source)
width, height = img.size
scatter = Scatter(size_hint=(None, None), size=(width,height), auto_bring_to_front=False, bbox=((10,10),(10,10)))
image = Image(source=source, size=(width,height))
scatter.add_widget(image)
grid.add_widget(scatter)
else:
label = Label(text="Please Start/Load a Game")
grid.add_widget(label)
stack.add_widget(grid)
box.add_widget(stack)
Container.MapPopup.content = box
Container.MapPopup.open()
It doesn't matter what values I put in at bbox=((?,?),(?,?)) I get the error;
TypeError: 'NoneType' object is not callable
Removing the bbox and all works normally, but the image I'm housing is spilling outside of the popup box. I don't want to resize the image using scale=.
From the docs:
bbox Added in 1.0.0
Bounding box of the widget in parent space:
((x, y), (w, h))
I think my issue is that it's scatter.scatter.bbox instead of scatter.bbox due to the ScatterLayout implementing FloatLayout, and Scatter implementing ScatterLayout, or something daft like that - can anyone help?
Many thanks!
Full Traceback;
Traceback (most recent call last):
File "main.py", line 1069, in <module>
TextGameTopiaApp().run()
File "/usr/lib/python2.7/dist-packages/kivy/app.py", line 792, in run
runTouchApp()
File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 481, in runTouchApp
EventLoop.window.mainloop()
File "/usr/lib/python2.7/dist-packages/kivy/core/window/window_pygame.py", line 381, in mainloop
self._mainloop()
File "/usr/lib/python2.7/dist-packages/kivy/core/window/window_pygame.py", line 287, in _mainloop
EventLoop.idle()
File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 324, in idle
self.dispatch_input()
File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 309, in dispatch_input
post_dispatch_input(*pop(0))
File "/usr/lib/python2.7/dist-packages/kivy/base.py", line 275, in post_dispatch_input
wid.dispatch('on_touch_up', me)
File "_event.pyx", line 316, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:4550)
File "/usr/lib/python2.7/dist-packages/kivy/uix/behaviors.py", line 110, in on_touch_up
self.dispatch('on_release')
File "_event.pyx", line 312, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:4504)
File "/usr/lib/python2.7/dist-packages/kivy/lang.py", line 1299, in custom_callback
exec(__kvlang__.co_value, idmap)
File "./textgametopia.kv", line 258, in <module>
on_release: root.openMap()
File "main.py", line 311, in openMap
scatter = Scatter(size_hint=(None, None), size=(width,height), auto_bring_to_front=False, bbox=((10,10),(10,10)))
File "/usr/lib/python2.7/dist-packages/kivy/uix/scatter.py", line 363, in __init__
super(Scatter, self).__init__(**kwargs)
File "/usr/lib/python2.7/dist-packages/kivy/uix/widget.py", line 163, in __init__
super(Widget, self).__init__(**kwargs)
File "_event.pyx", line 156, in kivy._event.EventDispatcher.__init__ (kivy/_event.c:3184)
File "properties.pyx", line 345, in kivy.properties.Property.__set__ (kivy/properties.c:3589)
File "properties.pyx", line 1219, in kivy.properties.AliasProperty.set (kivy/properties.c:19329)
TypeError: 'NoneType' object is not callable

TypeError for pickle of array of custom objects in Python

I have the following method:
def save(self):
vertices = self.mindmap.get_vertices()
edges = self.mindmap.get_edges()
output = open('mindmap.pkl', 'wb')
pickle.dump(edges, output)
pickle.dump(vertices, output)
output.close()
When I call this method, I get the following error:
Traceback (most recent call last):
File "main.py", line 72, in <module>
MindMapApp().run()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/app.py", line 792, in run
runTouchApp()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 481, in runTouchApp
EventLoop.window.mainloop()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/core/window/window_pygame.py", line 381, in mainloop
self._mainloop()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/core/window/window_pygame.py", line 287, in _mainloop
EventLoop.idle()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 324, in idle
self.dispatch_input()
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 309, in dispatch_input
post_dispatch_input(*pop(0))
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/base.py", line 275, in post_dispatch_input
wid.dispatch('on_touch_up', me)
File "_event.pyx", line 316, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:4537)
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/uix/behaviors.py", line 110, in on_touch_up
self.dispatch('on_release')
File "_event.pyx", line 312, in kivy._event.EventDispatcher.dispatch (kivy/_event.c:4491)
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/lang.py", line 1299, in custom_callback
exec(__kvlang__.co_value, idmap)
File "./mindmap.kv", line 14, in <module>
on_release: app.save()
File "main.py", line 27, in save
pickle.dump(vertices, output)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1370, in dump
Pickler(file, protocol).dump(obj)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
self.save(obj)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 600, in save_list
self._batch_appends(iter(obj))
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 615, in _batch_appends
save(x)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 306, in save
rv = reduce(self.proto)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy_reg.py", line 71, in _reduce_ex
state = base(self)
TypeError: __init__() takes exactly 0 positional arguments (1 given)
I do not get this error if both arrays are empty. When the above was generated edges was empty and vertices had a size of one. I really don't know how to interpret this error, and as such have no idea how to resolve it.
This is my Vertex class __init__:
def __init__(self, pos=None, size=None):
super(Vertex, self).__init__()
self.pos = pos
self.size = size
if self.height < self.MINIMUM_HEIGHT + self.TEXT_OFFSET:
self.height = self.MINIMUM_HEIGHT + self.TEXT_OFFSET
if self.width < self.MINIMUM_WIDTH + self.TEXT_OFFSET:
self.width = self.MINIMUM_WIDTH + self.TEXT_OFFSET
self.originalSize = self.size
self.originalWidth = self.size[0]
self.newIdea = TextInput(
height = self.MINIMUM_HEIGHT,
size_hint = (None, None),
pos_hint = {'center_x':.5, 'center_y':.5},
background_active = '',
background_normal = '',
use_bubble = True,
foreground_color = (.4, .4, .4, 1),
cursor_color = (.4, .4, .4, 1))
self.newIdea.bind(text=self.on_text)
self.idea = Label(
height = self.MINIMUM_HEIGHT,
size_hint = (None, None),
pos_hint = {'center_x':.5, 'center_y':.5},
color = (.4, .4, .4,1))
Logger.info('Vertex: created')
Indeed, the Kivy EventDispatcher object is at fault here; the object.__reduce_ex__ method searches for the first baseclass that is not a custom class (it does not have the Py_TPFLAGS_HEAPTYPE flag set), to call __init__ on that class. This fails for that base class, as it's __init__ method doesn't support passing in a single argument.
The work-around is to use the latest pickle protocol instead; it uses a different codepath:
pickle.dump(edges, output, pickle.HIGHEST_PROTOCOL)
pickle.dump(vertices, output, pickle.HIGHEST_PROTOCOL)
because the latest pickle protocol (2) supports new-style classes much more efficiently.
Seeing the complexity of the Kivy object hierarchy, you may need to customise the pickling process.
I'd look into implementing the __getnewargs__ method for example:
def __getnewargs__(self):
return (self.pos, self.size)
would ensure that just that data is pickled, nothing more. On unpickling, these two values are then used to create new Vertex objects. Note that for this method to be used you have to use the latest protocol!
You could also implement a __getstate__ method; if you return a dictionary, no mirror __setstate__ method is required, but would allow you to initialise some more attributes.
If neither is implemented, the contents of the instance __dict__ is pickled instead. Make sure that everything contained can be pickled.

Categories

Resources