Пример #1
0
    def export(self, fileName=None, toBytes=False, copy=False):
        if toBytes is False and copy is False and fileName is None:
            self.fileSaveDialog(filter="Scalable Vector Graphics (*.svg)")
            return
        #self.svg = QtSvg.QSvgGenerator()
        #self.svg.setFileName(fileName)
        #dpi = QtGui.QDesktopWidget().physicalDpiX()
        ### not really sure why this works, but it seems to be important:
        #self.svg.setSize(QtCore.QSize(self.params['width']*dpi/90., self.params['height']*dpi/90.))
        #self.svg.setResolution(dpi)
        ##self.svg.setViewBox()
        #targetRect = QtCore.QRect(0, 0, self.params['width'], self.params['height'])
        #sourceRect = self.getSourceRect()

        #painter = QtGui.QPainter(self.svg)
        #try:
        #self.setExportMode(True)
        #self.render(painter, QtCore.QRectF(targetRect), sourceRect)
        #finally:
        #self.setExportMode(False)
        #painter.end()

        ## Workaround to set pen widths correctly
        #data = open(fileName).readlines()
        #for i in range(len(data)):
        #line = data[i]
        #m = re.match(r'(<g .*)stroke-width="1"(.*transform="matrix\(([^\)]+)\)".*)', line)
        #if m is not None:
        ##print "Matched group:", line
        #g = m.groups()
        #matrix = list(map(float, g[2].split(',')))
        ##print "matrix:", matrix
        #scale = max(abs(matrix[0]), abs(matrix[3]))
        #if scale == 0 or scale == 1.0:
        #continue
        #data[i] = g[0] + ' stroke-width="%0.2g" ' % (1.0/scale) + g[1] + '\n'
        ##print "old line:", line
        ##print "new line:", data[i]
        #open(fileName, 'w').write(''.join(data))

        ## Qt's SVG generator is not complete. (notably, it lacks clipping)
        ## Instead, we will use Qt to generate SVG for each item independently,
        ## then manually reconstruct the entire document.
        xml = generateSvg(self.item)

        if toBytes:
            return xml.encode('UTF-8')
        elif copy:
            md = QtCore.QMimeData()
            md.setData('image/svg+xml', QtCore.QByteArray(xml.encode('UTF-8')))
            QtGui.QApplication.clipboard().setMimeData(md)
        else:
            with open(fileName, 'w') as fh:
                fh.write(xml.encode('UTF-8'))
Пример #2
0
    def generatePath(self, x, y):
        prof = debug.Profiler('PlotCurveItem.generatePath', disabled=True)
        path = QtGui.QPainterPath()

        ## Create all vertices in path. The method used below creates a binary format so that all
        ## vertices can be read in at once. This binary format may change in future versions of Qt,
        ## so the original (slower) method is left here for emergencies:
        #path.moveTo(x[0], y[0])
        #for i in range(1, y.shape[0]):
        #    path.lineTo(x[i], y[i])

        ## Speed this up using >> operator
        ## Format is:
        ##    numVerts(i4)   0(i4)
        ##    x(f8)   y(f8)   0(i4)    <-- 0 means this vertex does not connect
        ##    x(f8)   y(f8)   1(i4)    <-- 1 means this vertex connects to the previous vertex
        ##    ...
        ##    0(i4)
        ##
        ## All values are big endian--pack using struct.pack('>d') or struct.pack('>i')

        n = x.shape[0]
        # create empty array, pad with extra space on either end
        arr = np.empty(n + 2, dtype=[('x', '>f8'), ('y', '>f8'), ('c', '>i4')])
        # write first two integers
        prof.mark('allocate empty')
        arr.data[12:20] = struct.pack('>ii', n, 0)
        prof.mark('pack header')
        # Fill array with vertex values
        arr[1:-1]['x'] = x
        arr[1:-1]['y'] = y
        arr[1:-1]['c'] = 1
        prof.mark('fill array')
        # write last 0
        lastInd = 20 * (n + 1)
        arr.data[lastInd:lastInd + 4] = struct.pack('>i', 0)
        prof.mark('footer')
        # create datastream object and stream into path
        buf = QtCore.QByteArray(
            arr.data[12:lastInd +
                     4])  # I think one unnecessary copy happens here
        prof.mark('create buffer')
        ds = QtCore.QDataStream(buf)
        prof.mark('create datastream')
        ds >> path
        prof.mark('load')

        prof.finish()
        return path
Пример #3
0
    def export(self, fileName=None, toBytes=False, copy=False):
        if toBytes is False and copy is False and fileName is None:
            self.fileSaveDialog(filter="Scalable Vector Graphics (*.svg)")
            return

        ## Qt's SVG generator is not complete. (notably, it lacks clipping)
        ## Instead, we will use Qt to generate SVG for each item independently,
        ## then manually reconstruct the entire document.
        xml = generateSvg(self.item)

        if toBytes:
            return xml.encode('UTF-8')
        elif copy:
            md = QtCore.QMimeData()
            md.setData('image/svg+xml', QtCore.QByteArray(xml.encode('UTF-8')))
            QtGui.QApplication.clipboard().setMimeData(md)
        else:
            with open(fileName, 'wb') as fh:
                fh.write(asUnicode(xml).encode('utf-8'))
Пример #4
0
    def arrayToQPath(x, y, connect='all'):
        """Convert an array of x,y coordinats to QPainterPath as efficiently as possible.
        The *connect* argument may be 'all', indicating that each point should be
        connected to the next; 'pairs', indicating that each pair of points
        should be connected, or an array of int32 values (0 or 1) indicating
        connections.
        """

        ## Create all vertices in path. The method used below creates a binary format so that all
        ## vertices can be read in at once. This binary format may change in future versions of Qt,
        ## so the original (slower) method is left here for emergencies:
        # path.moveTo(x[0], y[0])
        # if connect == 'all':
        # for i in range(1, y.shape[0]):
        # path.lineTo(x[i], y[i])
        # elif connect == 'pairs':
        # for i in range(1, y.shape[0]):
        # if i%2 == 0:
        # path.lineTo(x[i], y[i])
        # else:
        # path.moveTo(x[i], y[i])
        # elif isinstance(connect, np.ndarray):
        # for i in range(1, y.shape[0]):
        # if connect[i] == 1:
        # path.lineTo(x[i], y[i])
        # else:
        # path.moveTo(x[i], y[i])
        # else:
        # raise Exception('connect argument must be "all", "pairs", or array')

        ## Speed this up using >> operator
        ## Format is:
        ##    numVerts(i4)
        ##    0(i4)   x(f8)   y(f8)    <-- 0 means this vertex does not connect
        ##    1(i4)   x(f8)   y(f8)    <-- 1 means this vertex connects to the previous vertex
        ##    ...
        ##    cStart(i4)   fillRule(i4)
        ##
        ## see: https://github.com/qt/qtbase/blob/dev/src/gui/painting/qpainterpath.cpp

        ## All values are big endian--pack using struct.pack('>d') or struct.pack('>i')

        path = QtGui.QPainterPath()

        n = x.shape[0]

        # create empty array, pad with extra space on either end
        arr = np.empty(n + 2, dtype=[('c', '>i4'), ('x', '>f8'), ('y', '>f8')])

        # write first two integers
        byteview = arr.view(dtype=np.ubyte)
        byteview[:16] = 0
        byteview.data[16:20] = struct.pack('>i', n)

        # Fill array with vertex values
        arr[1:-1]['x'] = x
        arr[1:-1]['y'] = y

        # inf/nans completely prevent the plot from being displayed starting on
        # Qt version 5.12.3; these must now be manually cleaned out.
        isfinite = None
        qtver = [int(x) for x in QtVersion.split('.')]
        if qtver >= [5, 12, 3]:
            isfinite = np.isfinite(x) & np.isfinite(y)
            if not np.all(isfinite):
                # credit: Divakar https://stackoverflow.com/a/41191127/643629
                mask = ~isfinite
                idx = np.arange(len(x))
                idx[mask] = -1
                np.maximum.accumulate(idx, out=idx)
                first = np.searchsorted(idx, 0)
                if first < len(x):
                    # Replace all non-finite entries from beginning of arr with the first finite one
                    idx[:first] = first
                    arr[1:-1] = arr[1:-1][idx]

        # decide which points are connected by lines
        if eq(connect, 'all'):
            arr[1:-1]['c'] = 1
        elif eq(connect, 'pairs'):
            arr[1:-1]['c'][::2] = 0
            arr[1:-1]['c'][
                1::2] = 1  # connect every 2nd point to every 1st one
        elif eq(connect, 'finite'):
            # Let's call a point with either x or y being nan is an invalid point.
            # A point will anyway not connect to an invalid point regardless of the
            # 'c' value of the invalid point. Therefore, we should set 'c' to 0 for
            # the next point of an invalid point.
            if isfinite is None:
                isfinite = np.isfinite(x) & np.isfinite(y)
            arr[2:]['c'] = isfinite
        elif isinstance(connect, np.ndarray):
            arr[1:-1]['c'] = connect
        else:
            raise Exception(
                'connect argument must be "all", "pairs", "finite", or array')

        arr[1]['c'] = 0  # the first vertex has no previous vertex to connect

        byteview.data[-20:-16] = struct.pack('>i', 0)  # cStart
        byteview.data[-16:-12] = struct.pack('>i',
                                             0)  # fillRule (Qt.OddEvenFill)

        # create datastream object and stream into path

        ## Avoiding this method because QByteArray(str) leaks memory in PySide
        # buf = QtCore.QByteArray(arr.data[12:lastInd+4])  # I think one unnecessary copy happens here

        path.strn = byteview.data[16:-12]  # make sure data doesn't run away
        try:
            buf = QtCore.QByteArray.fromRawData(path.strn)
        except TypeError:
            buf = QtCore.QByteArray(bytes(path.strn))

        ds = QtCore.QDataStream(buf)
        ds >> path

        return path
Пример #5
0
def _generateItemSvg(item, nodes=None, root=None):
    ## This function is intended to work around some issues with Qt's SVG generator
    ## and SVG in general.
    ## 1) Qt SVG does not implement clipping paths. This is absurd.
    ##    The solution is to let Qt generate SVG for each item independently,
    ##    then glue them together manually with clipping.
    ##
    ##    The format Qt generates for all items looks like this:
    ##
    ##    <g>
    ##        <g transform="matrix(...)">
    ##            one or more of: <path/> or <polyline/> or <text/>
    ##        </g>
    ##        <g transform="matrix(...)">
    ##            one or more of: <path/> or <polyline/> or <text/>
    ##        </g>
    ##        . . .
    ##    </g>
    ##
    ## 2) There seems to be wide disagreement over whether path strokes
    ##    should be scaled anisotropically.
    ##      see: http://web.mit.edu/jonas/www/anisotropy/
    ##    Given that both inkscape and illustrator seem to prefer isotropic
    ##    scaling, we will optimize for those cases.
    ##
    ## 3) Qt generates paths using non-scaling-stroke from SVG 1.2, but
    ##    inkscape only supports 1.1.
    ##
    ##    Both 2 and 3 can be addressed by drawing all items in world coordinates.

    profiler = debug.Profiler()

    if nodes is None:  ## nodes maps all node IDs to their XML element.
        ## this allows us to ensure all elements receive unique names.
        nodes = {}

    if root is None:
        root = item

    ## Skip hidden items
    if hasattr(item, 'isVisible') and not item.isVisible():
        return None

    ## If this item defines its own SVG generator, use that.
    if hasattr(item, 'generateSvg'):
        return item.generateSvg(nodes)

    ## Generate SVG text for just this item (exclude its children; we'll handle them later)
    tr = QtGui.QTransform()
    if isinstance(item, QtGui.QGraphicsScene):
        xmlStr = "<g>\n</g>\n"
        doc = xml.parseString(xmlStr)
        childs = [i for i in item.items() if i.parentItem() is None]
    elif item.__class__.paint == QtGui.QGraphicsItem.paint:
        xmlStr = "<g>\n</g>\n"
        doc = xml.parseString(xmlStr)
        childs = item.childItems()
    else:
        childs = item.childItems()
        tr = itemTransform(item, item.scene())

        ## offset to corner of root item
        if isinstance(root, QtGui.QGraphicsScene):
            rootPos = QtCore.QPoint(0, 0)
        else:
            rootPos = root.scenePos()
        tr2 = QtGui.QTransform()
        tr2.translate(-rootPos.x(), -rootPos.y())
        tr = tr * tr2

        arr = QtCore.QByteArray()
        buf = QtCore.QBuffer(arr)
        svg = QtSvg.QSvgGenerator()
        svg.setOutputDevice(buf)
        dpi = QtGui.QDesktopWidget().logicalDpiX()
        svg.setResolution(dpi)

        p = QtGui.QPainter()
        p.begin(svg)
        if hasattr(item, 'setExportMode'):
            item.setExportMode(True, {'painter': p})
        try:
            p.setTransform(tr)
            item.paint(p, QtGui.QStyleOptionGraphicsItem(), None)
        finally:
            p.end()
            ## Can't do this here--we need to wait until all children have painted as well.
            ## this is taken care of in generateSvg instead.
            #if hasattr(item, 'setExportMode'):
            #item.setExportMode(False)

        if USE_PYSIDE:
            xmlStr = str(arr)
        else:
            xmlStr = bytes(arr).decode('utf-8')
        doc = xml.parseString(xmlStr.encode('utf-8'))

    try:
        ## Get top-level group for this item
        g1 = doc.getElementsByTagName('g')[0]
        ## get list of sub-groups
        g2 = [
            n for n in g1.childNodes
            if isinstance(n, xml.Element) and n.tagName == 'g'
        ]

        defs = doc.getElementsByTagName('defs')
        if len(defs) > 0:
            defs = [
                n for n in defs[0].childNodes if isinstance(n, xml.Element)
            ]
    except:
        print(doc.toxml())
        raise

    profiler('render')

    ## Get rid of group transformation matrices by applying
    ## transformation to inner coordinates
    correctCoordinates(g1, defs, item)
    profiler('correct')
    ## make sure g1 has the transformation matrix
    #m = (tr.m11(), tr.m12(), tr.m21(), tr.m22(), tr.m31(), tr.m32())
    #g1.setAttribute('transform', "matrix(%f,%f,%f,%f,%f,%f)" % m)

    #print "=================",item,"====================="
    #print g1.toprettyxml(indent="  ", newl='')

    ## Inkscape does not support non-scaling-stroke (this is SVG 1.2, inkscape supports 1.1)
    ## So we need to correct anything attempting to use this.
    #correctStroke(g1, item, root)

    ## decide on a name for this item
    baseName = item.__class__.__name__
    i = 1
    while True:
        name = baseName + "_%d" % i
        if name not in nodes:
            break
        i += 1
    nodes[name] = g1
    g1.setAttribute('id', name)

    ## If this item clips its children, we need to take care of that.
    childGroup = g1  ## add children directly to this node unless we are clipping
    if not isinstance(item, QtGui.QGraphicsScene):
        ## See if this item clips its children
        if int(item.flags() & item.ItemClipsChildrenToShape) > 0:
            ## Generate svg for just the path
            #if isinstance(root, QtGui.QGraphicsScene):
            #path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape()))
            #else:
            #path = QtGui.QGraphicsPathItem(root.mapToParent(item.mapToItem(root, item.shape())))
            path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape()))
            item.scene().addItem(path)
            try:
                #pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0]
                pathNode = _generateItemSvg(
                    path, root=root)[0].getElementsByTagName('path')[0]
                # assume <defs> for this path is empty.. possibly problematic.
            finally:
                item.scene().removeItem(path)

            ## and for the clipPath element
            clip = name + '_clip'
            clipNode = g1.ownerDocument.createElement('clipPath')
            clipNode.setAttribute('id', clip)
            clipNode.appendChild(pathNode)
            g1.appendChild(clipNode)

            childGroup = g1.ownerDocument.createElement('g')
            childGroup.setAttribute('clip-path', 'url(#%s)' % clip)
            g1.appendChild(childGroup)
    profiler('clipping')

    ## Add all child items as sub-elements.
    childs.sort(key=lambda c: c.zValue())
    for ch in childs:
        csvg = _generateItemSvg(ch, nodes, root)
        if csvg is None:
            continue
        cg, cdefs = csvg
        childGroup.appendChild(
            cg
        )  ### this isn't quite right--some items draw below their parent (good enough for now)
        defs.extend(cdefs)

    profiler('children')
    return g1, defs
Пример #6
0
    def initUI(self, e=None):
        """ Definition and initialisation of the GUI plus staring the measurement.
        """

        self._scanning_logic = self.connector['in']['confocallogic1']['object']
        print("Scanning logic is", self._scanning_logic)

        # setting up the window
        self._mw = QtGui.QMainWindow()
        self._mw.setWindowTitle('qudi: Counter Test GUI')
        self._mw.resize(400, 100)
        self._cw = QtGui.QWidget()
        self._mw.setCentralWidget(self._cw)

        # defining buttons
        self._start_stop_button = QtGui.QPushButton('Start Scan')
        self._start_stop_button.released.connect(self.start_clicked)

        # defining the parameters to edit
        self._x_label = QtGui.QLabel('x:')
        self._x_display = QtGui.QSpinBox()
        self._x_display.setRange(0, 1e3)
        self._x_display.setValue(10)
        self._x_display.valueChanged.connect(self.x_changed)

        self._y_label = QtGui.QLabel('y:')
        self._y_display = QtGui.QSpinBox()
        self._y_display.setRange(0, 1e3)
        self._y_display.setValue(10)
        self._y_display.valueChanged.connect(self.x_changed)

        self._z_label = QtGui.QLabel('z:')
        self._z_display = QtGui.QSpinBox()
        self._z_display.setRange(0, 1e3)
        self._z_display.setValue(10)
        self._z_display.valueChanged.connect(self.x_changed)

        self._a_label = QtGui.QLabel('a:')
        self._a_display = QtGui.QSpinBox()
        self._a_display.setRange(0, 1e3)
        self._a_display.setValue(10)
        self._a_display.valueChanged.connect(self.x_changed)

        # creating a layout for the parameters to live in and aranging it nicely
        self._hbox_layout = QtGui.QHBoxLayout()
        self._hbox_layout.addWidget(self._x_label)
        self._hbox_layout.addWidget(self._x_display)
        self._hbox_layout.addStretch(1)
        self._hbox_layout.addWidget(self._y_label)
        self._hbox_layout.addWidget(self._y_display)
        self._hbox_layout.addStretch(1)
        self._hbox_layout.addWidget(self._z_label)
        self._hbox_layout.addWidget(self._z_display)
        self._hbox_layout.addStretch(1)
        self._hbox_layout.addWidget(self._a_label)
        self._hbox_layout.addWidget(self._a_display)

        # funny gifs
        self.movie_screen = QtGui.QLabel()
        #        self.movie_screen.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        self.movie_screen.setFixedHeight(300)
        self.movie_screen.setFixedWidth(300)
        self.movie_screen.setAlignment(QtCore.Qt.AlignCenter)
        self.movie_screen.setStyleSheet(
            'QLabel {background-color: white; color: red;}')
        # Add the QMovie object to the label
        self.idle_movie = QtGui.QMovie('artwork/idle_smiley.gif',
                                       QtCore.QByteArray(), self)
        self.idle_movie.setCacheMode(QtGui.QMovie.CacheAll)
        self.idle_movie.setSpeed(100)
        #        self.idle_movie.setBackgroundColor(QtGui.QColor(255,255,255))
        self.idle_movie.start()
        self.active_movie = QtGui.QMovie('artwork/active_smiley.gif',
                                         QtCore.QByteArray(), self)
        self.active_movie.setCacheMode(QtGui.QMovie.CacheAll)
        self.active_movie.setSpeed(100)
        self.active_movie.start()
        self.movie_screen.setMovie(self.idle_movie)

        self._hbox_layout_icon = QtGui.QHBoxLayout()
        self._hbox_layout_icon.addStretch(1)
        self._hbox_layout_icon.addWidget(self.movie_screen)
        self._hbox_layout_icon.addStretch(1)

        # kombining the layouts with the plot
        self._vbox_layout = QtGui.QVBoxLayout()
        self._vbox_layout.addLayout(self._hbox_layout)
        self._vbox_layout.addWidget(self._start_stop_button)
        self._vbox_layout.addStretch(1)
        self._vbox_layout.addLayout(self._hbox_layout_icon)

        # applying all the GUI elements to the window
        self._cw.setLayout(self._vbox_layout)
        self._mw.show()

        # starting the physical measurement
        self._scanning_logic.start_scanner()
        self.signal_start_scan.connect(self._scanning_logic.start_scanning)
        self.signal_stop_scan.connect(self._scanning_logic.stop_scanning)
        self._scanning_logic.signal_scan_updated.connect(self.update_state)