The Application API
This section covers the basic application API. The application API consists of the main application class and a couple of classes that represent the user interface. This sections presents a selection of classes that make up the application API. These classes provide the main entry points into the application API. Further classes are documented in RBA Class Index.
All classes discussed herein are contained in the RBA namespace. In you code you either have to use qualified names (i.e. RBA::Application) or include the RBA module in you macro's namespace.
The Application class
The Application class is documented in detail in Application. It represents the application and because there is just one application, there also is just one instance of the application object. That instance can be obtained through the "instance" class method:
The application object is the main entry point into the API. It offers a couple of methods. In particular:
The MainWindow class
The MainWindow class is documented in detail in MainWindow. It represents the main application window. The main window instance can be obtained with:
The main window object is the entry point to all user-interface related objects. It offers a couple of methods. In particular:
The MainWindow can supply two events to Observer objects. See Observers, Events And Callbacks for details about observers. These are the events:
The LayoutView class
The LayoutView class is documented in detail in LayoutView. It represents one layout tab in the main window. A layout view can show multiple layouts. The specification about which layout and which cell of that layout is shown is combined in the CellView objects (CellView). Each LayoutView has a list of CellView objects corresponding to the layouts shown in the same panel.
A LayoutView object can be obtained from the main window either by obtaining the current view or by obtaining the view object for a tab given by the tab index:
# Current view: Application::instance.main_window.current_view # or short: LayoutView::current # By index: Application::instance.main_window.view(index) # Note: the index of the current view is Application::instance.main_window.current_view_index # and the number of views is Application::instance.main_window.views
The index is 0 for the first tab. Note that the value returned by current_view can be "nil" if no layout is shown.
A layout view is the container for a couple of resources. These resources are mainly display objects like annotations, markers and images. In addition, the report database system in anchored in the LayoutView object. The following resources are managed in the layout view:
The layout view offers a couple of methods. Here's a brief explanation of some of these methods:
Undo/Redo functionality is implemented by using "transactions". Transactions are groups of operations which comprise one user operation. Transactions are built internally and automatically once a transaction is initiated. Most operations performed in the framework of the LayoutView and Layout objects are tracked within these transactions. When a transacting is finished, it needs to be committed. After that, a new operation will be available for "Undo" or "Redo".
begin view.transaction("Some operation") ... do your thing here ... ensure view.commit end
Manipulating the selection
The selection of geometrical objects can be manipulated by providing the necessary ObjectInstPath objects. Each such object provides a "pointer" to a shape or instance through the hierarchy. Specifically it lists all the cells and their instantiation transformations down to the shape selected. By accumulating these selections, a shape can be addressed in a flat view, even if the shape is instantiated many levels down in the hierarchy.
Generating such instantiation path object is somewhat tedious, but usually the requirement is not to generate such paths, but to take an existing selecting, manipulate it somehow and then reset it back. This is fairly easy by taking a copy of the selection, manipulation of the shapes and setting the manipulated selection as the new one.
The following is a sample which replaces all shapes by their hull polygons. Note that is provides undo/redo support through "transaction":
view = mw.current_view begin view.transaction("Convert selected shapes to polygons") sel = view.object_selection sel.each do |s| if !s.is_cell_inst? && !s.shape.is_text? ly = view.cellview(s.cv_index).layout # convert to polygon s.shape.polygon = s.shape.polygon end end view.object_selection = sel ensure view.commit end
Events to observers
The LayoutView can supply several events to Observer objects. See Observers, Events And Callbacks for details about observers. These are the events:
Working with layer properties
The API provides methods by which the layer properties list of the layout view can be traversed and manipulated in many ways. In particular:
Many of these functions use LayerPropertiesIterator objects (LayerPropertiesIterator) to identify entries in the layer tree. Such an object is basically a pointer into the tree. The term "iterator" refers to the fact, that such a pointer can be moved to neighboring entries in the layer tree. By default, the LayerPropertiesIterator performs a preorder, depth-first traversal of the layer properties tree (the virtual root object is omitted). This is how to work with LayerPropertiesIterator objects:
layout_view = Application::instance.main_window.current_view # Get the iterator for the first entry: lp = layout_view.begin_layers # advance to the next entry (preorder, depth-first traversal): lp.next # advance to the next sibling lp.next_sibling(1) # advance to the previous sibling lp.next_sibling(-1) # move down in the hierarchy to the first child lp.down_first_child # move down in the hierarchy to the last child lp.down_last_child # move up to the parent node lp.up # get the value of the current node props = lp.current
The LayerPropertiesIterator has a couple of attributes:
Iterators can be compared against each other. If two iterators point to the same object, the equality operator "==" returns true.
Note that deleting a layer entry invalidates any layer iterator. Hence you cannot delete entries inside an iterator loop. You can however, collect layers to delete and pass them to the LayoutView's delete_layers method. The following code sample demonstrates three ways to delete all layers:
layout_view = RBA::Application::instance.main_window.current_view # without iterating: while !layout_view.begin_layers.at_end? layout_view.delete_layer(layout_view.begin_layers) end # collecting layers and using delete_layers: iters =  li = layout_view.begin_layers while !li.at_end? iters.push(li.dup) li.next end layout_view.delete_layers(iters) # all of which is equivalent to: layout_view.clear_layers
The actual entry that the iterators "current" property points to is a LayerPropertiesNode object. This object is derived from the LayerProperties class (LayerProperties). The LayerProperties object holds the display properties of the entry. The LayerPropertiesNode represents a LayerProperties object embedded in a layer properties hierarchy. In particular for embedded nodes, the parent may override or contribute attributes which are merged to the child entries' properties.
The LayerPropertiesNode object contributes only a few methods, namely:
The actual properties of the layer are accessible through methods of the LayerProperties object. Since the parent node may override or contribute properties, a LayerProperties object has a twofold identity: the way it appears finally ("real") and the way it is configured ("local"). The property accessors have a "real" parameter and deliver the real value if this parameter is set to true and the local value otherwise. There are also convenience methods which always deliver the "real" value.
Currently, LayerProperties entries cannot be manipulated directly. Instead, you have to create a copy, manipulate the value and set the new properties. For example:
lp = layout_view.begin_layers # create a copy with the effective value props = lp.current.flat # manipulate it props.width = 2 props.fill_color = 0x80ff40 # install the new settings layout_view.set_layer_properties(lp, props)
For the same reason, it is not possible to directly manipulate the tree. Instead, it is possible to get and manipulate a node for example by adding child nodes:
lp = layout_view.begin_layers # create a copy that we can manipulate props = lp.current.dup # add two child nodes props.add_child(RBA::LayerProperties::new).source = "100/0" props.add_child(RBA::LayerProperties::new).source = "101/0" # replace the node by the new one: layout_view.replace_layer_node(lp, props)
New entries can be created by using LayoutView's insert_layer method and a LayerPropertiesIterator to specify the location where the node shall be created. Here is an example how to create a child entry using that technique. Please note how "down_first_child" is used to navigate into the node's child space which works even if there are no children yet:
lp = layout_view.begin_layers # let the iterator point to the first child, even if it does not exist lp.down_first_child # (lp.current may not be valid, but still lp is a valid insert position) # prepare a new entry for insert: props = RBA::LayerProperties.new props.source = "100/0" # insert the child node: layout_view.insert_layer(lp, props) # now, lp points to a valid object: lp.current.source == "100/0"
The LayerProperties object (LayerProperties) represents one entry in the layer properties tree and has a couple of basic properties. For each of these properties, a getter for the real and local value exists as well as a setter that installs a local value. For example, for the width property, the following methods are defined:
Width is a "weak" property. That means that for computing the effective width, child nodes can override the settings inherited from the parent nodes. A width of 0 is considered "not set" and does not override parent defined widths. Other properties like visibility are "strong", i.e. the parent can override the properties set for it's children. Another form of combination is "additive" where the effective property value is the "sum" (or in general combination) of all local properties from parent to child.
Some properties like "fill_color" do not have a neutral value but instead they can be cleared (in that case with "clear_fill_color"). The LayerProperties object can be asked whether a fill color is set using the "has_fill_color?" method.
This is a brief list of properties:
In addition, a couple of getters for computed and derived values are present (i.e. "eff_frame_color"). There are no setters for these properties. The effective frame color for example delivers the frame color which results from combining the frame color and the frame brightness.
The CellView class
The CellView (CellView) identifies the cell drawn and the context the cell is drawn in. A CellView can be created as a object but usually it is obtained from a LayoutView object. In the following example, the active cell view is used:
Alternatively, a cell view can be addressed by index:
lv = RBA::Application::instance.main_window.current_view num_cellviews = lv.cellviews # number of cell views lv.cellview(0) # first one
A cellview carries the following information:
Currently, the cell view is a information object delivered by the layout view. Currently, a cell view object cannot be manipulated to change the cell. Instead, use LayoutView's "select_cell" method for example.
Unspecific and specific path, context cell
In addition to the cell the cell view specifies how the cell is embedded in the hierarchy. Embedding can happen in two ways: an unspecific and a specific way. Both ways contribute to a path which leads from a top cell to the cell drawn.
The first part is always the unspecific path. This path specifies, where the cell drawn is located in the cell tree. That has no effect on the drawing, but is determines what entry in the cell tree is selected. Giving a path for that information is required, because a cell can be child of different cells which itself can be children of other cells. The unspecific path lists the top cell and further cells which are all direct or indirect parents of the cell addressed.
The unspecific path ends at the "context cell" which usually is identical to the cell addressed by the cell view. However, KLayout allows addressing of a specific instance of a direct or indirect child cell as the actual cell. In that case, the specific path comes into play. Bascially that means, that a cell is drawn within a context of embedding layout. The specific path leads from the context cell to the cell view's target cell and consists of specific instances (hence the name "specific path"). The "descend" and "ascend" feature bascially adds or removes instances from that path.
The unspecific path can be obtained with the CellView#path method, the specific path with the CellView#context_path method. The unspecific path is just an array of cell indexes specifying the top cell and further cells down to the context cell and includes the context cell. The specific path is an array of InstElement objects (InstElement). Each InstElement object describes a specific instantiation (a cell instance plus information when a specific array instance is addressed). When there is no context, the specific path is an empty array.
The Image class
Images can be placed onto the drawing canvas and display colored or monochrome images below the layout. Images are represented by Image objects (Image). Basically an image is a two-dimensional array of pixel values with a specification how these pixels are to be displayed on the canvas. An image can be created an placed on the canvas like this:
lv = RBA::Application::instance.main_window.current_view image = RBA::Image::new("image.png") lv.insert_image(image)
An image can be configured by using different properties and attributes:
An image can be transformed using one of the Image#transformed methods. It can be hidden or shown using the Image#visible= method. The bounding box of the image can be obtained with the Image#box method.
The Annotation class
Annotations (Annotation) are basically rulers but can be used for other purposes as well, i.e. to simply add a text object. Annotations, like images, are objects stored in the LayoutView and can be selected, deleted, transformed etc.
Programmatically, annotations are created that way:
lv = RBA::Application::instance.main_window.current_view ant = RBA::Annotation::new ant.p1 = RBA::DPoint.new(0.0, 0.0) ant.p2 = RBA::DPoint.new(100.0, 0.0) lv.insert_annotation(ant)
The annotation carries a couple of properties, which copy the properties that can be set using the ruler properties dialog for example. The most important properties are the two positions (start and end position) accessible through the Annotation#p1 and Annotation#p2 properties, the style (Annotation#style property) and the outline (Annotation#outline property).
Annotations cannot be manipulated directly. But annotations can be replaced by new annotations. For replacement, the annotation ID is the key. The following example demonstrates how rulers are manipulated. In this example, the style of all rulers is set to "arrow on both sides". Note, how in this example transactions are used to implement undo/redo:
view = RBA::LayoutView::current begin view.transaction("Restyle annotations") view.each_annotation do |a| adup = a.dup adup.style = RBA::Annotation::StyleArrowBoth view.replace_annotation(a.id, adup) end ensure view.commit end
The Marker class
A marker is a temporary highlight object. A marker is represented by the Marker class (Marker). Markers appear when they are created and disappear when they are destroyed. Since destruction by the garbage collector happens at undefined times, the destroy method can be used to destroy the marker explicitly. Markers accept some plain shapes (i.e. a Box) which will be displayed as the marker. Markers can be configures in manifold ways, i.e. the colors, the fill pattern, line width etc. See the class documentation for details about the configuration properties.
This is how to create and destroy a marker:
lv = RBA::Application::instance.main_window.current_view marker = RBA::Marker.new(lv) marker.set(RBA::DBox::new(0.0, 0.0, 100.0, 200.0)) # to hide the marker: marker.destroy
Markers are temporary objects intended for highlighting a certain area or shape. Markers are not persisted in sessions nor can they be edited.
The Plugin and PluginFactory classes
Plugins (Plugin) are objects which provide modular extensions of KLayout. Plugins are the only way to handle mouse events in the canvas. The basic operation of a plugin is the following:
The PluginFactory itself acts as a singleton per plugin class and provides not only the ability to create Plugin objects but also a couple of configuration options and a global handler for configuration and menu events. The configuration includes:
A PluginFactory must be instantiated and register itself. Menu items and configuration options should be set before the object is registered. Upon registration, a unique name must be specified for the plugin class. Also, the tool button title and optionally an icon can be specified.
The main objective of the PluginFactory class however is to create the actual plugin object. For this, the create_plugin method needs to be reimplemented. The implementation is supposed to create an object of the specific class.
The actual implementation of the plugin is a class derived from Plugin (Plugin). The plugin comes into life, when it is activated. That is, when the tool button is pressed that is associated with the plugin. When the plugin is activated, the Plugin#activated method is called. The method can be reimplemented in order to prepare the plugin for taking actions on mouse events. When the plugin is not longer active, i.e. because another mode has been selected, the Plugin#deactivated method is called.
Every plugin has the ability to receive and intercept mouse events. Various mouse events are available: mouse moved, mouse button clicked (button pressed and released), mouse button double clicked, mouse button pressed, mouse button released, entry or leave of the window and agitation of the mouse wheel. Each event follows a certain protocol depending whether the plugin is active or not. In addition, plugins can request exclusive control over the mouse by "grabbing" the mouse. Each event is associated with a certain callback. The callback has a parameter - "prio" - which determines the role of the event. The protocol is described here:
In an mouse event handler, the plugin can take any action, i.e. transform objects or create/remove markers. This allows implementing of interactive functionality upon KLayout's canvas object. Using "set_cursor", the plugin can set the mouse cursor to a specific shape for example. A plugin should consider implementing "drag_cancel" in order to terminate any pending dragging operations. Plugin#drag_cancel is called by KLayout to regain control over the mouse in certain circumstances and is supposed to put the plugin into a "watching" instead of "dragging" state.