AJN
AJN

Reputation: 1206

tkinter: How to serialize a treeview?

I am working on a python tkinter application that reads initial data from yaml file into a hierarchical TreeView to be edited further by the user.

To implement "save data" and "undo" functions, should I walk the treeview and reconstruct the data into a python object to be serialized (pickle)? Or is there a python module allowing, for example, to specify the treeview and the output file to be saved on?

Upvotes: 3

Views: 1679

Answers (3)

WinEunuuchs2Unix
WinEunuuchs2Unix

Reputation: 1949

Another Q&A shows how to pickle a treeview on exit and reload it on startup:

The OP has information laid out thusly:

#----------TreeViewlist----------------------
Header =['Website','Username','Password','etc']

The gist of the treeview is a record of each website the OP visits, what user ID is used and the password used.

To summarize the accepted answer:

Save treeview to pickle on exit

x=[tree.item(x)['values'] for x in tree.get_children()]
filehandler = open('data.pickle', 'wb')
pickle.dump(x,filehandler)
filehandler.close()

Load pickle to build treeview on startup

items = []
try:
    filehandler = open('data.pickle', 'rb')
    items = pickle.load(filehandler)
    filehandler.close()
except:
    pass

for item in items:
    tree.insert('','end',values=item)

The answer appears straight forward (to me) but if you have any questions post a comment below. If you see a flaw or bug in the code post a comment in the link above.

Upvotes: 0

Mike McKerns
Mike McKerns

Reputation: 35217

Out of the box, the answer is no, you can't serialize a TreeView. dill is probably your best bet at serialization out of the box… and it fails to pickle a TreeView object.

>>> import ttk
>>> import Tkinter as tk
>>> 
>>> f = tk.Frame()
>>> t = ttk.Treeview(f)
>>> 
>>> import dill
>>> dill.pickles(t)
False
>>> dill.detect.errors(t)
PicklingError("Can't pickle 'tkapp' object: <tkapp object at 0x10eda75e0>",)
>>> 

You might be able to figure out how to pickle a TreeView, and then add that method to the pickle registry… but, that could take some serious work on your part to chase down how things fail to pickle.

You can see what happens, it hits the __dict__ of the Tkinter.Tk object, and dies trying to pickle something.

>>> dill.detect.trace(True)
>>> dill.dumps(t)
C2: ttk.Treeview
D2: <dict object at 0x1147f5168>
C2: Tkinter.Frame
D2: <dict object at 0x1147f1050>
C2: Tkinter.Tk
D2: <dict object at 0x1148035c8>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mmckerns/lib/python2.7/site-packages/dill-0.2.3.dev0-py2.7.egg/dill/dill.py", line 194, in dumps
    dump(obj, file, protocol, byref, fmode)#, strictio)
  File "/Users/mmckerns/lib/python2.7/site-packages/dill-0.2.3.dev0-py2.7.egg/dill/dill.py", line 184, in dump
    pik.dump(obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 224, in dump
    self.save(obj)
  File "/opt/local/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 "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 725, in save_inst
    save(stuff)
  File "/opt/local/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 "/Users/mmckerns/lib/python2.7/site-packages/dill-0.2.3.dev0-py2.7.egg/dill/dill.py", line 678, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 681, in _batch_setitems
    save(v)
  File "/opt/local/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 "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 725, in save_inst
    save(stuff)
  File "/opt/local/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 "/Users/mmckerns/lib/python2.7/site-packages/dill-0.2.3.dev0-py2.7.egg/dill/dill.py", line 678, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 681, in _batch_setitems
    save(v)
  File "/opt/local/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 "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 725, in save_inst
    save(stuff)
  File "/opt/local/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 "/Users/mmckerns/lib/python2.7/site-packages/dill-0.2.3.dev0-py2.7.egg/dill/dill.py", line 678, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 681, in _batch_setitems
    save(v)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 313, in save
    (t.__name__, obj))
pickle.PicklingError: Can't pickle 'tkapp' object: <tkapp object at 0x10eda7648>
>>> 

That something is a tkapp object.

So, if you'd like to dig further, you can use the methods in dill.detect to help you uncover exactly why it's not pickling… and try to get around it.

I'm doubtful that pickling a widget is the right way to go. You probably also don't want to go the route of pulling the state out of a treeview into a shadow class, and saving that class. The problem is that the treeview is not really built with a good separation in mind for saving state.

If you can redesign to cleanly separate the state of your application from the widgets themselves, then that's more likely to do what you want. So, when you ask How to serialize a treeview, this is really not what you are asking. You want to know how to save the state of your application.

There are packages that can do something like that very easily. I'd suggest you looking at enaml and/or traits. enaml is a declarative markup that asks you to build a class that describes how your application interface works. It forces you to separate the inner workings of the thing your are displaying from the code that's necessary top operate the user interface… and it does it in a very easy to build way -- where the state of the application is separate from the user interface wiring. Thus, an instance of the class you build contains the state of the application at any time -- regardless of whether it has a UI on it or not, or two or three UIs for that matter. It makes saving the state of the application very easy because you never have to worry about saving the state of the UI -- the UI has no state -- it's just layout painted on top of the application. Then you won't have to worry about pickling widgets…

Check out enaml here: https://github.com/nucleic/enaml

and traits here: http://docs.enthought.com/traits

Upvotes: 0

martineau
martineau

Reputation: 123483

I doubt there's any Python module that does what you want, and even if there was, I don't think you'd want to structure your application around using it. Instead you would probably be better off decoupling things and storing the primary data in something independent of the human interface (which may or may not be graphical and might vary or otherwise be changed in the future). This is sometimes called the application "Model".

Doing so will allow you to load and save it regardless of what constitutes the current human interface. So, for example, you would then be free to use pickle if the internal Model is comprised of one or more Python objects. Alternatively you could save the data back into a yaml format file which would make loading it back in again later a cinch since the program can already do that.

Likewise, as the user edits the TreeView, equivalent changes should be made to the Model to keep the two in sync.

Take some time out from coding and familiarize yourself with the Model–View–Controller (MVC) design pattern.

Upvotes: 6

Related Questions