Skip to content

Commit

Permalink
Merge pull request #2 from atait/development
Browse files Browse the repository at this point in the history
v0.1.4
  • Loading branch information
atait committed Aug 22, 2018
2 parents 4a1dd81 + 1617d78 commit 84d8b93
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 36 deletions.
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ By separating the processes, the server GUI can be fully featured, initializing

### Detail: a debug process flow looks like this
1. [process #1] Launch klayout.app
- From menu item, start a simple server on port <XXY>
- From menu item, start a simple server on port 11078
2. [process #2: programmer] Run a python program in _either_ klayout or python
- Import `lyipc` client package
- Stop this program in a debugger like PDB or Spyder (examine/change program variables)
- Write to a file "x.gds"
- Call `ipc.load("x.gds")`
3. [process #2: lyipc] The lyipc.client module will
- Initiate a socket connection on port <XXY>
- Initiate a socket connection on port 11078
- Send a command that means load
4. [process #1] Upon receiving a connection request
- Received command is parsed
Expand All @@ -33,30 +33,29 @@ By separating the processes, the server GUI can be fully featured, initializing
- (future) Behavioral unit tests: Test whether code changes break previous layout behavior by keeping a reference.gds and creating a test.gds, then send them to klayout's visual diff tool.

## Installation
#### From salt package manager
#### Server from salt package manager
In klayout.app, go to "Tools>Manage Packages". Go to "Install New Packages" and find "klayout_ipc". Double-click it, and press "Apply".

_At the time of writing, the grain is NOT live._

#### From source
#### Server from source
1. In a directory of choice, `git clone git@github.com:atait/klayout-ipc.git`
2. "Install" in .klayout system files
- (OSX, Linux) `ln -s ~/your/path/to/klayout-ipc/klayout_dot_config ~/.klayout/salt/klayout_ipc`
- (Windows) right click the folder and create an alias to C:\\Username\KLayout\salt [path right?]
- (Windows) right click the folder and create an alias to C:\\Username\KLayout\salt [path correct?]

or

2. Show klayout where it is
- `export KLAYOUT_PATH="~/your/path/to/klayout-ipc/klayout_dot_config"`

## Setup
When an open file changes on disk, by default, KLayout asks whether to reload it. These prompts persist when reload is triggered by a communicating process instead of a human. Disable checks by going to klayout.app's preferences > Application > General, and uncheck the box for "Check for file updates."

#### Client
The lyipc package is visible within klayout's interpreter namespace, but it is not on the system PYTHONPATH. In order for any python-based client to use it, lyipc must be installed with
```sh
pip install ~/.klayout/salt/klayout_ipc/python
```
For development mode, use pip's `-e` flag.
(will be different on windows). For development mode, use pip's `-e` flag.

#### Application setup
When an open file changes on disk, by default, KLayout asks whether to reload it. These prompts persist when reload is triggered by a communicating process instead of a human. Disable checks by going to klayout.app's preferences > Application > General, and uncheck the box for "Check for file updates."

## Usage
#### Server side
Expand Down Expand Up @@ -89,6 +88,6 @@ klayout -b -r test-lyipc.py
```
The former is faster because a new klayout instance is not created, but of course, the latter must be used for `pya` to work.

Usage examples for klayout and non-klayout layout clients are included in this repo.
Usage examples for klayout and non-klayout layout clients are included in this repo in the "examples" folder.

#### Author: Alex Tait, June 2018
22 changes: 11 additions & 11 deletions klayout_dot_config/doc/README.html
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,7 @@
<h3 id="detail-a-debug-process-flow-looks-like-this">Detail: a debug process flow looks like this<a class="headerlink" href="#detail-a-debug-process-flow-looks-like-this" title="Permanent link"></a></h3>
<ol>
<li>[process #1] Launch klayout.app<ul>
<li>From menu item, start a simple server on port <XXY></li>
<li>From menu item, start a simple server on port 11078</li>
</ul>
</li>
<li>[process #2: programmer] Run a python program in <em>either</em> klayout or python<ul>
Expand All @@ -1077,7 +1077,7 @@ <h3 id="detail-a-debug-process-flow-looks-like-this">Detail: a debug process flo
</ul>
</li>
<li>[process #2: lyipc] The lyipc.client module will<ul>
<li>Initiate a socket connection on port <XXY></li>
<li>Initiate a socket connection on port 11078</li>
<li>Send a command that means load</li>
</ul>
</li>
Expand All @@ -1095,15 +1095,14 @@ <h3 id="other-uses">Other uses<a class="headerlink" href="#other-uses" title="Pe
<li>(future) Behavioral unit tests: Test whether code changes break previous layout behavior by keeping a reference.gds and creating a test.gds, then send them to klayout&rsquo;s visual diff tool.</li>
</ul>
<h2 id="installation">Installation<a class="headerlink" href="#installation" title="Permanent link"></a></h2>
<h4 id="from-salt-package-manager">From salt package manager<a class="headerlink" href="#from-salt-package-manager" title="Permanent link"></a></h4>
<h4 id="server-from-salt-package-manager">Server from salt package manager<a class="headerlink" href="#server-from-salt-package-manager" title="Permanent link"></a></h4>
<p>In klayout.app, go to &ldquo;Tools&gt;Manage Packages&rdquo;. Go to &ldquo;Install New Packages&rdquo; and find &ldquo;klayout_ipc&rdquo;. Double-click it, and press &ldquo;Apply&rdquo;.</p>
<p><em>At the time of writing, the grain is NOT live.</em></p>
<h4 id="from-source">From source<a class="headerlink" href="#from-source" title="Permanent link"></a></h4>
<h4 id="server-from-source">Server from source<a class="headerlink" href="#server-from-source" title="Permanent link"></a></h4>
<ol>
<li>In a directory of choice, <code>git clone git@github.com:atait/klayout-ipc.git</code></li>
<li>&ldquo;Install&rdquo; in .klayout system files<ul>
<li>(OSX, Linux) <code>ln -s ~/your/path/to/klayout-ipc/klayout_dot_config ~/.klayout/salt/klayout_ipc</code></li>
<li>(Windows) right click the folder and create an alias to C:\Username\KLayout\salt [path right?]</li>
<li>(Windows) right click the folder and create an alias to C:\Username\KLayout\salt [path correct?]</li>
</ul>
</li>
</ol>
Expand All @@ -1114,12 +1113,13 @@ <h4 id="from-source">From source<a class="headerlink" href="#from-source" title=
</ul>
</li>
</ol>
<h2 id="setup">Setup<a class="headerlink" href="#setup" title="Permanent link"></a></h2>
<p>When an open file changes on disk, by default, KLayout asks whether to reload it. These prompts persist when reload is triggered by a communicating process instead of a human. Disable checks by going to klayout.app&rsquo;s preferences &gt; Application &gt; General, and uncheck the box for &ldquo;Check for file updates.&rdquo;</p>
<h4 id="client">Client<a class="headerlink" href="#client" title="Permanent link"></a></h4>
<p>The lyipc package is visible within klayout&rsquo;s interpreter namespace, but it is not on the system PYTHONPATH. In order for any python-based client to use it, lyipc must be installed with
<div class="codehilite"><pre>pip install ~/your/path/to/klayout-ipc/klayout_dot_config/python
<div class="codehilite"><pre>pip install ~/.klayout/salt/klayout_ipc/python
</pre></div>
For development mode, use pip&rsquo;s <code>-e</code> flag.</p>
(will be different on windows). For development mode, use pip&rsquo;s <code>-e</code> flag.</p>
<h4 id="application-setup">Application setup<a class="headerlink" href="#application-setup" title="Permanent link"></a></h4>
<p>When an open file changes on disk, by default, KLayout asks whether to reload it. These prompts persist when reload is triggered by a communicating process instead of a human. Disable checks by going to klayout.app&rsquo;s preferences &gt; Application &gt; General, and uncheck the box for &ldquo;Check for file updates.&rdquo;</p>
<h2 id="usage">Usage<a class="headerlink" href="#usage" title="Permanent link"></a></h2>
<h4 id="server-side">Server side<a class="headerlink" href="#server-side" title="Permanent link"></a></h4>
<p>&ndash; press Ctrl+<code>I</code> &ndash;</p>
Expand All @@ -1141,5 +1141,5 @@ <h4 id="client-side">Client side<a class="headerlink" href="#client-side" title=
<div class="codehilite"><pre>klayout -b -r <span class="nb">test</span>-lyipc.py
</pre></div>
The former is faster because a new klayout instance is not created, but of course, the latter must be used for <code>pya</code> to work.</p>
<p>Usage examples for klayout and non-klayout layout clients are included in this repo.</p>
<p>Usage examples for klayout and non-klayout layout clients are included in this repo in the &ldquo;examples&rdquo; folder.</p>
<h4 id="author-alex-tait-june-2018">Author: Alex Tait, June 2018<a class="headerlink" href="#author-alex-tait-june-2018" title="Permanent link"></a></h4></article></body></html>
2 changes: 1 addition & 1 deletion klayout_dot_config/grain.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<name>klayout_ipc</name>
<token/>
<hidden>false</hidden>
<version>0.1.3</version>
<version>0.1.4</version>
<api-version/>
<title>KLayout IPC</title>
<doc>Socket communication between klayout and external processes</doc>
Expand Down
14 changes: 11 additions & 3 deletions klayout_dot_config/python/lyipc/client/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@ def reload():


fast_realpath = lru_cache(maxsize=4)(os.path.realpath) # Since the argument is going to be the same every time
def load(filename):
def load(filename, mode=None):
'''
Modes are
- 0 (default): replacing the current layout view
- 1: making a new view
- 2: adding the layout to the current view (mode 2)
'''
filename = fast_realpath(filename)
# TODO: use a temporary file
send('load {}'.format(filename))
tokens = ['load', filename]
if mode is not None:
tokens.append(str(mode))
send(' '.join(tokens))


def kill():
Expand Down
62 changes: 62 additions & 0 deletions klayout_dot_config/python/lyipc/client/phidl.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
'''
from __future__ import print_function
import phidl
from phidl import Device

import time
import os
from contextlib import contextmanager
from functools import wraps

# Makes it so that only one import is needed: lyipc.client.phidl will drop in for lyipc.client
from .general import *
Expand All @@ -21,3 +24,62 @@ def new_add(self, *args, **kwargs):
klayout_quickplot(device, file, fresh=False)
return retval
phidl.device_layout.Device.add = new_add


@contextmanager
def save_or_visualize(device_name=None, out_file=None):
''' Handles a conditional write to file or send over lyipc connection.
The context manager yields a new empty Device.
The context block then modifies that device by adding references to it. It does not need to return anything.
Back to the context manager, the Device is saved if out_file is not None, or it is sent over ipc
Example::
with save_or_visualize(out_file='my_box.gds') as D:
r = D << phidl.geometry.rectangle(size=(10, 10), layer=1)
r.movex(20)
will write the device with a rectangle to a file called 'my_box.gds' and do nothing with lyipc.
By changing out_file to None, it will send an ipc load command instead of writing to a permanent file,
(Although ipc does write a file to be loaded by klayout, it's name or persistence is not guaranteed.)
'''
if device_name is None:
CELL = Device()
else:
CELL = Device(device_name)
yield CELL
if out_file is None:
klayout_quickplot(CELL, 'debugging.gds', fresh=True)
else:
CELL.write_gds(out_file)


def contained_geometry(func):
'''
Converts a function that takes a Device argument to one that takes a filename argument.
This is used to develop fixed geometry creation blocks and then save them as reference files.
Bad idea to try to use this in a library or call it from other functions.
func should take *only one* argument that is a Device, modify that Device, and return nothing.
It's sort of a decorator version of save_or_visualize.
When called with a None argument, it will use klayout_quickplot.
Example::
@contained_geometry
def boxer(D):
r = D << phidl.geometry.rectangle(size=(10, 10), layer=1)
r.movex(20)
Usage::
boxer() # displays in klayout over ipc
boxer('temp.gds') # saves to file instead
'''
@wraps(func)
def geometry_container(out_file=None):
with save_or_visualize(out_file=out_file) as TOP:
func(TOP)
return geometry_container
25 changes: 17 additions & 8 deletions klayout_dot_config/python/lyipc/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@
import pya


def quiet_load_layout(filename):
def quiet_load_layout(filename, mode=0):
''' Swallow the error if the file is not completely written yet,
or if the last file was not completely rendered.
This doesn't seem to fatally affect the program
This doesn't seem to fatally affect the program.
Modes are
- 0 (default): replacing the current layout view
- 1: making a new view
- 2: adding the layout to the current view (mode 2)
'''
main = pya.Application.instance().main_window()
try:
view = main.load_layout(filename, 0)
view = main.load_layout(filename, mode)
except RuntimeError as err:
if err.args[0].split()[0] in ['Stream', 'Unexpected']:
print(err)
Expand All @@ -38,20 +43,24 @@ def parse_message(message):
It is prepended with a status token and encoded (as a str) to be sent back over the socket
'''
return_val = None
tokens = message.split(' ')
try:
# if message == 'kill':
# quickmsg('Stopping server -- remote shutdown')
# pya.Application.exit(pya.Application.instance())

if message == 'reload view':
if tokens == ['reload', 'view']:
main = pya.Application.instance().main_window()
main.cm_reload()

elif message.startswith('load '):
filename = message.split(' ')[1]
filename = os.path.realpath(filename)
elif tokens[0] == 'load':
filename = os.path.realpath(tokens[1])
quickmsg(filename)
quiet_load_layout(filename)
if len(tokens) > 2:
mode = int(tokens[2])
quiet_load_layout(filename, mode)
else:
quiet_load_layout(filename)

else:
quickmsg('Received {}'.format(message))
Expand Down
2 changes: 1 addition & 1 deletion klayout_dot_config/python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def readme():
return fx.read()

setup(name='lyipc',
version='0.1.3',
version='0.1.4',
description='Inter-process communication for Klayout',
long_description=readme(),
author='Alex Tait',
Expand Down

0 comments on commit 84d8b93

Please sign in to comment.