This chapter is about programming extensions for KLayout using the integrated Ruby API (RBA) or Python API (pya).
To use RBA scripts, KLayout must be compiled with the Ruby interpreter. Check under "Help/About" whether support is available. If there is, the "Build options" will include a "Ruby" or "Python" interpreter or both. RBA scripts require a Ruby interpreter. To use pya scripts, Python support must be included.
KLayout comes with the Qt library included into the Ruby or Python API. This means, KLayout scripts can access the full Qt API if Qt binding is available. Check whether "Qt bindings for scripts" is included in the build options on the "Help/About" page.
Basically there are scripts and macros:
Before you start, please make yourself familiar with the macro development integrated environment (About Macro Development). This documentation also assumes that you familiar with the Ruby programming language. There are numerous books and tutorials about Ruby. The most famous one is the "pickaxe book" (Programming Ruby - The Pragmatic Programmers Guide) by Dave Thomas. If you are familiar with Ruby there is a technical article about the way Ruby and KLayout's core are integrated (The Ruby Language Binding). There are special articles about the integrated Qt binding (The Qt Binding) and PCell programming (Coding PCells In Ruby). If you want to use Python, please read the python implementation article (Using Python) for details about how to translate Ruby samples into Python and specific details of the Python integration.
An introduction into the basic concepts of the KLayout API are given in the article about the application API (The Application API) and about the database API (The Database API).
The first sample is already a complete macro which counts all selected paths, boxes, polygons or text objects. It demonstrates how to set up a macro, how to deal with the selection and how to access the layout database.
Here is the code:
module MyMacro include RBA app = Application.instance mw = app.main_window lv = mw.current_view if lv == nil raise "Shape Statistics: No view selected" end paths = 0 polygons = 0 boxes = 0 texts = 0 lv.each_object_selected do |sel| shape = sel.shape if shape.is_path? paths += 1 elsif shape.is_box? boxes += 1 elsif shape.is_polygon? polygons += 1 elsif shape.is_text? texts += 1 end end s = "Paths: #{paths}\n" s += "Polygons: #{polygons}\n" s += "Boxes: #{boxes}\n" s += "Texts: #{texts}\n" MessageBox::info("Shape Statistics", s, MessageBox::Ok) end
To run the macro, create a new macro in the macro development IDE: choose "Macros/Macro Development". Create a new macro using the "+" button. Rename the macro to a suitable name. Copy the code above into the text. Load a layout, select some objects and in the macro development IDE press F5. A message box will appear the tells us how may boxes, polygons etc. we have selected.
If we look at the code, the first observation is that we put the whole script into our own namespace (we can use any name which is not used otherwise). The advantage of that approach is that we can import the "RBA" namespace which makes life somewhat easier. The RBA namespace contains all KLayout classes and constants. If we would import the RBA namespace into the main namespace we would do that for all other scripts in KLayout since the main namespace is a common resource for all scripts.
The first thing we do inside the macro is to access the layout view:
app = Application.instance mw = app.main_window lv = mw.current_view if lv == nil raise "Shape Statistics: No view selected" end
The Application class (Application) is a representative for the KLayout application. Since there is only one application, it is a singleton. We can obtain the singleton instance with the class method ("static" in the language of C++) "instance". It delivers a reference to the only Application object which is the main entrance to all internals of KLayout.
The next object which is important is the MainWindow object (MainWindow). Currently there is only one MainWindow object which can be obtained with the "main_window" method of the Application object. The MainWindow object represents the application's window and manages the top level visual objects of the application. The main visual components of the main window are the menus, the tool panels (cell tree, layer list, tool box, navigator ...) and the layout views.
The layout view is the representation of a layout tab (LayoutView). That is basically the window to the layouts loaded into that tab. All related information such as the display settings, the zoom area, the layer properties and the information about the cell shown, the hierarchy levels and further settings go here.
A main window can display multiple tabs. Hence there are multiple LayoutView objects available. The currently selected tab can be addressed with the "current_view" method. This method delivers the LayoutView object associated with that tab. If no layout is loaded, that method returns nil (the Ruby for "nothing")
Actually the preparation step can be simplified without needing the Application and MainWindow object. For demonstration purposes it was included however. Here is the short version:
lv = LayoutView.current || raise "Shape Statistics: No view selected"
The actual layouts loaded are entities separated from the views. Technically, there is a many-to-many relationship between layout views and layout objects. A layout view may display multiple layouts and one layout may be displayed in multiple layout views. In addition, a layout view can address different cells from a layout. A layout view has a current cell and a path that leads to that cell. The path consists of a specific and unspecific part. The unspecific part of the path tells where the cell we show as the current cell is located in the cell tree. The unspecific part is a tribute to the fact that a cell can appear in different branches of the cell tree (i.e. as child of cell A and cell B). The specific part of the path addresses a specific instance of within some cell (the "context cell") above in the hierarchy. A specific path is created when we descend down in the hierarchy along a certain instantiation path.
Layout, current cell, context cell, specific and unspecific path are combined into the CellView object (CellView). A layout view can have multiple cell views corresponding to the different layouts that can be loaded into a panel. The cell views do not necessarily have to point to different layouts.
For our sample we don't need the CellView objects, because we get all information directly from the view. But the concept of that object is important to understand the API documentation. Here we ask the layout view for all selected objects and collect the object counts:
lv.each_object_selected do |sel| shape = sel.shape if shape.is_path? paths += 1 elsif shape.is_box? boxes += 1 elsif shape.is_polygon? polygons += 1 elsif shape.is_text? texts += 1 end end
"each_object_selected" is a method of the LayoutView object. More precisely it's an iterator which calls the given block for each selected object. Since the layout view can show multiple layouts, the selected objects may originate from different layouts. In addition, the object may be selected in a child cell of the current cell. Hence, the selection is described by a cell view index (indicating which layout it resided in), an instantiation path (a sequence of instances leading to the cell containing the selected object from the current cell) and the actual object selected. That information is combined into an ObjectInstPath object (ObjectInstPath).
In our case we are not interested in the cell view that shape lives in. Neither are we in the instantiation path. Hence all we need is the shape and we can obtain it with the "shape" method. This method delivers a Shape object (Shape), which is some kind of pointer (a "proxy") to the actual shape. The actual shape is either a polygon, a box, a text or a path. The Shape object has multiple identities and we can ask it what shape type is represents. For that, the Shape object offers methods like "is_box?" etc. If we know the type we can ask it for the actual object and fetch a Polygon (Polygon), a Box (Box), a Text (Text) or a Path object (Path). For our sample however we don't need access to the actual object.
Finally we put together a message and display it in a message box:
s = "Paths: #{paths}\n" s += "Polygons: #{polygons}\n" s += "Boxes: #{boxes}\n" s += "Texts: #{texts}\n" MessageBox::info("Shape Statistics", s, MessageBox::Ok)
MessageBox (MessageBox) is a class that provides modal message dialogs through several class methods. "info" shows an information box and with the given title and message. The third parameter indicates which buttons will be shown. In that case, one "Ok" button is sufficient because we don't want to take specific actions when the message box is closed. MessageBox is not the Qt class, which is also available (QMessageBox), but less portable in case a user does not have Qt binding enabled.
This is just a simple example, but it already illustrates some basic concepts. For a in-depth introduction into the API, read The Application API and The Database API.