De-activating plugin

My python macro creates a plugin via the architecture suggested on this page:

class MyPlugin(pya.Plugin):
  def __init__(self):
    super(MyPlugin, self).__init__()

  def activated(self):
    # do a whole bunch of beautiful things
    # including spawning dialogs with 'finish'
    # buttons
    pass


class MyPluginFactory(pya.PluginFactory):
  def __init__(self):
    super(MyPluginFactory, self).__init__()
    toolbar_btn = self.register(-1000, "my_plugin", "My Plugin")

  def create_plugin(self, manager, root, view):
    return MyPlugin()


MyPluginFactory()

When I run this macro, a new button is added to my window toolbar. When this toolbar button is clicked, it stays pressed, runs activated(), and launches my dialogs. That's all good!

Alas when my user is finished and closes the dialogs, the plugin's toolbar button stays pressed. It undesirably cannot be clicked again. I need some way to programmatically deactivate the plugin so that its toolbar button becomes un-pressed, but remains in the toolbar, available to be clicked again and re-trigger a MyPlugin() instantiation.

The most relevant plugin method seems to be _destroy(), but only kills/corrupts the state of the plugin, and I expect isn't intended to be used for this purpose. So; what can I call?

Comments

  • Was lucky enough to accidentally find the answer in this thread! I simply call

    pya.MainWindow.instance().menu().action("@toolbar.select").trigger()
    

    to select another toolbar menu (the "select" tool incidentally), which un-presses my plugin's toolbar button, and triggers the MyPlugin class's deactivated() method.

  • Hi TysonJones,

    Not quit sure what is the correct way to exit the plug in,
    the way I exit the plugin is sending a esc key command by script.

        def deactive(self):
            esc_key  = 16777216 
            keyPress = pya.QKeyEvent(pya.QKeyEvent.KeyPress, esc_key, pya.Qt.NoModifier)
            pya.QApplication.sendEvent(self.view.widget(), keyPress) 
    
  • Exiting via triggering an Esc press seems fine too.

    I have a new issue with both methods however. Sometimes the conditions for my plugin have not been met (a user has not pre-selected polygons, for example) and the activated() function of MyPlugin above must "exit". That is, in lieu of displaying dialogs, it triggers plugin deactivation immediately, for instance via...

    def deactivate_plugin():
        pya.MainWindow.instance().menu().action("@toolbar.select").trigger()
    
    class MyPlugin(pya.Plugin):
      # ...
    
      def activated(self):
        if not conditions_met:
            deactivate_plugin()
    

    The above code leaves kLayout in a bugged state where the My Plugin toolbar button remains pressed, and cannot be pressed again. In this state, hitting the Esc key also does nothing. The user must manually select another toolbar tool to deactivate the plugin.

    I figured this is because the plugin has not had "sufficient time" after activated() to load completely before the toolbar trigger. So, I try to delay the deactivation via QTimer. Something as simple as using Qt's doc'd singleShot...

    pya.QTimer.singleShot(1, self, deactivate_plugin)
    

    throws an error 'getset_descriptor' object has no attribute 'instance', because singleShot has been made a python property as per here.

    So, I instead attempt to verbosely create a single-shot timer, via:

    timer = pya.QTimer()
    timer.setSingleShot(True)
    timer.timeout.connect(deactivate_plugin)
    timer.start(1)
    

    where the Qt connect method of timer.timeout is documented in many places, like here.
    Alas this also fails with error 'pya._Signal' object has no attribute 'connect'.

    The kLayout-specific QTimer doc says that "The object exposes a writable attribute 'timeout'. This is the setter." Alas, changing the line to

    timer.timeout = deactivate_plugin
    

    still doesn't work; now, nothing happens, and deactivate_plugin() is never called after timer.start().

    This is a horrible rabbit hole to tumble down in order to workaround a bugged implementation of what I expect to be a common kLayout macro use-case; an early-exit after input validation. What am I doing wrong?

  • edited September 2023

    Hi TysonJones,

    I ran into similar issue, I like the user to pre-select some shapes for further process,
    but failed ti find a way to exit directly using normal method.

    My work around is to send a esc by script in mouse_moved_event, so once the cursor moves into the layout region it'll do the check and determine whether the script should proceed or exit.

    def mouse_moved_event(self, p, buttons, prio):
        if prio:
    
            if  withSelected:
                # do stuff here
    
            else:
                tip = "Select Shapes before activate plugin"
                tooltip = pya.QToolTip()
                tooltip.showText( pya.QCursor.pos, f'\n\t{tip}\t\t\n')
                self.deactive()
    
            return True
        return False
    

    This shows a hint to remind user to select something before activate the plugin.
    after showing the tip at cursor, exit by calling self.deactive() and send the esc key using code in previous post

  • Dear all,

    thanks for this discussion!

    I need to say I do not fully understand the initial request. "activated" is a callback that is not meant to triggering dialogs. It just says someone triggered this mode.

    If you simply need a button in the toolbar that does something fancy, you do not need the Plugin API. This API is intended for creating interactive tools acting on the canvas. This will also create a new button in the toolbar:

    import pya
    
    def pushed():
      pya.MessageBox.warning("Warning", "Outch!", pya.MessageBox.Ok)  
    
    action = pya.Action()
    action.title = "Push me!"
    action.on_triggered = pushed
    
    mw = pya.MainWindow.instance()
    mw.menu().insert_item("@toolbar.end", "new_action", action)
    

    Matthias

Sign In or Register to comment.