How does one efficiently develop scripts

I am trying to get into scripting in kicad. I managed to run my script by typing: exec(open("path/to/file.py").read()) in the “python scripting console”.

The problem with this is that this is a rather annoying task to have to do after every change I want to test. I was kinda hoping to be able to do this, either through a hotkey in kicad, or from a makefile, so I could run it from my text editor / IDE / programming environment (vim in my case).

I know you can turn the file into a plugin, but then, as far as I can tell, you still have to navigate to the plugins dropdown twice (to refresh and run the script), which is even more mouse movement.

I also thought about creating a python script that just monitors a dummy file to see if it has been toched, and then run my script, so that I could just start that script from kicad which keeps running and put a touch "dummy filename" in my makefile. But then I found out that a blocking setup which is constantly checking a file just freezes kicad while it is running (there is probably some sort of event based system I could use, but I have no idea where to even start with that).

Alternatively, I know you can use the pcbnew python module to edit the pcb file directly, but then you would have to somehow reload that file in kicad. I found a hotkey labeled “Revert” in the settings, which I thought would do that, but just doesn’t do anything as far as I can tell.

So, I was wondering if someone here knows of a way to do this.

I personally use this method

as well as running my plugin as a standalone script and then open it’s result automatically.

Other things you can do:

  1. In your makefile or whatever the test/launch action is, look for pcbnew process, kill it, run script, open result with pcbnew. This way result of your script will be opened automatically every time, you just have to alt tab.
  2. Make your script into a plugin with a dialog, add file system watcher to it so that it detects changes, reloads and runs relevant module automatically. Then run your plugin once.
  3. Use UI automation tools to navigate pcbnew menus for you with a shortcut.

General note: if you find yourself running the script a lot during development then you may be falling into debugger trap. Here’s what helps me any time I write code that I can’t easily run and get results in seconds (think large integration tests that take an hour to run, client-server interactions that need deployment, anything that takes more than few seconds to compile):

  1. Figure out your tools and APIs. In case of KiCad test simple things out in scripting console, study the API docs, read source code of kicad and other plugins with similar functionality.
  2. Test things that are unclear in small separate chunks. Ideally write small independent unit tests that you can later refer to as your own examples of correct API usage.
  3. Write down your business logic using proven bits from 1) and 2)
  4. When things inevitably break add debug logging statements. Lots of them in all places that seem important or have even remote chance to help you figure out what’s wrong. Not just with the error you see now, with things down the line too. In case of kicad plugin you can use print statements (on any platform except windows) or wx.MessageBox().

Also with python adding type hints everywhere and relying on decent IDE syntax highlighting and linting helps avoid a lot of stupid mistakes that you won’t have to waste time debugging later.

1 Like

Tnx for the reply.

That file system watcher option seems like an option that could work. I guess kicad uses that wx library thing do its ui stuff? And that file system watcher is a sort of event thing in there? Guess I’ll try to figure that out tomorrow.

As for that debug trap, 99% of the time, these errors are me trying to figure out how to interact with kicad, not problems in my own logic. And that doxygen documentation from kicad is way more of a pain than just printing what is happening. Especially when I am still figuring out how everything works.

KiCad uses wxWidgets, it’s a c++ gui toolkit. The link I gave you is for python wrapper of wxWidgets that is called wxPython or sometimes wxPhoenix. Yes, it’s event based like all gui toolkits.

Exactly why I recommend to figure those things out separately in small chunks before you get bogged down in your own logic.

Basically, there are Two types of Kicad Plugin’s; Simple and Complex.
You can read about them (many posts).

That said, here’s a link to my Trace-Length plugin that provides everything needed for making a Simple plugin…

The Complex plugin isn’t much different, just a few things plus adding a Folder to contain the things needed (given that a Complex one will most likely call other code…)

Well… yes… figuring that stuff out in small chunks is exactly why I need to run the script over and over again…

I finally got some time to look into this, and to write this plugin. I managed to get a simple plugin working, but I have no idea how I am supposed to get this wx FileSystemWatcher thing working. I that piece of documentation you provided isn’t really helping me with my limited python knowledge. Google doesn’t really get me any wxpython filesystemwatcher examples either.

That documentation page mentions an “fswatcher sample”, but I haven’t been able to find this “sample” they are talking about.

Here’s a minimal example that will watch current directory

import wx


app = wx.App()


class FSWatcher(wx.FileSystemWatcher):
    def __init__(self):
        super().__init__()
        self.Bind(wx.EVT_FSWATCHER, self.OnEvent)

    def OnEvent(self, event: wx.FileSystemWatcherEvent):
        print(event.GetPath(), event.GetChangeType())

frame = wx.Frame(None, title='Simple application')
frame.Show()

watcher = FSWatcher()
watcher.Add("./")

app.MainLoop()
1 Like

I put your example code in the minimal plugin I got working and that worked, thank you so much.

(I tried running the example you provided outside of kicad first, but then it complains about needing an event loop, idk if it was supposed to work standalone, but I’ll mention it just in case someone finds this thread in the future)

That’s a full complete example that works fine for me, maybe you missed the app.MainLoop() in the end?

I didn’t though, it is a complete copy paste of your sample code. Though, there seems to be a different error now?, not very descriptive though:

Traceback (most recent call last):
  File "/home/user/Creation/programming/test/wx_python/test.py", line 18, in <module>
    watcher = FSWatcher()
  File "/home/user/Creation/programming/test/wx_python/test.py", line 9, in __init__
    super().__init__()

You didn’t copy the full error. At least the exception type should be there.

Nope, nothing there, like I said, not very discriptive

Can you run it in terminal and copy full text starting from execution command to the prompt after it ends.

Nvm, maybe something wrong with my shell, the error is there now:

Traceback (most recent call last):
  File "/home/user/Creation/programming/test/wx_python/test.py", line 18, in <module>
    watcher = FSWatcher()
  File "/home/user/Creation/programming/test/wx_python/test.py", line 9, in __init__
    super().__init__()
wx._core.wxAssertionError: C++ assertion ""loop"" failed at /usr/src/debug/wxWidgets-3.2.0/src/unix/fswatcher_inotify.cpp(66) in Init(): File system watcher needs an event loop

edit: definitely something weird with my shell, I get the full error on one terminal, and one with that last line missing in another terminal

That’s very new, I don’t have that yet. You probably need a more complete example with a custom frame, put the FSWatcher into the frame and create the frame in app init. That should make event loop available.

You can also try wx.DisableAsserts() at the top to suppress overzealous asserts.

Well, that indeed supresses the error, but it doesn’t see any file changes. I guess probably something to do with my wxWidgets version. Either way, it works in kicad, so I can setup my scripting setup for my simple scripts for now (not planning to do any real UI stuff, just planning to play around with the pcbnew api for now).