Adding Undo/Redo for script-based actions

edited November 2015 in Ruby Scripting

Hello,

Say I have a script that adds a shape, and another script the deletes some shapes.

Is there a way to enable Undo/Redo functionality? i.e., can I register my modifications to the layout into some undo/redo queue?

Presently, when I click on Undo, it undoes whatever happened before I ran the script, which really isn't the desired behaviour.

Thank you

Lukas

Comments

  • edited November -1

    Pasting from E4K's "Draw a box, with undo.lym". You'll have to translate to python.

    ## Draw a box, with undo.lym
    ## By davidnhutch
    ## 
    ## Same as Draw a box.lym, but condenses the code and wraps it in something that allows the user to do Edit > Undo.
    
    module DrawBoxWithUndo
    
      include RBA
      app = Application.instance
      mw = app.main_window
      lv = mw.current_view
      lv || raise("No view selected")
    
      lv.transaction("Draw a box, with undo")
    
      begin
        ly = lv.active_cellview.layout
        dbu = ly.dbu
    
        cell = lv.active_cellview.cell
    
        layer_number = 1
        datatype = 0
        layer_info = LayerInfo.new(layer_number,datatype)
        layer_index = ly.layer(layer_info)
    
        width  = 200
        height = 100 
        left   = -width / 2.0 
        bottom = -height / 2.0
        right  = width / 2.0
        top    = height / 2.0
    
        box = Box.new(left/dbu,bottom/dbu,right/dbu,top/dbu)
    
        cell.shapes(layer_index).insert(box)
    
        lv.add_missing_layers
        lv.zoom_fit
    
        msg = "Inserted a box with coordinates left = #{left}, bottom = #{bottom}, right = #{right}, top = #{top} microns)"
        puts msg
    
      ensure
        lv.commit # Commit the transaction, so we can undo it if needed
      end
    
    end
    

    Sorry to hijack the thread.. but Matthias: I observed a strange behavior related to redraw:

    If I put this line after puts msg:

    MessageBox.info("Done!",msg,MessageBox::Ok)
    

    Then the box does get created but isn't visible on the screen until you zoom or pan. Because the MessageBox is hijacking the redraw I suppose. Can we get around this somehow -- e.g. by calling something like a redraw method somewhere?

    Thanks,
    David

  • edited November -1

    Thank you David.

    I confirm that undo/redo works. However, I also confirm the bug you found.

    Another thing I found is that if you don't call "commit", you have a problem:

    • The layout screen goes empty, and I can't see the box. The Cell list also is empty. I can't get it to display, even with zooming, panning. I can select the box, but the layer colour is gone. The only thing that fixes it is closing the layout and starting over (or issuing a commit).

    Here's the code:

    import pya
    
    # Configure variables to draw structures in the presently selected cell:
    lv = pya.Application.instance().main_window().current_view()
    if lv == None:
      raise Exception("No view selected")
    # Find the currently selected layout.
    ly = pya.Application.instance().main_window().current_view().active_cellview().layout() 
    if ly == None:
      raise Exception("No layout")
    # find the currently selected cell:
    cell = pya.Application.instance().main_window().current_view().active_cellview().cell
    if cell == None:
      raise Exception("No cell")
    # fetch the database parameters
    
    SiLayer = pya.LayerInfo(1, 0)
    SiLayerN = ly.layer(SiLayer)
    
    # Record a transaction, to enable "undo"
    lv.transaction("Script undo test")
    
    box = pya.Box(0,0,1000,1000)
    cell.shapes(SiLayerN).insert(box)
    
    lv.add_missing_layers()
    
    #pya.MessageBox.info("Done!","new box", pya.MessageBox.Ok)
    
    # Record a transaction, to enable "undo"
    lv.commit()
    
  • edited November -1

    Hi all,

    thanks for mention these topics.

    Basically the layout will be in "under construction" mode until you commit the transaction. The reasoning behind this mode is that during a sequence of operations the layout object may be in some invalid state: for example if you delete a cell before you delete the instances of the cell then you have a temporary state where cell instances may point to non-existing cells.

    The idea of "commit" is both to release the layout from this state and also to mark the end of a sequence of operations which start with a consistent state and end with one. It's important to call "commit" to leave the state - that's why it's in the "ensure" branch in Ruby and will always be executed this way. The equivalent in Python is "finally".

    Apparently opening a message box confuses the program since that will enter the event loop while the layout is still in "under construction" mode. The modifications on the layout have already requested a redraw (triggered by the event loop), but the drawing engine will reject drawing of "under construction" layouts in order not to compromise stability. I think you can easily work around this issue by putting the message box after the "commit".

    Matthias

Sign In or Register to comment.