Screenshot with all the layer and screenshot only one layer

Hi,
I'm trying to do a screenshot of all the layers of a gds file.
Right now I've managed to take a screenshot but it doesn't contain all the layers. (see code below)

include RBA

tailleX=500
tailleY=500
imgsave=ENV['HOME']+"/imagesGDS/"+File.basename($gdsfile, ".gds")+".png"

if (File.exist?(imgsave))
     exit
end

mw = RBA::Application::instance.main_window
mw.load_layout($gdsfile,1)
mw.current_view.save_image(imgsave,tailleX,tailleY)

I would like to be able to select a specific layer as well as select all the layers if possible.
Thank you.

Comments

  • Not sure this is helpful, but seems a lot simpler to
    just "Show All" or "Show Only Selected" in the layer
    window, and Alt-Pr-Scrn? I mean, even invoking the
    script takes more keyboard / mouse activity than
    that.

    But I'd expect that those menu-clicks have a hook
    somewhere in the pile-o'-functions.

  • Hi, Florent!

    You could access layer properties using LayoutView.begin_layers() that returns iterator. Then <iterator>.current().visible (of LayerProperties type) allows to control visibility.

    For example (sorry for Python-centrism):

    it = current_view.begin_layers()
    while not it.at_end():
           it.current().visible = use criteria for visibility there, for example, based on it.current().source_layer and source_datatype
           it.next()
    
  • edited January 2021

    Okay finally worked my way around this.
    I searched deeper in the forum and found something that works :)
    I'll leave the code here if someone want to use it. (not the cleanest thing I've done)

    include RBA
    
    tailleX=500
    tailleY=500
    imgsave=ENV['HOME']+"/imagesGDS/"+File.basename($gdsfile, ".gds")+".png"
    imgpics=ENV['HOME']+"/imagesGDS/"+File.basename($gdsfile, ".gds")+"_pics.png"
    
    if (File.exist?(imgpics))
        if (File.exist?(imgsave))
            exit
        end
    end
    
    mw = RBA::Application::instance.main_window
    mw.load_layout($gdsfile,0)   # $gdsfile is used in another script
    layer_view = mw.current_view
    layer_view.max_hier
    
    #export an image of all the layers
    mw.current_view.save_image(imgsave,tailleX,tailleY)
    
    view = RBA::LayoutView::current
    
    li = view.begin_layers
    while !li.at_end?
      lp = li.current
      if lp.source_layer == 1 && lp.source_datatype == 0
        new_lp = lp.dup
        new_lp.visible = true
        view.set_layer_properties(li, new_lp)
      else
        new_lp.visible = false
        view.set_layer_properties(li, new_lp)
      end
      li.next
    end
    #export an image of only one layer
    mw.current_view.save_image(imgpics,tailleX,tailleY)
    

    Topic that helped me: changing-the-layer-visibility-by-using-ruby

  • Yes, very good! Thanks for sharing the code! :)

    The trick is "layer_view.max_hier". Without that you just get cell frames.

    Kind regards,

    Matthias

  • Hi Matthias-

    Is it possible to use the save_image or save_screenshot and have the layer panel included in the output image?

    Thanks,

  • HI mikamar

    by using this you can get the layer color, texture and border style image,

    view = pya.Application.instance().main_window().current_view()
    layer_iter = view.begin_layers()
    while not(layer_iter.at_end()):
        #get layer icons in image form
        pixels = view.icon_for_layer(layer_iter, 25, 10, 1).to_png_data())
        layer_iter.next()
    

    by combining the image and layer info into a widget, and using Qt Render to save user interface into a png
    we can have layer panel screen shot.

    import pya
    import os
    
    def view():
        return pya.Application.instance().main_window().current_view()
    
    class ScreenShotWidget(pya.QWidget):
        def __init__(self, parent = None):
            super(ScreenShotWidget, self).__init__()  
            self.l = pya.QLabel()
            self.layout = pya.QVBoxLayout()
            self.layout.addWidget(self.l)
            self.layout.addStretch()
            self.setLayout(self.layout)
    
        def getScreen(self):
            if view():
                self.l.setPixmap(pya.QPixmap().fromImage(view().get_image(400, 400)))
            else:
                self.l.setText("No layout opened to take screenshot from")                
    
    
    class LayerIconWidget(pya.QWidget):
        def __init__(self, parent = None):
            super(LayerIconWidget, self).__init__()  
            self.layout = pya.QVBoxLayout()
            self.setLayout(self.layout)
            self.setFixedWidth(100)
    
        def getLayerIcon(self):
            if view():
                while self.layout.count() > 0:
                    self.layout.removeItem(self.layout.itemAt (0))
                self.layout = pya.QVBoxLayout()
                self.setLayout(self.layout)                
                bgc  = view().get_config("background-color")
                txtc = "#FFFFFF" if (int(bgc[1:3], 16) + int(bgc[3:5], 16) + int(bgc[5:7], 16)) <= (255) else "#000000"
                layer_iter = view().begin_layers()
                self.setStyleSheet("background-color:%s;" % bgc)
    
                while not(layer_iter.at_end()):
                    layerPixmap = pya.QPixmap()
                    layerImage  = pya.QLabel(self)
                    layerLabel  = pya.QLabel(self)
                    layerLayout = pya.QHBoxLayout(self)
                    layerProp   = layer_iter.current()
                    layerLabel.setText(f"{layerProp.source_layer}/{layerProp.source_datatype}")
                    layerLabel.setStyleSheet("color:%s;" % txtc)
                    layerPixmap.loadFromData(view().icon_for_layer(layer_iter, 25, 10, 1).to_png_data())
                    layerImage.setPixmap(layerPixmap)
                    layerLayout.addWidget(layerImage)
                    layerLayout.addWidget(layerLabel)
                    layerLayout.addStretch()
    
                    self.layout.addLayout(layerLayout)
                    layer_iter.next()
                self.layout.addStretch()
                self.update()
    
    
    class LayerScreenShotWidget(pya.QWidget):
        def __init__(self, parent = None):
            super(LayerScreenShotWidget, self).__init__()  
            self.layerIconWidget  = LayerIconWidget()
            self.screenShotWidget = ScreenShotWidget()
            self.layout           = pya.QHBoxLayout()
            self.setStyleSheet("background-color:black;")
            self.layout.addWidget(self.layerIconWidget)
            self.layout.addWidget(self.screenShotWidget)
            self.setLayout(self.layout)
    
        def getScreen(self):
            self.setStyleSheet("background-color:%s;" % view().get_config("background-color"))
            self.screenShotWidget.getScreen()
            self.layerIconWidget.getLayerIcon()
            self.update()
    
    
        def saveScreen(self, path):
            basedir = os.path.dirname(path)
            if os.path.exists(basedir):
                widgetImage = pya.QPixmap(self.size)
                self.render(widgetImage)
                widgetImage.save(path)
                pya.QMessageBox.information(None, 'Status', 'file saved')
            else:
                 pya.QMessageBox.information(None, 'Status', 'Error: invalid path')
    
    
    class ScreenShotControlWidget(pya.QWidget):
        def __init__(self, parent = None):
            super(ScreenShotControlWidget, self).__init__()     
            self.layerScreenShotWidget = LayerScreenShotWidget()
            self.pathLabel             = pya.QLabel("Path:")
            self.pathEdit              = pya.QLineEdit()
            self.getScreenPB           = pya.QPushButton("Get image")
            self.saveScreenPB          = pya.QPushButton("Save Image")
            self.cancelPB              = pya.QPushButton("Cancel")
            self.ctrlLayout            = pya.QHBoxLayout()
            self.filePathLayout        = pya.QHBoxLayout()
            self.layout                = pya.QVBoxLayout()
    
    
    
            self.filePathLayout.addWidget(self.pathLabel)
            self.filePathLayout.addWidget(self.pathEdit)
            self.ctrlLayout.addWidget(self.getScreenPB)
            self.ctrlLayout.addWidget(self.saveScreenPB)
            self.ctrlLayout.addWidget(self.cancelPB)        
            self.layout.addWidget(self.layerScreenShotWidget)
            self.layout.addLayout(self.filePathLayout)
            self.layout.addLayout(self.ctrlLayout)
    
            self.setLayout(self.layout)
            self.getScreenPB.clicked(lambda : self.getScreen())
            self.saveScreenPB.clicked(lambda : self.layerScreenShotWidget.saveScreen(self.pathEdit.text))
    
            self.cancelPB.clicked(lambda : self.close())
            self.resize(500, 500)
            self.getScreen()
    
    
        def getScreen(self):
            filepath =  "\\".join(pya.CellView().active().filename().split("\\")[0:-1])
            filepath = (filepath if filepath else pya.Application.instance().klayout_path()[-2]) + "\\out.png"
            self.pathEdit.setText(filepath)
            self.layerScreenShotWidget.getScreen()
            self.update()
    
    
    screenShotControlWidget = ScreenShotControlWidget()
    screenShotControlWidget.show()
    
    
  • @RawrRanger Wow ... best screen shot feature I've seen so far. And nice documentation too!

  • Really nice tool!
    I am running it on Windows11 and every time I click a different program or window, the output window turns black and the button controls at the bottom disappear. Any modification I can make to keep the controls visible? The should be a form.redraw or refresh command that keeps the form updated.
    When I change the windows size, I can see the screenshot and controls.

    When I defocus the windows or move my mouse over it, the controls disappear.

    Is it possible to use the layer property names from a lyp file in the screenshot instead of just the gds layer numbers?
    Thanks!
    Holger

  • Hi @hkl_quint,

    Did you maybe run the script from the IDE? When a script is run from there, some events are suppressed to avoid interference of the IDE UI with script-implemented event handlers. This will change in the next minor release, but so far, the best solution is to bind the script to a menu item using the macro properties ():

    It will be found in the "Macro" menu then.

    Matthias

  • Hi Matthias,
    Thanks for the suggestion. Yes, I was running the script from the IDE.
    I tried your suggestion by just enabling the 'show in menu' option. When I execute the macro from within the menu, it still behaves weird.

    In this example, I first resized the form to make all controls visible again before hovering my mouse over the right button and the text input field which made them disappear.

    Thanks
    Holger

  • edited July 2024

    Hi Matthias,
    I just downloaded the latest version 0.29.4 and the problem is resolved. The form controls now stay visible.
    Thank you for this fix.
    I was using 0.29.2 before that showed the render bug.
    Holger

  • edited July 2024

    Hi there,

    Old version was a prove of concept, so I didn't dive too deep for making it actually usable.

    this version can choose what type of layer info you would like to show.

    Cases such as complex layer grouping or ignoring empty layers is not yet supported, :*

  • Hi @hkl_quint ,
    This looks like a really nice cross -section view. Is the source code available for this tool?

    Regards,
    Ege_Bey

  • Hi Ege_Bey,

    That seems to be generated by klayout xsection, which can be installed from klayout package control [Tool] --> [Package control]

    https://klayoutmatthias.github.io/xsection/DocIntro

    Another crosssection view I also recommend is XView from https://boscorp.nl/

    which is not a opensource software and only supports Win 64, but provide a is a free version that generates image with water mark

  • Thank you!

  • Hi RawRanger,

    This looks very cool but V2 makes KLayout 0.28.17. to get unresponsive (even from the menu). Does it only work for 0.29.x?

    Cheers,

    Tomas

  • from the macro development window:

  • edited July 2024

    Hi Tomas,

    I've tested on 0.28.17, indeed the code on line 238 will trigger this error, I believe is changed on version > 0.29.0

    An change log from V0.29.0:
    Synonyms: connect/disconnect for events for better compatibility with PyQt5

    The line of code that causes this issue can be comment out and will not affect major function.

    triggers resize of windows automatically after image shown, and size the window to match the image.

    if this line is comment out, than you'll need to resize the window manually if you change the image size.

        def initSignal(self):
            self.timer = pya.QTimer()
            self.timer.setInterval(200)
            self.timer.timeout.connect(lambda : self.updateSize())   # <<< this line
            self.control.getScreenPB.clicked(lambda : self.getScreen())
    

    I've comment out the lines of code that related to this timer, and some minor change to make it work on 0.28.12

    but for user that with newr version, i will still recommended to use previous version

  • Hi RawrRanger,

    I can confirm that it works fine now on 0.28.17. Since my layer names in the test case were quite long I had to increase the setFixedWidth value to 250 to avoid having the layers names not fully displayed (and the sliding bar at the bottom). Thanks a lot for this cool feature!

    Cheers,

    Tomas

  • Hi all,
    There is also a Python version of the cross section tool.
    I haven't tried it yet.
    Cheers
    Holger

  • Hi @RawrRanger,
    Very nice update to the screenshot tool. Works great on 0.29.4. Are you developing this on Github as public repo or maybe as a Klayout package?
    Another great feature would be new button to copy the grabbed image to the clipboard.
    Cheers
    Holger

Sign In or Register to comment.