From d35e21be29b31c5494dac4da16da6d6686e00b4d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 9 Aug 2026 16:43:11 +0200 Subject: [PATCH] Rewriting Python samples to klayout.* modules, fixing some bugs and flaws in the samples --- .../pcell_declaration_helper.lym | 12 ++-- src/doc/doc/programming/events.xml | 14 ++--- src/doc/doc/programming/introduction.xml | 5 +- src/doc/doc/programming/python.xml | 44 +++++++++----- src/lay/lay/built_in_pymacros/qt_helper.lym | 10 +++- .../drag_box_sample_python.lym | 60 ++++++++++--------- .../editor_hooks_sample_python.lym | 21 +++---- .../lay/macro_templates/new_python_macro.lym | 3 +- src/lay/lay/macro_templates/pcell.lym | 7 +++ src/lay/lay/macro_templates/pcell_python.lym | 23 ++++--- .../macro_templates/pcell_sample_python.lym | 20 +++---- .../macro_templates/qt_designer_python.lym | 10 ++-- .../lay/macro_templates/qt_dialog_python.lym | 20 ++++--- .../lay/macro_templates/qt_server_python.lym | 30 +++++----- .../via_pcell_sample_python.lym | 34 +++++------ 15 files changed, 177 insertions(+), 136 deletions(-) diff --git a/src/db/db/built-in-pymacros/pcell_declaration_helper.lym b/src/db/db/built-in-pymacros/pcell_declaration_helper.lym index 8b763e6e7..c399e192b 100644 --- a/src/db/db/built-in-pymacros/pcell_declaration_helper.lym +++ b/src/db/db/built-in-pymacros/pcell_declaration_helper.lym @@ -8,10 +8,6 @@ @class [db] PCellDeclarationHelper < PCellDeclaration @brief A helper class to simplify the declaration of a PCell (Python version) -NOTE: in the following, "pya" can be replaced by "klayout.db" which is -the canonical module and the preferred way of addressing the -external Python library. - This class provides adds some convenience to the PCell declaration based on PCellDeclaration. PCellDeclaration is a C++ object which is less convenient to use than a Ruby-based approach. In particular this class @@ -22,10 +18,10 @@ The basic usage of this class is the following: @code -import pya +import klayout.db as kdb # Derive your PCell from PCellDeclarationHelper -class MyPCell(pya.PCellDeclarationHelper): +class MyPCell(kdb.PCellDeclarationHelper): def __init__(self): @@ -35,7 +31,7 @@ class MyPCell(pya.PCellDeclarationHelper): # your initialization: add parameters with name, type, description and # optional other values self.param("p", self.TypeInt, "The parameter", default = 1) - self.param("l", self.TypeLayer, "The layer", default = pya.LayerInfo(1, 0)) + self.param("l", self.TypeLayer, "The layer", default = kdb.LayerInfo(1, 0)) # add other parameters .. # reimplement display_text_impl @@ -71,7 +67,7 @@ we can access the layer index with the "l_layer" method: @code def produce_impl(self): - cell.shapes(self.l_layer).insert(pya.Box(0, 0, self.p*100, self.p*200)) + cell.shapes(self.l_layer).insert(kdb.Box(0, 0, self.p*100, self.p*200)) @/code Again in this sample, we used "p" to access the parameter "p". diff --git a/src/doc/doc/programming/events.xml b/src/doc/doc/programming/events.xml index 044ea331b..13d82fb91 100644 --- a/src/doc/doc/programming/events.xml +++ b/src/doc/doc/programming/events.xml @@ -85,8 +85,7 @@ end The Python version is this: -
-from pya import BrowserSource, BrowserDialog
+  
from klayout.lay import BrowserSource, BrowserDialog
 
 class MyBrowserSource(BrowserSource):
   def get(self, url):
@@ -157,8 +156,7 @@ end
   The Python version is:
   

-
-from pya import Action, MessageBox, Application
+  
from klayout.lay import Action, MessageBox, Application
 
 def on_triggered():
   MessageBox.info("A message", "The action was triggered", MessageBox.Ok)
@@ -235,8 +233,8 @@ end
   The Python version is:
   

-
-from pya import QDialog, QVBoxLayout, QLineEdit, QLabel, Application
+  
from klayout.lay import Application
+from klayout.QtWidgets import QDialog, QVBoxLayout, QLineEdit, QLabel
 
 dialog = QDialog(Application.instance().main_window())
 layout = QVBoxLayout(dialog)
@@ -285,8 +283,8 @@ end
   with the Python version:
   

-
-from pya import QDialog, QVBoxLayout, QLineEdit, QLabel, Application
+  
from klayout.lay import Application
+from klayout.QtWidgets import QDialog, QVBoxLayout, QLineEdit, QLabel
 
 dialog = QDialog(Application.instance().main_window())
 layout = QVBoxLayout(dialog)
diff --git a/src/doc/doc/programming/introduction.xml b/src/doc/doc/programming/introduction.xml
index dcc1fbd03..b91dbaade 100644
--- a/src/doc/doc/programming/introduction.xml
+++ b/src/doc/doc/programming/introduction.xml
@@ -9,16 +9,17 @@
   
   
   
+  
   
 
   

- This chapter is about programming extensions for KLayout using the integrated Ruby API (RBA) or Python API (pya). + This chapter is about programming extensions for KLayout using the integrated Ruby API (RBA) or Python API (klayout.db, klayout.layer etc.).

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. + RBA scripts require a Ruby interpreter. To use Python scripts, Python support must be compiled in.

diff --git a/src/doc/doc/programming/python.xml b/src/doc/doc/programming/python.xml index 9389c848b..d62aac1a0 100644 --- a/src/doc/doc/programming/python.xml +++ b/src/doc/doc/programming/python.xml @@ -7,6 +7,7 @@ + @@ -55,8 +56,11 @@

Apart from a few specialities and the different language of course, Python macros do not look much different from Ruby - macros. Ruby's "RBA" namespace is "pya" for Python (lowercase to conform with PEP-8). The - class and methods names are the same with very few exceptions and the documentation can be + macros. Ruby's "RBA" namespace is "klayout" for Python (lowercase to conform with PEP-8). + The "klayout" module has a number of submodules like "klayout.db", "klayout.lay" etc. + The old combined Python module "pya" is still available, but deprecated in favor of + aligning code with the standalone "klayout" Python module. + The class and methods names are the same with very few exceptions and the documentation can be used for Python too. Where necessary, a special remark is made regarding the Python implementation.

@@ -68,12 +72,12 @@
 # Python version:
 
-import pya
+import klayout.db as kdb
 
-layout = pya.Layout()
+layout = kdb.Layout()
 top = layout.create_cell("TOP")
 l1 = layout.layer(1, 0)
-top.shapes(l1).insert(pya.Box(0, 0, 1000, 2000))
+top.shapes(l1).insert(kdb.Box(0, 0, 1000, 2000))
 
 layout.write("t.gds")
 
@@ -139,7 +143,12 @@ layout.write("t.gds")
  • KLayout module: - KLayout's module is "pya" (lowercase conforming to PEP 8). + KLayout's module is "klayout" (lowercase conforming to PEP 8). + It has a number of submodules: "db", "lay", "pex", "rdb", "tl" and optionally + "QtCore", "QtGui" etc. + The built-in documentation mentiones the module containing a specific class + at the top of the class documentation. For example, "Box" is present in the + "db" submodule - i.e. it is present in "klayout.db".
  • Reserved names: @@ -154,8 +163,9 @@ layout.write("t.gds") Assignment methods (i.e. "Box#left=" are available as attributes. If there is a read accessor method too, the attribute can be read and written. For example: -
    -box = pya.Box()
    +    
    import klayout.db as kdb
    +
    +box = kdb.Box()
     box.left = 10
     box.right = box.right + 100
     
    @@ -200,8 +210,9 @@ box.right = box.right + 100
  • Iterators: Iterator binding: -
    -edges = pya.Edges()
    +    
    import klayout.db as kdb
    +
    +edges = kdb.Edges()
     ...
     for edge in edges.each():
       ...
    @@ -221,8 +232,10 @@ for edge in edges:
     
         

    Most methods support keyword arguments, for example:

    -
    # a 45 degree rotation
    -t = pya.CplxTrans(rot = 45)
    +
    import klayout.db as kdb
    +
    +# a 45 degree rotation
    +t = kdb.CplxTrans(rot = 45)

    Exceptions are some built-in methods like "assign". Keyword arguments can be used @@ -257,10 +270,11 @@ t = pya.CplxTrans(rot = 45)

  • Deep copies: - Deep copies of pya objects can be made with dup() + Deep copies of klayout objects can be made with dup() -
    -box = pya.Box(10, 20, 110, 220)
    +    
    import klayout.db as kdb 
    +    
    +box = kdb.Box(10, 20, 110, 220)
     copy_box = box.dup()
     
  • diff --git a/src/lay/lay/built_in_pymacros/qt_helper.lym b/src/lay/lay/built_in_pymacros/qt_helper.lym index 9dcb0a086..e56f7b4bf 100644 --- a/src/lay/lay/built_in_pymacros/qt_helper.lym +++ b/src/lay/lay/built_in_pymacros/qt_helper.lym @@ -9,7 +9,8 @@ python - import pya + +import pya def _qt_signal(s): """ @@ -39,9 +40,16 @@ def _getattr_with_child_objects(s, n): # If Qt binding is enabled, install the new getattr function. # Also install the qt_signal and qt_slot functions which go into the pya module. if "QObject_Native" in pya.__dict__: + pya.QObject_Native.__getattr__ = _getattr_with_child_objects pya.__dict__["qt_signal"] = _qt_signal pya.__dict__["qt_slot"] = _qt_slot + # Do the same for klayout.QtCore + import klayout.QtCore + klayout.QtCore.QObject_Native.__getattr__ = _getattr_with_child_objects + klayout.QtCore.__dict__["qt_signal"] = _qt_signal + klayout.QtCore.__dict__["qt_slot"] = _qt_slot + diff --git a/src/lay/lay/macro_templates/drag_box_sample_python.lym b/src/lay/lay/macro_templates/drag_box_sample_python.lym index ffd328910..2463dbf4d 100644 --- a/src/lay/lay/macro_templates/drag_box_sample_python.lym +++ b/src/lay/lay/macro_templates/drag_box_sample_python.lym @@ -26,12 +26,16 @@ # Register this macro as "autorun" to enable the plugin # on startup. +from klayout.lay import EditorOptionsPage, ConfigPage, Plugin, PluginFactory, MainWindow, Marker +from klayout.db import DBox, DPoint +from klayout.QtWidgets import QGridLayout, QVBoxLayout, QHBoxLayout, QLabel, QSpinBox, QLineEdit + cfg_color = "drag-box-color" cfg_width = "drag-box-width" # The widget placed into the editor options dock -class DragBoxEditorOptionsPage(pya.EditorOptionsPage): +class DragBoxEditorOptionsPage(EditorOptionsPage): """ An option page providing a single entry box for configuring the line width @@ -47,12 +51,12 @@ class DragBoxEditorOptionsPage(pya.EditorOptionsPage): super(DragBoxEditorOptionsPage, self).__init__("Options", 1) - layout2 = pya.QVBoxLayout(self) - layout = pya.QHBoxLayout(self) + layout2 = QVBoxLayout(self) + layout = QHBoxLayout(self) layout2.addLayout(layout) - label = pya.QLabel("Line width", self) + label = QLabel("Line width", self) layout.addWidget(label) - self.spin_box = pya.QSpinBox(self) + self.spin_box = QSpinBox(self) self.spin_box.setMinimum(1) self.spin_box.setMaximum(16) layout.addWidget(self.spin_box) @@ -87,7 +91,7 @@ class DragBoxEditorOptionsPage(pya.EditorOptionsPage): # The modal dialog page that appears when "Tab" is pressed -class DragBoxFocusPage(pya.EditorOptionsPage): +class DragBoxFocusPage(EditorOptionsPage): """ A (modal) option page, also called a "focus page". This page is @@ -115,21 +119,21 @@ class DragBoxFocusPage(pya.EditorOptionsPage): self.focus_page = True self.modal_page = True - self.box = pya.DBox() - self.pfix = pya.DPoint() + self.box = DBox() + self.pfix = DPoint() self.update_box = None - layout = pya.QGridLayout(self) + layout = QGridLayout(self) layout.setColumnStretch(1, 1) - label = pya.QLabel("Width", self) + label = QLabel("Width", self) layout.addWidget(label, 0, 0, 1, 1) - self.le_width = pya.QLineEdit(self) + self.le_width = QLineEdit(self) layout.addWidget(self.le_width, 0, 1, 1, 1) - label = pya.QLabel("Height", self) + label = QLabel("Height", self) layout.addWidget(label, 1, 0, 1, 1) - self.le_height = pya.QLineEdit(self) + self.le_height = QLineEdit(self) layout.addWidget(self.le_height, 1, 1, 1, 1) layout.setRowStretch(2, 1) @@ -175,11 +179,11 @@ class DragBoxFocusPage(pya.EditorOptionsPage): # issue the event (call the handler) to inform the plugin of this change if self.update_box is not None: - self.update_box(pya.DBox(l, b, r, t)) + self.update_box(DBox(l, b, r, t)) # The widget placed into the configuration page -class DragBoxConfigPage(pya.ConfigPage): +class DragBoxConfigPage(ConfigPage): """ A configuration page with a single entry box to change @@ -197,10 +201,10 @@ class DragBoxConfigPage(pya.ConfigPage): super(DragBoxConfigPage, self).__init__("Drag Box|Configure") - layout = pya.QHBoxLayout(self) - label = pya.QLabel("Color (hex, rrggbb)", self) + layout = QHBoxLayout(self) + label = QLabel("Color (hex, rrggbb)", self) layout.addWidget(label) - self.line_edit = pya.QLineEdit(self) + self.line_edit = QLineEdit(self) layout.addWidget(self.line_edit) layout.addStretch(1) @@ -217,7 +221,7 @@ class DragBoxConfigPage(pya.ConfigPage): """ dispatcher.set_config(cfg_color, self.line_edit.text) -class DragBoxPlugin(pya.Plugin): +class DragBoxPlugin(Plugin): """ The custom plugin implementation. @@ -275,14 +279,14 @@ class DragBoxPlugin(pya.Plugin): self.marker = None # reset to idle self.ungrab_mouse() - pya.MainWindow.instance().message("Box finished: " + str(self.box), 10000) + MainWindow.instance().message("Box finished: " + str(self.box), 10000) def _update_marker(self): """ updates the marker with the current box """ if self.marker is None: - self.marker = pya.Marker(self.view) + self.marker = Marker(self.view) self._configure_marker() self.marker.set(self.box) @@ -339,7 +343,7 @@ class DragBoxPlugin(pya.Plugin): plugin is activated - i.e. the mode is selected """ - pya.MainWindow.instance().message("Click on point to start dragging a box", 10000) + MainWindow.instance().message("Click on point to start dragging a box", 10000) def deactivated(self): @@ -349,7 +353,7 @@ class DragBoxPlugin(pya.Plugin): """ self._clear_marker() - pya.MainWindow.instance().message("", 0) + MainWindow.instance().message("", 0) def mouse_click_event(self, p, buttons, prio): @@ -363,15 +367,15 @@ class DragBoxPlugin(pya.Plugin): # stop dragging it and freeze the box if self.marker is None: p = self.snap2(p) - self.box = pya.DBox(p, p) + self.box = DBox(p, p) self.start_point = p self._clear_marker() self._update_marker() self.grab_mouse() - pya.MainWindow.instance().message("Drag the box and click again", 10000) + MainWindow.instance().message("Drag the box and click again", 10000) else: p = self.snap2(p, self.start_point, True, self.ac_from_buttons(buttons)) - self._update_box(pya.DBox(self.start_point, p)) + self._update_box(DBox(self.start_point, p)) self._finish() return True return False @@ -395,13 +399,13 @@ class DragBoxPlugin(pya.Plugin): self.clear_mouse_cursors() p = self.snap2(p, self.start_point, True, self.ac_from_buttons(buttons), visualize=True) self.add_mouse_cursor(p) - self.box = pya.DBox(self.start_point, p) + self.box = DBox(self.start_point, p) self._update_marker() # NOTE: we must not digest this event (i.e. return True) # to allow the mouse tracker to receive the events as well return False -class DragBoxPluginFactory(pya.PluginFactory): +class DragBoxPluginFactory(PluginFactory): """ Implements a "plugin factory". diff --git a/src/lay/lay/macro_templates/editor_hooks_sample_python.lym b/src/lay/lay/macro_templates/editor_hooks_sample_python.lym index ced2aa4a4..62a98a0c9 100644 --- a/src/lay/lay/macro_templates/editor_hooks_sample_python.lym +++ b/src/lay/lay/macro_templates/editor_hooks_sample_python.lym @@ -7,7 +7,8 @@ false pymacros python - import pya + import klayout.db as kdb +import klayout.lay as klay """ This demo installs editor hooks to indicate the forbidding regions around a shape @@ -24,7 +25,7 @@ are drawn. Space between instances: 0.4 """ -class MyEditorHooks(pya.EditorHooks): +class MyEditorHooks(klay.EditorHooks): def __init__(self): @@ -36,9 +37,9 @@ class MyEditorHooks(pya.EditorHooks): self.space = 0.0 self.spaces = { - pya.LayerInfo(1, 0): ( 0.2, pya.Region.Euclidian ), - pya.LayerInfo(2, 0): ( 0.5, pya.Region.Projection ), - pya.LayerInfo(3, 0): ( 1.0, pya.Region.Projection ) + kdb.LayerInfo(1, 0): ( 0.2, kdb.Region.Euclidian ), + kdb.LayerInfo(2, 0): ( 0.5, kdb.Region.Projection ), + kdb.LayerInfo(3, 0): ( 1.0, kdb.Region.Projection ) } self.instance_space = 0.4 @@ -61,12 +62,12 @@ class MyEditorHooks(pya.EditorHooks): p = shape.polygon if p is not None and p.num_points() < 100: - r = pya.Region() + r = kdb.Region() r.merged_semantics = (p.num_points() != 2) r.insert(p) r = r.drc_hull(self.metrics, self.space) for pp in r.each(): - m = pya.Marker(self.view) + m = klay.Marker(self.view) m.line_style = 2 m.vertex_size = 0 m.set_polygon(trans * pp) @@ -74,7 +75,7 @@ class MyEditorHooks(pya.EditorHooks): t = shape.text if t is not None: - m = pya.Marker(self.view) + m = klay.Marker(self.view) m.set_box(trans * t.bbox().enlarged(self.space)) self.markers.append(m) @@ -117,7 +118,7 @@ class MyEditorHooks(pya.EditorHooks): self.markers = [] def create_instance(self, instance, trans): - m = pya.Marker(self.view) + m = klay.Marker(self.view) m.set_box(trans * instance.bbox().enlarged(self.instance_space / self.layout.dbu)) self.markers.append(m) @@ -143,7 +144,7 @@ class MyEditorHooks(pya.EditorHooks): if path.shape is None: - m = pya.Marker(self.view) + m = klay.Marker(self.view) m.set_box(trans * (applied * path.inst().bbox()).enlarged(self.instance_space / self.layout.dbu)) self.markers.append(m) diff --git a/src/lay/lay/macro_templates/new_python_macro.lym b/src/lay/lay/macro_templates/new_python_macro.lym index 54354d1bf..9e839f2e8 100644 --- a/src/lay/lay/macro_templates/new_python_macro.lym +++ b/src/lay/lay/macro_templates/new_python_macro.lym @@ -7,7 +7,8 @@ python pymacro - import pya + import klayout.db as kdb +import klayout.lay as klay # Enter your Python code here .. diff --git a/src/lay/lay/macro_templates/pcell.lym b/src/lay/lay/macro_templates/pcell.lym index eb89f8096..25633bd77 100644 --- a/src/lay/lay/macro_templates/pcell.lym +++ b/src/lay/lay/macro_templates/pcell.lym @@ -78,6 +78,13 @@ module PCellLibModule # In lazy mode, the user has to acknowledge parameter changes before # they are executed. # end + # + # optional: + # def callback_impl(name) + # TODO: implement functionality to change user interface element + # states for PCell parameter editors based on certain events + # like value changes. + # end end diff --git a/src/lay/lay/macro_templates/pcell_python.lym b/src/lay/lay/macro_templates/pcell_python.lym index 474f91a9a..29bc0710c 100644 --- a/src/lay/lay/macro_templates/pcell_python.lym +++ b/src/lay/lay/macro_templates/pcell_python.lym @@ -7,18 +7,15 @@ false python - import pya + import klayout.db as kdb # PCell template # This macro template provides the framework for a PCell library -# It is recommended to put PCell code into namespaces. -# TODO: change the module name - # The PCell declaration # Each PCell must provide a declaration. It is recommended to use the PCell name as the class name. # TODO: change the class name -class PCell(pya.PCellDeclarationHelper): +class PCell(kdb.PCellDeclarationHelper): def __init__(self): @@ -26,8 +23,8 @@ class PCell(pya.PCellDeclarationHelper): super(PCell, self).__init__() # declare the parameters - # i.e. self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0)) - # self.param("s", self.TypeShape, "", default = pya.DPoint(0, 0)) + # i.e. self.param("l", self.TypeLayer, "Layer", default = kdb.LayerInfo(1, 0)) + # self.param("s", self.TypeShape, "", default = kdb.DPoint(0, 0)) def display_text_impl(self): # Provide a descriptive text for the cell @@ -35,10 +32,12 @@ class PCell(pya.PCellDeclarationHelper): def coerce_parameters_impl(self): # TODO: use x to access parameter x and set_x to modify its value + pass def produce_impl(self): # TODO: produce the cell content - # i.e. self.cell.shapes(self.l_layer).insert(pya.Polygon(...)) + # i.e. self.cell.shapes(self.l_layer).insert(kdb.Polygon(...)) + pass # optional: # def can_create_from_shape_impl(self): @@ -60,6 +59,12 @@ class PCell(pya.PCellDeclarationHelper): # TODO: return "True" here if the PCell takes a long time to compute. # In lazy mode, the user has to acknowledge parameter changes before # they are executed. + # + # optional: + # def callback_impl(self, name): + # TODO: implement functionality to change user interface element + # states for PCell parameter editors based on certain events + # like value changes. # TODO: add more PCell classes .. @@ -68,7 +73,7 @@ class PCell(pya.PCellDeclarationHelper): # The main purpose of this class is to provide the PCell declarations and to register itself # with a proper name. # TODO: change the class name -class PCellLib(pya.Library): +class PCellLib(kdb.Library): def __init__(self): diff --git a/src/lay/lay/macro_templates/pcell_sample_python.lym b/src/lay/lay/macro_templates/pcell_sample_python.lym index 775f3241c..c95bd22fe 100644 --- a/src/lay/lay/macro_templates/pcell_sample_python.lym +++ b/src/lay/lay/macro_templates/pcell_sample_python.lym @@ -8,7 +8,7 @@ pymacros python - import pya + import klayout.db as kdb import math """ @@ -22,7 +22,7 @@ implementation. The macro is also set to "auto run" to install the PCell when KLayout is run. """ -class Circle(pya.PCellDeclarationHelper): +class Circle(kdb.PCellDeclarationHelper): """ The PCell declaration for the circle """ @@ -34,7 +34,7 @@ class Circle(pya.PCellDeclarationHelper): # declare the parameters self.param("l", self.TypeLayer, "Layer") - self.param("s", self.TypeShape, "", default = pya.DPoint(0, 0)) + self.param("s", self.TypeShape, "", default = kdb.DPoint(0, 0)) self.param("r", self.TypeDouble, "Radius", default = 0.1) self.param("n", self.TypeInt, "Number of points", default = 64) # this hidden parameter is used to determine whether the radius has changed @@ -53,15 +53,15 @@ class Circle(pya.PCellDeclarationHelper): # radius ru) and set ru to the effective radius. We also update the # numerical value or the shape, depending on which on has not changed. rs = None - if isinstance(self.s, pya.DPoint): + if isinstance(self.s, kdb.DPoint): # compute distance in micron - rs = self.s.distance(pya.DPoint(0, 0)) + rs = self.s.distance(kdb.DPoint(0, 0)) if rs != None and abs(self.r-self.ru) < 1e-6: self.ru = rs self.r = rs else: self.ru = self.r - self.s = pya.DPoint(-self.r, 0) + self.s = kdb.DPoint(-self.r, 0) self.rd = 2*self.r @@ -83,7 +83,7 @@ class Circle(pya.PCellDeclarationHelper): def transformation_from_shape_impl(self): # Implement the "Create PCell from shape" protocol: we use the center of the shape's # bounding box to determine the transformation - return pya.Trans(self.shape.bbox().center()) + return kdb.Trans(self.shape.bbox().center()) def produce_impl(self): @@ -91,13 +91,13 @@ class Circle(pya.PCellDeclarationHelper): # compute the circle da = math.pi * 2 / self.n - pts = [ pya.DPoint(self.ru * math.cos(i * da), self.ru * math.sin(i * da)) for i in range(0, self.n) ] + pts = [ kdb.DPoint(self.ru * math.cos(i * da), self.ru * math.sin(i * da)) for i in range(0, self.n) ] # create the shape - self.cell.shapes(self.l_layer).insert(pya.DPolygon(pts)) + self.cell.shapes(self.l_layer).insert(kdb.DPolygon(pts)) -class MyLib(pya.Library): +class MyLib(kdb.Library): """ The library where we will put the PCell into """ diff --git a/src/lay/lay/macro_templates/qt_designer_python.lym b/src/lay/lay/macro_templates/qt_designer_python.lym index dffd41731..b8d9b12dc 100644 --- a/src/lay/lay/macro_templates/qt_designer_python.lym +++ b/src/lay/lay/macro_templates/qt_designer_python.lym @@ -10,14 +10,16 @@ python - import pya + from klayout.QtUiTools import QUiLoader +from klayout.QtCore import QFile, QIODevice +from klayout.lay import Application import types # Create the form object from the uic file: -ui_file = pya.QFile(":/macro-templates/qt_designer.ui") -ui_file.open(pya.QIODevice.ReadOnly) -form = pya.QUiLoader().load(ui_file, pya.Application.instance().main_window()) +ui_file = QFile(":/macro-templates/qt_designer.ui") +ui_file.open(QIODevice.ReadOnly) +form = QUiLoader().load(ui_file, Application.instance().main_window()) ui_file.close() # Install an event handler for the button clicked event diff --git a/src/lay/lay/macro_templates/qt_dialog_python.lym b/src/lay/lay/macro_templates/qt_dialog_python.lym index 3e4cee917..1527cbff3 100644 --- a/src/lay/lay/macro_templates/qt_dialog_python.lym +++ b/src/lay/lay/macro_templates/qt_dialog_python.lym @@ -14,9 +14,11 @@ python - import pya + from klayout.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton +from klayout.QtGui import QPixmap, QFont +from klayout.lay import Application -class ScreenshotDialog(pya.QDialog): +class ScreenshotDialog(QDialog): """ This class implements a dialog with a screenshot display area and a screenshot button @@ -25,11 +27,11 @@ class ScreenshotDialog(pya.QDialog): def button_clicked(self, checked): """ Event handler: "Screenshot" button clicked """ - view = pya.Application.instance().main_window().current_view() + view = Application.instance().main_window().current_view() # get the screenshot and place it in the image label if not view is None: - self.image.setPixmap(pya.QPixmap.fromImage(view.get_image(400, 400))) + self.image.setPixmap(QPixmap.fromImage(view.get_image(400, 400))) else: self.image.setText("No layout opened to take screenshot from") @@ -42,14 +44,14 @@ class ScreenshotDialog(pya.QDialog): self.resize(400, 120) - layout = pya.QVBoxLayout(self) + layout = QVBoxLayout(self) self.setLayout(layout) - self.image = pya.QLabel("Press the button to fetch a screenshot", self) + self.image = QLabel("Press the button to fetch a screenshot", self) layout.addWidget(self.image) - button = pya.QPushButton('Screenshot', self) - button.setFont(pya.QFont('Times', 18, int(pya.QFont.Bold))) + button = QPushButton('Screenshot', self) + button.setFont(QFont('Times', 18, int(QFont.Bold))) layout.addWidget(button) # attach the event handler @@ -57,7 +59,7 @@ class ScreenshotDialog(pya.QDialog): # Instantiate the dialog and make it visible initially. # Passing the main_window will make it stay on top of the main window. -dialog = ScreenshotDialog(pya.Application.instance().main_window()) +dialog = ScreenshotDialog(Application.instance().main_window()) dialog.show() diff --git a/src/lay/lay/macro_templates/qt_server_python.lym b/src/lay/lay/macro_templates/qt_server_python.lym index 64585a4ec..4bae0d5b7 100644 --- a/src/lay/lay/macro_templates/qt_server_python.lym +++ b/src/lay/lay/macro_templates/qt_server_python.lym @@ -14,10 +14,12 @@ python - import pya -import re + import re +from klayout.QtNetwork import QTcpServer, QTcpSocket, QHostAddress +from klayout.QtCore import QUrl, QBuffer, QIODevice, QObject, qt_signal, qt_slot +from klayout.lay import Application -class MyServer(pya.QTcpServer): +class MyServer(QTcpServer): """ Implements a TCP server listening on port 8082 This server will accept HTTP requests and deliver a HTML page containing an image @@ -39,7 +41,7 @@ class MyServer(pya.QTcpServer): connection = self.nextPendingConnection() # Read the request header - while connection.isOpen() and connection.state() == pya.QTcpSocket.ConnectedState: + while connection.isOpen() and connection.state() == QTcpSocket.ConnectedState: if connection.canReadLine(): line = connection.readLine().decode("utf-8") if line.rstrip('\n').rstrip('\r') == "": @@ -47,19 +49,19 @@ class MyServer(pya.QTcpServer): else: m = re.match("GET\\s+(.*)\\s+HTTP", line) if not m is None: - url = pya.QUrl(m.groups()[0]) + url = QUrl(m.groups()[0]) else: connection.waitForReadyRead(100) - view = pya.Application.instance().main_window().current_view() + view = Application.instance().main_window().current_view() if url is None: pass - elif url.path == "/screenshot.png": + elif url.path() == "/screenshot.png": # Deliver the image if not view is None: - b = pya.QBuffer() - b.open(pya.QIODevice.WriteOnly) + b = QBuffer() + b.open(QIODevice.WriteOnly) view.get_image(800, 800).save(b, "PNG") b.close() connection.write(b.buffer()) @@ -70,13 +72,13 @@ class MyServer(pya.QTcpServer): connection.write("HTTP/1.0 200 OK\nContent-Type: text/html\n\n" + '<html><body>No view open to take screenshot from</body></html>\n\n') # automatically delete when disconnected - # Note: we cannot use RBA signal bindings for that purpose because the Ruby object might get + # Note: we cannot use native Python signal bindings for that purpose because the Python object might get # deleted before the C++ object is. Hence we do it explicitly. connection.disconnectFromHost() #connection.destroy() - signal = pya.qt_signal("disconnected()") - slot = pya.qt_slot("deleteLater()") - pya.QObject.connect(connection, signal, connection, slot) + signal = qt_signal("disconnected()") + slot = qt_slot("deleteLater()") + QObject.connect(connection, signal, connection, slot) except Exception as ex: print("ERROR " + str(ex)) @@ -89,7 +91,7 @@ class MyServer(pya.QTcpServer): super(MyServer, self).__init__(parent) - ha = pya.QHostAddress("0.0.0.0") + ha = QHostAddress("0.0.0.0") self.listen(ha, 8082) # install an event on a new connection diff --git a/src/lay/lay/macro_templates/via_pcell_sample_python.lym b/src/lay/lay/macro_templates/via_pcell_sample_python.lym index 64fb2adb8..f08623fec 100644 --- a/src/lay/lay/macro_templates/via_pcell_sample_python.lym +++ b/src/lay/lay/macro_templates/via_pcell_sample_python.lym @@ -7,7 +7,7 @@ false pymacros python - import pya + import klayout.db as kdb import math """ @@ -33,7 +33,7 @@ implementation. The macro is also set to "auto run" to install the PCell when KLayout is run. """ -class ViaPCell(pya.PCellDeclarationHelper): +class ViaPCell(kdb.PCellDeclarationHelper): def __init__(self): @@ -73,18 +73,18 @@ class ViaPCell(pya.PCellDeclarationHelper): self.via_type_list = [] # Via1 - vt = pya.ViaType("V1", "Via1 (0.2x0.2)") + vt = kdb.ViaType("V1", "Via1 (0.2x0.2)") vt.wbmin = 0.4 vt.hbmin = 0.4 vt.wtmin = 0.5 vt.htmin = 0.5 - vt.bottom = pya.LayerInfo(1, 0) - vt.cut = pya.LayerInfo(2, 0) - vt.top = pya.LayerInfo(3, 0) + vt.bottom = kdb.LayerInfo(1, 0) + vt.cut = kdb.LayerInfo(2, 0) + vt.top = kdb.LayerInfo(3, 0) vt.bottom_grid = 0.01 vt.top_grid = 0.01 - # foreign attributes (not used by pya, but handy for + # foreign attributes (not used by KLayout, but handy for # internal use): vt.enc_bottom = 0.1 vt.enc_top = 0.15 @@ -94,18 +94,18 @@ class ViaPCell(pya.PCellDeclarationHelper): self.via_type_list.append(vt) # Via2 - vt = pya.ViaType("V2", "Via2 (0.3x0.3)") + vt = kdb.ViaType("V2", "Via2 (0.3x0.3)") vt.wbmin = 0.5 vt.hbmin = 0.5 vt.wtmin = 0.5 vt.htmin = 0.5 - vt.bottom = pya.LayerInfo(3, 0) - vt.cut = pya.LayerInfo(4, 0) - vt.top = pya.LayerInfo(5, 0) + vt.bottom = kdb.LayerInfo(3, 0) + vt.cut = kdb.LayerInfo(4, 0) + vt.top = kdb.LayerInfo(5, 0) vt.bottom_grid = 0.01 vt.top_grid = 0.01 - # foreign attributes (not used by pya, but handy for + # foreign attributes (not used by kdb, but handy for # internal use): vt.enc_bottom = 0.1 vt.enc_top = 0.1 @@ -159,7 +159,7 @@ class ViaPCell(pya.PCellDeclarationHelper): """ PCell interface implementation """ - return pya.Trans() + return kdb.Trans() def min_dim(self, a, b, da, db): """ @@ -248,8 +248,8 @@ class ViaPCell(pya.PCellDeclarationHelper): ltop = self.layout.layer(vt.top) lcut = self.layout.layer(vt.cut) - self.cell.shapes(lbottom).insert(pya.DBox(wbottom, hbottom)) - self.cell.shapes(ltop).insert(pya.DBox(wtop, htop)) + self.cell.shapes(lbottom).insert(kdb.DBox(wbottom, hbottom)) + self.cell.shapes(ltop).insert(kdb.DBox(wtop, htop)) scut = self.cell.shapes(lcut) @@ -257,10 +257,10 @@ class ViaPCell(pya.PCellDeclarationHelper): x = (ix - 0.5 * (nx - 1)) * (vt.vwidth + vt.vspace) for iy in range(0, ny): y = (iy - 0.5 * (ny - 1)) * (vt.vwidth + vt.vspace) - scut.insert(pya.DBox(vt.vwidth, vt.vwidth).moved(x, y)) + scut.insert(kdb.DBox(vt.vwidth, vt.vwidth).moved(x, y)) -class MyViaLib(pya.Library): +class MyViaLib(kdb.Library): """ A declaration for a test library