-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Copy Undo over from apptools #813
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d3f9133
copy Undo over from apptools
aaronayres35 024e55a
copy over undo examples from apptools
aaronayres35 e6199b3
copy over undo documentation from apptools
aaronayres35 11e3f4b
update copyright headers
aaronayres35 025fbc5
follow convention of instantiating traits for trait definitions
aaronayres35 61ffe41
add comment about circular dependency workaround
aaronayres35 eba2f19
add #: to trait definitions comments so traits come up with autogener…
aaronayres35 244c6be
remove unneeded redundant portion of docs mentioning API overview (th…
aaronayres35 8654a7b
getting the undo example working
aaronayres35 3e16b61
undoing an unneeded change
aaronayres35 8fcedc5
adding more tests
aaronayres35 a35c2f7
add some simple tests for undo.actions
aaronayres35 76bdcc0
Apply suggestions from code review
aaronayres35 012e27f
another suggestion from code review
aaronayres35 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,4 @@ Submodules | |
Data View <data_view> | ||
Fields <fields> | ||
Timers <timer> | ||
Undo <undo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
Undo Framework | ||
============== | ||
|
||
The Undo Framework is a component of the Enthought Tool Suite that provides | ||
developers with an API that implements the standard pattern for do/undo/redo | ||
commands. | ||
|
||
The framework is completely configurable. Alternate implementations of all | ||
major components can be provided if necessary. | ||
|
||
|
||
Framework Concepts | ||
------------------ | ||
|
||
The following are the concepts supported by the framework. | ||
|
||
- Command | ||
|
||
A command is an application defined operation that can be done (i.e. | ||
executed), undone (i.e. reverted) and redone (i.e. repeated). | ||
|
||
A command operates on some data and maintains sufficient state to allow it to | ||
revert or repeat a change to the data. | ||
|
||
Commands may be merged so that potentially long sequences of similar | ||
commands (e.g. to add a character to some text) can be collapsed into a | ||
single command (e.g. to add a word to some text). | ||
|
||
- Macro | ||
|
||
A macro is a sequence of commands that is treated as a single command when | ||
being undone or redone. | ||
|
||
- Command Stack | ||
|
||
A command is done by pushing it onto a command stack. The last command can | ||
be undone and redone by calling appropriate command stack methods. It is | ||
also possible to move the stack's position to any point and the command stack | ||
will ensure that commands are undone or redone as required. | ||
|
||
A command stack maintains a *clean* state which is updated as commands are | ||
done and undone. It may be explicitly set, for example when the data being | ||
manipulated by the commands is saved to disk. | ||
|
||
PyFace actions are provided as wrappers around command stack methods | ||
to implement common menu items. | ||
|
||
- Undo Manager | ||
|
||
An undo manager is responsible for one or more command stacks and maintains | ||
a reference to the currently active stack. It provides convenience undo and | ||
redo methods that operate on the currently active stack. | ||
|
||
An undo manager ensures that each command execution is allocated a unique | ||
sequence number, irrespective of which command stack it is pushed to. Using | ||
this it is possible to synchronise multiple command stacks and restore them | ||
to a particular point in time. | ||
|
||
An undo manager will generate an event whenever the clean state of the active | ||
stack changes. This can be used to maintain some sort of GUI status | ||
indicator to tell the user that their data has been modified since it was | ||
last saved. | ||
|
||
Typically an application will have one undo manager and one undo stack for | ||
each data type that can be edited. However this is not a requirement: how the | ||
command stack's in particular are organised and linked (with the user | ||
manager's sequence number) can need careful thought so as not to confuse the | ||
user - particularly in a plugin based application that may have many editors. | ||
|
||
To support this typical usage the PyFace ``Workbench`` class has an | ||
``undo_manager`` trait and the PyFace ``Editor`` class has a ``command_stack`` | ||
trait. Both are lazy loaded so can be completely ignored if they are not used. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
# (C) Copyright 2007-2020 Enthought, Inc., Austin, TX | ||
# All rights reserved. | ||
# | ||
# This software is provided without warranty under the terms of the BSD | ||
# license included in LICENSE.txt and may be redistributed only under | ||
# the conditions described in the aforementioned license. The license | ||
# is also available online at http://www.enthought.com/licenses/BSD.txt | ||
# | ||
# Thanks for using Enthought open source! | ||
|
||
# ----------------------------------------------------------------------------- | ||
# Copyright (c) 2007, Riverbank Computing Limited | ||
# All rights reserved. | ||
# | ||
# This software is provided without warranty under the terms of the BSD | ||
# license included in enthought/LICENSE.txt and may be redistributed only | ||
# under the conditions described in the aforementioned license. The license | ||
# is also available online at http://www.enthought.com/licenses/BSD.txt | ||
# Thanks for using Enthought open source! | ||
# | ||
# Author: Riverbank Computing Limited | ||
# Description: <Enthought undo package component> | ||
# ----------------------------------------------------------------------------- | ||
|
||
|
||
# Enthought library imports. | ||
from traits.api import Instance, Int, Str | ||
from pyface.undo.api import AbstractCommand | ||
|
||
# Local imports. | ||
from model import Label | ||
|
||
|
||
class LabelIncrementSizeCommand(AbstractCommand): | ||
""" The LabelIncrementSizeCommand class is a command that increases the | ||
size of a label's text. This command will merge multiple increments | ||
togther. | ||
""" | ||
|
||
#### 'ICommand' interface ################################################# | ||
|
||
# The data being operated on. | ||
data = Instance(Label) | ||
|
||
# The name of the command. | ||
name = Str("&Increment size") | ||
|
||
#### Private interface #################################################### | ||
|
||
_incremented_by = Int() | ||
|
||
########################################################################### | ||
# 'ICommand' interface. | ||
########################################################################### | ||
|
||
def do(self): | ||
self.data.increment_size(1) | ||
self._incremented_by = 1 | ||
|
||
def merge(self, other): | ||
# We can merge if the other command is the same type (or a sub-type). | ||
if isinstance(other, type(self)): | ||
self._incremented_by += 1 | ||
merged = True | ||
else: | ||
merged = False | ||
|
||
return merged | ||
|
||
def redo(self): | ||
self.data.increment_size(self._incremented_by) | ||
|
||
def undo(self): | ||
self.data.decrement_size(self._incremented_by) | ||
|
||
|
||
class LabelDecrementSizeCommand(AbstractCommand): | ||
""" The LabelDecrementSizeCommand class is a command that decreases the | ||
size of a label's text. This command will merge multiple decrements | ||
togther. | ||
""" | ||
|
||
#### 'ICommand' interface ################################################# | ||
|
||
# The data being operated on. | ||
data = Instance(Label) | ||
|
||
# The name of the command. | ||
name = Str("&Decrement size") | ||
|
||
#### Private interface #################################################### | ||
|
||
_decremented_by = Int() | ||
|
||
########################################################################### | ||
# 'ICommand' interface. | ||
########################################################################### | ||
|
||
def do(self): | ||
self.data.decrement_size(1) | ||
self._decremented_by = 1 | ||
|
||
def merge(self, other): | ||
# We can merge if the other command is the same type (or a sub-type). | ||
if isinstance(other, type(self)): | ||
self._decremented_by += 1 | ||
merged = True | ||
else: | ||
merged = False | ||
|
||
return merged | ||
|
||
def redo(self): | ||
self.data.decrement_size(self._decremented_by) | ||
|
||
def undo(self): | ||
self.data.increment_size(self._decremented_by) | ||
|
||
|
||
class LabelNormalFontCommand(AbstractCommand): | ||
""" The LabelNormalFontCommand class is a command that sets a normal font | ||
for a label's text. | ||
""" | ||
|
||
#### 'ICommand' interface ################################################# | ||
|
||
# The data being operated on. | ||
data = Instance(Label) | ||
|
||
# The name of the command. | ||
name = Str("&Normal font") | ||
|
||
########################################################################### | ||
# 'ICommand' interface. | ||
########################################################################### | ||
|
||
def do(self): | ||
# Save the old value. | ||
self._saved = self.data.style | ||
|
||
# Calling redo() is a convenient way to update the model now that the | ||
# old value is saved. | ||
self.redo() | ||
|
||
def redo(self): | ||
self.data.style = 'normal' | ||
|
||
def undo(self): | ||
self.data.style = self._saved | ||
|
||
|
||
class LabelBoldFontCommand(AbstractCommand): | ||
""" The LabelNormalFontCommand class is a command that sets a bold font for | ||
a label's text. | ||
""" | ||
|
||
#### 'ICommand' interface ############################################# | ||
|
||
# The data being operated on. | ||
data = Instance(Label) | ||
|
||
# The name of the command. | ||
name = Str("&Bold font") | ||
|
||
########################################################################### | ||
# 'ICommand' interface. | ||
########################################################################### | ||
|
||
def do(self): | ||
# Save the old value. | ||
self._saved = self.data.style | ||
|
||
# Calling redo() is a convenient way to update the model now that the | ||
# old value is saved. | ||
self.redo() | ||
|
||
def redo(self): | ||
self.data.style = 'bold' | ||
|
||
def undo(self): | ||
self.data.style = self._saved | ||
|
||
|
||
class LabelItalicFontCommand(AbstractCommand): | ||
""" The LabelNormalFontCommand class is a command that sets an italic font | ||
for a label's text. | ||
""" | ||
|
||
#### 'ICommand' interface ################################################# | ||
|
||
# The data being operated on. | ||
data = Instance(Label) | ||
|
||
# The name of the command. | ||
name = Str("&Italic font") | ||
|
||
########################################################################### | ||
# 'ICommand' interface. | ||
########################################################################### | ||
|
||
def do(self): | ||
# Save the old value. | ||
self._saved = self.data.style | ||
|
||
# Calling redo() is a convenient way to update the model now that the | ||
# old value is saved. | ||
self.redo() | ||
|
||
def redo(self): | ||
self.data.style = 'italic' | ||
|
||
def undo(self): | ||
self.data.style = self._saved |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# (C) Copyright 2008-2020 Enthought, Inc., Austin, TX | ||
# All rights reserved. | ||
# | ||
# This software is provided without warranty under the terms of the BSD | ||
# license included in LICENSE.txt and may be redistributed only under | ||
# the conditions described in the aforementioned license. The license | ||
# is also available online at http://www.enthought.com/licenses/BSD.txt | ||
# | ||
# Thanks for using Enthought open source! | ||
|
||
# ----------------------------------------------------------------------------- | ||
# Copyright (c) 2008, Riverbank Computing Limited | ||
# All rights reserved. | ||
# | ||
# This software is provided without warranty under the terms of the BSD | ||
# license included in enthought/LICENSE.txt and may be redistributed only | ||
# under the conditions described in the aforementioned license. The license | ||
# is also available online at http://www.enthought.com/licenses/BSD.txt | ||
# Thanks for using Enthought open source! | ||
# | ||
# Author: Riverbank Computing Limited | ||
# Description: <Enthought undo package component> | ||
# ----------------------------------------------------------------------------- | ||
|
||
|
||
# Standard library imports. | ||
import logging | ||
|
||
# Enthought library imports. | ||
from pyface.api import GUI, YES | ||
from pyface.workbench.api import Workbench | ||
from pyface.undo.api import UndoManager | ||
|
||
# Local imports. | ||
from example_undo_window import ExampleUndoWindow | ||
from model import Label | ||
|
||
|
||
# Log to stderr. | ||
logging.getLogger().addHandler(logging.StreamHandler()) | ||
logging.getLogger().setLevel(logging.DEBUG) | ||
|
||
|
||
class ExampleUndo(Workbench): | ||
""" The ExampleUndo class is a workbench that creates ExampleUndoWindow | ||
windows. | ||
""" | ||
|
||
#### 'Workbench' interface ################################################ | ||
|
||
# The factory (in this case simply a class) that is used to create | ||
# workbench windows. | ||
window_factory = ExampleUndoWindow | ||
|
||
########################################################################### | ||
# Private interface. | ||
########################################################################### | ||
|
||
def _exiting_changed(self, event): | ||
""" Called when the workbench is exiting. """ | ||
|
||
if self.active_window.confirm('Ok to exit?') != YES: | ||
event.veto = True | ||
|
||
return | ||
|
||
|
||
def main(argv): | ||
""" A simple example of using the the undo framework in a workbench. """ | ||
|
||
# Create the GUI. | ||
gui = GUI() | ||
|
||
# Create the workbench. | ||
workbench = ExampleUndo(state_location=gui.state_location) | ||
|
||
window = workbench.create_window(position=(300, 300), size=(400, 300)) | ||
window.open() | ||
|
||
# Create some objects to edit. | ||
label = Label(text="Label") | ||
label2 = Label(text="Label2") | ||
|
||
# Edit the objects. | ||
window.edit(label) | ||
window.edit(label2) | ||
|
||
# Start the GUI event loop. | ||
gui.start_event_loop() | ||
|
||
return | ||
|
||
|
||
if __name__ == '__main__': | ||
import sys | ||
main(sys.argv) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you open an issue regd porting the undo example code from pyface workbench to pyface tasks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened #821