mirror of https://github.com/KLayout/klayout.git
Rewriting Python samples to klayout.* modules, fixing some bugs and flaws in the samples
This commit is contained in:
parent
22042a0b10
commit
d35e21be29
|
|
@ -8,10 +8,6 @@
|
|||
<doc>@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".
|
||||
|
|
|
|||
|
|
@ -85,8 +85,7 @@ end
|
|||
|
||||
The Python version is this:
|
||||
|
||||
<pre>
|
||||
from pya import BrowserSource, BrowserDialog
|
||||
<pre>from klayout.lay import BrowserSource, BrowserDialog
|
||||
|
||||
class MyBrowserSource(BrowserSource):
|
||||
def get(self, url):
|
||||
|
|
@ -157,8 +156,7 @@ end
|
|||
The Python version is:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
from pya import Action, MessageBox, Application
|
||||
<pre>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:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
from pya import QDialog, QVBoxLayout, QLineEdit, QLabel, Application
|
||||
<pre>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:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
from pya import QDialog, QVBoxLayout, QLineEdit, QLabel, Application
|
||||
<pre>from klayout.lay import Application
|
||||
from klayout.QtWidgets import QDialog, QVBoxLayout, QLineEdit, QLabel
|
||||
|
||||
dialog = QDialog(Application.instance().main_window())
|
||||
layout = QVBoxLayout(dialog)
|
||||
|
|
|
|||
|
|
@ -9,16 +9,17 @@
|
|||
<keyword name="Ruby scripts"/>
|
||||
<keyword name="Python"/>
|
||||
<keyword name="pya"/>
|
||||
<keyword name="Python module"/>
|
||||
<keyword name="Python scripts"/>
|
||||
|
||||
<p>
|
||||
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.).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
<keyword name="Programming"/>
|
||||
<keyword name="Python"/>
|
||||
<keyword name="pya"/>
|
||||
<keyword name="Python module"/>
|
||||
<keyword name="Python scripts"/>
|
||||
|
||||
|
||||
|
|
@ -55,8 +56,11 @@
|
|||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
|
|
@ -68,12 +72,12 @@
|
|||
<pre>
|
||||
# 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")
|
||||
</pre>
|
||||
|
|
@ -139,7 +143,12 @@ layout.write("t.gds")
|
|||
|
||||
<ul>
|
||||
<li><b>KLayout module:</b>
|
||||
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".
|
||||
</li>
|
||||
|
||||
<li><b>Reserved names:</b>
|
||||
|
|
@ -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:
|
||||
|
||||
<pre>
|
||||
box = pya.Box()
|
||||
<pre>import klayout.db as kdb
|
||||
|
||||
box = kdb.Box()
|
||||
box.left = 10
|
||||
box.right = box.right + 100
|
||||
</pre>
|
||||
|
|
@ -200,8 +210,9 @@ box.right = box.right + 100
|
|||
<li><b>Iterators:</b>
|
||||
Iterator binding:
|
||||
|
||||
<pre>
|
||||
edges = pya.Edges()
|
||||
<pre>import klayout.db as kdb
|
||||
|
||||
edges = kdb.Edges()
|
||||
...
|
||||
for edge in edges.each():
|
||||
...
|
||||
|
|
@ -221,8 +232,10 @@ for edge in edges:
|
|||
|
||||
<p>Most methods support keyword arguments, for example:</p>
|
||||
|
||||
<pre># a 45 degree rotation
|
||||
t = pya.CplxTrans(rot = 45)</pre>
|
||||
<pre>import klayout.db as kdb
|
||||
|
||||
# a 45 degree rotation
|
||||
t = kdb.CplxTrans(rot = 45)</pre>
|
||||
|
||||
<p>
|
||||
Exceptions are some built-in methods like "assign". Keyword arguments can be used
|
||||
|
|
@ -257,10 +270,11 @@ t = pya.CplxTrans(rot = 45)</pre>
|
|||
</li>
|
||||
|
||||
<li><b>Deep copies:</b>
|
||||
Deep copies of pya objects can be made with dup()
|
||||
Deep copies of klayout objects can be made with dup()
|
||||
|
||||
<pre>
|
||||
box = pya.Box(10, 20, 110, 220)
|
||||
<pre>import klayout.db as kdb
|
||||
|
||||
box = kdb.Box(10, 20, 110, 220)
|
||||
copy_box = box.dup()
|
||||
</pre>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
<group-name/>
|
||||
<menu-path/>
|
||||
<interpreter>python</interpreter>
|
||||
<text>import pya
|
||||
<text>
|
||||
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
|
||||
|
||||
</text>
|
||||
</klayout-macro>
|
||||
|
|
|
|||
|
|
@ -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".
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
<show-in-menu>false</show-in-menu>
|
||||
<category>pymacros</category>
|
||||
<interpreter>python</interpreter>
|
||||
<text>import pya
|
||||
<text>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)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
<shortcut/>
|
||||
<interpreter>python</interpreter>
|
||||
<category>pymacro</category>
|
||||
<text>import pya
|
||||
<text>import klayout.db as kdb
|
||||
import klayout.lay as klay
|
||||
|
||||
# Enter your Python code here ..
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -7,18 +7,15 @@
|
|||
<show-in-menu>false</show-in-menu>
|
||||
<shortcut></shortcut>
|
||||
<interpreter>python</interpreter>
|
||||
<text>import pya
|
||||
<text>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):
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<shortcut></shortcut>
|
||||
<category>pymacros</category>
|
||||
<interpreter>python</interpreter>
|
||||
<text>import pya
|
||||
<text>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
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -10,14 +10,16 @@
|
|||
<group-name/>
|
||||
<menu-path/>
|
||||
<interpreter>python</interpreter>
|
||||
<text>import pya
|
||||
<text>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
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@
|
|||
<menu-path/>
|
||||
<interpreter>python</interpreter>
|
||||
<dsl-interpreter-name/>
|
||||
<text>import pya
|
||||
<text>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()
|
||||
</text>
|
||||
</klayout-macro>
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@
|
|||
<menu-path/>
|
||||
<interpreter>python</interpreter>
|
||||
<dsl-interpreter-name/>
|
||||
<text>import pya
|
||||
import re
|
||||
<text>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
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<show-in-menu>false</show-in-menu>
|
||||
<category>pymacros</category>
|
||||
<interpreter>python</interpreter>
|
||||
<text>import pya
|
||||
<text>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
|
||||
|
|
|
|||
Loading…
Reference in New Issue