RBA examples

This section contains some example scripts that hopefully are instructive and may serve as starting points for own experiments:

Using the HTML browser dialog I: a location browser

The code for this example can be found here: browser.rb.
This kind of script is written in the "traditional" (pre-0.22) style of script programming. See Traditional Ruby Programming for a description of how to run that script. See Programming Ruby scripts for a general introduction.

The HTML browser dialog is very handy to implement simple UI's based on HTML code and a client/server scheme. This setup is similar to that of the HTTP client/server pair. The BrowserDialog object acts as a HTML browser and a BrowserSource object can be used to deliver the HTML code for that browser.

More specific, each link with the "int:" scheme that the HTML browser encounters is resolved not by loading the appropriate resource but by asking the BrowserSource object to deliver the data for that URL. This scheme can be used to build user interfaces in the same way that a web application would implement a simple user interface.

In addition to simply delivering data, the BrowserSource object may perform actions on the KLayout API, such as zooming to a certain location, opening files, etc. This enables a new class of applications based on HTML and direct interaction with the application core.

The example given here employs this technique to implement a simple location browser: given a set of three locations, the user can browse to one of these locations by clicking the link. To try this application, load a layout and select the "Browser" item in the toolbar.

Using the HTML browser dialog II: a screenshot gallery

The code for this example can be found here: screenshots.rb.
This kind of script is written in the "traditional" (pre-0.22) style of script programming. See Traditional Ruby Programming for a description of how to run that script. See Programming Ruby scripts for a general introduction.

This example employs the HTML browser dialog to implement a simple screenshot gallery: by clicking on the "Add screenshot" item in the toolbar, a screenshot is taken and placed in the HTML browser window. Each screenshot will be represented by a thumbnail image and a screen-size image. The browser will display the thumbnails together with a link that will put the viewer to the original location. By clicking on the thumbnail image, the enlarged version is shown in the browser window.

Dynamic database manipulation: a "Sokoban" implementation

The code for this example can be found here: sokoban.rb.
This kind of script is written in the "traditional" (pre-0.22) style of script programming. See Traditional Ruby Programming for a description of how to run that script. See Programming Ruby scripts for a general introduction.

This toy application dynamically changes the database to realize a game arena. As a trial application, it implements one level of the famous "Sokoban" game.

Creating layouts I: the Koch curve

The code for this example can be found here: fractal.rb.
This kind of script is written in the "traditional" (pre-0.22) style of script programming. See Traditional Ruby Programming for a description of how to run that script. See Programming Ruby scripts for a general introduction.

This application creates a Koch curve which is constructed by the recursive application of a generation recipe. In our case, this recipe is implemented by instantiating cells. An exact implementation would require a cell to call itself, but this is not allowed in this framework. Instead, a set of up to 20 cells is created with each cell calling the successive one in the same fashion.

When zooming deeply into the curve, the viewer gets pretty slow which is a consequence of the performance degradation of the underlying quad tree when the quads get really small. However, since this application is a pretty artificial one, I hope that this is not a serious imperfection ...

Creating layouts II: data visualisation

The code for this example can be found here: datamap.rb.
This kind of script is written in the "traditional" (pre-0.22) style of script programming. See Traditional Ruby Programming for a description of how to run that script. See Programming Ruby scripts for a general introduction.

This application creates a 2-dimensional function plot by employing differently colored layers to display the pixel of the data map. 256 Layers are created representing values from -1.0 to 1.0 of the function "sin(r)/r". The function is evaluated on the 500x500 grid, each grid point is assigned a value, the value is mapped to a layer and a box is created to represent the pixel.

Menus: dumping the menu structure

The code for this example can be found here: dump_menu.rb.
This kind of script is written in the "traditional" (pre-0.22) style of script programming. See Traditional Ruby Programming for a description of how to run that script. See Programming Ruby scripts for a general introduction.

This application dumps the menu structure into a HTML browser window. Beyond acting as an example, this script is quite useful to visualize the menu structure and to determine insert points when installing new items.

Editing: hierarchical propagation

The code for this example can be found here: flatten.rb.
This kind of script is written in the "traditional" (pre-0.22) style of script programming. See Traditional Ruby Programming for a description of how to run that script. See Programming Ruby scripts for a general introduction.

This application provides two new toolbar entries bound to keys F7 and F8. The first function brings up all selected shapes and instances to the current cell level and removes them from their original cell. This makes sense only if the selection contains objects from subcells (hence not in "top level only" selection mode). The second function brings up such objects one level in hierarchy. Both functions just bring up objects along the selection path, not into all instances of the selected cell. They are very similar to the "Move up in hierarchy" function in the "Edit/Selection" menu.

The new functions can only be used in "Edit" mode and require version 0.16 or later.

This code demonstrates in particular:

  • How to use the selection set of objects
  • How to modify geometrical objects (transform, erase, copy)
  • How to implement undo/redo support, which is pretty simple using the LayoutView's "transaction" and "commit" methods

Using the Qt binding: creating a custom dialog

This example employs the Qt binding of KLayout version 0.22 and later. The Qt Binding gives an introduction about the Qt binding feature. Note that you need to have Qt binding enabled in the KLayout build to use that script.

This sample is available as a template for RBA macros. Choose "Tools/Macro Development" to open the integrated development environment (IDE), create a new macro using the "Add" button (the one on the right side with the "plus" icon). Choose "Qt dialog sample" in the "Samples" group for the template. About Macro Development gives you more information about the macro IDE.

The macro will bring up a custom dialog with a screenshot button and a display area. When you press the screenshot button, it will take a snapshot of the current view and show it in the display area until you take a new one. To run it choose "Run current script" (Shift+F5) in the macro IDE. The new dialog will appear and stay open when the IDE is closed.

The following listing shows the code for this macro. Please note that unlike the other examples, this script is intended to be run from the macro IDE, and it can be bound to a menu or configured for automatic execution on startup.

module MyMacro

  include RBA

  # This class implements a dialog with a screenshot display area and a
  # screenshot button
  class ScreenshotDialog < QDialog
  
    include RBA

    def initialize(parent = nil)

      super

      setWindowTitle("Screenshot Saver")

      resize(400, 120)

      layout = QVBoxLayout.new(self)
      setLayout(layout)

      @image = QLabel.new("Press the button to fetch a screenshot", self)
      layout.addWidget(@image)

      button = QPushButton.new('Screenshot', self)
      button.setFont(QFont.new('Times', 18, QFont::Bold))
      layout.addWidget(button)
      
      button.clicked do
        view = Application.instance.main_window.current_view
        if view
          @image.setPixmap(QPixmap::fromImage(view.get_image(400, 400)))
        else
          @image.setText("No layout opened to take screenshot from")
        end
      end

    end

  end

  # Instantiate the dialog and make it visible initially.
  # Passing the $main_window will make it stay on top of the main window.
  $dialog && $dialog.destroy
  $dialog = ScreenshotDialog.new(Application::instance.main_window)
  $dialog.show

end

Using the Qt binding: transforming KLayout into a HTTP server

This example employs the Qt binding of KLayout version 0.22 and later. The Qt Binding gives an introduction about the Qt binding feature. Note that you need to have Qt binding enabled in the KLayout build to use that script.

This sample is available as a template for RBA macros. Choose "Tools/Macro Development" to open the integrated development environment (IDE), create a new macro using the "Add" button (the one on the right side with the "plus" icon). Choose "Qt server sample" in the "Samples" group for the template. About Macro Development gives you more information about the macro IDE.

This sample converts KLayout into a HTTP server running on port 8081 which delivers a HTML page with a snapshot of the current view. To run it choose "Run current script" (Shift+F5) in the macro IDE. The server will keep running when the IDE is closed.

The following listing shows the code for this macro. Please note that unlike the other examples, this script is intended to be run from the macro IDE, and it can be bound to a menu or configured for automatic execution on startup.

module MyMacro

  include RBA

  # Implements a TCP server listening on port 8081
  # This server will accept HTTP requests and deliver a HTML page containing an image
  # with the current snapshot. The idea is taken from the fortune cookie server 
  # in the Qt samples. See there for more documentation about Qt server and connection
  # objects.
  class MyServer < QTcpServer
  
    include RBA

    # Initialize the server and put into listen mode (port is 8081)
    def initialize(parent = nil)
    
      super
      
      ha = QHostAddress.new("0.0.0.0")
      listen(ha, 8081)
    
      # install an event on a new connection
      newConnection do 
    
        begin
		
          url = nil
        
          connection = nextPendingConnection
          while connection.isOpen && connection.state == QTcpSocket::ConnectedState
            if connection.canReadLine
              line = connection.readLine.to_s
              File.open("c:\\users\\matthias\\log.txt", "a") do |log|
              log.puts(line.chomp.inspect)
              end
              if line.chomp == "" 
                break
              elsif line =~ /GET\s+(.*)\s+HTTP/
                url = QUrl.new($1)
              end
            else
              connection.waitForReadyRead(100)
            end
          end
    
          view = Application.instance.main_window.current_view

          if url && url.path == "/screenshot.png" 
            # Deliver the image
            if view
              buffer = QBuffer::new
              buffer.open(QIODevice::WriteOnly.to_i)
              view.get_image(800, 800).save(buffer, "PNG")
              buffer.close
              connection.write(buffer.buffer)
            end
          # Deliver the HTML page
          elsif url && view
            connection.write("HTTP/1.0 200 OK\nContent-Type: text/html\n\n" + '<html><body><img src="screenshot.png"/></body></html>')
          elsif url
            connection.write("HTTP/1.0 200 OK\nContent-Type: text/html\n\n" + '<html><body>No view open to take screenshot from</body></html>')
          end
    
          # automatically delete when disconnected
          # Note: we cannot use RBA signal bindings for that purpose because the Ruby object might get 
          # deleted before the C++ object is. Hence we do it explicitly. 
          QObject::connect(connection, qt_signal("disconnected()"), connection, qt_slot("deleteLater()"))
          connection.disconnectFromHost()
    
        rescue 
          puts "ERROR #{$!}"
        end
    
      end
    end
    
  end

  # Start the screenshot server 
  if $server
    $server.destroy
  end
  $server = MyServer.new

end