KLayout / klayout

KLayout Main Sources
http://www.klayout.org
GNU General Public License v3.0
754 stars 196 forks source link

lay.Marker missing in python standalone module #1655

Closed sebastian-goeldi closed 2 weeks ago

sebastian-goeldi commented 4 months ago

Hello Matthias,

I hope you are doing well.

I am currently trying to use markers in the python qt-less LayoutView (totally not related to https://github.com/KLayout/klayout/issues/1632 ;) ). I noticed that in KLayout GUI pya.Marker works like I would expect (to some degree see bottom). In python (or more specifically the rdb view of kweb 2.0) I cannot get marker(s) to show.

I assume there is some backend magic which is disabled (or straight up missing because it's part of the gui) for the python binding?

Best, Sebastian

P.S. is it possible to make multiple markers show up at the same time? My uneducated guess is the marker is a singleton in C++ (per LayoutView) and thererfore they erase each other, but the marker browser can do it, so I assume there is at least a possibility to do so.

klayoutmatthias commented 4 months ago

Hi Sebastian,

Markers are not singletons - you can add as many markers as you like. Display gets kind of sluggish when you have 1 million markers or so, but a thousand should not be an issue.

I need to debug why markers don't show with standalong LayoutView. Maybe a similar issue than what we had in the past. The standalone view is special because there is no event loop running in the background and some essential update events may not happen.

Thanks for reporting this issue,

Matthias

sebastian-goeldi commented 4 months ago

Thanks Matthias!

I am pretty sure the marker class behaves kind of singleton-ish even in the macroeditor in klayout. I will update it later today.

I think I won't need 1 million markers ;). I was planning to hard-limit it to a default of 100 anyway, since I need to serialize and deserialize the markers (and RdbItems) and send them through the websocket.

klayoutmatthias commented 4 months ago

I think I get some understanding what is going on.

First, the functionality of the marker seems to be okay in the standalone module.

This code works:

from klayout.db import DBox, DPoint
from klayout.lay import LayoutView, Annotation, Marker

lv = LayoutView()
lv.load_layout("./testdata/lvs/resistor.gds")
lv.max_hier()

marker = Marker(lv)
marker.set(DBox(6.0, 0.0, 8.0, 2.0))

lv.get_pixels(600, 600).write_png("test.png")

which produces this image:

test

However, this code does not produce two markers, but only one:

from klayout.db import DBox, DPoint
from klayout.lay import LayoutView, Annotation, Marker

lv = LayoutView()
lv.load_layout("./testdata/lvs/resistor.gds")
lv.max_hier()

marker = Marker(lv)
marker.set(DBox(6.0, 0.0, 8.0, 2.0))

marker = Marker(lv)
marker.set(DBox(9.0, 0.0, 11.0, 2.0))

lv.get_pixels(600, 600).write_png("test.png")

test

The reason is that marker appearance is tied to the object lifetime. When the second object is created, it will replace the first one as they use the same variable. Because of this, the first object is deleted and vanishes.

For the same reason, this code does not produce any marker at all:

from klayout.db import DBox, DPoint
from klayout.lay import LayoutView, Annotation, Marker

lv = LayoutView()
lv.load_layout("./testdata/lvs/resistor.gds")
lv.max_hier()

def make_marker(lv):
  marker = Marker(lv)
  marker.set(DBox(6.0, 0.0, 8.0, 2.0))

make_marker(lv)
lv.get_pixels(600, 600).write_png("test.png")

Here the reason is scope: when the "make_marker" function returns, the marker object stored inside the "marker" variable is destroyed and the marker vanishes.

To solve the issue, a reference to the markers need to be kept. A way to doing that is to attach them to the view:

from klayout.db import DBox, DPoint
from klayout.lay import LayoutView, Annotation, Marker

lv = LayoutView()
lv.load_layout("./testdata/lvs/resistor.gds")
lv.max_hier()

lv.markers = []

marker = Marker(lv)
marker.set(DBox(6.0, 0.0, 8.0, 2.0))
lv.markers.append(marker)

marker = Marker(lv)
marker.set(DBox(9.0, 0.0, 11.0, 2.0))
lv.markers.append(marker)

lv.get_pixels(600, 600).write_png("test.png")

And here are the two markers :)

test

I can basically change the lifetime management for the markers and transfer ownership to the layout view. I just wonder whether that would break existing code.

Matthias

sebastian-goeldi commented 3 months ago

Thanks Matthias! I will use this. Whether to attach it to the LayoutView; I would suggest to do so (I would have expected it to behave similar to a Shapes container but on um (grid) and therefore there also being a clear_makers() or similar. But maybe that doesn't make sense from an architecture point of view. But I can also live with the current implementation. Important to know that the objects can be garbage collected and then "vanish", but workable.

klayoutmatthias commented 3 months ago

Yes, I admit that is kind of freaky, but it has been like this for long now.

I could provide an alternative path of creating markers and adding them to the view, thus transferring ownership. Maybe that is easier to work with than to manage the ownership separately.

klayoutmatthias commented 2 weeks ago

I provided the following alternative way for Marker lifetime management:

lv = ... # some LayoutView
m = pya.Marker()   # without view argument!
lv.add_marker(m)   # now owned by the view - the variable can go out of scope now.

# to delete:
lv.clear_markers()
# or:
m._destroy()

Matthias

sebastian-goeldi commented 2 weeks ago

Thank you Matthias!