Repository: napari/napari

Issue:
Viewer screenshot method returns only the first layer.
## 🐛 Bug
When screenshot method is used on a viewer with multiple layers, only the first layer is returned.

## To Reproduce

Steps to reproduce the behavior:

1. import

%gui qt5 
from napari import ViewerApp
import vispy.io as io

2. Use ViewerApp() and display multiple layers.

3. save screenshot of current view.

screen=viewer2D.screenshot()
io.write_png('vectorexport.png',screen)

![vectorexport](https://user-images.githubusercontent.com/2934183/54791053-84e23a00-4bf5-11e9-9657-6fe25981ef9d.png)

## Expected behavior
Return an RGB image of the current view a numpy array.
![image (2)](https://user-images.githubusercontent.com/2934183/54791008-62502100-4bf5-11e9-8c41-598d438535a1.png)

## Environment

 - napari Version (e.g., 1.0): vector branch, March 20, 2019
 - OS (e.g., Linux): Mac OS Mojave 10.4
 - Python version: 3.7
 - Any other relevant information:

## Additional context

<!-- Add any other context about the problem here. -->



Discussion:
I had a quick poke around this one, and it seems like the ordering and opacities are wrong and improving the situation for the screenshot makes the situation for the main display worse. I think this may relate to the issue @kne42 identified in #22 but I am unable to see how to fix it and the things I tried did not work

Resolving patch:
diff --git a/examples/to_screenshot.py b/examples/to_screenshot.py
new file mode 100644
--- /dev/null
+++ b/examples/to_screenshot.py
@@ -0,0 +1,127 @@
+"""
+Display one shapes layer ontop of one image layer using the add_shapes and
+add_image APIs. When the window is closed it will print the coordinates of
+your shapes.
+"""
+
+import numpy as np
+from skimage import data
+from skimage.io import imsave
+import napari
+from vispy.color import Colormap
+
+
+with napari.gui_qt():
+    # create the viewer and window
+    viewer = napari.Viewer()
+
+    # add the image
+    img_layer = viewer.add_image(data.camera(), name='photographer')
+    img_layer.colormap = 'gray'
+
+    # create a list of polygons
+    polygons = [
+        np.array([[11, 13], [111, 113], [22, 246]]),
+        np.array(
+            [
+                [505, 60],
+                [402, 71],
+                [383, 42],
+                [251, 95],
+                [212, 59],
+                [131, 137],
+                [126, 187],
+                [191, 204],
+                [171, 248],
+                [211, 260],
+                [273, 243],
+                [264, 225],
+                [430, 173],
+                [512, 160],
+            ]
+        ),
+        np.array(
+            [
+                [310, 382],
+                [229, 381],
+                [209, 401],
+                [221, 411],
+                [258, 411],
+                [300, 412],
+                [306, 435],
+                [268, 434],
+                [265, 454],
+                [298, 461],
+                [307, 461],
+                [307, 507],
+                [349, 510],
+                [352, 369],
+                [330, 366],
+                [330, 366],
+            ]
+        ),
+    ]
+
+    # add polygons
+    layer = viewer.add_shapes(
+        polygons,
+        shape_type='polygon',
+        edge_width=1,
+        edge_color='coral',
+        face_color='royalblue',
+        name='shapes',
+    )
+
+    # change some properties of the layer
+    layer.selected_data = list(range(layer.nshapes))
+    layer.edge_width = 5
+    layer.opacity = 0.75
+    layer.selected_data = []
+
+    # add an ellipse to the layer
+    ellipse = np.array([[59, 222], [110, 289], [170, 243], [119, 176]])
+    layer.add(
+        ellipse,
+        shape_type='ellipse',
+        edge_width=5,
+        edge_color='coral',
+        face_color='purple',
+        opacity=0.75,
+    )
+    layer.refresh()
+
+    masks = layer.to_masks([512, 512])
+    masks_layer = viewer.add_image(masks.astype(float), name='masks')
+    masks_layer.opacity = 0.7
+    masks_layer.colormap = Colormap(
+        [[0.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 1.0]]
+    )
+
+    labels = layer.to_labels([512, 512])
+    labels_layer = viewer.add_labels(labels, name='labels')
+
+    points = np.array([[100, 100], [200, 200], [333, 111]])
+    size = np.array([10, 20, 20])
+    viewer.add_points(points, size=size)
+
+    # sample vector coord-like data
+    n = 100
+    pos = np.zeros((n, 2, 2), dtype=np.float32)
+    phi_space = np.linspace(0, 4 * np.pi, n)
+    radius_space = np.linspace(0, 100, n)
+
+    # assign x-y position
+    pos[:, 0, 0] = radius_space * np.cos(phi_space) + 350
+    pos[:, 0, 1] = radius_space * np.sin(phi_space) + 256
+
+    # assign x-y projection
+    pos[:, 1, 0] = 2 * radius_space * np.cos(phi_space)
+    pos[:, 1, 1] = 2 * radius_space * np.sin(phi_space)
+
+    # add the vectors
+    layer = viewer.add_vectors(pos, edge_width=2)
+
+    # take screenshot
+    screenshot = viewer.screenshot()
+    viewer.add_image(screenshot, multichannel=True, name='screenshot')
+    # imsave('screenshot.png', screenshot)
diff --git a/napari/_qt/qt_viewer.py b/napari/_qt/qt_viewer.py
--- a/napari/_qt/qt_viewer.py
+++ b/napari/_qt/qt_viewer.py
@@ -137,22 +137,8 @@ def _update_camera(self):
         # TO DO: Remove
         self.viewer._view = self.view
 
-    def screenshot(self, region=None, size=None, bgcolor=None):
-        """Render the scene to an offscreen buffer and return the image array.
-
-        Parameters
-        ----------
-        region : tuple | None
-            Specifies the region of the canvas to render. Format is
-            (x, y, w, h). By default, the entire canvas is rendered.
-        size : tuple | None
-            Specifies the size of the image array to return. If no size is
-            given, then the size of the *region* is used, multiplied by the
-            pixel scaling factor of the canvas (see `pixel_scale`). This
-            argument allows the scene to be rendered at resolutions different
-            from the native canvas resolution.
-        bgcolor : instance of Color | None
-            The background color to use.
+    def screenshot(self):
+        """Take currently displayed screen and convert to an image array.
 
         Returns
         -------
@@ -160,8 +146,23 @@ def screenshot(self, region=None, size=None, bgcolor=None):
             Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the
             upper-left corner of the rendered region.
         """
+        img = self.canvas.native.grabFramebuffer()
+        b = img.constBits()
+        h, w, c = img.height(), img.width(), 4
+
+        # As vispy doesn't use qtpy we need to reconcile the differences
+        # between the `QImage` API for `PySide2` and `PyQt5` on how to convert
+        # a QImage to a numpy array.
+        if API_NAME == 'PySide2':
+            arr = np.array(b).reshape(h, w, c)
+        else:
+            b.setsize(h * w * c)
+            arr = np.frombuffer(b, np.uint8).reshape(h, w, c)
 
-        return self.canvas.render(region, size, bgcolor)
+        # Format of QImage is ARGB32_Premultiplied, but color channels are
+        # reversed.
+        arr = arr[:, :, [2, 1, 0, 3]]
+        return arr
 
     def _open_images(self):
         """Adds image files from the menubar."""


Test patch: