diff --git a/src/lay/doc/manual/main_window.png b/src/lay/doc/manual/main_window.png index e09b12e88..7dae120f8 100644 Binary files a/src/lay/doc/manual/main_window.png and b/src/lay/doc/manual/main_window.png differ diff --git a/src/lay/doc/programming/events.xml b/src/lay/doc/programming/events.xml index 761cff3de..ee9b4e657 100644 --- a/src/lay/doc/programming/events.xml +++ b/src/lay/doc/programming/events.xml @@ -14,7 +14,7 @@
In some places, the API requires to attach code to an event. An event could be a menu item which is selected or a change of some status which might require some action. The API allows implementation of specific code which is - called in that case. This enables us to implement initialisation functionality for example or the functionality + called in that case. This enables us to implement the functionality behind a menu item. In this text we will refer to such functionality by the general term "callback". In general a callback is custom code that is called from the API in contrast to API code that is called from the custom code.
@@ -24,7 +24,7 @@+ The "Observer" class which was there prior to KLayout 0.25 has been dropped in favour of the more + flexible events. It is no longer supported. +
+@@ -98,65 +103,10 @@ end end -
- Using the Observer pattern requires an additional object. Again reimplementation - allows attaching custom functionality to the callback. In contrast to the Strategy pattern, - the interface is very generic and can supply only few parameters, if there are parameters - at all. -
- -
- An observer is an object of a class derived from the Observer class (
module MyMacro
-
- include RBA
-
- class MyObserver < Observer
- def signal
- Application::instance.main_window.message("Selection changed", 1000)
- end
- end
-
- observer = MyObserver::new
- Application::instance.main_window.current_view.add_selection_changed_observer(observer)
-
-end
-
- - In this example, an observer is created and attached to the "selection_changed" event of - LayoutView using "add_selection_changed_observer". When the selection changes, the "signal" - method of the observer is called. By reimplementing that method we show a short message - in the MainWindow's status bar. -
- -- Because Ruby is a dynamic language and allows overriding of methods per instance, we can simplify - the example further which avoids having to create a new class: -
- -module MyMacro
-
- include RBA
-
- observer = MyObserver::new
- def observer.signal
- Application::instance.main_window.message("Selection changed", 1000)
- end
-
- Application::instance.main_window.current_view.add_selection_changed_observer(observer)
-
-end
-
- Events are the callback variant that is the easiest one to use. Using an event it is possible + Events are the callback variant which is the easiest one to use. Using an event it is possible to directly attach a block of code to a callback. An event has a specific signature, i.e. the parameters it provides. The block can obtain this parameters by listing them in it's argument list.
@@ -211,7 +161,7 @@ endIf the Qt binding is available (see ), Qt signals - are available as events which simplifies the implementation of a Qt dialog. In this example, + are implemented as events. This way it's very simple to create a Qt dialog. In following example, the "textChanged" signal of QLineEdit is attached a code block which copies the text of the input field to the label below:
@@ -230,12 +180,34 @@ end # implement the textChanged signal as event: input.textChanged { |text| label.text = text } + dialog.exec + end- Please note that unlike Qt signal/slots, this technique does not allow to attach multiple - handlers to one event/signal. + Using the += operator on the event, multiple handlers can be added to a signal:
+module MyMacro
+
+ include RBA
+
+ dialog = QDialog::new(Application::instance.main_window)
+ layout = QVBoxLayout::new(dialog)
+ input = QLineEdit::new(dialog)
+ label1 = QLabel::new(dialog)
+ label2 = QLabel::new(dialog)
+ layout.addWidget(input)
+ layout.addWidget(label1)
+ layout.addWidget(label2)
+
+ # two signal consumers:
+ input.textChanged += lambda { |text| label1.text = text }
+ input.textChanged += lambda { |text| label2.text = text.reverse }
+
+ dialog.exec
+
+end
+
diff --git a/src/lay/doc/programming/index.xml b/src/lay/doc/programming/index.xml
index f950b8337..af91a3d75 100644
--- a/src/lay/doc/programming/index.xml
+++ b/src/lay/doc/programming/index.xml
@@ -20,7 +20,6 @@
build.sh -with-qtbinding ...
- The API provided covers the functionality of Qt 4.6. To build KLayout with Qt binding, - at least Qt version 4.6.2 is required. The API covers the following Qt modules: + The API provided covers the functionality of a certain Qt version. Currently this is Qt 4.6 and Qt 5.5. + The API covers the following Qt 4 and Qt 5 modules:
diff --git a/src/lay/doc/programming/ruby_binding.xml b/src/lay/doc/programming/ruby_binding.xml index 37fd9a846..805c4f8b0 100644 --- a/src/lay/doc/programming/ruby_binding.xml +++ b/src/lay/doc/programming/ruby_binding.xml @@ -79,8 +79,8 @@
RBA/GSI follows a simple principle that significantly simplifies the implementation: who created - an object is responsible for cleaning it up. In different words: a reference is not transferred between C++ and Ruby space. - Thus we have to consider two cases: The + an object is responsible for cleaning it up. In other words: a ownership of an object is usually + not transferred between C++ and Ruby space. Thus we have to consider two cases: The object is created in Ruby or the object is created in C++ code. Literally the object "lives" in Ruby space or in C++ space. In both cases, there is a pair of objects, but one of them is controlling the other. @@ -121,12 +121,13 @@ cell.shapes(layer).insert(box) Ruby to C++. RBA handles that case by explicitly transferring control when a QObject or one of the derived objects is created with a parent reference in Ruby code. Qt implements it's own mechanism of controlling the lifetime which includes monitoring of the lifetime of child objects. This feature makes transferring the - control feasible for these kind of objects. + control feasible for these kind of objects. For some Qt methods which are know to transfer the ownership + of an object, the ownership is transferred explicitly.
An object living in Ruby space can be explicitly deleted to free resources for example. For this, the - "destroy" method is provided. This method will only deleted the C++ object and not the Ruby object. However, + "_destroy" method is provided. This method will only deleted the C++ object and not the Ruby object. However, the Ruby object will become invalid and calling a method on such an object will result in an error.
@@ -137,36 +138,12 @@ cell.shapes(layer).insert(box) Ruby object is still alive but the C++ object is deleted. In that case, the flavor of the link between the Ruby proxy and C++ object is important: if the link is bidirectional, the C++ object will inform the Ruby proxy that the reference will become invalid. The Ruby proxy will mark itself as being invalid and - will block further calls to methods. If the link is unidirectional that is not possible and this situation - bears the danger of invalid references with fatal consequences if an attempt is made to call a method - then. + will block further calls to methods. Object supporting this reference binding are the API classes + and "bigger" database objects such as Cell or Layout.- Fortunately that case is rare. A situation frequently encountered is that: -
- -// C++:
-const Box &Polygon::bounding_box() const { return m_bounding_box; }
-
-# Ruby:
-polygon = RBA::Polygon::new
-...
-box = polygon.bounding_box
-polygon = nil
-... after some time the polygon is deleted and if box was a reference this would crash:
-puts box.width
-
- - From that example we learn that we shall be careful by exposing aggregations - through references. The solution is to create a copy from a - const reference. The copy then lives in Ruby space and is basically detached from the - C++ object. -
- -- If copying is not an option because the object is too heavy, a bidirectional link will be used - in most cases. For example: + For example:
main_window = ... # the RBA::MainWindow object @@ -176,10 +153,19 @@ view = main_window.current_view main_window.close_all # this will fail, because the view is a Ruby proxy that knows that the C++ object # has been deleted: -view.load_layout(...)+view.load_layout(...) +# You can check this by asking "_destroyed?". This will return "true": +view._destroyed?
- Calling "destroy" on an object living in C++ space is not safe in general. In some cases, this can have + For "lightweight" objects such as the geometry primitives (Box, Polygon etc.), the link is unidirectional + and lifetime monitoring is not possible. This situation + bears the danger of invalid references with fatal consequences if an attempt is made to call a method + then. Fortunately this case is rare and usually mitigated by providing an object clone. +
+ ++ Calling "_destroy" on an object living in C++ space is not safe in general. In some cases, this can have fatal consequences (i.e. destroying the MainWindow object). An exception from that rule are Qt objects because Qt does lifetime monitoring internally and destroying an object from the outside (Ruby) is a valid operation in most cases (although there are exceptions). @@ -388,8 +374,10 @@ A::new.f(x)
- Some functions support hashes for arguments and return types. Unlike Ruby, C++ hashes - (maps) are strictly typed, so it's important to provide the right key and value pairs. + Associative containers (std::map, QHash etc.) are mapped to Ruby hashes. + Unlike Ruby, C++ associative containers are strictly typed, so it's important to provide the right key and value pairs. +
+- Before version 0.21 and the development IDE, Ruby scripts had to be prepared externally and KLayout had to be - restarted to use them. In addition, binding a script to a menu entry required some coding. - This method still is supported for version 0.22, hence it is described here. It is provided for backward compatibility - and because KLayout can be used as a standalone engine for RBA scripts without a user interface. -
- -- To use RBA, the script location must be passed to KLayout using the "-r" option (in this example "hello_world.rb"): -
- --klayout -r hello_world.rb- -
- If used this way, all RBA functionality must be put into one script. Usually, this script will provide all the - classes and definitions required and register new menu items and handlers. -
- -- The ruby script given with the "-r" option is executed before the actual application is - started. In fact, the application execution is initiated by the script, if one is given. - In order to make the application start, the ruby script must contain at least this statement: -
- --RBA::Application.instance.exec- -
- "RBA" is the module provided by KLayout. "Application" is the main controller class (a singleton) - that refers to the application as a whole. It provides the "exec" method which runs the application - and returns if the main window is closed. -
- -- In most cases, the script will perform initialization steps before calling "exec" and may do cleanup - once the application returned. Initialization may involve loading of layouts, registering menu items, - initializing other resources etc. However, the "exec" call can be omitted and then KLayout acts as - a pure interpreter for the script. -
- -- In larger applications - however, source code is usually organised into libraries and a main code part. Libraries and - supplementary code can be loaded prior to the loading of the main source with the "-rm" option. Files loaded - with this option do not need to (and in fact must not) contain the "RBA::Application.instance.exec" call. This allows - providing independent libraries and initialisation code to a RBA script environment: -
- --klayout -rm setup1.rb -rm setup2.rb -r hello_world.rb- -
- RBA code can be installed globally by creating a file with suffix ".rbm" in the same directory than the - KLayout binary. If such files are encountered, they will be executed automatically - before all files specified with "-rm" and "-r" are read. -
- -- This example script registers a new menu item in the toolbar, which displays a message box saying "Hello, world!" - when selected, and runs the application: -
- -
- class MenuHandler < RBA::Action
- def triggered
- RBA::MessageBox::info( "Info", "Hello, world!", RBA::MessageBox::b_ok )
- end
- end
-
- app = RBA::Application.instance
-
- $menu_handler = MenuHandler.new
- $menu_handler.title = "RBA test"
-
- menu = app.main_window.menu
- menu.insert_item("@toolbar.end", "rba_test", $menu_handler)
- menu.insert_item("tools_menu.end", "rba_test", $menu_handler)
-
- app.exec
-
- - This simple example already demonstrates some important concepts: -
- -- Documentation for the various classes involved can be found in . -
- -- To give the menu callback a more "ruby style" look, a wrapper can be created what allows - to attach code to the menu in the style of a ruby iterator. Now the callback uses "yield" to - execute the code attached to the menu. In addition, the menu item now uses a icon and a keyboard - shortcut ("Shift+F7"): -
- -
- class MenuHandler < RBA::Action
- def initialize( t, k, i, &action )
- self.title = t
- self.shortcut = k
- self.icon = i
- @action = action
- end
- def triggered
- @action.call( self )
- end
- private
- @action
- end
-
- app = RBA::Application.instance
-
- $menu_handler = MenuHandler.new( "RBA test", "Shift+F7", "icon.png" ) {
- RBA::MessageBox::info( "Info", "Hello, world!", RBA::MessageBox::b_ok )
- }
-
- menu = app.main_window.menu
- menu.insert_item("@toolbar.end", "rba_test", $menu_handler)
- menu.insert_item("tools_menu.end", "rba_test", $menu_handler)
-
- app.exec
-
- - Starting with version 0.21, RBA features "events". Events allow to specify a Ruby block which - is called when a certain condition takes place. Using events eliminates the need for - deriving a from an existing class. In particular, with version 0.21, RBA::Action features one - event called "on_triggered". A block associated with this event is called, when the action - is triggered. -
- -- With events the example looks like that: -
- -
- app = RBA::Application.instance
-
- $menu_handler = RBA::Action.new
- $menu_handler.title = "RBA test"
- $menu_handler.shortcut = "Shift+F7"
- $menu_handler.icon = "icon.png"
-
- # install the event
- $menu_handler.on_triggered {
- RBA::MessageBox::info( "Info", "Hello, world!", RBA::MessageBox::b_ok )
- }
-
- menu = app.main_window.menu
- menu.insert_item("@toolbar.end", "rba_test", $menu_handler)
- menu.insert_item("tools_menu.end", "rba_test", $menu_handler)
-
- app.exec
-
- - KLayout can be used as a RBA interpreter without user interface. That allows implementation of - layout processing scripts using the provided RBA bindings to the layout database objects, namely RBA::Layout. - You cannot use user interface objects - in that mode and the RBA::MainWindow instance will be nil. You can pass parameters from the command line to - the script by defining Ruby variables. -
- -- Here is an example which reads a layout and converts it to OASIS with some special settings: -
- -- ly = RBA::Layout.new - ly.read($input) - - gzip = false # or true to use gzip on the file - - # special settings for OASIS output - opt = RBA::SaveLayoutOptions::new - opt.format = "OASIS" - opt.dbu = 0.0001 - opt.oasis_write_cblocks = true - opt.oasis_strict_mode = false - opt.oasis_compression_level = 10 - - ly.write($output, gzip, opt)- -
- Assume that script is saved to "write_oas.rb". To run that script, use the following KLayout call: -
- --klayout -rx -r write_oas.rb -z -rd input=in.gds -rd output=out.oas- -
- The two "-rd" options will instruct KLayout to define two Ruby variables for the input and output file name. They can be used - as "$input" and "$output" in the script. -
-"-z" will disable the user interface. Therefore, this - KLayout call can be used in scripts and on servers without X connection for example. -
-"-rx" will - disable all implicitly loaded scripts such as autorun macros which speeds up application start and - avoids undesired side effects. -
- -