← Back to team overview

kicad-developers team mailing list archive

Re: Getting kicad to work with wxPython Phoenix


Thanks all, for the replies

Thank you for merging my patch.

I'm pleased to read that the Kicad takes the python interface seriously.

Abstraction layer. I've considered writing one or at least submitting
patches to move in that direction[1] but I got sidetracked trying to
document what's already there. I will note that documenting the current
python interface indirectly documents some of the c++ (which is also not
the easiest to decipher) See this UML
potential changes from the python viewpoint could be helpful on the c++
side as well

SWIG work appears to be safe. It seems the can exist together. (the
possibility of a one or the other situation was very real in my mind.) Most
of Kicad's API do not involve wx types and the ones that do should be
passed back and forth as tuples anyway.

Tomasz: I will take a close look at your suggestions. In particular, the
wxpoint, vector2 stuff is inline with what I mentioned above.

I've had other thoughts, like the ability to add commands implemented in
python. This would involve the ability to add callbacks to onleftclick,
onrightclick, add to menu... That's a big one though [2]

Wayne: I did not try the scripts that come with kicad. I didn't try the
footprint generator.

I tried:
gensvg <https://github.com/mmccoo/kicad_mmccoo/blob/master/gensvg/gensvg.py>
generates as svg file from the layout of the currently loaded pcb.
replicates the placement of stuff based on sheet instances from the
schematic. You place the devices of one instance of a sheet and the script
moves the others to match. [3] I have a youtube demo here:
neither uses anything in the GUI. The current version of replicate doesn't
work, but with my patch that was just merged, I'll be able to push an
update does work.

I did try binding mouse and other canvas events. It's not on my github, so
I'm attaching it.

Not thorough in any way, but that it works at all felt like a success to me.


[1] like adding tyemaps for wxpoint and wxsize. Also, the different ways
lists are represented. Some are indexable, some are iterators. There seem
to be as many ways to represent a list as there are classes in pcbnew.

[2] I am not a OOP fanboy; I hate how Java insists on a class for
everything, but... I think it could make sense to "classify" the existing
commands. Simple base class with empty methods for onleftclick,
onrightclick, add to menu. Some of the switch statements in there, and the
nested ifs, are scary. The code for the commands are so spread out. Also a
big change, but I think it could be refactored in gradually. With such
class based structure, exposing it in python would be easier.

My experience in VLSI CAD is with a tool where the first implementation of
any new command was done in TCL (yay, TCL). I didn't bother to use c++
unless there was a performance problem.

[3] this reminds me that I'd really like the ability to pass user defined
properties from the schematic to the layout. eeschema already supports
UDPs. If they could be saved in the netlist and then read/stored in pcbnew,
that could be handy.

On Thu, Nov 23, 2017 at 6:04 PM, Maciej Sumiński <maciej.suminski@xxxxxxx>

> Hi Miles,
> On 11/23/2017 04:17 PM, miles mccoo wrote:
> > In a recent thread
> > <https://lists.launchpad.net/kicad-developers/msg31700.html> on this
> list,
> > it was mentioned that kicad may need to drop support for SWIG/Python due
> to
> > wx and wxPython limitations.
> >
> > Perhaps I misinterpreted what was said.
> > It's my perception that the kicad team doesn't see the value in a python
> > interface that I see
> > <https://kicad.mmccoo.com/2017/01/26/real-scripting-the-
> most-important-feature-a-tool-can-have/>.
> > Perhaps I'm wrong in that too
> I might have not expressed myself clearly. I am aware of huge potential
> in scripting/plugin/extensions interfaces. Our problem is we do not
> provide a nice interface, we simply expose C++ guts. It means that the
> users get upset every time we modify a class they use. I do not write
> many Python scripts, but I worked with unstable APIs, so I can tell what
> kind of frustration they cause. We *need* an abstraction layer to keep
> both sides happy, so we can change the C++ methods and the users have
> Python interface they may rely on.
> I never said we plan to drop Python support. I was simply afraid that we
> may spent a lot of time petting the SWIG interface, which we will nuke
> it and start from scratch because of inevitable switch to Phoenix. The
> only doubt I had is whether we can simply port our wxPython work to
> Phoenix.
> > Anyway, I have a version of kicad working with wxPython 4.0 (AKA Phoenix)
> >
> > A more detailed summary can be read here on my blog
> > <https://kicad.mmccoo.com/2017/11/23/learnings-from-
> moving-kicad-to-wxpython-4-0/>
> > and
> > a diff file of my changes is here:
> > http://mmccoo.com/random/diffs_for_phoenix
> This is very cool and appreciated, thank you! By reading your patch, I
> assume we can keep developing our SWIG files and at one point switch to
> Phoenix. If this is the case, then I think that work on a better Python
> interface using SWIG is very welcome, as it will be reused in the future.
> > The short version: Kicad will probably want to wait to move to the
> Phoenix
> > version of wxPython.
> True, I agree with Wayne - we need to wait until Phoenix is available in
> major Linux distros before we can even consider switching. Looking at
> your patch, I guess we could make KiCad compatible with both frameworks
> to assure smooth transition.
> Regards,
> Orson
> > The main issue is that the Phoenix equivalent of wx/wxPython/wxPython.h
> > (called from pcbnew/swig/python_scripting.h) exists, but isn't in the
> > releases yet. See here
> > <https://groups.google.com/forum/#!topic/wxpython-users/rIwPMKjQBeI>.
> >
> > Beyond that, I had some difficulties ensuring that the correct wx and
> > wxPython versions are installed and used[1]
> >
> > Once I did get it running, my kicad python scripts all work (save for the
> > things that broke since last time I updated kicad).
> >
> >
> >
> >
> >
> > Having said all that, I don't know that there is urgency to move to
> > Phoenix. AFAIK, wxPython 3.0 works fine with recent wxWidgets.
> >
> >
> > Hope it's helpful.
> > Miles
> >
> > PS - AFAIK, the patch that triggered mention of dumping python due to
> > wxPython still hasn't been denied or merged. :-)
> > https://lists.launchpad.net/kicad-developers/msg31700.html
> >
> >
> >
> >
> >
> > [1] on ubuntu, unstalling wxwidget package didn't remove the wxwidgets
> > libraries. It's quite possible that I was just confused and/or doing it
> > wrong.
> >
> >
> >
> > _______________________________________________
> > Mailing list: https://launchpad.net/~kicad-developers
> > Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> > Unsubscribe : https://launchpad.net/~kicad-developers
> > More help   : https://help.launchpad.net/ListHelp
> >
> _______________________________________________
> Mailing list: https://launchpad.net/~kicad-developers
> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> Unsubscribe : https://launchpad.net/~kicad-developers
> More help   : https://help.launchpad.net/ListHelp
import pcbnew
import wx
import wx.aui
import pdb
import time
import threading

print "loading capture.py"
for w in wx.GetTopLevelWindows():
    print "window {}".format(w.GetTitle().encode('utf-8'));

print "done printing dinwos"

def findPcbnewWindow():
    windows = wx.GetTopLevelWindows()
    pcbnew = [w for w in windows if w.GetTitle()[0:6] == "Pcbnew"]
    if len(pcbnew) != 1:
        raise Exception("Cannot find pcbnew window from title matching!")
    return pcbnew[0]

pcbwin = findPcbnewWindow()

scrolled = None
for win in pcbwin.Children:
    if (win.ClassName == "wxScrolledWindow"):
        scrolled = win

event_types = {}
event_objs  = {}
for ename in [x for x in dir(wx) if (x[:6] == "wxEVT_")]:
    event_types[getattr(wx, ename)] = ename
    if (ename[2:] in dir(wx)):
        event_objs[ename] = getattr(wx, ename[2:])

def mymousehandler(event):
    print("got mouse event {} at ({},{})".format(event_types[event.EventType], event.X, event.Y))

def activatehandler(event):
    print("got activate event {} {}".format(event_types[event.EventType], event.Active))

def menuevents(event):
    print("got menu event {}".format(event_types[event.EventType]))
    if ("GetEventObject" in dir(event)):
        print("object {}".format(event.GetEventObject()))        

ev = None
def menuhandler(event):
    print("got menu handler event {}".format(event_types[event.EventType]))
    print("tye {}".format(event.EventType))
    if (event.EventType == wx.EVT_MENU_OPEN.typeId):
        print("opening binding")
        #t = threading.Timer(5, lambda: event.GetMenu().Close())
        for evt in event_objs.keys():
            ev= event_objs[evt]
            event.GetMenu().Bind(event_objs[evt], menuevents)
    print("has menu: {}".format(str(event.GetMenu())))

def commandhandler(event):
    print("got command event {} {}".format(event_types[event.EventType], str(event)))
    #print("event dir {}".format(dir(event)))
    menu = event.GetEventObject()
    print("object {}".format(str(menu)))
    print("menu dir {}".format(dir(menu)))
    print("Id {}".format(event.GetId()))
    print("label {}".format(menu.GetLabel(event.GetId())))
scrolled.Bind(wx.EVT_MENU_OPEN,   menuhandler)
scrolled.Bind(wx.EVT_MENU_CLOSE,   menuhandler)
scrolled.Bind(wx.EVT_MENU,   commandhandler)  

scrolled.Bind(wx.EVT_MIDDLE_DOWN, mymousehandler)
scrolled.Bind(wx.EVT_LEFT_DOWN, mymousehandler)
scrolled.Bind(wx.EVT_RIGHT_DOWN, mymousehandler)
scrolled.Bind(wx.EVT_ENTER_WINDOW, mymousehandler)
scrolled.Bind(wx.EVT_LEAVE_WINDOW, mymousehandler)
scrolled.GetParent().Bind(wx.EVT_ACTIVATE, activatehandler)
# got mouse event wxEVT_RIGHT_DOWN at (181.97)

def SendEvent():
    #print("active window {}".format(str(wx.GetActiveWindow())))
    #activeevent = wx.ActivateEvent(wx.wxEVT_ACTIVATE, active=True)
    #wx.PostEvent(scrolled.GetParent(), activeevent)
    #enterevent = wx.MouseEvent(wx.wxEVT_ENTER_WINDOW)
    #wx.PostEvent(scrolled, enterevent)
    newevent = wx.MouseEvent(wx.wxEVT_RIGHT_DOWN) 
    newevent.X = 181
    newevent.Y = 97
    wx.PostEvent(scrolled, newevent)


Follow ups