KiCommand discussion and development - Easy pcbnew command line; 4.07 & 5.1.5/.6

Interesting project! I like the functionality added here! I do however find that this looks a lot like trying to run SQL querys on a PCB. I do not know if that is good or bad. But maybe you can find ideas for the syntax from there?

I like the postfix notation. It makes parsing dead simple, and chaining commands together is easier than dealing with variables. I find SQL hard to compose, and it also means abstracting python objects. I’ll give it some more thought…

Update on github to add better error message and command history in the combobox.

1 Like

Updates posted:

Added builtins, fcall, fcallargs to enable getting the ‘range’ function
“clear builtins range index list 5 int list print fcallargs”
For some reason, builtins appears on the stack as a dictionary, which
won’t work with ‘call’ or ‘callargs’, so ‘fcall’, ‘fcallargs’, and ‘sindex’
were created.

Added sindex to allow accessing Python dictionaries with string indexes.

Fixed pick command, was only returning top of stack.

Reworked ‘int’ command so that it returns a single value, not list, if there
is a single string without a comma. This allows the creation of a single
value on the stack (motivated in this case to enable ‘index’ to work well
with lists or dictionaries).
“: range int list builtins range index list swap print fcallargs ;”

Reworked ‘float’, ‘index’, ‘mm’, ‘mil’, ‘mils’ similarly to int.

Added quoted string using “double quotes”. All spaces inside the quote
marks are retained. Words are split on the double quote mark such that the
following are equal:
1 2 3 " 4 5 6 " 7 8 9
1 2 3" 4 5 6 "7 8 9
If in a file, pairs of quote marks must be on the same line.

Added load and save commands for the user dictionary. Lightly tested.

Any feedack so far? Has anyone given this a shot? Are there any questions I can answer?

Still making progress. Added a few Geometry and Drawing commands, and the Action commands can handle ARC segments (like connect).

There’re still some bugs and I haven’t released the newest version quite yet. I just wanted to post on some of the progress I’ve made.

Draw        - makeangle drawsegments pad2draw drawparams cut regular drawtext drawarc round 
Geometry    - rotatepoints ends angle rotate corners length 
Action      - select deselect rejoin connect 

Note that the selected segment is one piece:

and after the ‘cut’ command, the cut line is now in two pieces:

Here’s the help text for some of the new commands.

makeangle (Category: Draw) 
    [SEGMENTLIST ANGLE] Make the selected segments form the
    specified angle. arc radius is maintained, though angle and
    position are modified, while line segments are moved and
    stretched to be +/- n*angle specified. 
cut (Category: Draw) 
    Cut all segments with the selected segment at the
    intersection. 
regular (Category: Draw) 
    [SEGMENTLIST] Move/stretch the selected segments into a
    regular polygon (equal length sides, equal angles). 
drawarc (Category: Draw) 
     
round (Category: Draw) 
    [RADIUS SEGMENTLIST] Round the corners of connected line
    segments within SEGMENTLIST by adding ARCs of specified
    RADIUS. 
angle (Category: Geometry) 
    [SEGMENTLIST] Return the angle of each segment in
    SEGMENTLIST. 
rotate (Category: Geometry) 
    [SEGMENTLIST DEGREES] Rotate segments by DEGREES around
    additive center. 
length (Category: Geometry) 
    [SEGMENTLIST] Get the length of each segment (works with
    segment and arc types 

Note that I made the text in the screenshots parallel to the segments by using the angle command to find the segment angle.

2 Likes

I think if you are aiming at non-programmers, then postfix is not a good choice. There are very few people who are comfortable with RPN calculators.

Otherwise, it looks very much like the command language I envisaged, nice work.

Thank you. I’m a little concencend about postfix for non programmers as well. It may be relatively easy to learn for short command strings. My biggest concerns with regular procedural language are:

  • Difficulty of dealing with variables.
  • Difficulty in using/sharing multi line programs, as opposed to single line command strings).
  • If you really want procedural, then Python would be a better choice.

That said, I was also thinking (and in the very first stages, this started out to be) a simplified Python library that others could include. When I get some time, I could certainly convert the functions into a simplified library. Right now, the commands are lambda functions with a mixture of Python built in function calls and custom functions. The biggest hurdle is getting the arguments to be flexible: handling objects, lists of objects, and lists of lists of objects; and the variety of objects that need to be coerced into each other such as string, float, int, string as float/int, string as pcbnew constant.

A simplified library might give the best of both worlds. Simple command string or simplified Python program.

Edit to add: the biggest drawback of Python is the API and navigating the numerous functions and attributes for doing the simplest things like “give me all text”, “give me all vias”, “what is the slope of this line”, “how do I draw a line segment”, “what layers is the object on”, etc. That, I think, is the biggest advantage of KiCommand. You almost have to be a mid-level Python programmer to figure it out. I think with the simplified commands in KiCommand as a base, I’m hopeful I could construct a simplified Python library.

KiCommand updated on github. Added some drawing and geometry commands. Cleaned up the help system. It’s fairly well documented at this point. Be sure to save you work before executing commands. I’ve had a small number of crashes when executing some commands. I’ve cleaned up a few of those.

If anything gives you problems, you can file a bug on github, or comment here. I’ll be unavailable for a week or two.

Enjoy! Let me know how it works for you.

Some more updates on the development version, not yet released. I’ve noticed that no one has tried this out yet, but I’ll continue development for a little while at least. If this becomes useful to you, then let me know!

New commands in development version

rotate (Category: Geometry) 
    [SEGMENTLIST DEGREES] Rotate segments by DEGREES around
    additive center. 
ends (Category: Geometry) 
    Get the end points of the drawsegment (works with segment
    and arc types 
rotatepoints (Category: Geometry) 
    [POINTS CENTER DEGREES] Rotate POINTS around CENTER. POINTS
    can be in multiple formats such as EDA_RECT or a list of one
    or more points. 
remove (Category: Layer) 
    [OBJECTORLIST] remove items from board. Works with any items
    in Modules, Tracks, or Drawings. 
todrawsegments (Category: Layer) 
    [TRACKLIST LAYER] copy tracks in TRACKLIST to drawsegments
    on LAYER. Copies width of each track. 
tocopper (Category: Layer) 
    [DRAWSEGMENTLIST LAYER] put each DRAWSEGMENT on the copper
    LAYER. 
load (Category: Programming) 
    [FILENAME] executes commands from FILENAME. relative to
    ~/kicad/kicommand. Note that this command is not totally
    symmetric with the save command. 
save (Category: Programming) 
    [FILENAME] saves the user dictionary into FILENAME relative
    to ~/kicad/kicommand. Note that this command is not totally
    symmetric with the load command.
1 Like

Latest update pushed to GitHub. Should now work with 4.0.7.

Put file in plugins directory, then go to Scripting Console and enter:

import kicommand

Will show the KiCommand dialog.
Not all commands tested in 4.0.7.

Added scale and grid commands.

scale,grid explain

scale (Category: Draw) 
	[SEGMENTLIST FACTOR] Scale each item in SEGMENTLIST by
	FACTOR, using the midpoint of all segments as the center. 
grid (Category: Draw) 
	[SEGMENTLIST GRID] Move points of SEGMENTLIST to be a
	multiple of GRID. 

Here’s an example. With the existing commands, I created a new command called regularsize:

Usage: SIDELENGTH PARALLELANGLE regularsize

regularsize takes the selected segments, joins them into a regular polygon, then
   sizes the edges to the specified length, and places one of the edges parallel
   to the specified angle

Before:

30 mm 0 regularsize

After:

This works for any number of sides that you draw.

If you are interested, here is the regularsize command that I’ve defined:

You can use the see command and explain command to view details about user defined commands:

'regularsize see

: regularsize drawings selected copy connect copy
regular copy copy length delist stack 4 pick swap /f 
scale copy delist list angle delist 2 pick swap -f rotate
pop pop ; 

A lot of this is parameter manipulation (4 pick, copy, swap, pop) but it shows a little bit of the power of KiCommand. The key commands in the regularsize command are scale, angle and rotate.

'regularsize explain

: regularsize drawings selected copy connect copy regular copy copy length delist stack 4 pick swap /f scale copy delist list angle delist 2 pick swap -f rotate pop pop ; 
drawings (Category: Elements) 
    Get all top-level drawing objects (lines and text) 
selected (Category: Attributes) 
    [objects] Get selected objects 
: copy 0 pick ; 
0 - A literal value (argument) 
pick (Category: Stack) 
    [NUMBER] Copy the value that is NUMBER of objects deep in
    the stack to the top of the stack.
        Examples:
        0 pick - copies the top of the stack.
        1 pick - pushes a copy of the second item from the
    top of the stack onto the top of the stack. 
connect (Category: Action) 
    Using selected lines, connect all vertices to each closest
    one. 
: copy 0 pick ; 
0 - A literal value (argument) 
pick (Category: Stack) 
    [NUMBER] Copy the value that is NUMBER of objects deep in
    the stack to the top of the stack.
        Examples:
        0 pick - copies the top of the stack.
        1 pick - pushes a copy of the second item from the
    top of the stack onto the top of the stack. 
regular (Category: Draw) 
    [SEGMENTLIST] Move/stretch the selected segments into a
    regular polygon (equal length sides, equal angles). 
: copy 0 pick ; 
0 - A literal value (argument) 
pick (Category: Stack) 
    [NUMBER] Copy the value that is NUMBER of objects deep in
    the stack to the top of the stack.
        Examples:
        0 pick - copies the top of the stack.
        1 pick - pushes a copy of the second item from the
    top of the stack onto the top of the stack. 
: copy 0 pick ; 
0 - A literal value (argument) 
pick (Category: Stack) 
    [NUMBER] Copy the value that is NUMBER of objects deep in
    the stack to the top of the stack.
        Examples:
        0 pick - copies the top of the stack.
        1 pick - pushes a copy of the second item from the
    top of the stack onto the top of the stack. 
length (Category: Geometry) 
    [SEGMENTLIST] Get the length of each segment (works with
    segment and arc types 
delist (Category: Conversion) 
    [LIST] Output index 0 of LIST. 
stack (Category: Programming) 
    Output the string representation of the objects on the stack 
4 - A literal value (argument) 
pick (Category: Stack) 
    [NUMBER] Copy the value that is NUMBER of objects deep in
    the stack to the top of the stack.
        Examples:
        0 pick - copies the top of the stack.
        1 pick - pushes a copy of the second item from the
    top of the stack onto the top of the stack. 
swap (Category: Stack) 
    Switches the two top objects on the stack. 
/f (Category: Numeric) 
    [OPERAND1 OPERAND2] Return the the floating point OPERAND1 /
    OPERAND2. 
scale (Category: Draw) 
    [SEGMENTLIST FACTOR] Scale each item in SEGMENTLIST by
    FACTOR, using the midpoint of all segments as the center. 
: copy 0 pick ; 
0 - A literal value (argument) 
pick (Category: Stack) 
    [NUMBER] Copy the value that is NUMBER of objects deep in
    the stack to the top of the stack.
        Examples:
        0 pick - copies the top of the stack.
        1 pick - pushes a copy of the second item from the
    top of the stack onto the top of the stack. 
delist (Category: Conversion) 
    [LIST] Output index 0 of LIST. 
list (Category: Conversion) 
    [OBJECT] Make OBJECT into a list (with only OBJECT in it). 
angle (Category: Geometry) 
    [SEGMENTLIST] Return the angle of each segment in
    SEGMENTLIST. 
delist (Category: Conversion) 
    [LIST] Output index 0 of LIST. 
2 - A literal value (argument) 
pick (Category: Stack) 
    [NUMBER] Copy the value that is NUMBER of objects deep in
    the stack to the top of the stack.
        Examples:
        0 pick - copies the top of the stack.
        1 pick - pushes a copy of the second item from the
    top of the stack onto the top of the stack. 
swap (Category: Stack) 
    Switches the two top objects on the stack. 
-f (Category: Numeric) 
    [OPERAND1 OPERAND2] Return the the floating point OPERAND1 -
    OPERAND2. 
rotate (Category: Geometry) 
    [SEGMENTLIST DEGREES] Rotate segments by DEGREES around
    additive center. 
pop (Category: Stack) 
    Removes the top item on the stack. 
pop (Category: Stack) 
    Removes the top item on the stack.
3 Likes

Another update for overhauling the command definitions. They can (and should) now include categories and help text, which will show up in explain, see, helpcat, and seeall commands.

Defining commands and the details of the user, persist, and command dictionaries is now in the readme.

I’m testing with 4.0.7 stable, but this should also work in nightlies as an ActionPlugin (installation instructions are in the README file).

Please let me know if you have any problems or questions.

Enjoy!!

1 Like

Just stumbled upon your plugin …
Haven’t had the time to test it yet, but certainly will do so soon.
But from what I see here, I already love it!

Thanks,
Herbert

Awesome! Let me know how it goes for you.

Following up on this post where I explain how to put drawsegments commands into a file. Here’s another example:

-5,3,-5,2,-3,-2 mm drawsegments
-4,0,-5,-4,-5,-8,-4,-13,-4,-22,-2,-23,0,-22,0,-12,1,-10,2,-12,2,-22,4,-23,6,-22,6,-13,7,-8,7,-4,6,0 mm drawsegments
7,-10,8,-12,8,-18,9,-19,11,-18,11,-9,12,-7 mm drawsegments
6.5,-10,8,-10,10,-9,12,-7,13,-4,13,2,12,5,9,7 mm drawsegments
6,7,7,6,9,5,9,7,10,10,11,12,13,14,13,16,11,17,7,17,3,15 mm drawsegments
4,13,3,15,1,17,-2,17,-3,16,-4,14,-5,15 mm drawsegments
-1,9,-2,8,-3,8,-4,10,-5,15,-6,19,-8,22,-10,22,-11,21,-10,20,-9,20,-8,18,-8,10,-7,6,-6,4,-5,3,-2,2,-1,2,1,3,2,4 mm drawsegments
1,3,0,1,-2,0,-1,2 mm drawsegments
-2,0,-3,0,-1,-2 mm drawsegments
-8,16,-12,16,-13,15,-12,12,-10,10,-9,8,-8,3,-6.5,4.5 mm drawsegments
-2,11,-2,10,-3,10,-2,11 mm drawsegments
1,11,1,10,2,10,1,11 mm drawsegments 

KiCommand: elephant.txt load
hit F9 then F11
Select all the line elements of the drawing, then
KiCommand: drawings selected 180 rotate

This seems like a great way to keep Edge.Cuts outlines for boxes. I’m working on a way to interleave arcs within the sequence of lines to make it real simple.

The complexity right now is that you have to specify an arc by center, start, and angle. But this isn’t really natural for drawing an outline. There is a method called round which allows you to select two line segments, specify a radius, then round will create an arc with the specified radius that connects the two lines. Works great!

However, for inserting an arc into a series of line segments, it seems best to allow specifying two endpoints and connect them with a curve, where the endpoints are connecting to lines that are tangents of the arc.

For following specifications of cutouts, perhaps the intersection and radius work out well. I just have to figure out what the command strings look like and think about whether it can be improved.

1 Like

New update including fromsvg command.

See this post for an example.

2 Likes

Another fun thing:

Because CERN HQ is in Switzerland, I thought I’d find the rough coordinates for Switzerland’s borders and convert them into a file usable by KiCommand. If you save this into a file and then use the load command:

10.4712361532,46.8713542065,10.4575272152,46.5425001251,10.0513911482,46.5415271538,10.1363911903,46.2306911469,9.94694525824,46.3795820785,9.54486301486,46.3062451194,9.29378226635,46.5008271465,9.03666319477,45.8377731735,8.44500007067,46.2472182483,8.44145419034,46.4620821342,7.85574506735,45.9190541652,7.03805420048,45.9319361446,6.78347316919,46.1547182468,6.73778224139,46.4474910848,6.29555420208,46.3941640755,6.13326328943,46.149782144,5.96700924531,46.2072912201,6.11555417199,46.2615269953,6.12868207642,46.5880540927,6.9716631569,47.2919361484,6.99055429033,47.4972181779,7.38541820631,47.4333271207,7.58827317186,47.5844821721,8.5764180869,47.5913731023,8.40694522432,47.7018001508,8.56291802597,47.80666413,9.56672704172,47.5404542139,9.67034513489,47.3906910614,9.5335732618,47.2745450404,9.4746360771,47.0574540828,9.59863627664,47.0638360639,10.1094450479,46.8502732171,10.3907631769,47.0025641839,10.4712361532,46.8713542065 mm drawsegments delist

you’ll end up with this (I added the KiCad text manually):

Also included three more commands, rotate by 180 degrees, scale by a factor of 20, and round off the corners to 1mm radius.

drawings selected 180 rotate
drawings selected 20 scale
1 mm list drawings selected round

It works well EXCEPT for the few lines that have length less than the rounding radius.

Here’s a closeup of the top where this is a problem (I’ve highlighted the connecting line segment between the arcs):

1 Like

A major version update to KiCommand has been pushed to github.com.

The main additions are the unit tests and command to enable those unit tests. The tests included are equivalent to all of the KiCad python default unit tests.

  • It has been refactored to be a python package.
  • added unit tests equivalent to standard KiCad python tests. Invoke with “import kicommand.test” in the Script Console.
  • added parameters to make using kicommand.run() easier within other python scripts.
  • added ability to manipulate named user stacks (with spush spop, and scopy.
  • newboard creates a new board and puts it on the top of the Board stack. board now puts the top of the Board stack onto the main stack. board command is equivalent to Board scopy.
  • added ability to use different boards than what is in pcbnew UI (loading board from file, or created a new blank board). All commands using a board, now use the “current board” on the top of the Board user stack.
  • added ability to add new empty items such as modules and pads with newadd command, which creates a new empty specified element (such as MODULE or D_PAD) to a specified parent object (board or module, for example).

The new directory structure still works with 4.0.7, but has not specifically been tested with nightly. Please provide feedback on whether it works for you in nightly. It should automatically become available in the Tools > External Tools menu.

1 Like

Updated!

Still having issues with AutoPlugins in nightly. Should still work from Scripting Console and “import kicommand”

Updates::

  • RENAMED todrawsegments to tosegments to better fit nomenclature (see below)

  • Added commands regex, bool, printf, zip, fprintf, pwd, cduser, cdproject, cd

  • Added a couple more tests in kicommand.test

  • modified init.py to update import, user’s should still use “import kicommand”

  • Updated callargs to allow non-lists as input.
    Before: only a list of lists
    Now: A list of lists, a single list, or a single value.
    Before: [objectlist] [[arglist1],[arglist2]] where arglist1 applies to
    object1, arglist2 applies to object2, then arglist1 and 2 are alternated
    for each subsequent object.
    Now: if you only have one argument, or one argument list, it is used as the
    argument(s) for all objects.

SEGMENTS/POLY/POLYGON nomenclature:

“segments” as unconnected line segments (will extend later to nonlines such as arcs or curves) and “poly” as connected line segments.

  • “poly” is a single list of points where each point is a vertex of the joined lines.

  • “segments” are individual and independent x/y value pairs.

  • “polygon(s)” will refer to the upcoming polygon segments in v5/v6

Note that in either case, if a line is supposed to connect to the first point,
that first point (for poly) or segment (for segments) must be repeated at
the end of the list.

regex,bool,printf,zip,fprintf,pwd,cduser,cdproject,cd explain

Explaining regex,bool,printf,zip,fprintf,pwd,cduser,cdproject,cd 
regex (Category: Comparison) 
    [LIST REGEX] Create a LIST of True/False values
    corresponding to whether the values in LIST match the REGEX
    (for use prior to FILTER) 
bool (Category: Conversion) 
    [OBJECT] Return OBJECT as a boolean value or list. OBJECT
    can be a string, a comma separated list of values, a list of
    strings, or list of values. 
printf (Category: Output) 
    [LISTOFLISTS FORMAT] Output each list within LISTOFLISTS
    formatted according to FORMAT in Pythons {} string format
    (https://www.python.org/dev/peps/pep-3101/). 
zip (Category: Stack) 
    [LISTOFLISTS] Creates a list with parallel objects formed by
    each list in LISTOFLISTS ((1,2,3)(4,5,6)(7,8,9)) ->
    ((1,4,7)(2,5,8)(3,6,9)). 
fprintf (Category: Output) 
    [LISTOFLISTS FORMAT FILENAME] Output to FILENAME each list
    within LISTOFLISTS formatted according to FORMAT in Pythons
    {} string format
    (https://www.python.org/dev/peps/pep-3101/). 
pwd (Category: Programming) 
    Return the present working directory. 
cduser (Category: Programming) 
    Change working directory to ~/kicad/kicommand. 
cdproject (Category: Programming) 
    Return the project directory (location of .kicad_pcb file). 
cd (Category: Programming) 
    [PATH] Change working directory to PATH.
1 Like