Skip to content

Commit

Permalink
Merge pull request #143 from nucleic/feature-dock-area-save
Browse files Browse the repository at this point in the history
Make enamldef objects pickable
  • Loading branch information
sccolbert committed Mar 13, 2014
2 parents 0a7f1f1 + 3f3a3d2 commit db99d02
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 124 deletions.
36 changes: 35 additions & 1 deletion enaml/core/enamldef_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,29 @@
#
# The full license is in the file COPYING.txt, distributed with this software.
#------------------------------------------------------------------------------
from .declarative import DeclarativeMeta
from .declarative_meta import DeclarativeMeta


def __enamldef_newobj__(cls, *args):
""" An enamldef pickler function.
This function is not part of the public Enaml api.
"""
instance = cls.__new__(cls, *args)
cls.__node__(instance)
return instance


def __reduce_ex__(self, proto):
""" An implementation of the reduce protocol.
This method creates a reduction tuple for enamldef instances. It
is not part of the public Enaml api.
"""
args = (type(self),) + self.__getnewargs__()
return (__enamldef_newobj__, args, self.__getstate__())


class EnamlDefMeta(DeclarativeMeta):
Expand All @@ -17,6 +39,18 @@ class EnamlDefMeta(DeclarativeMeta):
#: be modified by user code.
__node__ = None

def __new__(meta, name, bases, dct):
""" Create a new enamldef subclass.
This method overrides the default Atom pickle reducer with one
which invokes the Enaml compiler node during unpickling.
"""
cls = DeclarativeMeta.__new__(meta, name, bases, dct)
if cls.__reduce_ex__ is not __reduce_ex__:
cls.__reduce_ex__ = __reduce_ex__
return cls

def __repr__(cls):
""" A nice repr for a type created by the `enamldef` keyword.
Expand Down
160 changes: 160 additions & 0 deletions examples/workbench/persistent_view.enaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#------------------------------------------------------------------------------
# Copyright (c) 2013, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#------------------------------------------------------------------------------
from enaml.layout.api import HSplitLayout, InsertItem
from enaml.widgets.api import Container, DockArea, DockItem, Html
from enaml.workbench.api import Extension, PluginManifest
from enaml.workbench.core.api import Command
from enaml.workbench.ui.api import ActionItem, MenuItem


print 'Imported Persistent View!'


class PickableDockItem(DockItem):
""" A custom pickable dock item class.

"""
def __getstate__(self):
""" Get the pickle state for the dock item.

This method saves the necessary state for the dock items used
in this example. Different applications will have different
state saving requirements.

The default __setstate__ method provided on the Atom base class
provides sufficient unpickling behavior.

"""
return {'name': self.name, 'title': self.title}


class PickableDockArea(DockArea):
""" A custom pickable dock area class.

"""
def get_save_items(self):
""" Get the list of dock items to save with this dock area.

"""
return [c for c in self.children if isinstance(c, PickableDockItem)]

def __getstate__(self):
""" Get the pickle state for the dock area.

This method saves the necessary state for the dock area used
in this example. Different applications will have different
state saving requirements.

"""
state = {
'name': self.name,
'layout': self.save_layout(),
'items': self.get_save_items(),
}
return state

def __setstate__(self, state):
""" Restore the state of the dock area.

"""
self.name = state['name']
self.layout = state['layout']
self.insert_children(None, state['items'])


enamldef Pane(PickableDockItem):
""" A pickable dock item with default Html content.

"""
Container:
Html:
source = '<h1><center>%s</center></h1>' % title


def create_new_area():
""" Create a new pickable dock area with two child panes.

"""
area = PickableDockArea(name='the_dock_area')
Pane(area, name='first', title='Pane 0')
Pane(area, name='second', title='Pane 1')
area.layout = HSplitLayout('first', 'second')
return area


def new_item(event):
""" The handler for the 'sample.persistent.new_item' command.

"""
ui = event.workbench.get_plugin('enaml.workbench.ui')
area = ui.workspace.content.find('the_dock_area')
count = len(area.dock_items())
name = '_pane_%d' % count
title = 'Pane %d' % count
item = Pane(area, name=name, title=title)
area.update_layout(InsertItem(item=item.name))


def destroy_items(event):
""" The handler for the 'sample.persistent.destroy_items' command.

"""
ui = event.workbench.get_plugin('enaml.workbench.ui')
area = ui.workspace.content.find('the_dock_area')
for item in area.dock_items():
item.destroy()


enamldef PersistentManifest(PluginManifest):
""" The manifest which is registered when the view is loaded.

This manifest contributes extra menu items to the menu bar and
new commands for manipulating the dock area items.

"""
id = 'sample.persistent'
Extension:
id = 'commands'
point = 'enaml.workbench.core.commands'
Command:
id = 'sample.persistent.new_item'
handler = new_item
Command:
id = 'sample.persistent.destroy_items'
handler = destroy_items
Extension:
id = 'actions'
point = 'enaml.workbench.ui.actions'
MenuItem:
path = '/debug'
label = 'Debug'
after = 'file'
MenuItem:
path = '/options'
label = 'Options'
MenuItem:
path = '/tools'
label = 'Tools'
before = 'workspace'
ActionItem:
path = '/debug/something'
label = 'Something'
ActionItem:
path = '/options/something_else'
label = 'Something Else'
ActionItem:
path = '/tools/destroy_items'
label = 'Destroy Items'
shortcut = 'Ctrl+X'
command = 'sample.persistent.destroy_items'
ActionItem:
path = '/file/new'
label = 'New'
shortcut = 'Ctrl+N'
group = 'user'
command = 'sample.persistent.new_item'
74 changes: 74 additions & 0 deletions examples/workbench/persistent_workspace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#------------------------------------------------------------------------------
# Copyright (c) 2013, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#------------------------------------------------------------------------------
import cPickle

from atom.api import Unicode

from enaml.widgets.api import Container
from enaml.workbench.ui.api import Workspace

import enaml
with enaml.imports():
from persistent_view import PersistentManifest, create_new_area


print 'Imported Persistent Workspace!'


#: Storage for the pickled dock area. This would be saved
#: to some persistent storage media in a real application.
PICKLED_DOCK_AREA = None


class PersistentWorkspace(Workspace):
""" A custom Workspace class for the crash course example.
"""
#: Storage for the plugin manifest's id.
_manifest_id = Unicode()

def start(self):
""" Start the workspace instance.
This method will create the container content and register the
provided plugin with the workbench.
"""
self.content = Container(padding=0)
self.load_area()
manifest = PersistentManifest()
self._manifest_id = manifest.id
self.workbench.register(manifest)

def stop(self):
""" Stop the workspace instance.
This method will unregister the workspace's plugin that was
registered on start.
"""
self.save_area()
self.workbench.unregister(self._manifest_id)

def save_area(self):
""" Save the dock area for the workspace.
"""
global PICKLED_DOCK_AREA
area = self.content.find('the_dock_area')
PICKLED_DOCK_AREA = cPickle.dumps(area, -1)

def load_area(self):
""" Load the dock area into the workspace content.
"""
if PICKLED_DOCK_AREA is not None:
area = cPickle.loads(PICKLED_DOCK_AREA)
else:
area = create_new_area()
area.set_parent(self.content)
25 changes: 9 additions & 16 deletions examples/workbench/sample_plugin.enaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,10 @@ def second_view_factory(workbench):
return space


def third_view_factory(workbench):
from sample_workspace import SampleWorkspace

import enaml
with enaml.imports():
from third_view import ThirdView, ThirdManifest

space = SampleWorkspace()
space.window_title = 'Third View'
space.content_def = ThirdView
space.manifest_def = ThirdManifest
def persistent_view_factory(workbench):
from persistent_workspace import PersistentWorkspace
space = PersistentWorkspace()
space.window_title = 'Persistent View'
return space


Expand Down Expand Up @@ -98,12 +91,12 @@ enamldef SampleManifest(PluginManifest):
command = 'enaml.workbench.ui.select_workspace'
parameters = {'workspace': 'sample.second_view'}
ActionItem:
path = '/workspace/third'
label = 'Third'
path = '/workspace/persistent'
label = 'Persistent'
shortcut = 'Ctrl+3'
group = 'spaces'
command = 'enaml.workbench.ui.select_workspace'
parameters = {'workspace': 'sample.third_view'}
parameters = {'workspace': 'sample.persistent_view'}
ActionItem:
path = '/workspace/close'
label = 'Close Workspace'
Expand All @@ -118,6 +111,6 @@ enamldef SampleManifest(PluginManifest):
point = 'enaml.workbench.ui.workspaces'
factory = second_view_factory
Extension:
id = 'third_view'
id = 'persistent_view'
point = 'enaml.workbench.ui.workspaces'
factory = third_view_factory
factory = persistent_view_factory
Loading

0 comments on commit db99d02

Please sign in to comment.