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

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

For those watching this thread, please note that I just updated KiCommand to work with the current (5.1.5)-3 release build.

And of course there’s a new update to KiCommand to fix some additional things. I’ve changed the append command! It now matches the python functionality (adding the item to the list). If the item itself is a list, then it is added as a single item at the end of the list argument. concat now does what append used to do. There’s a whole slew of basic tests to verify KiCommand functionality. Use import kicommand.test in the Scripting Console to run the tests. On my machine, it takes about 3 minutes to execute all the tests.

There is new “SEE ALSO” section for each command help to indicate which commands are similar or that are useful in conjunction with the command.

There is more help when there’s an error, basically listing the real python error and stack trace.

regularsize is fixed. It contained old /f and -f commands which are now just / and -.

Please let me know if you have any suggestions. I’ve found the refobj and newly added valobj very useful. For example, print all modules reference values with modules refobj textfromobj print or only the selected modules with modules selected refobj textfromobj print.

You can work on a command in small steps, use print and stack liberally while testing, then aggregate all the commands into a single user definition (removing the interspersed print and stack commands). Quite useful!

Use helpcat, explain, and see commands to explore what’s available and how things are defined. Many commands are defined by a sequence of other commands. You can build and save your own commands for future use!

I’m interested to find out if people have found KiCommand useful.

1 Like

I edited the thread title

1 Like

KiCommand has been updated again, this time with a few new commands and even more tests. The tests help find bugs, and serve as examples of usage for KiCommand commands. They also can verify a proper installation and can find minor problems with different versions of KiCAD. Please report any failing tests along with the error messages found.

If you find an error that isn’t revealed in any current tests. Please let me know and I can fix it and add a new test.

The new Tutorial is going smoothly. It will walk you through the basics of KiCommand like how to use the stack, and basic calculations. Start there if you’re new to KiCommand!

Still to come are instructions on how to find and manipulate pcb elements. There is some information there, but it’s not fully organized yet. Stay tuned!

2 Likes

Added some new commands and fixed a nasty bug in the not command. So simple, it seemed like it didn’t need testing. I was wrong. Everyone should download the latest version.

I really want to reiterate that if you’ve been wondering what KiCommand can do, you should really walk through the Tutorial. There, you’ll learn the very basics of KiCommand. There’s a little bit about querying pcb components and using the filter command.

There’s definitely more to come.

Seeking help in testing! Does anyone have time to test KiCommand with either the nightly builds or earlier 4.x releases of KiCAD? There are pretty extensive tests now in KiCommand. It just requires three steps:

  1. Copy the kicommand folder into the appropriate plugins folder.
  2. Possibly create a user-level kicommand folder. This is a non-standard folder that holds user-created commands.
  3. From the Script Console, run import kicommand.test

Hopefully, there will be a wide variety of conditions tested: operating systems (Mac, Windows, and Linux), python version (2.x and 3.x), and KiCAD version.s (4.x and 5.x). Another variable might be the wx library version.

Please list here the KiCAD “About” information as well as the KiCommand commit number (such as “ 1be79a27”).

I’ll summarize the results on the KiCommand wiki page.

I’m working through a tutorial on drawing KiCAD elements using KiCommand,

Polygons are relatively new to KiCAD: they were introduced after KiCommand got started. The drawpoly command and drawsegments command are currently synonymous. I’m thinking of changing drawpoly to actually draw a KiCAD polygon element.

Would anyone have an issue if the drawpoly command changed so that it instead created a proper POLYGON KiCAD element?

Major update to KiCommand, changing how it loads so it should show up in External Plugins… menu more reliably. Also added to the KiCommand Wiki showing how to create tracks and lines (DRAWSEGMENTS). Still working toward easy assignment of nets to tracks. Added some more tests to verify installation, and updated the README.md in the github repo. Please let me know if you see any issues.

Updated KiCommand to include four new commands for dealing with tracks and nets: setnetcode, setnetname, getnetcodefromname, and getallnetnames.

KiCommand has a fairly complete list of commands and each of these commands is defined by a command string. You can use the see command to view how they are defined. For example:

  • ’getnetcodefromname see 'setnetcode see - will show you how those two commands are built from other KiCommand commands.

Another update to KiCommand.

  • showparams now instead of “showparam”: to be consistent with drawparams and params commands.
  • New tests for some geometry and drawing commands.
  • round has been fixed to handle a closed loop of drawsegments.
  • rotate now works on arcs appropriately, and should handle all EDA_ITEMs.
  • list. - make a list of lists from a single list.
  • callargs now extends either args or objects to the length of the longest list.
  • These two changes enable the ability to list all layernames with the following command:
  • board list 0,64,1 range list. GetLayerName callargs - list all layer names from 0 to 63.

KiCommand now has numerous unit tests that test both KiCommand and the underlying Python commands. There are a bunch of extra commands and some changes to the help text and categories. Let me know if there’s anything unexpected.

Work continues on expanding support for nets and netclasses. Support for netclass commands will be uploaded soon.

Major update to KiCommand to include net and netclass commands. I haven’t tested the set* commands, but the get* commands work well.

I’ve tried to standardize on get* command using an argument, while commands that are nouns (like netnamemap, netcodemap, tracks, etc) retrieve board-wide lists/maps of objects. To do this, a few commands have changed, and there are perhaps a few holdover commands that still need updating.

Through all of this, one command seemed out of place: matchreference was renamed to filterrefregex (filter reference regular expression) and a corresponding filtervalregex added to complement. The filter* commands filter the objectlist provided as an argument based on matching criteria also provided as an argument.

Development continues, and comments, discussion, or questions are welcome.

In my attempt to be very flexible in handling lists, where functions operate seamlessly on the elements of a list, I’ve run into an ambiguity. I’ve recently extended call and callargs to work on lists where you can supply a list of objects and/or a list of arguments and/or a list of functions, and it works well. For example:

  • netclassmap Default sindex GetClearance,GetTrackWidth,GetViaDiameter,GetViaDrill,GetuViaDiameter,GetuViaDrill,GetDiffPairWidth,GetDiffPairGap split call - return as a list, each of the listed values from the netclassptr object.

The detection of a list is determined by identifying an iterator. If it is an iterator, it is considered a list. With this detection, a dictionary is identified as a ‘list’ because it has an iterator. This makes it more difficult to directly call dictionary functions, such as keys() or values().

The solution is either to surround the dictionary with list and delist to get past the detection mechanism, or to redefine the new call and callargs functions into call. and callargs., which indicate an element-by-element function. Then redefine the call and callargs to not operate on lists element by element.

I’m trying to keep in mind that KiCommand is primarily an interactive command line and determine what is most useful in that context.

While I decide how to proceed, comments are welcome.

Edit: Maybe I could add calld and callargsd to indicate “direct”, where I ignore the iterator status of the objectlist. My main problem is that I like the ease of use of handling lists automatically, and I want that to be the “default” for unadorned functions.

Hi @HiGreg, i wonder if i can use your KiCommand to highlight all “equal” components in PCBnew?
So for example if i wanted to highlight all resistors with value 1k and 0603 package could i use something like

show all r 1k 0603 

or can i alter specific attributes of a footprint with it like the “lock” with something like

lock all fp

I’ve read the wiki and your read.me but you talk a lot about data types and stuff but it’s not quite clear to me if and how i could achieve the two examples from above.
If this is off topic, tell me and i open a new thread.

This is exactly on topic. First thing to note is that KiCommand is stack based, so arguments occur before the command. You can get all 1k resistors by using either a regular expression or a string match. Let’s walk through the demo board available in the kicad install directory (share/kicad/demos/complex_hierarchy). Load that board then try the following:

  • modules getvaltext print - this will print the value text of all modules

on the demo board, I see:

[u’CONN_2’, u’CONN_2’, u’CONN_2’, u’CONN_2’, u’CONN_2’, u’CONN_2’, u’1N4148’, u’1N4148’, u’1N4148’, u’1N4148’, u’1N4148’, u’1N4148’, u’1N4148’, u’1N4148’, u’1N4007’, u’15nF’, u’4.7nF’, u’150nF’, u’150nF’, u’820pF’, u’15nF’, u’4.7nF’, u’820pF’, u’LM358N’, u’ICL7660’, u’LM358N’, u’5,6K’, u’220K’, u’1K’, u’1K’, u’47’, u’220K’, u’47’, u’220K’, u’47’, u’220K’, u’470’, u’4,7K’, u’22K’, u’22K’, u’1K’, u’1K’, u’5,6K’, u’4,7K’, u’220K’, u’470’, u’220K’, u’47’, u’22K’, u’1K’, u’22K’, u’1K’, u’47uF’, u’47uF/20V’, u’47uF/63V’, u’10uF’, u’10uF’, u’MPAS92’, u’MPAS42’, u’MPAS92’, u’MPAS42’, u’MPAS92’, u’MPAS42’, u’MPAS92’, u’MPAS42’, u’4,7K’, u’4,7K’, u’78L05’]

  • modules getreftext print - print the reference text of all modules

Result:

[u’P5’, u’P6’, u’P3’, u’P4’, u’P2’, u’P1’, u’D9’, u’D4’, u’D6’, u’D7’, u’D3’, u’D2’, u’D8’, u’D5’, u’D1’, u’C3’, u’C4’, u’C14’, u’C12’, u’C5’, u’C6’, u’C7’, u’C8’, u’U3’, u’U1’, u’U4’, u’R20’, u’R22’, u’R23’, u’R24’, u’R25’, u’R26’, u’R27’, u’R28’, u’R5’, u’R4’, u’R3’, u’R21’, u’R17’, u’R7’, u’R8’, u’R9’, u’R10’, u’R11’, u’R12’, u’R13’, u’R14’, u’R15’, u’R16’, u’R18’, u’R6’, u’R19’, u’C1’, u’C2’, u’C9’, u’C10’, u’C11’, u’Q1’, u’Q2’, u’Q3’, u’Q4’, u’Q5’, u’Q6’, u’Q7’, u’Q8’, u’RV1’, u’RV2’, u’U2’]

The next one is a little tricky. Getting the path of the footprint. Try this:

  • clear modules GetFPID call GetLibItemName call '__str__ call print - get the name of the footprint

One of the items here is named “R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal”, which seems to indicate its size (but it’s not guaranteed).

Now that we know a little bit about the board, we can start to hone our command to get only the desired modules.

Using some commands to filter on the name and value, assuming resistors start with “R”:

  • modules R.* filterrefregex 1K filtervalregex print - filter on modules with reference beginning with R and value of 1K

But we want to make sure we get the 0603, so let’s assume that string is in the footprint path.

  • copy GetFPID call GetLibItemName call '__str__ call - get the library name

We’ll call the following command string the module selection command. It’s a combination of all the above:

clear modules R.* filterrefregex 1K filtervalregex copy GetFPID call GetLibItemName call '__str__ call .*0204.* regex filter

That’s the command we’ll use to find the modules. You’ll want to change that to 0603 instead of 0204. Then we need to get or set their Lock status. There’s not a built in KiCommand to lock a footprint, but we can drop into the python function.

Execute the above module selection command to get all the modules desired. Then execute only one of the following.

Execute the module selection command, then:

  • IsLocked call - get the Locked status of modules

Execute the module selection command, then:

  • true SetLocked callargs - set the Locked status of modules

Finally, check that it worked. Execute the module selection command, then:

  • IsLocked call - get the Locked status of modules

Edit:

I just pushed some new commands for your use case: getlocked, setlocked, clearlocked, getfootprintname and filterfpregex.

The locked commands mirror get/set/clear visible, but don’t exactly match the Python commands underneath (SetLocked/IsLocked).

Example:

  • modules R.* filterrefregex 1K filtervalregex .*0603.* filterfpregex copy setlocked getlocked print - get modules matching each of the three criteria, set the locked status, then retrieve the locked status and print out the result. Should result in a list of “True” values.
3 Likes

Thank you for this extensive post. I sadly will have no time to proceed with this until the end of next week, but I’ll come back and report a bit about my experience once I’m done.

A bit of background on my use case:
i assemble PCBs in small batches like 1-5 pieces, by hand. So i use the PCB file as reference for assembly but keep selecting anything in PCBnew but the whole footprint, that’s quite annoying and the reason why i want to lock them. It’s also very useful to be able to show all equal components so i don’t have to search for the bag with the 1k resistor 25 times :wink:

@Detzi, whether or not this can be done with KiCommand, I would recommend trying the InteractiveHtmlBom plugin for hand assembly. It’s meant exactly for your use case.

4 Likes

KiCommand has been tested on pre release 5.1.6 (April 20) on Windows and no changes were necessary. There is one test (the now command) that occasionally fails, but the test will be fixed in the next release.

In the last couple of releases of KiCommand, there has been much work on making command consistent, along with updates to call and callargs that will be useful to users. Please feel free to reach out with comments, questions, or suggestions.

DRAWSEGMENT considerations?

In an attempt to unify the creation of DRAWSEGMENTs of all types,
KiCommand could use a few different approaches.

In general, the approaches would include a command with arguments. There would be two goals:

  1. Fairly easy command-line creation of new segments.
  2. (possibly) allow the extraction of segment definition, modify some of the parameters, then create a new segment with the modified parameters.

Let’s talk about the possible methods of creating a new DRAWSEGMENT. To do that, we’ll first talk about the different DRAWSEGMENTs that exist. There are 6 within the source code, and 4 that are accessible from the GUI.

  1. Line (S_SEGMENT)
  2. Polygon (S_POLYGON)
  3. Bezier Curve (S_CURVE)
  4. Circle (S_CIRCLE)
  5. Arc (S_ARC)
  6. Rectangle (S_RECT)

Only the bolded DRAWSEGMENT types are available from the GUI, so we’ll focus on those. Specifying Line and Polygon is fairly straightforward and are easily described by the end points and vertex points, respectively.

Arc and circle are a little different, and they have multiple ways that might be desired to specify the points. One of these methods, for each type, is also the native method for creating the DRAWSEGMENT within Python.

Methods for specifying circle. I’ll also supply a shorthand name for each method. All points and dimensions would be specified in native units.

  1. CircleP CenterPoint, StartPoint (a point on the circle) - this is the native method to create a circle.
  2. CircleR CenterPoint, Radius
  3. CircleC CirclePoint1, CirclePoint2, Radius (+/- indicates direction, CW/CCW or RW/LW, make sure this matches Angle direction)

Methods for specifying an arc.

  1. ArcA StartPoint, CenterPoint, AngleDegrees (+/- indicates direction) - this is the native method to create an arc.
  2. ArcP StartPoint, EndPoint, CenterPoint
  3. ArcR StartPoint, EndPoint, RadiusNative (+/- indicates direction, CW/CCW or RW/LW, make sure this matches Angle direction)

In addition to the numbers that specify points and dimensions, there are additional paramters that might be useful to specify: thickness and layer. These are normally specified in KiCommand for all new elements through the draw parameters. Draw parameters allows you to specify the thickness and layer for all subsequent draw commands. The layer used for drawing allows the possibility that the same element commands can create either DRAWSEGMENTs or TRACKS, depending on whether or not the layer is a copper layer.

One basic question on command structure is: should the command itself specify the DRAWSEGMENT type, or should the DRAWSEGMENT type be a parameter? To allow for the possibility of extracting an existing DRAWSEGMENT’s specification, it probably makes more sense to extract the type as well. This way, when extracting several DRAWSEGMENTs at one time, the exact type of DRAWSEGMENT is included in the results.

This could result in two possible commands:

  • getsegment - returns SegmentType,ArgX,…
  • newsegment - takes a single (list) argument SegmentType,Argx,…

Where segment type is one of the strings: Line, Polygon, ArcA, ArcP, ArcR, CircleP, CircleR, CircleC. Perhaps obviously, getsegment would by definition return only ArcA, or CircleP (the native methods of specifying). However, this would preclude being able to get, for example, the Circle radius, manipulating that, and drawing a new DRAWSEGMENT.

I should definitely allow specifying the thickness and layer with the draw parameters, as this would become tedious in use not to. It might be interesting to allow layer and thickness to be specified by parameter as well.

Open questions:

  1. Should SegmentType be specified by argument within the segment list, a parameter outside the segment list, or a different command for each type?
  2. Should Layer and Thickness be optional parameters, or parameters using a different command?
  3. Should points within the parameters be specified linearly or though nested lists (X, Y points)?

The answers depend on exactly the use cases I’m trying to develop for. Perhaps I’ll generate the use cases in a subsequent post.

Does anyone have any opinions or additional considerations?