KiCommand - Tutorial and Wiki

  • I use this plugin and find it useful.
  • N/A

0 voters

KiCommand allows simple command strings to be executed within pcbnew.
Command strings consist of a variety commands that retrieve, filter, and
process Kicad objects. Commands are very easily added with a simple syntax. KiCommand is a stack-based language designed for command line entry.

The KiCommand discussion page is located here. That thread is for asking questions and retaining the history of discussion. This wiki page is honed for up to date information and a tutorial.

Getting Started

The Readme.md file on github should be up to date. It is a great introduction and overview of KiCommand. Read through the Readme.md first to get a general overview. This Tutorial assumes you know about the stack and how to place things on it. In the current document, we’ll walk through the very basics of getting to know KiCommand. We start with the stack, show some examples, and I encourage you to follow along with KiCommand open. Try some commands while reading through this hands-on Tutorial!

In the Readme.md file are the basics, try following steps to install and run KiCommand. If you have trouble, please post on the KiCommand discussion page to get help:

  • installation
  • run the KiCommand tests to verify installation
  • start KiCommand, currently requires selecting “Refresh Plugins” on the “Tools > External Plugins…” menu. The KiCommand window will pop up.
  • read overview (stack, capitalization, command structure, calling python)

What the heck is going on?

Entering Commands

Enter commands in the command entry text box and the output, if any, is shown in the window below the text box. You can enter single commands one at a time, or string together many command arguments and commands, each separated by spaces. If an entry is not recognized as a command, it will be placed on the top of the stack to be used as an argument for a future command. Try it!

  • clear - clear the stack
  • 1 - not a command, so this is placed at the top of the stack
  • stack - you should see the (string) value “1” as the only element in the stack.

Now try entering this all at once in the command entry text box:

  • clear 1 stack - the results should be identical to the previous example

Now put two items on the stack:

  • clear itemA itemB stack - you will see two items on the stack, itemA and itemB.

Get and keep your bearings

Several commands are useful for getting and keeping your bearings while using KiCommand. The “entry point” for getting help is the help command, which also appears on startup. If you get lost, type help in the command window and you’ll see further commands for getting help.

  • help - overall help, start here
  • All helpcat - list all commands by category
  • command explain - list specific help for command
  • stack - print all elements of the stack. This is the command to show the stack and understand the current state of KiCommand.
  • print - print the top element of the stack

Any of these commands can be entered without affecting the stack. Use them liberally.

If you mistype a command and it’s not recognized, try the following commands

  • print - verify top element of the stack
  • pop - remove the top (mistake) element of the stack
  • print - verify (the new) top element of the stack

Working with the stack

You should have already tried basic commands like clear itemA itemB stack to put two items on the stack and view the result. Here we’ll review some basic stack manipulation commands. The primary commands are

  • swap - exchange the positions of the top two elements from the stack.
  • pop - remove the top item from the stack.
swap

Let’s see these in action!

  • clear itemA itemB stack - this will print “itemA itemB”. These are the two items on the stack, with itemB on the “top”.
  • swap stack - this will print “itemB itemA”, demonstrating that itemA is now at the top of the stack. They’re swapped!

Now let’s try it all in one:

  • clear - clear the stack to prepare for the following commands.
  • itemA itemB swap - the stack is now “itemB itemA” with itemA at the top.
  • stack - print out the stack to verify the previous command worked!
pop

pop simply removes the top of the stack and throws it away.

  • clear itemA itemB stack - prepare the stack and view it
  • pop - remove the top item from the stack
  • stack - view the resulting stack. It should contain only itemA

And now all in one:

  • clear itemA itemB swap pop stack - can you follow what this is doing? Try to determine what this is doing, then verify by running it.
copy and pick

The copy command simply duplicates the top of the stack.

  • clear itemA copy stack - prints out “itemA itemA” indicating that itemA is on the stack twice.

If you need to copy something from deeper in the stack, use the pick command. So far, we’ve executing commands that don’t need arguments. pick is different: it requires one argument. How deep from the top of the stack do you want to pick? When executed, the pick command gets and removes the argument from the top of the stack and places the result of the command on the top. As you can see, arguments for a command are put on the stack before the command.

  • pick - duplicate an item on the stack and place the duplicate at the top. The 0 item is the top of the stack, 1 is the second from the top, etc.

Let’s try pick:

  • 15 14 13 12 11 10 4 pick print - prints “14”
  • stack - this shows the full stack as “15 14 13 12 11 10 14”
  • notice that the “4” is the argument to pick. It was removed from the stack when pick command ran.

You may have noticed that you could implement the copy command using pick. How would you do it? Take a guess, then see if you were right. Try using 'copy see to look at the definition of copy. Don’t worry if the output doesn’t quite make sense. I’ll explain the output further below under Investigating commands and definitions.

Basic types: string, int, float, list

When you type a command string, it is separated into individual words separated by spaces. Each word is is either an item or a command. items are simply placed on the stack as a string. commands are executed by taking arguments, if any, from the stack, executing the command and placing the result, if any, on the top of the stack.

For example:

  • clear stringone stringtwo stringthree stack - this will show three string items on the stack.
  • clear 1 2 3 stack - this will also show three strings on the stack.

Most commands will automatically convert arguments to the correct type. But we can also explicitly convert between types.

  • clear 2 stack - this will print “2”, which is the string item
  • float stack - this will convert the string “2” into a float, and print it out the resulting stack: “2.0”.

Because it is difficult to see the difference between a string and an int on the stack, we use float here so we can see the conversions happening.

When converting from floats to ints, numbers are rounded towards zero (this is known as truncating, aka chopping off the decimal portion):

  • 1.6 int - result: 1
  • -1.6 int - result: -1

The string command will convert a value to its string representation. For simple objects such as floats and integers, the result is straightforward. And the resulting value can be converted back into an int or float easily. For more complex objects, the string command will output the string representation which usually cannot be converted back into the object. Note that the print command will convert a value to its string representation when printing.

  • stringvalue string - doesn’t do anything useful because stringvalue is already a string.
  • 1 float string - will output 1.0 as a string.
Lists

Lists are very useful in KiCommand. Many pcb board elements come in lists and many operations work seamlessly with lists as well as individual items. A list can be created from scratch with a comma-separated list of values (no spaces!). Let’s create a list:

  • 1,2,3,4,5 int - creates a list of five integers. Remember to not put spaces within the list of numbers.
  • 1,2,3,4,5 float - creates a list of five floats.

When printing results, square brackets will surround the list values. For example, “[1,2,3,4,5]” is a list containing five integer values. Generally, a command will be executed for each member of a list and the return value is a list containing the results. A list is simply collection of values, where order matters. A list can be contain any mixture of types.

  • 1,2,3,4,5 int - [1, 2, 3, 4, 5] - a list of five ints.
  • 1,2,3,4,5 float - [1.0, 2.0, 3.0, 4.0, 5.0] - a list of five floats.
  • 1,2,3,4,5 split - [u’1’, u’2’, u’3’, u’4’, u’5’] - a list of five strings.

Most basic operations will convert a comma-separated string value into a list. To convert a comma-separated string value explicitly to a list of strings, use the split command.

  • 1,2,3,4,5 split - results in a list of five strings!!
  • 1,2,3,4,5 split int - the split here is redundant because int does the split for you prior to the conversion to integers.

concat will concatenate lists and will concatenate strings. It also has the side effect of adding integers, but you should really use the + command for that.

  • one two concat - results in onetwo
  • 1 2 concat - results in 12.
  • 1,2 int 3,4 int concat - results in a list of integers: [1,2,3,4]
append and extend

Two useful commands to manipulate lists are append and extend.

append will append the item at the top of the stack (in this case, the second argument) to the list given as the first argument. extend will add the contents of the list that is the second argument to the list that is the first argument. Note the subtle difference is the commands below:

  • 1,2 int 3,4 int extend - [1, 2, 3, 4]
  • 1,2 int 3,4 int append - [1, 2, [3, 4]]
  • 1,2 int list 3,4 int append - [[1, 2], [3, 4]]
len

Finally, you can use the len command to determine the length of the list. This also has the side effect of giving you the length of a string.

  • 1,2 int len - result: 2
  • 1,2 split len - result: 2
  • 1,2 len - result: 3. Note that this is the length of the string “1,2”. It counts each digit and the comma within the string.
  • 1,2 int 3,4 int concat len - result: 4
Numeric operations

Numeric calculations can be used on ints, floats, or lists of ints or floats. The results are pretty straightforward. Add, Subtract, Multiply and Divide are all available using their traditional symbols: +, -, * and /. These commands also automatically convert arguments to float, but will not split comma-separated strings.

  • 2 3 + - result: 5
  • 2 3 - - result: -1
  • 2 3 + - result: 6
  • 2 4 / - result: 0.5

Be careful about floating point vs integer math. If you need to make sure you’re using integer math, explicitly convert values to integers.

Try running the following two commands and compare the results.

  • 5 3 / 5 3 / + - the result is based on floating point math
  • 5 3 / int 5 3 / int + - remember that int will truncate the value (i.e. round towards zero).
Math on lists

Each of the basic numeric operator has a corresponding function that operates on a list of numbers. The operators are followed by a dot “.”:

  • +.
  • -.
  • *.
  • /.
  • 1,2,3 float 2 *. - result: [2.0, 4.0, 6.0]

These are useful when working with lists of numbers. They also, by default, can handle a variety of input conditions.

When the first operand is a list and the second is a number, each member of the list is operated on using the number as the second operand. When both operands are lists, then the lists are operated on member by member. The second list is repeated and/or truncated to match the length of the first list.

  • 1,2,3 float 2,3 float *. - result: [2.0, 6.0, 6.0]
  • 7,8,9 float 2,3 float *. print - result: [14.0, 24.0, 18.0]
  • 7,8,9,10,11 float 2,3 float *. print - result: [14.0, 24.0, 18.0, 30.0, 22.0]

We’ll use the automatically-repeating list operators when working with pcb elements.

Unit conversion

Numbers within KiCAD commands and objects are usually using the native units: nanometers. You can convert numbers to nanometers with the mm, mil, and mils commands. These can convert single values or lists, and a comma-separated string will automatically be converted into floats.

  • 23 mm - results in 23000000.0. 23nm is equal to 23000000nm
  • 23 mils - results in 584200.0
  • 23 mil - results in 584200.0. The mil and mils commands are identical.
  • 1,2,3,4,5 mm - results in [1000000.0, 2000000.0, 3000000.0, 4000000.0, 5000000.0]

Lesson

Now that you have some commands under your belt, try creating a function that converts from native units (nanometers) into millimeters using three functions: multiply list (*.), divide (/), and mm. Note that currently, there is no divide list command (/. does not exist, yet).

Boolean

The bool command converts the argument into True or False. A list of values is converted into a list of True/False values. A False value is an empty string, 0 integer or 0.0 float. Also, any other python value/object that is not None is converted to True.

  • ’ bool - results in False. The single quote mark followed by a space creates an empty string.
  • 0 bool - results in True!! The “0” by itself is a string and the string is non empty, therefore bool converts it to true!
  • 0 int bool - results in False
  • 0.0 bool - results in True!!
  • 0.0 float bool - results in False
  • 0 float bool - results in False. This is the same as the previous since 0 float and 0.0 float both result in the floating point value of 0.0.
  • 0,1,0,1,0 bool - results in [True, True, True, True, True]. Remember these are strings until you convert them to int or float!
  • 0,1,0,1,0 int bool - results in [False, True, False, True, False]

Filters

Now that you know boolean values, you can use them with the filter command.

  • clear 45,76,54,12,15,56,87,16 split copy 1.* regex filter print - get all values that start with “1”.
  • 1 list ’ bool list filter - creates an empty list
  • zones copy getnetname OLDNAME = filter NEWNAME findnet SetNet callargs - get zones connected to net with OLDNAME, then set them to net with NEWNAME.

Let’s break this last one down. The copy, =, and filter commands are very common together.

  • zones - all zone objects
  • copy getnetname - make a copy of all zones, get a parallel array of netnames corresponding to each zone. Now on the stack we have a list of zones followed by the list of their corresponding net names.
  • OLDNAME = - convert the net names to an array, with True where the net name is equal to OLDNAME and False where it isn’t. After this command, we have the list of zones, the original used when we copied, and a parallel list of true/false values. This could just as easily be any set of commands that results in an array of true/false values corresponding to the list of zones, such as REGEX regex (calling the command regex with argument REGEX), or other commands in the Comparison category.
  • filter - this takes the top two elements of the stack and filters the first one using the true/false values in the second argument. The result of this is a list of zones with a net name corresponding to OLDNAME.
  • NEWNAME findnet - is the command to find the NETINFO object of the net with name NEWNAME. Now on the stack we have the list of zones with netname matching OLDNAME followed by the NETINFO object of the net with NEWNAME.
  • SetNet callargs - finally, this calls the SetNet command with the NETINFO object as the argument on each of the zones in the list.

This is a common method to get a filtered list of items.

  1. get a full list of items
  2. copy the list of items, then get an attribute of the items
  3. filter the list of items based on the value of the attribute.
  4. repeat steps 2 and 3 as many times as desired.
  5. do something with the filtered list of items.

Advanced types: dict(ionary), list of lists

Python dictionaries can be built with two parallel lists using the dict command.

  • one,two,three split 1,2,3 int dict print - result: {u’three’: 3, u’two’: 2, u’one’: 1}

Then you can extract one value using sindex:

  • two sindex print - result: 2

The sindex command is string index and allows you to get a member of a dictionary using a string index value, much like index does for lists in python.

A List Of Lists argument is necessary for some commands, such as callargs. The simplest List Of Lists is a list containing a single-item list (this looks like [[1]]) and can be created with the command list. To create a list of lists containing a single list which contains a single integer, you would use the following command:

  • 1 int list list - create a list containing a list containing an integer.

The int command converts the string “1” to an integer. The first list command puts that integer into a single list. The second list command wraps that into another list. If we wanted to create a list of integers in another list, we use

  • 1,2 int list - create a single list as the only item within another list.

Note that in this case, the int command itself creates the first list, when the argument to int is a comma-separated string with integers in it.

Once you have the first list, you can append another list to it.

  • 1,2 int list 3,4 int append 5,6 int append - [[1,2],[3,4],[5,6]]
list. (list with a dot after it)

To take each element within a list, and convert it to a list, use the list. command. This will allow you to work with callargs more effectively. If the function you use in callargs takes a single argument and you want to call the function multiple times, each with an element of the argument list, you will need to create a list of lists from the single list.

  • 1,2,3,4,5 int list. print - [[1], [2], [3], [4], [5]]
pairwise

To collect each list in a list of lists into pairs of values, use pairwise. The primary purpose of pairwise is to transform a list of point lists into x/y coordinates. For this reason, pairwise requires a list of lists to start and ends with a list of list of point pairs. This is a little cumbersome if you only have a single list of numbers, but can work if you need it.

  • 1,2,3,4,5,6 int pairwise print - this is an error, the argument needs to be a list of lists, not just a single list. To do what is intended here, do the following:
  • 1,2,3,4,5,6 int list pairwise print - [[(1, 2), (3, 4), (5, 6)]]
  • 1,2,3,4,5,6 int list 7,8,9,10,11,12 int append pairwise print - [[(1, 2), (3, 4), (5, 6)], [(7, 8), (9, 10), (11, 12)]]

Useful command for manipulating lists and list of lists include zip, zip2

zip takes one argument and will join the first elements of each list into the first element of the result, and so on with second elements, etc.

  • zip - run right after the previous command, the result is [[1,3,5],[2,4,6]]

Note that zip is its own inverse: run it again to return the original list.

  • zip - run again to return the original list [[1,2],[3,4],[5,6]]

  • zip2 - takes two arguments, and treats it as a two-element list for zip. Technically, this is the same as swap list swap append zip.

Finally, you can undo a listoflists into a single list using flatlist

  • 1,2 int list 3,4 int append 5,6 int append - [[1,2],[3,4],[5,6]]
  • flatlist - [1,2,3,4,5,6]
1 Like

Part 2

Working with pcb elements

First, lets make sure our commands work on the board that is loaded into the window. Using the normal KiCAD UI, load a board with elements into the KiCAD. After that, execute clear setcurrentboard commands in KiCommand.

This will print the file name of the loaded board, set it as the default, and leave the stack empty. Now all the “top-level” commands will operate on this board.

Top-level Elements

The following commands will retrieve all available elements on the current pcb.

  • drawings - get all top-level lines and text
  • modules - get all modules
  • pads - get all pads (e.g. within modules)
  • tracks - get all top-level tracks segments and vias
  • areas - get all keepouts and zones

There are also commands that will retrieve subsets of design elements.

  • toptextobj - get all top-level text objects
  • keepouts - get all areas designated as keepouts
  • zones - get all areas designated as zones.
  • moduletextobj - all module’s text objects that are neither Value nor Reference objects.

Or, you can create your own “subset” of elements using the filter command. For example, tracks returns all tracks and vias. Let’s just pull out the vias by using filter. First, we have to determine how to identify a via. We can use istype to identify them.

  • Using the KiCAD UI, load the demo board “share/kicad/demos/flat_hierarchy/flat_hierarchy.kicad_pcb”
  • setcurrentboard - should print out the path to the loaded board

Let’s get our bearings with a few commands.

  • tracks len print - This should be the same number as tracks plus vias in the KiCAD/pcbnew window.
  • tracks VIA istype print - You should see a whole bunch of False values with a few True values mixed in.
  • tracks copy VIA istype filter len print - this should match the number in the pcbnew window listed as Vias

A peculiar thing is that VIAs are also identified as TRACKs, which is why you get all the tracks and vias in the list of tracks. We’ve pulled out the vias, but we can also pull out the tracks. Normally, you might think the following would work.

  • tracks copy TRACK istype filter len print - but that’s not quite right because vias are tracks, too. This filter doesn’t do anything.
  • tracks copy VIA istype not filter len print - here we simply get all tracks that aren’t vias with the not command. This should match the number listed in the pcbnew window for “Tracks”.

Note that the valid types for istype are within the pcbnew python object and listed in the KiCAD documentation.

Now let’s get the center point of the vias. You can use GetPosition function call.

  • clear tracks copy VIA istype filter GetPosition call print - this will print out the positions of the vias, but it’s printed as wxPoints in the native measurement, nanometers.
  • flatlist 1 1 mm / list *. 6 roundn list pairwise print - let’s clean up the output by flattening the list of wxPoint values, converting to mm, rounding off the values (to 6 decimal places) then joining up the values pairwise to represent X/Y values.

If your pcbnew is displaying in mm, then you should be able to easily locate the vias now. If you were displaying in inches, then you can print the values in inches instead:

  • clear tracks copy VIA istype filter GetPosition call flatlist 1 1 mil 1000 * / list *. 6 roundn list pairwise print

That’s quite a mouthful! See if you can decode exactly how this is working. Can you see how we are converting to inches using mils (which are 1000 per inch)?

Uniqueness and comparing pcb elements

Note that you can’t just blindly compare elements with =. When a component is retrieved from pcbnew, it is within what is called a Swig object. Each time a component is retrieved, python creates a new Swig object that is different even if the underlying component is the same. Let’s see this in action:

  • clear modules ilist 0 index list copy refobj textfromobj - get the first module’s reference text.
  • modules swap delist matchreference stack - generate another MODULE DLIST (with the modules command) and get the module that matches the reference from the last modules list. We generated the MODULE DLIST twice, each time with the modules command.

The two resulting modules have the same reference and are, in fact, the same module. Let’s show this by getting their reference text.

  • concat copy refobj textfromobj stack - this will print a list with two identical reference texts

The stack shows two different Swig objects (look at the last few numbers) that are modules with the same reference text: they are referring to the exact same module.

Now let’s do this in a way that only uses a single module list. Using the copy command will copy the python Swig object, not create a new one.

  • clear modules copy ilist 0 index list copy refobj textfromobj 2 pick ilist swap delist matchreference stack - the ilist command is what generates the individual Swig objects from the MODULE DLIST. Note the copy of the MODULE DLIST (right after the modules command).
  • clear modules ilist copy 0 index list copy refobj textfromobj 2 pick swap delist matchreference stack - now we work the identical copy of the individual Swig objects. Note the copy right after generating the ilist of modules.

Notice the subtle difference in each of the statements above. In the first one, the MODULE DLIST is retained and a MODULE Swig object is generated twice, each time from the same MODULE DLIST. In the second command string, the MODULE Swig objects are generated from the MODULE DLIST first, and the individual Swig objects are copied and filtered for the sought-after Module’s reference. Once you understand these two examples, you are well on your way to being an expert KiCommand user.

Modules

Let’s do this with modules and explore the reference and value objects. The useful commands here are

  • valobj - get the value objects.
  • refobj - get the ref objects.
  • textfromobj - get the text string from the text object

Note that valobj and refobj return an object, not the text directly. From the object, we can get, for example, the position, bounding box, and text.

Let’s use these in a few commands:

  • modules valobj textfromobj print - print the value text of all the modules
  • modules refobj textfromobj print - print the reference text of all the modules
  • modules refobj GetPosition call print - print the reference object position of all the modules

The previous commands get all the modules from the loaded pcb. But you can also query a subset of elements. Let’s use filter to grab a subset of modules. If you wanted to grab the modules that start with “R”, try

  • modules copy refobj textfromobj R.* regex filter print - this is the list of modules that have reference text beginning with “R”
  • refobj textfromobj print - take the list just generated, and get the reference text to verify.

Now let’s string this all together and get the position of all modules with references that begin with “R”.

  • modules copy refobj textfromobj R.* regex filter GetPosition call print - print out the positions of the text objects, the wxPoint in nm.

  • flatlist 1 1 mm / list *. 6 roundn list pairwise print - and we can clean it up and display in mm.

  • clear modules R8 matchreference valobj textfromobj print - get the module with reference text of “R8” and print its value text.

You can also get the pads associated with a list of modules

  • modules selected getpads - get the pads associated with the selected modules.

Create your first track and using drawing parameters

You can draw line segments or tracks with drawsegments. Let’s try it! If you draw segments on a copper layer, they will be created as tracks. Otherwise, they will be line segments.

  • 0,0,10,10,10,20 mm drawsegments - treat the list of numbers as x/y pairs and draw multiple segments. One from 0,0 to 10,10 and another from 10,10 to 10,20, specified in mm.

There is a bug in KiCAD where it doesn’t update the display, so you will have to change display modes to see the new segments. Change the display mode to Legacy Toolset (from the Preferences menu, or use the hotkeys F9 and F11 to return to the Modern Toolset). Now the new lines appear on the board!

The line was drawn using the default parameters. You can see the parameter values with getparams print and you can change the parameters with drawparams or param commands

The drawing parameters for lines are:

  • l - layer number
  • t - thickness (for lines)

The full list of drawing parameters are:

  • l - layer number
  • t - thickness (for lines)
  • h - height (for text)
  • w - width (for text)
  • zt - ZoneType (ZoneType is one of NO_HATCH, DIAGONAL_FULL, or DIAGONAL_EDGE)
  • zp - ZonePriority

drawparams takes a list of numbers and a layer name for arguments. It will automatically convert the layer name into a layer number.

  • getparams print - {‘h’: 1000000.0, ‘l’: 40, ‘t’: 300000.0, ‘w’: 1000000.0, ‘zt’: 0, ‘zp’: 0}
  • .3,.1,5 mm F.SilkS drawparams - specify [THICKNESS,WIDTH,HEIGHT LAYER] for future draw commands.

The param command does not convert layer names, so you’ll use layernums to convert a layer name into a layer number. It’s easier to issue multiple param commands, one for the numbers (converted to native units, nanometers) and another for the layer.

  • .3,.1,5 mm t,w,h param F.SilkS layernums l param - equivalent to the previous drawparams command.

So to create a track, just change the layer to a copper layer, like F.Cu. KiCommand takes care of the underlying python. However, you do have to subsequently set the net of tracks. There are two commands to set the net of tracks: setnetcode and setnetname and two additional convenience commands getnetcodefromname getallnetnames.

  • F.Cu layernums l param - that’s a lowercase “L” character (for “layer”).
  • 0,0,10,10,10,20 mm drawsegments - this is now a track (using the default netcode of 0
  • GND list list setnetname - sets the created tracks (that are still on the stack) to the net named “GND”

Calling python commands

Calling functions that have zero arguments

Many commands that get information about an object do not require additional arguments beyond the object. For such python commands, the KiCommand call command is sufficient.

For example, if we want to query a list of pads and obtain their center point, we could execute the following KiCommand command string:

  • modules getpads GetPosition call - this will result in a list of wx points representing the position of each pad on the board.
call and callargs

These are the main python function commands. call and callargs are both very flexible in the way they handle arguments. You can call a function on each member of a list and return the results in a parallel list. This is what happens with the command mentioned previously:

  • modules getpads GetPosition call - execute GetPosition python command on each pad in each module.

callargs requires a list of lists and executes the named function with each object and arguments in the parallel lists. You don’t need to supply functions or arguments for every object in the first list: the functions or arguments will be repeated as necessary. This can be useful for calling numerous functions on a single object, or the same function with different argument lists.

Note that callargs is a little picky on the FUNCTION* argument. If you want to call a function multiple times, once for each object, then it needs to be a list.

Load up a suitable board, and try the following commands.

  • clear board 1 int FindNet callargs getnetname print - get the name of net 1
  • clear board 1,2 int list. FindNet list callargs getnetname print - get the names of nets 1 and 2.

Note that both call and callargs will return the exact return value if there is only one object, and will return a parallel list of return values if there are multiple objects in a list or if there are multiple functions. If both objects and functions are lists, call and callargs will return a list of lists.

fcall, fcallargs and builtins

While call and callargs call a python function that is an attribute of a python object, fcall and fcallargs allow you to give the function as an argument to the command. This is useful when the python function you want to call is a bare function, such as those basic python functions within the builtins python object.

Suppose we want to call the python function pow() which is available in the builtins python object. We’d execute this:

  • builtins pow sindex list 2,3 float list fcallargs print - computes 2 to the power of 3 with the result of [8.0].

The builtins command retrieves a dictionary of objects associated with the builtins python object. You can then use sindex to get a specific member.

The sindex command is string index and allows you to get a member of a dictionary using a string index value, much like index does for lists in python.

callfilter, callnotfilter

You’ve seen the filter command use parallel lists of objects and True/False values. callfilter and callnotfilter are extensions of the filter function to call a python function on an object, then include that object in the result if the return value of the function is True (for callfilter) or False (for callnotfilter). These functions do not need the parallel list of True/False values, they create the True or False value using the function call.

TODO: calllist
attr

Finally, the attr command allows you to get a python object’s attribute. This, it turns out, is less useful than calling a function with call or callargs, but is available if needed.

TODO: More programming

Working with Named Stacks

Users can define any new Named Stack desired, except for the (very few) “Special Named Stacks” (Board and drawparams).

The following KiCommand commands are used for manipulating Named Stacks.

  • spush, spop, sdelete, scopy, scopyall

Special Named Stacks

There is currently two special Named Stacks, namely Board and drawparams. The top of the Board stack is used as the default board for most commands that operate on a pcb board. The drawparams stack is where the current drawing parameter settings are located.

You could manipulate these special Named Stacks with the spop and spush commands (among others), but doing so should be approached with great caution. Better, however, is to use the setcurrentboard (and potentially a subsequent Board spop command to revert to the previous board).

  • setcurrentboard - will set the current board to that which is loaded in the pcbnew UI, and print out the pathname. Behind the scenes, this adds the board to the “Board” Named Stack.

Drawing parameters should only be modified with the three commands designed to do so: drawparams, param, and getparams.

TODO: Working with boards

Investigating commands and definitions

KiCommand maintains several different dictionaries from which it obtains command definitions. These dictionaries are named user, persist, and command.

The command dictionary contains python functions within the KiCommand source code and cannot be detailed further from the command line. You can view the source code to see how these commands are constructed.

The persist dictionary contains python functions defined by default and
supplied with KiCommand. These commands are constructed from other KiCommand commands and can be viewed with the see and seeall commands. These commands can be created with the :persist command, but it is not recommended that users use this function.

The user dictionary contains commands defined by the user and constructed as a command string from other commands. These can also be viewed with the see and seeall commands. These commands can be created with the : command and they can be saveed and loaded from the user’s ~/kicad/commands directory.

It should be noted that persist commands can redefine command commands and user commands can redefine both persist and command commands.

The following commands are helpful for investigating this area of KiCommand:

  • see - shows a specific command string from the user or persist dictionaries.
  • seeall - shows all commands in the user and persist dictionaries.
  • helpcat - shows all categories of commands after they are superseded. If any commands are redefined by user or persist commands, then only the redefined command is shown.
  • explain - shows the help text for any command that defines it. It is highly recommended that any defined commands include a category and help text.

Defining your own commands

You can define your own commands. To do so, use the colon command (:). End the command definition with a semicolon (;).

The recommended way of defining a new command is:

: commandname “Category [Argument1 Argument2] Help Text To Explain the Command, including the arguments. seealso_command1,seealso_command2” arguments and commands ;

Here’s an example:

  • : setselect “Elements [OBJECTLIST] Sets the objects as Selected. clearselect,select” SetSelected call ; - define the setselect command in the user dictionary in category Elements and “SEE ALSO” commands of clearselect and select

After defined in this way, the 'setselect explain command will show the help text. Note the initial single quote mark. The newly defined setselect command will be listed in the Elements category with the helpcat command. And you can see the command definition with 'setselect see.

You can also delete current user commands (be careful!):

  • : commandname ; - removes the definition of commandname from the user dictionary.
  • : ; - removes all definitions from the user dictionary.

More examples can be seen in the kicommand_persist.commands file or by using the seeall command.

Types of commands, user, persist, and command dictionaries

KiCommand maintains several different dictionaries from which it obtains command
definitions. These dictionaries are named user, persist, and command.

The command dictionary contains python functions within the KiCommand source
code and cannot be detailed further from the command line. You can view the source
code to see how these commands are constructed.

The persist dictionary contains python functions defined by default and supplied with KiCommand. These commands are constructed from other KiCommand commands and can be viewed with the see and seeall commands. These commands can be created with the :persist command, but it is not recommended that users use this function.

The user dictionary contains commands defined by the user and constructed
as a command string from other commands. These can also be viewed with the
see and seeall commands. These commands can be created with the : command and they can be saveed and loaded from the user’s ~/kicad/commands directory.

It should be noted that persist commands can redefine command commands
and user commands can redefine both persist and command commands.

The following commands are helpful for investigating this area of KiCommand:

  • see - shows a specific command string from the user or persist dictionaries.
  • seeall - shows all commands in the user and persist dictionaries.
  • helpcat - shows all categories of commands after they are superceded. If any commands are redefined by user or persist commands, then only the redefined command is shown.
  • explain - shows the help text for any command that defines it. It is highly recommended that any defined commands include a category and help text.
  • : commandname ; - removes the definition of commandname from the user dictionary.
  • : ; - removes all definitions from the user dictionary.
2 Likes

Part 3 - Using geoms to Create New Elements

A geom consists of a list of values used to specify one or more geometrical shapes. The list is easily generated manually, or can be extracted from existing objects such as DRAWSEGMENTs, TRACKs, and ZONEs. There are five commands that use a geom and a wide variety of shape types to cover each of the shape types within a DRAWSEGMENT. The same geom specification can (soon) be used to generate TRACKs and ZONEs.

Commands that use geom

(Current commands)

  • getgeom - extract the geometries into an easy-to-read list called a “geom”. This will (currently) lose some information of the object, such as thickness, corner smoothing, and layer. The output of getgeom is not a string but instead a list of strings, wxPoints, and numbers. Note that getgeom will not necessarily show you how the drawing was originally specified since many drawing types actually draw different DRAWSEGMENT types, e.g. Polyline will be specified as individual DRAWSEGMENTs of type S_SEGMENT, which are extracted as geom type Line.
  • newdrawing - will create a DRAWSEGMENT of specified shape on the active layer (from drawparams)

(Future commands)

  • getgeomstring - extract the geometries into an easy-to-read string called a “geom”. This is suitable for cut and paste operations to duplicate the shape of an object (excluding some object parameters such as thickness and layer). This can take a list of objects or a list of geoms.
  • newzone - will create a ZONE_CONTAINER of specified shape on the active layer (from drawparams). If the zone is on copper, it will have the default net (0/’’).
  • newtrack - will create TRACK, ARC, or ZONE based on the specified shape (Line/Polyline->TRACK, Arc/Circle->ARC, Polygon->ZONE) on the active layer, which must be copper. When created, TRACKs have the default net (0/’’).

General Rules for geom specification

  • A geom consists of a list of values. The first value is a shapetype, the second and optional value is a unit, followed by points and numbers (collectively, “arguments”) that specify the shape. geoms can be specified successively in the same list.
  • All arguments can be listed as a comma-separated string to make specifications easy to enter by hand. Currently you would use the split command after your comma-separated string, but this requirement might be removed.
  • All arguments can be in arbitrarily-nested lists. This might often be paired x/y points in their own list. It won’t matter how they’re paired or nested.
  • In the specification of a geom, you can put a unit string after the shape type. Units are one of mm, mil, mils, native, or nm. This affects all parameters except for angles and numbers already specified as wxPoints. It is presumed that actual wxPoints would have come from getgeom.
  • Any number of numeric argument “sets” can be specified after the segmenttype string to indicate more than one segment/object (except for polygon or (new) polyline).
  • All numeric arguments can be specified as float, int, or string. If strings are used, they can be comma separated. Strings are converted to floats. (I might consider converting number strings without a decimal point to integers.)
  • Center, if specified, is first.
  • If Start and End are both specified, Start is before End.
  • Radius or Angle, if specified, is last.
  • Arithmetic operators are modified to pass through strings (such as segment type) so you can operate on the numbers in a segment specification without affecting the segment type. It will just “pass through” unmodified.

Argument Key:

  • C - is the center point of an arc or circle.
  • B - is a control point on a Bezier curve.
  • O - is a point on the drawing.
  • R - is a radius of an arc or circle.
  • A - is an angle of an arc.
  • S - is a start point on drawing.
  • E - is a end point on drawing.
  • P - is a point

Circle and Arc Suffix Key:

  • “C”: specified by C(O)N (N=A or R)
  • “R”: specified by OOR (Circle only)
  • “O”: specified by all points on the drawing.
  • “P”: specified by points CO(O) (Circle only)

All geoms:

The one-to-four capital letters after the shape name are the arguments expected based on the “Argument Key.” See also the “Special Shapes” section below.

  • Line SE StartPoint, EndPoint
  • Bezier SBBE StartPoint, ControlPoint1, ControlPoint2, EndPoint
  • Polygon P - a sequence of vertex points
  • Polyline P - a sequence of vertix points.
  • CircleC CR CenterPoint, Radius
  • CircleR OOR CirclePoint1, CirclePoint2, +/-Radius
  • CircleO OOO StartPoint, CirclePoint1, CirclePoint2
  • CircleP CO CenterPoint, StartPoint
  • ArcC COA CenterPoint, StartPoint, Angle
  • ArcO OOO StartPoint, MidPoint, EndPoint - Although this indicates “Mid” point, any point on the arc will suffice.

Note that for all geoms, you can repeat sets of arguments for another shape of the same type. To specify this, just continue listing arguments.

  • Line,Units,Start1,End1,Start2,End2,Start3,End3,Start4,End4

This is different than Polyline, which creates segments identical to Line, but chains them together and only requires a list of vertex/points.

  • Polyline,mm,P1,P2,P3,P4

That will create segments:

  • P1,P2
  • P2,P3
  • P3,P4

Note that Polyline is not necessarily a closed area, you need to repeat P1 at the end of the point list if you want it closed. Whereas,

  • Polygon,mm,P1,P2,P3,P4

will create a closed polygon with specified vertices, with P4 connected to P1.

Special Shapes

  • Text Strings - Text is specified as comma-separated and encoded strings (backslash escape sequences such as \n for newline. Commas are replaced with spaces, and there can be no actual spaces in the geom string. The text must end with the keyword EndText.
  • Via [Type] CopperDiameter DrillDiameter [ToLayer] - Place a VIA from the current layer to ToLayer, and make ToLayer the current layer. Width/Drill must be in mm (this may change in the future). Type and ToLayer are optional.
  • Dot Diameter
  • Corner Radius
  • Point P

There are four special shapes that are designed to work with Polyline. If these shapes are followed by a Polyline (keyword and arguments), then the subsequent Polyline will continue from the special shape’s position. There is no need to list the point again. It addition, the position of the special shape is the position of the last shape’s end point. If you begin a geom string with a special shape, you can specify the starting point with the keyword Point followed by the desired point. A Corner uses the last line segment and the next line segment and joins the lines with the given radius. The resulting line is continuous and smooth. The line segments are shortened to meet the new corner shape (arc). Although specifically designed for in the middle of Polylines, these special shapes may eventually be extended to other shapes (such as Bezier, for example).

Other control keywords

  • Layer LayerName - change the layer in drawparams (for all future geoms, until changed)
  • Thickness N - change the layer in drawparams (for all future geoms, until changed)

Example Commands:

  • Line,mm,20,10,22,12 split newdrawing refresh
  • ArcC,mm,20,10,22,12,90 split newdrawing refresh
  • CircleP,mm,-10,-10,5 split newdrawing refresh
  • Polygon,mm,30,0,30,1,25,1 split newdrawing refresh
  • Bezier,mm,30,0,30,1,25,1 split newdrawing refresh
  • Polyline,mm,20,20,25,25,20,25,Via,mm,2,1,B.Cu,Polyline,mm,30,30,35,35,Via,mm,3,2,F.Cu,Polyline,mm,40,35 split newdrawing refresh

Additional Thoughts

I originally defined ArcP and ArcR but they do not fully specify an Arc. To add similar commands, I’ll likely have to define additional commands such as ArcMajP and ArcMajR that would specify a Major Arc (angle > 180 degrees). I’ll wait to see if there is a suggestion to do this. If this is done, my current thought is that ArcP and ArcR would draw the minor arc.

  • ArcP COO CenterPoint, StartPoint, EndPoint - defines the minor arc
  • ArcR OOR StartPoint, EndPoint, +/-Radius - defines the minor arc
  • ArcMajP COO CenterPoint, StartPoint, EndPoint - defines the major arc
  • ArcMajR OOR StartPoint, EndPoint, +/-Radius - defines the major arc

Polygons for DRAWSEGMENTs and ZONEs are treated differently by KiCAD. A ZONE treats vertices as the outside boundary, with corners rounded within those bounds based on a parameter that geoms don’t currently preserve. DRAWSEGMENTs and TRACKs (and Polylines) treat vertices as the midpoint of the drawn line, with material/layer extending beyond the points by the thickness. It’s not clear yet how/if KiCommand should handle conversion of these shapes so that ZONEs and DRAWSEGMENT Polygons turn out identical.

I also want to support DRAWSEGMENTS, TRACKS, ZONE_CONTAINER and ZONE_CONTAINERS in the midst of geom strings, that way you can copy any existing shape from or to a drawing, track, or zone.

I’m still not completely happy with specifying a rounded rectangle by its points. I want to also be able to specify a rounded rectangle simply with (center?) position, width, height, and corner radius.