Skip to content

Commit faf695c

Browse files
author
Angel
committed
Implemented an undo stack using the QUndoStack framework. Undo/redo actions only affect ROI deletion or ROI shape updates. Added classes for these commands to establish how to handle undo and redo calls.
1 parent 0bf9389 commit faf695c

File tree

1 file changed

+86
-16
lines changed

1 file changed

+86
-16
lines changed

roibuddy/roi_buddy.py

Lines changed: 86 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,70 @@ def handle_final_shape(self, shape):
189189
super(EllipseTool, self).handle_final_shape(shape)
190190

191191

192+
class DeleteCmd(QUndoCommand):
193+
"""Instance of revertable delete command."""
194+
195+
def __init__(self, plot, mode, items_to_remove):
196+
"""Initialize the delete command."""
197+
super(DeleteCmd, self).__init__()
198+
self.plot = plot
199+
self.mode = mode
200+
self.items_to_remove = items_to_remove
201+
202+
def redo(self):
203+
"""Delete the selected items."""
204+
self.plot.unselect_all()
205+
if self.mode == 'align':
206+
for item in self.items_to_remove:
207+
item.parent.roi_list.remove(item)
208+
self.plot.del_items(self.items_to_remove)
209+
self.plot.replot()
210+
211+
def undo(self):
212+
"""Add the previously removed items back."""
213+
if self.mode == 'align':
214+
for item in self.items_to_remove:
215+
item.parent.roi_list.append(item)
216+
for item in self.items_to_remove:
217+
self.plot.add_item(item)
218+
self.plot.select_some_items(self.items_to_remove)
219+
self.plot.replot()
220+
221+
222+
class UpdateROIShapeCmd(QUndoCommand):
223+
"""Instance of revertable ROI shape update command."""
224+
225+
def __init__(self, plot, roi_to_update, new_points):
226+
"""Initialize the update_roi command."""
227+
super(UpdateROIShapeCmd, self).__init__()
228+
self.plot = plot
229+
self.roi_to_update = roi_to_update
230+
self.new_points = new_points
231+
self.old_points = self.roi_to_update.get_points()
232+
233+
def redo(self):
234+
"""Update the ROI object's points to the new values."""
235+
self.roi_to_update.set_points(self.new_points)
236+
self.plot.replot()
237+
238+
def undo(self):
239+
"""Revert the ROI object's points to the original values."""
240+
self.roi_to_update.set_points(self.old_points)
241+
self.plot.replot()
242+
243+
192244
class RoiBuddy(QMainWindow, Ui_ROI_Buddy):
193245
"""Instance of the ROI Buddy Qt interface."""
194-
def __init__(self):
195-
"""
196-
Initialize the application
197-
"""
198246

247+
def __init__(self):
248+
"""Initialize the application."""
199249
# initialize the UI and parent class
200250
QMainWindow.__init__(self)
201251
self.setupUi(self)
202252
self.setWindowTitle('ROI Buddy')
203253

254+
self.undoStack = QUndoStack(self)
255+
204256
# define actions for menu bar and ROI manager
205257
self.define_actions()
206258

@@ -296,6 +348,7 @@ def arrow_press_event(self, event):
296348
self.plot.replot()
297349

298350
def create_menu(self):
351+
"""Initialize the file menu."""
299352
self.file_menu = self.menuBar().addMenu("&File")
300353

301354
qthelpers.add_actions(self.file_menu, [self.add_tseries_action,
@@ -310,16 +363,20 @@ def create_menu(self):
310363
self.edit_tags_action,
311364
self.update_roi_action,
312365
None,
366+
self.undo_action,
367+
self.redo_action,
368+
None,
313369
self.merge_rois,
314370
self.unmerge_rois,
315371
None,
316372
self.quit_action])
317373

318374
def create_status_bar(self):
375+
"""Initialize the status bar."""
319376
self.statusBar().addWidget(QLabel(""), 1)
320377

321378
def define_actions(self):
322-
# File menu actions
379+
"""File menu actions."""
323380
self.quit_action = qthelpers.create_action(
324381
self, "&Quit", triggered=self.close, shortcut="Ctrl+Q",
325382
tip="Close ROI Buddy")
@@ -336,7 +393,8 @@ def define_actions(self):
336393
self, "&Save current ROIs",
337394
triggered=lambda: self.save([self.tSeries_list.currentItem()]),
338395
shortcut="Ctrl+S",
339-
icon=QIcon(os.path.join(icon_filepath,"document-save-5.png")),
396+
icon=QIcon(os.path.join(icon_filepath,
397+
"document-save-5.png")),
340398
tip="Save the current ROI set to file.")
341399

342400
self.save_all_action = qthelpers.create_action(
@@ -377,7 +435,6 @@ def define_actions(self):
377435
self.update_roi_action = qthelpers.create_action(
378436
self, "&Update", triggered=self.update_roi, shortcut="B",
379437
tip='Redraw the selected ROI with new ROI')
380-
# self.addAction(self.update_roi)
381438

382439
self.merge_rois = qthelpers.create_action(
383440
self, "&Merge", triggered=self.merge_ROIs, shortcut="M",
@@ -416,11 +473,22 @@ def define_actions(self):
416473
shortcut='Ctrl+A')
417474
self.addAction(self.select_all_action)
418475

476+
self.undo_action = qthelpers.create_action(
477+
self, "&Undo Action", triggered=self.undoStack.undo,
478+
shortcut="Z", tip="Undo deletion of an object.")
479+
self.addAction(self.undo_action)
480+
481+
self.redo_action = qthelpers.create_action(
482+
self, "&Redo Action", triggered=self.undoStack.redo,
483+
shortcut="Y", tip="Redo deletion of an object.")
484+
self.addAction(self.redo_action)
485+
419486
self.debug_action = qthelpers.create_action(
420487
self, "&Debug", triggered=debug_trace, shortcut="F10")
421488
self.addAction(self.debug_action)
422489

423490
def connectSignals(self):
491+
"""Connect events to their respective slots."""
424492

425493
# toggle the edit or align mdoe
426494
self.modeSelection.buttonClicked.connect(self.toggle_mode)
@@ -660,12 +728,10 @@ def delete(self):
660728
items = self.plot.get_selected_items()
661729
# Don't delete the base image
662730
items = [item for item in items if not isinstance(item, ImageItem)]
663-
self.plot.unselect_all()
664-
if self.mode == 'align':
665-
for item in items:
666-
item.parent.roi_list.remove(item)
667-
self.plot.del_items(items)
668-
self.plot.replot()
731+
cmd = DeleteCmd(self.plot,
732+
self.mode,
733+
items)
734+
self.undoStack.push(cmd)
669735

670736
def activate_freeform_tool(self):
671737
if self.mode == 'edit':
@@ -1110,8 +1176,10 @@ def edit_tags(self):
11101176

11111177
def update_roi(self):
11121178
"""
1113-
Update the shape of an ROI with the shape of newly drawn ROI by
1114-
grabbing the shape of the new ROI drawn from the active drawing tool
1179+
Update the shape of an ROI.
1180+
1181+
The ROI's shape is set to the the shape of newly drawn ROI by
1182+
the active drawing tool.
11151183
"""
11161184
if self.mode is 'edit':
11171185
new_points = self.viewer.active_tool.shape.get_points()
@@ -1129,7 +1197,9 @@ def update_roi(self):
11291197
QMessageBox.Ok)
11301198
return
11311199
roi_to_update = roi_to_update[0]
1132-
roi_to_update.set_points(new_points)
1200+
cmd = UpdateROIShapeCmd(self.plot, roi_to_update,
1201+
new_points)
1202+
self.undoStack.push(cmd)
11331203
self.plot.del_item(self.viewer.active_tool.shape)
11341204
self.viewer.active_tool.reset()
11351205
self.plot.unselect_all()

0 commit comments

Comments
 (0)