It looks like you're new here. If you want to get involved, click one of these buttons!
Hello,
I'm attempting to create a plugin (in Klayout 0.30.5) that loads a second layout if some conditions are triggered, and want to be able to use the plugin there. I'm having a bit of trouble understanding the structure of plugins and when the mouse_button_pressed_event etc. are actually called.
Here's the relevant skeleton of my code:
# $description: test
# $show-in-menu
# $menu-path: tools_menu>end("Test").end
import pya as kl
class PluginTestFactory(kl.PluginFactory):
def __init__(self):
self.add_submenu("menu_name", "inset_pos", "title")
self.register(29318, "plugin_name", "test_plugin")
def create_plugin(self, manager, dispatcher, view):
return PluginTest(view)
class PluginTest(kl.Plugin):
def __init__(self,view):
self.curr_annot = kl.Annotation()
self.curr_annot.p1 = kl.DPoint(0,0)
self.curr_annot.p2 = kl.DPoint(0,0)
self.curr_annot.fmt = ""
self.curr_annot.fmt_x = ""
self.curr_annot.fmt_y = ""
self.curr_annot.outline = self.curr_annot.OutlineBox
self.curr_annot.style = self.curr_annot.StyleLine
view.insert_annotation(self.curr_annot)
def mouse_button_pressed_event(self, p, buttons, prio):
print("hello")
def mouse_button_released_event(self, p, buttons, prio):
self.match()
def mouse_moved_event(self, p, buttons, prio):
self.curr_annot.p2 = p
def match(self):
big_menu = kl.QDialog()
big_menu.layout = kl.QGridLayout()
containing_layout = kl.QWidget()
self.layoutwidg = kl.LayoutViewWidget(containing_layout)
cv = self.layoutwidg.view().create_layout(True)
viewing_layout = self.layoutwidg.view().cellview(cv).layout()
big_menu.layout.addWidget(self.layoutwidg,0,0,1,1)
big_menu.show()
if __name__ == "__main__":
PluginTestFactory()
When I click on the Tools>test menu button, a "test_plugin" button appears on the toolbar. The annotation appears and follows my mouse around before pressing the test_plugin button; I expected the annotation to appear, but the fact that it follows my mouse around implies that the mouse_moved_event is being called implicitly.
When I click the button in the toolbar, the "Basic Editing" pane pops up (why does that happen, by the way? It's annoying.), but no other effects occur; clicking and dragging and releasing does nothing. But when I open the debugger, I can put a breakpoint on the mouse_button_pressed_event and the mouse_button_released_event lines and it stops at both of them. If I step past the self.match() call, nothing else happens.
But if I step into the self.match() call and let it run, it opens up the QDialog I asked for. Within this QDialog, it appears that the mouse_button_pressed_event is completely inactive, whereas the mouse_button_released_event and mouse_moved_event calls are still active (in the sense that it stops at a breakpoint in the mouse_button_released_event function).
This is all very confusing to me, and I would like to understand what's going on. Is there some sort of infinite recursion being headed off by avoiding the self.match() call when the debugger breakpoint is absent, and is that why it isn't run?
The simplest question that would move me forward is: How do I force the mouse_button_pressed_event to be recognized in the layout in this pop-up window?
Thanks so much!
Noah
Comments
Hi Noah,
did you check the documentation?
https://www.klayout.de/doc-qt5/programming/application_api.html#k_11 explains the concept of the mouse events. Specifically it says:
Here is more example code: https://www.klayout.de/doc-qt5/code/class_PluginFactory.html
There are packages implementing Plugins, for example this one: https://github.com/s910324/SplitShapePlugin. You can study that one for example. It is well made.
Matthias
Thanks for your reply! My two questions are:
1. It says in the documentation that
However, when I open this new LayoutView in the pop-up window, there is no tool bar button to press, so the plugin never gets activated (and the Select tool appears to always be activated). If I re-click on the button in the main window, it doesn't seem to activate the plugin in the pop-up window. And if I manually call
self.activated, nothing seems to happen. So I'm asking how to activate the plugin, either manually or through code, in this new LayoutView.2. It seems that in this case, the other mouse events are working as intended, but I cannot seem to get
mouse_button_pressed_eventto be called withprio=False. I saw at this link that there had been trouble in the past with mouse_button_pressed_event not working perfectly, so I'm concerned there might be a bug in its implementation, even though that link says it's been fixed.I am sorry because these seem like basic questions, but it feels like what I'm seeing is counter to the documentation. Thanks so much for your help!
(P.S. I'm sorry I posted this in the Verification category; I could not figure out how to either delete the topic or change the category after it was posted.)
Noah
So then let's start with a minimum example:
If I run this example, I get a new button in the tool bar, named "test_plugin". That is the name, the plugin got in the "register" call (third parameter).
While that function is not active, I receive calls to "mouse_button_moved" with prio = False. These are the background events that are sent to the plugin with low priority and because no other plugin claims them. If I activate the function by pressing the "test_plugin" function, I receive these events with prio = True. Unlike other plugins, mine claims these events by returning True from the "mouse_moved_event" callback. Because of these, these events are not available to other plugins and this is why for example the cursor position display freezes when the plugin is activated. Usually it's best not to claim the events and to return False.
The same is true, when you click at one point - in that case, "mouse_click_event" calls are issued.
Now to the "mouse_button_pressed_event" and "mouse_button_released_event": unlike Qt events for example, these events get issued only alternatively to the click event. A click event is issued, when you press the mouse and release the button on the same location. If you move the mouse in between pressing and releasing (a drag operation), you will receive a mouse button press event, some move events and then a mouse button release event. Since the application can't know what is going to happen, the mouse button press event is delayed into the first move operation.
That concept is basis for all mouse-driven features inside KLayout and supports "click-move-click" (pick and place) and "press-move-release" (drag) scenarios. But you cannot get an immediate event on mouse button press, if that is what you are asking for.
Matthias
Hello Matthias,
Again, thanks for your response and your helpful code. Can you verify the following for me in this plugin?
If this is the case, I think there is a bug that might need attending to.
No, that is not how it is intended.
When the "test_plugin" is not activated, there is no mouse_click_event with prio=False because the active plugin (Select) claims that event.
When the "test_plugin" is not activated, there is a mouse_button_pressed_event with prio=False, but only after you moved the mouse a little, so that it is considered a drag operation. The active plugin (Select) is not claiming that event, so it is delivered to the test plugin with prio=False. Pressing the button, moving the mouse, and releasing the button gives a sequence of events: mouse_button_pressed_event, mouse_moved_event (n times), mouse_button_released_event, all with prio=False.
When the "test_plugin" is activated, when clicking a button, you receive a mouse_click_event with prio=True. When pressing the button and moving the mouse, you get a mouse_button_pressed_event, then mouse_moved_events and finally a mouse_button_released_event with prio=True, but only after you moved the mouse a little, so the events can be distinguished from a single click.
Matthias
Hello Matthias,
I'm so sorry, I see what's happening now. So after running the script but before clicking the plugin button, Select is not claiming any of _clicked, _pressed, _released or _moved. But after clicking the plugin button and then re-clicking Select, it claims _clicked and _pressed, but still not _released or _moved. Is this correct? And that's why it's printing _released and _moved? Interesting that it doesn't claim _released.
Well, Select claims "clicked" and "pressed". Both because that is part of the functionality of "Select". A single click will select the object below the click, a "pressed" event will start dragging a selection rectangle. For some reason, the Select tool does not claim "moved" and "released" while it drags, but the main intention is to claim the two activation events, so it can do it's job in peace.
Don't trust the events which you get with "prio=False". As a rule of thumb, "moved" should be available always, but it depends on the selected tool, if you receive activation events such as a mouse click. The main goal is to prevent duplicate actions on a single click.
Only if the tool is active, you are first class citizen in the event system. Your tool will receive all events with "prio=True" and can decide if it claims them (return True) or allows other tools to see them with "prio=False" by returning False.
KLayout is basically built around ~20 plugins that all use that scheme. The mouse tracker you see at the bottom is a plugin that simply listens to mouse move events with prio=False for example. The mouse tracker is also responsible for displaying a crosshair cursor if you enabled it.
The zoom feature is a plugin that listens to mouse wheel events and mouse pressed events with prio=False to start dragging the zoom box. Because the Select tool claims only pressed events with the left mouse button, the zoom box will see right-click events with prio=False and can start dragging a zoom box. Because the zoom box claims mouse move events in that case and because the mouse tracker is after the zoom box in the plugin order, the mouse tracker and the crosshair cursor do not change while dragging a zoom box.
The box drawing tool is a plugin that listens to click events with prio=True and starts a box drawing operation then. Some plugins do not handle mouse events at all, but only register menu items (like "Tools/Browse Shapes").
So you see there is a ecosystem inhabited by a number of animals that collaborate using a simple standard protocol. Just make sure your plugin fits into that.
Matthias