Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

capture a mouse click and its location

Is it possible to have an example of code that capture a mouse click and its location (ideally in Python but ruby is also fine).

For instance: on click, print “You have clicked at the location X,Y of the design”

My main target is to make a small script that can ask you to click somewhere and set this new point as a new origin for the design. If it is a function that already exists, also please let me know.

Thanks a lot!

Comments

  • edited November 2018

    Hi Jonathan,

    you can capture mouse events in a "plugin". Below is a small piece of code illustrating how this works. Basically you create a plugin to implement a new mode. If selecting this mode by clicking on the respective tool bar entry, you can capture mouse events until the mode becomes inactive again.

    Plugins are slightly clumsy currently. For example, there is no method to activate a mode from your code. Hence, the only way to integrate your mode is through toolbar buttons. When I'm debugging the code, I have to execute the macro twice before the code becomes effective - I did not debug this yet as it's just a little bit annoying.

    And please note the workaround for bug 191.

    Matthias

    # Register this macro as "autorun" to enable the plugin
    
    # First thing is to implement a "plugin factory". This 
    # object is asked to provide a plugin for each view
    
    class SetOriginOnMouseClickPluginFactory(pya.PluginFactory):
    
      def __init__(self):
        super(SetOriginOnMouseClickPluginFactory, self).__init__()
        pya.MainWindow.instance()  # (workaround for bug 191)
        self.register(-1000, "set_origin", "Cell Origin")
    
      def create_plugin(self, manager, root, view):
        return SetOriginOnMouseClickPlugin()
    
      # Keeps the singleton instance
      instance = None
    
    # Create and store the singleton instance
    SetOriginOnMouseClickPluginFactory.instance = SetOriginOnMouseClickPluginFactory()
    
    
    # Second thing is the actual plugin
    
    class SetOriginOnMouseClickPlugin(pya.Plugin):
    
      def __init__(self):
        super(SetOriginOnMouseClickPlugin, self).__init__()
        pass
    
      def activated(self):
        pya.MainWindow.instance().message("Click on point to set the origin", 10000)
    
      def deactivated(self):
        pya.MainWindow.instance().message("", 0)
    
      def mouse_click_event(self, p, buttons, prio):
        # This is where everything happens
        if prio:
          print("Mouse clicked at " + str(p))
          return True
        return False
    
  • Hello Mathias, Thank you very much for this answer! and sorry for coming back on it so late.

    I would like to now share the program I now have:

    • the program does not need to be run twice if the classes are written in the order.
    • There are some points I am not so satisfied with and I wonder if there is a better way to do it:
      1- ideally I could snap the cursor to a given grid, is it visually possible?
      2- to move the layout I use layout transformation and zoom at the corrected location, is there maybe a more concise way?
      3- to release the tool I call the PluginFactory again, I made a thread which is also more general on this topic

    here is the program:

    class SetOriginOnMouseClickPlugin(pya.Plugin):
      def __init__(self):
        super(SetOriginOnMouseClickPlugin, self).__init__()
        pass
      def activated(self):
        self.grab_mouse()
        pya.MainWindow.instance().message("Click on point to set the origin", 10000)
      def deactivated(self):
        pya.MainWindow.instance().message("", 0)
      def mouse_moved_event(self, p, buttons, prio):
        if prio:
          self.set_cursor(pya.Cursor.Cross)
          return False
      def mouse_click_event(self, p, buttons, prio):
        if prio:
          view = pya.Application.instance().main_window().current_view()
          layout = view.active_cellview().layout()
          zoomRect = pya.DBox.new(view.box().p1.x-p.x,view.box().p1.y-p.y,view.box().p2.x-p.x,view.box().p2.y-p.y)
          layout.transform(pya.Trans(0, False, -p.x/layout.dbu, -p.y/layout.dbu))
          view.zoom_box(zoomRect)
          self.ungrab_mouse()
          SetOriginOnMouseClickPluginFactory.instance = SetOriginOnMouseClickPluginFactory()
          return True
        return False
    
    class SetOriginOnMouseClickPluginFactory(pya.PluginFactory):
      def __init__(self):
        super(SetOriginOnMouseClickPluginFactory, self).__init__()
        pya.MainWindow.instance()  # (workaround for bug 191)
        TheButton = self.register(-1000, "set_origin", "Cell Origin")
      def create_plugin(self, manager, root, view):
        return SetOriginOnMouseClickPlugin()
      instance = None
    SetOriginOnMouseClickPluginFactory.instance = SetOriginOnMouseClickPluginFactory()
    
  • Hi Jonathan,

    thanks for the hint about the class order.

    Regarding your points:

    1.) There is no cursor - actually there is just the mouse pointer and it can't (or should not) be manipulated. So if you want a snapping cursor, you have to implement a marker and move that with the mouse. You could then set the mouse cursor to invisible and this way you'd basically get your own cursor. I have not tried this yet - probably there is a lot of event handling - i.e. when the mouse enters or leaves the canvas etc.

    2.) Moving the layout is a valid operation, but this will not work when you're inside a child cell. So either you catch this condition as an error or implement a transformation at cell level (which is not as straightforward as layout transformation as there is no single method for this yet)

    3.) See my answers in that thread

    Matthias

  • Hello Matthias,

    Thanks again for the support: here I share again the result with the update:

    • handling mouse cursor is actually quite easy, I implemented a cursor which display also coordinates!
    • added an interface to snap the cursor to a grid
    • After click the tool is switched to Selection and therefore also run deactivated(self)

    The issues known/found:

    • The cursor has some issues when zooming around minimum 1nm grid: main one is that the text does not stick to bellow the located point
    • The script still only work on the Topcell (I may work on this later, should be possible but not necessary for me at the moment)
    • adding a marker layer on 10000.0 this layer needs to be unused (most likely the case for IC designers)
    • add_missing_layers() is running but I never hide layers ok for me
    • I wanted to extend this program to a nice visual labelling but I meet this issue

    :smile:

    GlbSnap = 10
    
    def GetLayer(num,dt):
      li = pya.Application.instance().main_window().current_view().begin_layers()
      while not li.at_end():
        lp = li.current()
        if((lp.source_layer ==num) and (lp.source_datatype == dt) ):
          return lp
        li.next()
      raise ValueError("Layer " + str(num)+"."+str(dt)+ " Not Found")
    
    
    class SetOriginOnMouseClickPlugin(pya.Plugin):
      def __init__(self):
        super(SetOriginOnMouseClickPlugin, self).__init__()
        pass
      def activated(self):
        self.grab_mouse()
        pya.MainWindow.instance().message("Click on point to set the origin", 10000)
      def activated(self):
        self.grab_mouse()
        pya.MainWindow.instance().message("Click on point to set the origin", 10000)
        self.layH =  pya.Application.instance().main_window().current_view().active_cellview().layout().layer(10000, 0)
        pya.Application.instance().main_window().current_view().add_missing_layers()
        layer = GetLayer(10000,0)
        layer.visible = True
        layer.name = "Marker_Layer"
        self.top = pya.Application.instance().main_window().current_view().active_cellview().layout().top_cell()
        self.dialog = pya.QDialog(pya.Application.instance().main_window())
        QLay = pya.QGridLayout(self.dialog)
        l1 =  pya.QLabel('snap (um)= ',self.dialog)
        self.l2 = pya.QLineEdit('{0:g}'.format(GlbSnap),self.dialog)
        self.l2.setToolTip('this will snap to grid')
        b1= pya.QPushButton('apply!',self.dialog)
        b1.clicked = self.ApplySnap
        QLay.addWidget(l1,0,0)
        QLay.addWidget(self.l2,0,1)
        QLay.addWidget(b1,3,1)
        self.dialog.show()
        print (dir(self.dialog))
      def ApplySnap(self) :
        global GlbSnap
        GlbSnap= float(self.l2.text)
      def deactivated(self):
        pya.MainWindow.instance().message("", 0)
        self.dialog.close()
      def mouse_moved_event(self, p, buttons, prio):
        if prio:
          self.set_cursor(pya.Cursor.Blank)
          pya.Application.instance().main_window().current_view().active_cellview().layout().clear_layer(self.layH)
          xMouse= int(p.x/GlbSnap)*GlbSnap
          yMouse= int(p.y/GlbSnap)*GlbSnap
          text = pya.Text('x='+str(xMouse)+' y='+str(yMouse), xMouse*1000,yMouse*1000)
          text.halign = 1
          text.valign = 2
          self.top.shapes(self.layH).insert(text)
          boxsize = (viewBox.p2.x-viewBox.p1.x)/300
          if boxsize < 0.001:
            boxsize =0.001
          poly =pya.Polygon([pya.Point(xMouse*1000,yMouse*1000),
            pya.Point((xMouse-boxsize)*1000,(yMouse+2*boxsize)*1000),
            pya.Point((xMouse+boxsize)*1000,(yMouse+2*boxsize)*1000)])
          self.top.shapes(self.layH).insert(poly)
          return False
      def mouse_click_event(self, p, buttons, prio):
        if prio:
          view = pya.Application.instance().main_window().current_view()
          layout = view.active_cellview().layout()
          zoomRect = pya.DBox.new(view.box().p1.x-p.x,view.box().p1.y-p.y,view.box().p2.x-p.x,view.box().p2.y-p.y)
          layout.transform(pya.Trans(0, False, -p.x/layout.dbu, -p.y/layout.dbu))
          view.zoom_box(zoomRect)
          self.ungrab_mouse()
          pya.Application.instance().main_window().current_view().active_cellview().layout().clear_layer(self.layH)
          pya.MainWindow.instance().menu().action("@toolbar.select").trigger()
          return True
        return False
    
    class SetOriginOnMouseClickPluginFactory(pya.PluginFactory):
      def __init__(self):
        super(SetOriginOnMouseClickPluginFactory, self).__init__()
        pya.MainWindow.instance()  # (workaround for bug 191)
        TheButton = self.register(-1000, "set_origin", "Cell Origin")
      def create_plugin(self, manager, root, view):
        return SetOriginOnMouseClickPlugin()
      instance = None
    SetOriginOnMouseClickPluginFactory.instance = SetOriginOnMouseClickPluginFactory()
    
  • Ah ... now the posts connect :-)

    Please see my comments in the other post.

    Thanks for sharing this code and best regards,

    Matthias

Sign In or Register to comment.