Reputation: 49
I'm making a little game with Tkinter, and it has a save function using pickle. However, when I try to save, it throws up the following message;
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1533, in __call__
return self.func(*args)
File "C:\Users\Benedict\Documents\Python\Migrant Simulator\MigSim 2016.10\migrant-stimulator.py", line 260, in save
pickle.dump(self.game,file)
_pickle.PicklingError: Can't pickle <class 'tkapp'>: attribute lookup tkapp on __main__ failed
The thing is, the data I'm trying to pickle contains nothing Tkinter-related, so I don't understand why it says it is <class 'tkapp'>
Here is a summary of the relevant bits of code:
...
class Game(object):
def __init__(self,name,nodes={},start=None,history=[]):
self.name=name
self.nodes=nodes
self.start=start
self.history=history
class App:
def __init__(self, master):
self.master=master
...
def save(self):
if self.file_name==None:
self.save_as()
file=open(self.file_name,'wb')
pickle.dump(self.game,file) # self.game is an instance of the Game class defined elsewhere
print(str(type(self.game)))
file.close()
def save_as(self):
self.file_name=filedialog.asksaveasfilename()
self.save()
...
root = Tk()
app = App(root)
root.mainloop()
How can I fix this? I've tried changing __getstate__
as suggested in a related question, but it didn't work.
EDIT: Never mind, it turns out that deep in my data structure, I had left a BooleanVar.
Upvotes: 4
Views: 5465
Reputation: 1085
In addition to what Brian Oakley suggested, it is also possible to save data as dill export:
def save(self):
data = {"name": self.name,
"nodes": self.nodes,
...
}
with open('data.pkl', 'wb') as f:
dill.dump(data, f)
def load(self):
with open('data.pkl', 'rb') as f:
data = dill.load(f)
self.name = data["name"]
self.nodes = data["nodes"]
...
Note that here it is necessary to specify binary mode at opening the file.
Upvotes: 0
Reputation: 386315
The short answer is, you can't pickle anything tkinter related. The reason is that tkinter applications use an embedded Tcl interpreter which maintains the state of the GUI in memory, and Tcl doesn't know anything about the python pickle format (and likewise, pickle knows nothing about the tcl interpreter). There's simply no way to save and restore the data managed by the Tcl interpreter.
You will have to convert the information you want to save into some type of data structure, and then save and load this data structure.
For example
def save(self):
data = {"name": self.name,
"nodes": self.nodes,
...
}
with open('data.json', 'w') as f:
json.dump(data, f)
def load(self):
with open('data.json') as f:
data = json.load(f)
self.name = data["name"]
self.nodes = data["nodes"]
...
If any of the values you want to store contain references to tkinter objects (eg: widgets, list of canvas item ids, etc.), you'll have to convert them to something else, and restore them at startup.
Upvotes: 3