Python - place/move footprint with a mouse

I have a script that loads footprint from file and places on the board.

        fp : pcbnew.FOOTPRINT = pcbnew.FootprintLoad(libpath, footprint_name)
        fp.SetPosition(pcbnew.VECTOR2I(0, 0))
        board.Add(fp)
        pcbnew.Refresh()
        # pcbnew.dragWithMouse(fp)

Is it possible to make it so that at the end it is moved with the mouse cursor? - Similar to what happens when footprints are added after updating pcbnew from the schematic.

I’m not very experienced with wxWidgets, but I’ll give this a shot.
You’ll probably need to watch the mouse events from the main window.
https://docs.wxwidgets.org/3.0/classwx_mouse_event.html

The other option is to poll the mouse position (yuck!)

I found this.

If we cannot zoom to a specific location (which would be ok for me), then manipulating objects with a mouse seems even less feasible.

Ohh, the above topic is (getting) outdated.
I found in the nightly (7.99) FocusOnItem() method.

I just placed it at the end of my script:

fp : pcbnew.FOOTPRINT = pcbnew.FootprintLoad(libpath, footprint_name)
fp.SetPosition(pcbnew.VECTOR2I(0, 0))
board.Add(fp)
pcbnew.Refresh()
pcbnew.FocusOnItem(fp)

The screen and cursor moved onto the newly added footprint. This is decent.

Even without that, I found that the footprint is always selected right after adding to board. Maybe sending a keystroke “m” to Kicad could trigger mouse move? But I don’t know how to send keystroke.

Check out KiBuzzard plugin code. If I remember correctly it does exactly what you are asking for (i.e. attaches generated object to move action after it completes).

1 Like

Indeed, in KiBuzzard do this https://github.com/gregdavill/KiBuzzard/blob/main/KiBuzzard/plugin.py#L124-L133. In their case, they load string-based footprint to the clipboard and paste using ctrl+v keystrokes. Pasting is nice because it doesn’t leave a footprint in a corner if Esc is pressed.

It was a bit too complicated for my liking though, but fortunately, I achieved similar by sending “m” key.

Here final code snippet:

fp : pcbnew.FOOTPRINT = pcbnew.FootprintLoad(libpath, footprint_name)
        fp.SetPosition(pcbnew.VECTOR2I(0, 0))
        board.Add(fp)
        pcbnew.Refresh()
        pcbnew.FocusOnItem(fp)

        //find PCB window and send 'm' key to initiate move command:
        self._pcbnew_frame = [x for x in wx.GetTopLevelWindows() if ('pcbnew' in x.GetTitle().lower() and 'python' not in x.GetTitle().lower()) or ('pcb editor' in x.GetTitle().lower())]
        if len(self._pcbnew_frame) == 1:
            self._pcbnew_frame = self._pcbnew_frame[0]
        
            wnd = [i for i in self._pcbnew_frame.Children if i.ClassName == 'wxWindow'][0]
            evt = wx.KeyEvent(wx.wxEVT_CHAR_HOOK)
            evt.SetKeyCode(ord('m'))
            wx.PostEvent(wnd, evt)

Keep in mind that shortcuts can be changed by user. You can read kicad’s user config file (they are json) or if you have some settings, let user choose the key sent at the end.

1 Like

My solution is not perfect.
I found out, actually, I can only select the item with FocusOnItem(fp). But this moves the canvas, which is undesired when dragging with the mouse.

Additionally, if there was only another footprint selected, both will be moved after sending the keystroke. :confused:

I will try KiBuzzard’s clipboard solution later and post it here, as it seems optimal, even though requires extra code.

Here is the method of placing footprint with a mouse using clipboard workaround:

import pcbnew
import wx

clipboard = wx.Clipboard.Get()
if clipboard.Open():
    # read file
    with open(os.path.join(libpath, component_name + ".kicad_mod"), 'r') as file:
        footprint_string = file.read()
    clipboard.SetData(wx.TextDataObject(footprint_string))
    clipboard.Close()

if self._pcbnew_frame is None:
    try:
        self._pcbnew_frame = [x for x in wx.GetTopLevelWindows() if ('pcbnew' in x.GetTitle().lower() and not 'python' in x.GetTitle().lower()) or ('pcb editor' in x.GetTitle().lower())]
        if len(self._pcbnew_frame) == 1:
            self._pcbnew_frame = self._pcbnew_frame[0]
        else:
            self._pcbnew_frame = None
    except:
        pass

# Footprint string pasting based on KiBuzzard https://github.com/gregdavill/KiBuzzard/blob/main/KiBuzzard/plugin.py
if self.IsVersion(['5.99','6.', '7.']):
    if self._pcbnew_frame is not None:
        # Set focus to main window and attempt to execute a Paste operation 
        try:
            evt = wx.KeyEvent(wx.wxEVT_CHAR_HOOK)
            evt.SetKeyCode(ord('V'))
            #evt.SetUnicodeKey(ord('V'))
            evt.SetControlDown(True)
            # self.logger.log(logging.INFO, "Using wx.KeyEvent for paste")
            wnd = [i for i in self._pcbnew_frame.Children if i.ClassName == 'wxWindow'][0]
            # self.logger.log(logging.INFO, " Injecting event: {} into window: {}".format(evt, wnd))
            wx.PostEvent(wnd, evt)
        except:
            # Likely on Linux with old wx python support :(
            # self.logger.log(logging.INFO, "Using wx.UIActionSimulator for paste")
            keyinput = wx.UIActionSimulator()
            self._pcbnew_frame.Raise()
            self._pcbnew_frame.SetFocus()
            wx.MilliSleep(100)
            wx.Yield()
            # Press and release CTRL + V
            keyinput.Char(ord("V"), wx.MOD_CONTROL)
            wx.MilliSleep(100)
    else:
        # self.logger.log(logging.ERROR, "No pcbnew window found")
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.