Example #1
0
    def _horizontalGradientHelper(cls, painter: QPainter, spanRect: QRect, rect: QRect, lightColored: bool):
        if lightColored:
            shadowGradient = QLinearGradient(rect.topLeft(), rect.bottomLeft())
            shadowGradient.setColorAt(0, 0xf0f0f0)
            shadowGradient.setColorAt(1, 0xcfcfcf)
            painter.fillRect(rect, shadowGradient)
            return

        base = cls.baseColor(lightColored)
        highlight = cls.highlightColor(lightColored)
        shadow = cls.shadowColor(lightColored)
        grad = QLinearGradient(rect.topLeft(), rect.bottomLeft())
        grad.setColorAt(0, highlight.lighter(120))
        if rect.height() == cls.navigationWidgetHeight:
            grad.setColorAt(0.4, highlight)
            grad.setColorAt(0.401, base)

        grad.setColorAt(1, shadow)
        painter.fillRect(rect, grad)

        shadowGradient = QLinearGradient(spanRect.topLeft(), spanRect.topRight())
        shadowGradient.setColorAt(0, QColor(0, 0, 0, 30))
        lighterHighlight = highlight.lighter(130)
        lighterHighlight.setAlpha(100)
        shadowGradient.setColorAt(0.7, lighterHighlight)
        shadowGradient.setColorAt(1, QColor(0, 0, 0, 40))
        painter.fillRect(rect, shadowGradient)
Example #2
0
def decorate_welcome_icon(icon, background_color):
    """Return a `QIcon` with a circle shaped background.
    """
    welcome_icon = QIcon()
    sizes = [32, 48, 64, 80]
    background_color = NAMED_COLORS.get(background_color, background_color)
    background_color = QColor(background_color)
    grad = radial_gradient(background_color)
    for size in sizes:
        icon_pixmap = icon.pixmap(5 * size / 8, 5 * size / 8)
        icon_size = icon_pixmap.size()
        icon_rect = QRect(QPoint(0, 0), icon_size)

        pixmap = QPixmap(size, size)
        pixmap.fill(QColor(0, 0, 0, 0))
        p = QPainter(pixmap)
        p.setRenderHint(QPainter.Antialiasing, True)
        p.setBrush(QBrush(grad))
        p.setPen(Qt.NoPen)
        ellipse_rect = QRect(0, 0, size, size)
        p.drawEllipse(ellipse_rect)
        icon_rect.moveCenter(ellipse_rect.center())
        p.drawPixmap(icon_rect.topLeft(), icon_pixmap)
        p.end()

        welcome_icon.addPixmap(pixmap)

    return welcome_icon
Example #3
0
def decorate_welcome_icon(icon, background_color):
    """Return a `QIcon` with a circle shaped background.
    """
    welcome_icon = QIcon()
    sizes = [32, 48, 64, 80]
    background_color = NAMED_COLORS.get(background_color, background_color)
    background_color = QColor(background_color)
    grad = radial_gradient(background_color)
    for size in sizes:
        icon_pixmap = icon.pixmap(5 * size / 8, 5 * size / 8)
        icon_size = icon_pixmap.size()
        icon_rect = QRect(QPoint(0, 0), icon_size)

        pixmap = QPixmap(size, size)
        pixmap.fill(QColor(0, 0, 0, 0))
        p = QPainter(pixmap)
        p.setRenderHint(QPainter.Antialiasing, True)
        p.setBrush(QBrush(grad))
        p.setPen(Qt.NoPen)
        ellipse_rect = QRect(0, 0, size, size)
        p.drawEllipse(ellipse_rect)
        icon_rect.moveCenter(ellipse_rect.center())
        p.drawPixmap(icon_rect.topLeft(), icon_pixmap)
        p.end()

        welcome_icon.addPixmap(pixmap)

    return welcome_icon
Example #4
0
	def paintEvent( self, event ):
		"""
		Das automatisch ausgelöste paintEvent, das das Widget bei jeder Fensterveränderung neu zeichnet.
		"""

		if self.__maximum > 0:
			frameWidth = 1
			separatorWidth = 1

			# Damit der Rahmen nicht irgendwie abgeschnitten wird, muß das Quadrat entsprechend kleiner sein.
			squareSideLength = 10
			framePen = QPen( frameWidth )
			framePen.setColor( self.__colorFrame )

			squareSideLengthPlus = squareSideLength + 2 * frameWidth

			painter = QPainter( self )

			windowWidth = self.width() / min( self.__maximum, self.__columnMax )
			windowHeight = self.__maximum / self.__columnMax
			windowHeight = math.ceil( windowHeight )
			windowHeight = self.height() / windowHeight
			side = min( windowWidth, windowHeight )

			painter.setRenderHint( QPainter.Antialiasing )

			# Wenn das Widget disabled ist, muß ich den Alphakanal meiner Farben verändern.
			if ( not self.isEnabled() ):
				painter.setOpacity( .5 )

			#painter.translate( float( windowWidth ), float( windowHeight ) )

			painter.scale( side / squareSideLengthPlus, side / squareSideLengthPlus )
			painter.setPen( framePen )
			painter.setBrush( self.__colorEmpty )

			painter.save()

			squareColumnIter = 0
			squareLineIter = 0
			squareCount = 0
			for squareCount in range(self.__maximum):
				square = QRect(
					( squareSideLength + separatorWidth ) * squareColumnIter + frameWidth * ( squareColumnIter + 1 ),
					( squareSideLength + separatorWidth ) * squareLineIter + frameWidth * ( squareLineIter + 1 ), squareSideLength, squareSideLength
				)
				painter.drawRect( square )

				# Wir zeichnen die ausgekreuzten Quadrate
				if (self.__value > (self.__columnMax * squareLineIter + squareColumnIter)):
					painter.drawLine(square.bottomLeft(), square.topRight())
					painter.drawLine(square.topLeft(), square.bottomRight())

				squareColumnIter += 1

				if ( squareColumnIter >= self.__columnMax ):
					squareColumnIter = 0
					squareLineIter += 1

			painter.restore()
Example #5
0
 def horizontalGradient(self, painter, spanRect, clipRect):
     key = 'fancy vertical gradient %d %d %d %d %d'%(spanRect.width(), spanRect.height(), clipRect.width(),
                                          clipRect.height(), self.baseColor.rgb())
     pixmap = QPixmap()
     p = painter
     rect = QRect(clipRect)
     
     if self._usePixmapCache and not QPixmapCache.find(key, pixmap):
         pixmap = QPixmap(clipRect.size())
         p = QPainter(pixmap)
         rect = QRect(0, 0, clipRect.width(), clipRect.height())
     
     base = self.baseColor
     grad = QLinearGradient(QPointF(rect.topLeft()), QPointF(rect.bottomLeft()))
     grad.setColorAt(0, self.highlightColor.lighter(120))
     if rect.height() == self.navigationWidgetHeight:
         grad.setColorAt(0.4, self.highlightColor)
         grad.setColorAt(0.401, base)
     grad.setColorAt(1, self.shadowColor)
     p.fillRect(rect, grad)
     
     shadowGradient = QLinearGradient(QPointF(spanRect.topLeft()), QPointF(spanRect.topRight()))
     shadowGradient.setColorAt(0, QColor(0, 0, 0, 30))
     highlight = self.highlightColor.lighter(130)
     highlight.setAlpha(100)
     shadowGradient.setColorAt(0.7, highlight)
     shadowGradient.setColorAt(1, QColor(0, 0, 0, 40))
     p.fillRect(rect, shadowGradient)
 
     if self._usePixmapCache and not QPixmapCache.find(key, pixmap):
         painter.drawPixmap(clipRect.topLeft(), pixmap)
         p.end()
         del p
         QPixmapCache.insert(key, pixmap)
Example #6
0
    def _drawStyledBar(self, painter, option):
        rect = option.rect
        key = "fancy styledbar %d %d %d" % (rect.width(), rect.height(),
                                            theme.baseColor.rgb())
        pixmap = QPixmap()
        p = painter
        if theme.usePixmapCache() and not QPixmapCache.find(key, pixmap):
            pixmap = QPixmap(rect.size())
            p = QPainter(pixmap)
            rect = QRect(0, 0, rect.width(), rect.height())

        horizontal = option.state & QStyle.State_Horizontal
        offset = self.window().mapToGlobal(
            option.rect.topLeft()) - self.mapToGlobal(option.rect.topLeft())
        gradientSpan = QRect(offset, self.window().size())

        if horizontal:
            theme.horizontalGradient(p, gradientSpan, rect)
        else:
            theme.verticalGradient(p, gradientSpan, rect)

        painter.setPen(theme.borderColor)

        if horizontal:
            lighter = QColor(255, 255, 255, 40)
            if self._topBorder:
                p.drawLine(rect.topLeft(), rect.topRight())
                p.setPen(lighter)
                p.drawLine(rect.topLeft() + QPoint(0, 1),
                           rect.topRight() + QPoint(0, 1))
            else:
                p.drawLine(rect.bottomLeft(), rect.bottomRight())
                p.setPen(lighter)
                p.drawLine(rect.topLeft(), rect.topRight())
        else:
            p.drawLine(rect.topLeft(), rect.bottomLeft())
            p.drawLine(rect.topRight(), rect.bottomRight())

        if theme.usePixmapCache() and not QPixmapCache.find(key, pixmap):
            painter.drawPixmap(rect.topLeft(), pixmap)
            p.end()
            del p
            QPixmapCache.insert(key, pixmap)
Example #7
0
    def _verticalGradientHelper(cls, painter: QPainter, spanRect: QRect, rect: QRect, lightColored):
        """
        :type painter: QPainter
        :type spanRect: QRect
        :type rect: QRect
        :type lightColored: bool
        """
        highlight = StyleHelper.highlightColor(lightColored)
        shadow = StyleHelper.shadowColor(lightColored)
        grad = QLinearGradient(spanRect.topRight(), spanRect.topLeft())
        grad.setColorAt(0, highlight.lighter(117))
        grad.setColorAt(1, shadow.darker(109))
        painter.fillRect(rect, grad)

        light = QColor(255, 255, 255, 80)
        painter.setPen(light)
        painter.drawLine(rect.topRight() - QPoint(1, 0), rect.bottomRight() - QPoint(1, 0))
        dark = QColor(0, 0, 0, 90)
        painter.setPen(dark)
        painter.drawLine(rect.topLeft(), rect.bottomLeft())
    def add_item(self, thumbnail_image, date, info, day_index=-1, archive_path=None):
        """

        @param thumbnail_image: Image to be used in thumbnail
        @param date: Date of image
        @param info: Copyright info for image
        @param day_index: Day index of image
        @param archive_path: Path to the local file, or None if image source is the RSS feed.

        @type thumbnail_image: QImage
        @type date: QDate
        @type info: str
        @type day_index: int
        @type archive_path: unicode or None
        """
        if date.year() == QDate.currentDate().year():
            date_label = str(date.toString('dddd dd MMMM'))
        else:
            date_label = str(date.toString('dddd dd MMMM, yyyy'))
        if date_label in self.added_dates:
            # This date has already been added. Don't bother adding it again.
            print 'Ignored', date_label
            return
        if archive_path:
            pixmap = QPixmap.fromImage(thumbnail_image)
            painter = QPainter(pixmap)
            painter.setRenderHint(QPainter.Antialiasing)
            circle_area = QRect(pixmap.width() - 35, pixmap.height() - 35, 25, 25)
            painter.setOpacity(0.7)
            painter.setPen(Qt.lightGray)
            painter.setBrush(Qt.lightGray)
            painter.drawEllipse(circle_area)
            painter.drawPixmap(circle_area.topLeft(), self.pixmap_hd)
            painter.end()
        else:
            pixmap = QPixmap.fromImage(
                thumbnail_image.scaled(QSize(200, 125), Qt.IgnoreAspectRatio, Qt.SmoothTransformation))

        icon = QIcon(pixmap)
        widget_item = ListWidgetItem(icon, date_label, self)
        widget_item.setToolTip(info)
        widget_item.image_day_index = day_index
        widget_item.archive_path = archive_path
        widget_item.image_date = date
        self.added_dates.add(date_label)
Example #9
0
    def verticalGradient(cls, painter: QPainter, spanRect: QRect, clipRect: QRect, lightColored=False):
        if StyleHelper.usePixmapCache():
            keyColor = cls.baseColor(lightColored)
            key = "mh_vertical {1} {2} {3} {4} {5}".format(
                spanRect.width(), spanRect.height(), clipRect.width(),
                clipRect.height(), keyColor.rgb())

            pixmap = QPixmap()
            if not QPixmapCache.find(key, pixmap):
                pixmap = QPixmap(clipRect.size())
                p = QPainter(pixmap)
                rect = QRect(0, 0, clipRect.width(), clipRect.height())
                StyleHelper._verticalGradientHelper(p, spanRect, rect, lightColored)
                p.end()
                QPixmapCache.insert(key, pixmap)

            painter.drawPixmap(clipRect.topLeft(), pixmap)
        else:
            StyleHelper._verticalGradientHelper(painter, spanRect, clipRect, lightColored)
Example #10
0
class PlottingThread(QThread):
    def __init__(self, parent):
        QThread.__init__(self)
        self.result = None
        self.parent = parent
        self._stopped = False
        self.mutex = QMutex()
        self.filePrefix = None
        self.fileFormat = None
        self.wallColoring = None
        self.cellColoring = None
        self.pointColoring = None
        self.extraDrawing = []
        self.pointSize = None
        self.pointLineColor = None
        self.pointLineThickness = None
        self.ellipsisDraw = None
        self.overSampling = None
        self.wallThickness = None
        self.bgColor = None
        self.loading = False
        self._crop = QRect(0,0,1,1)
        self._pix = None
        self._end_image_plot = False
        self._loading_arguments = {}
        self.retryObject = None

    def end_step(self):
        return len(self.result)+1

    def stop(self, value = True):
        self.mutex.lock()
        self._stopped = value
        self.mutex.unlock()

    def stopped(self):
        self.mutex.lock()
        val = self._stopped
        self.mutex.unlock()
        return val

    def nextImage(self):
        QCoreApplication.postEvent(self.parent, NextImageEvent())

    def abort(self, reason, **others):
        e = AbortPlottingEvent(reason)
        if others:
            e.others = others
        QCoreApplication.postEvent(self.parent, e)

    def finished(self):
        if self.loading:
            QCoreApplication.postEvent(self.parent, FinishLoadingEvent())
            self.loading = False
        else:
            QCoreApplication.postEvent(self.parent, FinishPlottingEvent())

    def image_ready(self):
        QCoreApplication.postEvent(self.parent, ImageReadyPlottingEvent())

    def update_nb_images(self, nb):
        QCoreApplication.postEvent(self.parent, UpdateNbImageEvent(nb))

    @property
    def crop_left(self):
        return self._crop.left()

    @crop_left.setter
    def crop_left(self, value):
        self._crop.moveLeft(int(value))

    @property
    def crop_top(self):
        return self._crop.top()

    @crop_top.setter
    def crop_top(self, value):
        self._crop.moveTop(int(value))

    @property
    def crop_width(self):
        return self._crop.width()

    @crop_width.setter
    def crop_width(self, value):
        self._crop.setWidth(int(value))

    @property
    def crop_height(self):
        return self._crop.height()

    @crop_height.setter
    def crop_height(self, value):
        self._crop.setHeight(int(value))

    def reset_crop(self):
        self._crop = QRect(QPoint(0,0), self.img_size)

    @property
    def crop(self):
        return QRect(self._crop)

    @crop.deleter
    def crop(self):
        self.reset_crop()

    @property
    def end_image_plot(self):
        '''
        If true, plot the growth data on the end image rather than the start image of the growth calculation.
        '''
        return self._end_image_plot

    @end_image_plot.setter
    def end_image_plot(self, value):
        self._end_image_plot = bool(value)

    @property
    def pix(self):
        '''Thread-safe image storage.'''
        self.mutex.lock()
        pix = self._pix
        self.mutex.unlock()
        return pix

    @pix.setter
    def pix(self, value):
        self.mutex.lock()
        self._pix = value
        self.mutex.unlock()

    def render_valid(self):
        if self.result is None:
            log_debug("result is None")
            return False
        if self.parent is None:
            log_debug("parent is None")
            return False
        if self.ellipsisDraw is None:
            log_debug("ellipsisDraw is None")
            return False
        if self.cellColoring is None:
            log_debug("cellColoring is None")
            return False
        if self.wallColoring is None:
            log_debug("wallColoring is None")
            return False
        if self.pointColoring is None:
            log_debug("pointColoring is None")
            return False
        if self.pointSize is None:
            log_debug("pointSize is None")
            return False
        if self.pointLineThickness is None:
            log_debug("pointSize is None")
            return False
        if self.pointLineColor is None:
            log_debug("pointSize is None")
            return False
        if self.wallThickness is None:
            log_debug("wallThickness is None")
            return False
        if self.overSampling is None:
            log_debug("overSampling is None")
            return False
        if self.bgColor is None:
            log_debug("bgColor is None")
            return False
        return True

    def valid(self):
        if self.filePrefix is None:
            log_debug("filePrefix is None")
            return False
        if not self.filePrefix:
            log_debug("filePrefix is Empty")
            return False
        if self.fileFormat is None:
            log_debug("fileFormat is None")
            return False
        return self.render_valid()

    def drawImage(self, imageid):
        cache = image_cache.cache
        cellColoring = self.cellColoring
        wallColoring = self.wallColoring
        pointColoring = self.pointColoring
        ellipsisDraw = self.ellipsisDraw
        overSampling = self.overSampling
        extraDrawing = self.extraDrawing
        bgColor = self.bgColor.rgb()
        result = self.result
        if self.result_type == "Data":
            data = result
            img_name = result.images_name[imageid]
        else:
            data = result.data
            img_name = result.images[imageid]
        #scale = data.images_scale[img_name]
        min_scale = data.minScale()
        img = cache.image(data.image_path(img_name))
        img_data = data[img_name]
        size = self._crop.size()
        pix = QImage(size*overSampling, QImage.Format_ARGB32)
        pix.fill(bgColor)
        painter = QPainter()
        if not painter.begin(pix):
            self.abort("Cannot create painter on QImage")
            return None, None, None
        painter.setRenderHints(QPainter.SmoothPixmapTransform, True)
        painter.setRenderHints(QPainter.Antialiasing, True)
        if overSampling > 1:
            painter.scale(overSampling, overSampling)
        painter.translate(-self._crop.topLeft())
        painter.save()
        painter.translate(self.translate)
        log_debug("Translating: %gx%g" % (self.translate.x(), self.translate.y()) )
        painter.scale(1/min_scale, 1/min_scale)
        painter.save()
        matrix = img_data.matrix()
        painter.setWorldTransform(matrix, True)
        painter.drawImage(QPoint(0,0), img)
        painter.restore()
        #pt_matrix = QTransform()
        #pt_matrix.scale(1/min_scale, 1/min_scale)
        #painter.setTransform(pt_matrix, True)
        cellColoring.startImage(painter, imageid)
        wallColoring.startImage(painter, imageid)
        for ed in extraDrawing:
            ed.startImage(painter, imageid)
        if self.result_type == "Growth":
            cells = result.cells[imageid]
            walls = result.walls[imageid]
        else:
            cells = img_data.cells
            walls = set()
            for cid in img_data.cells:
                pts = [ pt for pt in data.cells[cid] if pt in img_data ]
                if len(pts) > 1:
                    for i in range(len(pts)):
                        walls.add(data.wallId(pts[i-1], pts[i]))
        # Now, draw the cells and the ellipsis
        for cid in cells:
            painter.setPen(Qt.NoPen)
            color = cellColoring(imageid, cid)
            painter.setBrush(color)
            pts = data.cellAtTime(cid, img_data.index)
            if pts:
                pts.append(pts[0])
                ppts = []
                for p1,p2 in zip(pts[:-1], pts[1:]):
                    ppts.append(img_data[p1])
                    ppts.extend(img_data.walls[p1,p2])
                ppts.append(ppts[0])
                poly = QPolygonF(ppts)
                painter.drawPolygon(poly)
        # And draw the walls
        wallThickness = self.wallThickness*min_scale
        for wid in walls:
            color = wallColoring(imageid, wid)
            if color.alpha() > 0:
                pen = QPen(color)
                pen.setWidthF(wallThickness)
                painter.setPen(pen)
                pts = [img_data[wid[0]]] + img_data.walls[wid[0], wid[1]] + [img_data[wid[1]]]
                #painter.drawLine(img_data[wid[0]], img_data[wid[1]])
                painter.drawPolyline(*pts)
        # Then, draw the points
        pointSize = self.pointSize*min_scale
        pointLineColor = self.pointLineColor
        pointLineThickness = self.pointLineThickness*min_scale
        log_debug("pointSize = %g" % pointSize)
        for pid in img_data:
            color = pointColoring(imageid, pid)
            if color.alpha() > 0:
                pen = QPen(pointLineColor)
                pen.setWidthF(pointLineThickness)
                brush = QBrush(color)
                painter.setPen(pen)
                painter.setBrush(brush)
                pos = img_data[pid]
                rect = QRectF(pos.x()-pointSize, pos.y()-pointSize, 2*pointSize, 2*pointSize)
                painter.drawEllipse(rect)
        if ellipsisDraw.plot:
            for cid in cells:
                pts = data.cellAtTime(cid, img_data.index)
                if pts:
                    pts.append(pts[0])
                    ppts = []
                    for p1,p2 in zip(pts[:-1], pts[1:]):
                        ppts.append(img_data[p1])
                        ppts.extend(img_data.walls[p1,p2])
                    ppts.append(ppts[0])
                    #poly = QPolygonF(ppts)
                    #painter.drawPolygon(poly)
                    ellipsisDraw(painter, imageid, cid, ppts, min_scale)
        # At last, draw the extra data
        for ed in extraDrawing:
            ed(painter, imageid)
        tr = painter.worldTransform()
        painter.restore()
        pic_w = wallColoring.finalizeImage(painter, imageid, tr, self.crop)
        pic_c = cellColoring.finalizeImage(painter, imageid, tr, self.crop)
        for ed in extraDrawing:
            ed.finalizeImage(painter, imageid, tr, self.crop)
        painter.end()
        return pix, pic_w, pic_c

    def start(self):
        if self.isRunning():
            assert not self.rendering_all, "Cannot run twice the rendering of all images with the same object."
            return
        if parameters.instance.use_thread:
            log_debug("Starting rendering thread.")
            QThread.start(self)
            return False
        else:
            self.run()
            return True

    def render_all(self):
        self.rendering_all = True
        return self.start()

    def render_single(self, img_id, retry=False):
        if retry:
            while self.isRunning():
                self.wait(10000)
        elif self.isRunning():
            return
        self.rendering_all = False
        self.current_image = img_id
        return self.start()

    def load(self, filename):
        self.loading = True
        self.result = filename
        return self.start()

    def run(self):
        if self.loading:
            self.run_loader()
        elif self.rendering_all:
            self.run_full()
        else:
            self.run_single()

    def run_single(self):
        img = self.current_image
        self.cellColoring.init()
        self.wallColoring.init()
        self.pointColoring.init()
        log_debug("Rendering image %d" % img)
        self.pix, self.pic_w, self.pic_c = self.drawImage(img)
        if self.pic_w is not None:
            log_debug("Has wall image")
        if self.pic_c is not None:
            log_debug("Has cell image")
        if self.pix is not None:
            log_debug("Pix correctly rendered")
        log_debug("Rendered image %d  = %s" % (img, self.pix))
        self.image_ready()

    def reload(self):
        if self.retryObject is None:
            return
        self._loading_arguments.update(self.retryObject.method_args)
        self.load(self.retryObject.filename)

    def run_loader(self):
        filename = self.result
        try:
            self.retryObject = None
# First, prepare the data by getting the images and computing how big they
# should be
            f = open(filename)
            first_line = f.readline()
            f.close()
            if first_line.startswith("TRKR_VERSION"):
                result = Result(None)
                result.load(self.result, **self._loading_arguments)
                result_type = "Growth"
            else:
                result = TrackingData()
                result.load(self.result, **self._loading_arguments)
                result_type = "Data"
            self.result = result
            self.result_type = result_type
            if result_type == "Data":
                data = result
                images = data.images_name
                if data.cells:
                    self.has_cells = True
                    self.has_walls = True
                else:
                    self.has_cells = False
                    self.has_walls = False
                self.has_points = bool(data.cell_points)
            else:
                data = result.data
                images = result.images
                self.has_cells = False
                self.has_walls = False
                self.has_points = False
            self.images = images
            cache = image_cache.cache
            self.update_nb_images(len(result))
            bbox = QRectF()
            ms = data.minScale()
            for i in range(len(result)):
                img_name = images[i]
                img_data = data[img_name]
                img = cache.image(data.image_path(img_name))
                matrix = QTransform()
                matrix = img_data.matrix()
                sc = QTransform()
                sc.scale(1.0/ms, 1.0/ms)
                matrix *= sc
                r = QRectF(img.rect())
                rbox = matrix.map(QPolygonF(r)).boundingRect()
                bbox |= rbox
                log_debug("Image '%s':\n\tSize = %gx%g\n\tTransformed = %gx%g %+g %+g\n\tGlobal bbox = %gx%g %+g %+g\n" %
                             (img_name, r.width(), r.height(), rbox.width(), rbox.height(), rbox.left(), rbox.top(),
                              bbox.width(), bbox.height(), bbox.left(), bbox.top()))
                log_debug("Matrix:\n%g\t%g\t%g\n%g\t%g\t%g\n" %
                            (matrix.m11(), matrix.m12(), matrix.dx(), matrix.m21(), matrix.m22(), matrix.dy()))
                if result_type == "Growth":
                    if result.cells[i]:
                        self.has_cells = True
                    if result.walls[i]:
                        self.has_walls = True
                    self.has_points = bool(result.data.cell_points)
                self.nextImage()
            translate = bbox.topLeft()
            translate *= -1
            self.translate = translate
            size = bbox.size().toSize()
            self.img_size = size
            self._crop = QRect(QPoint(0,0), size)
            self.finished()
            self._loading_arguments = {} # All done, we don't need that anymore
        except RetryTrackingDataException as ex:
            ex.filename = filename
            self.retryObject = ex
            self.finished()
            return
        except Exception as ex:
            _, _, exceptionTraceback = sys.exc_info()
            self.abort(ex, traceback=exceptionTraceback)
            raise

    def run_full(self):
        if not self.valid():
            self.abort("Object was not correctly initialized")
            return
        self.stop(False)
        painter = None
        try:
            result = self.result
            self.update_nb_images(len(result))
#            if self.result_type == "Data":
#                data = result
#                images = result.images_name
#            else:
#                data = result.data
#                images = result.images
#            cache = image_cache.cache
            cellColoring = self.cellColoring
            wallColoring = self.wallColoring
            pointColoring = self.pointColoring
            file_format = self.fileFormat
            file_pattern = "%s%%0%dd.%s" % (self.filePrefix, len(str(len(result))), file_format)
            wall_file_pattern = "%s%%0%dd_wall.%s" % (self.filePrefix, len(str(len(result))), file_format)
            cell_file_pattern = "%s%%0%dd_cell.%s" % (self.filePrefix, len(str(len(result))), file_format)
            cellColoring.init()
            wallColoring.init()
            pointColoring.init()
            self.nextImage()
            for i in range(len(result)):
                if self.stopped():
                    self.abort("User interruption")
                    return
                pix, pic_w, pic_c = self.drawImage(i)
                pix.save(file_pattern % (i+1), file_format)
                if pic_w is not None:
                    self.saveExtra(pic_w, wall_file_pattern % (i+1), file_format)
                if pic_c is not None:
                    self.saveExtra(pic_c, cell_file_pattern % (i+1), file_format)
                self.nextImage()
            self.finished()
        except Exception as ex:
            if painter is not None:
                painter.end()
            _, _, exceptionTraceback = sys.exc_info()
            self.abort(ex, traceback=exceptionTraceback)
            raise

    def saveExtra(self, picture, file_name, file_format):
        rect = picture.boundingRect()
        pix = QImage(rect.size(), QImage.Format_ARGB32)
        pix.fill(QColor(0, 0, 0, 0).rgba())
        paint = QPainter()
        paint.begin(pix)
        paint.drawPicture(rect.topLeft()*-1, picture)
        paint.end()
        pix.save(file_name, file_format)
Example #11
0
class PlottingThread(QThread):
    def __init__(self, parent):
        QThread.__init__(self)
        self.result = None
        self.parent = parent
        self._stopped = False
        self.mutex = QMutex()
        self.filePrefix = None
        self.fileFormat = None
        self.wallColoring = None
        self.cellColoring = None
        self.pointColoring = None
        self.extraDrawing = []
        self.pointSize = None
        self.pointLineColor = None
        self.pointLineThickness = None
        self.ellipsisDraw = None
        self.overSampling = None
        self.wallThickness = None
        self.bgColor = None
        self.loading = False
        self._crop = QRect(0, 0, 1, 1)
        self._pix = None
        self._end_image_plot = False
        self._loading_arguments = {}
        self.retryObject = None

    def end_step(self):
        return len(self.result) + 1

    def stop(self, value=True):
        self.mutex.lock()
        self._stopped = value
        self.mutex.unlock()

    def stopped(self):
        self.mutex.lock()
        val = self._stopped
        self.mutex.unlock()
        return val

    def nextImage(self):
        QCoreApplication.postEvent(self.parent, NextImageEvent())

    def abort(self, reason, **others):
        e = AbortPlottingEvent(reason)
        if others:
            e.others = others
        QCoreApplication.postEvent(self.parent, e)

    def finished(self):
        if self.loading:
            QCoreApplication.postEvent(self.parent, FinishLoadingEvent())
            self.loading = False
        else:
            QCoreApplication.postEvent(self.parent, FinishPlottingEvent())

    def image_ready(self):
        QCoreApplication.postEvent(self.parent, ImageReadyPlottingEvent())

    def update_nb_images(self, nb):
        QCoreApplication.postEvent(self.parent, UpdateNbImageEvent(nb))

    @property
    def crop_left(self):
        return self._crop.left()

    @crop_left.setter
    def crop_left(self, value):
        self._crop.moveLeft(int(value))

    @property
    def crop_top(self):
        return self._crop.top()

    @crop_top.setter
    def crop_top(self, value):
        self._crop.moveTop(int(value))

    @property
    def crop_width(self):
        return self._crop.width()

    @crop_width.setter
    def crop_width(self, value):
        self._crop.setWidth(int(value))

    @property
    def crop_height(self):
        return self._crop.height()

    @crop_height.setter
    def crop_height(self, value):
        self._crop.setHeight(int(value))

    def reset_crop(self):
        self._crop = QRect(QPoint(0, 0), self.img_size)

    @property
    def crop(self):
        return QRect(self._crop)

    @crop.deleter
    def crop(self):
        self.reset_crop()

    @property
    def end_image_plot(self):
        '''
        If true, plot the growth data on the end image rather than the start image of the growth calculation.
        '''
        return self._end_image_plot

    @end_image_plot.setter
    def end_image_plot(self, value):
        self._end_image_plot = bool(value)

    @property
    def pix(self):
        '''Thread-safe image storage.'''
        self.mutex.lock()
        pix = self._pix
        self.mutex.unlock()
        return pix

    @pix.setter
    def pix(self, value):
        self.mutex.lock()
        self._pix = value
        self.mutex.unlock()

    def render_valid(self):
        if self.result is None:
            log_debug("result is None")
            return False
        if self.parent is None:
            log_debug("parent is None")
            return False
        if self.ellipsisDraw is None:
            log_debug("ellipsisDraw is None")
            return False
        if self.cellColoring is None:
            log_debug("cellColoring is None")
            return False
        if self.wallColoring is None:
            log_debug("wallColoring is None")
            return False
        if self.pointColoring is None:
            log_debug("pointColoring is None")
            return False
        if self.pointSize is None:
            log_debug("pointSize is None")
            return False
        if self.pointLineThickness is None:
            log_debug("pointSize is None")
            return False
        if self.pointLineColor is None:
            log_debug("pointSize is None")
            return False
        if self.wallThickness is None:
            log_debug("wallThickness is None")
            return False
        if self.overSampling is None:
            log_debug("overSampling is None")
            return False
        if self.bgColor is None:
            log_debug("bgColor is None")
            return False
        return True

    def valid(self):
        if self.filePrefix is None:
            log_debug("filePrefix is None")
            return False
        if not self.filePrefix:
            log_debug("filePrefix is Empty")
            return False
        if self.fileFormat is None:
            log_debug("fileFormat is None")
            return False
        return self.render_valid()

    def drawImage(self, imageid):
        cache = image_cache.cache
        cellColoring = self.cellColoring
        wallColoring = self.wallColoring
        pointColoring = self.pointColoring
        ellipsisDraw = self.ellipsisDraw
        overSampling = self.overSampling
        extraDrawing = self.extraDrawing
        bgColor = self.bgColor.rgb()
        result = self.result
        if self.result_type == "Data":
            data = result
            img_name = result.images_name[imageid]
        else:
            data = result.data
            img_name = result.images[imageid]
        #scale = data.images_scale[img_name]
        min_scale = data.minScale()
        img = cache.image(data.image_path(img_name))
        img_data = data[img_name]
        size = self._crop.size()
        pix = QImage(size * overSampling, QImage.Format_ARGB32)
        pix.fill(bgColor)
        painter = QPainter()
        if not painter.begin(pix):
            self.abort("Cannot create painter on QImage")
            return None, None, None
        painter.setRenderHints(QPainter.SmoothPixmapTransform, True)
        painter.setRenderHints(QPainter.Antialiasing, True)
        if overSampling > 1:
            painter.scale(overSampling, overSampling)
        painter.translate(-self._crop.topLeft())
        painter.save()
        painter.translate(self.translate)
        log_debug("Translating: %gx%g" %
                  (self.translate.x(), self.translate.y()))
        painter.scale(1 / min_scale, 1 / min_scale)
        painter.save()
        matrix = img_data.matrix()
        painter.setWorldTransform(matrix, True)
        painter.drawImage(QPoint(0, 0), img)
        painter.restore()
        #pt_matrix = QTransform()
        #pt_matrix.scale(1/min_scale, 1/min_scale)
        #painter.setTransform(pt_matrix, True)
        cellColoring.startImage(painter, imageid)
        wallColoring.startImage(painter, imageid)
        for ed in extraDrawing:
            ed.startImage(painter, imageid)
        if self.result_type == "Growth":
            cells = result.cells[imageid]
            walls = result.walls[imageid]
        else:
            cells = img_data.cells
            walls = set()
            for cid in img_data.cells:
                pts = [pt for pt in data.cells[cid] if pt in img_data]
                if len(pts) > 1:
                    for i in range(len(pts)):
                        walls.add(data.wallId(pts[i - 1], pts[i]))
        # Now, draw the cells and the ellipsis
        for cid in cells:
            painter.setPen(Qt.NoPen)
            color = cellColoring(imageid, cid)
            painter.setBrush(color)
            pts = data.cellAtTime(cid, img_data.index)
            if pts:
                pts.append(pts[0])
                ppts = []
                for p1, p2 in zip(pts[:-1], pts[1:]):
                    ppts.append(img_data[p1])
                    ppts.extend(img_data.walls[p1, p2])
                ppts.append(ppts[0])
                poly = QPolygonF(ppts)
                painter.drawPolygon(poly)
        # And draw the walls
        wallThickness = self.wallThickness * min_scale
        for wid in walls:
            color = wallColoring(imageid, wid)
            if color.alpha() > 0:
                pen = QPen(color)
                pen.setWidthF(wallThickness)
                painter.setPen(pen)
                pts = [img_data[wid[0]]
                       ] + img_data.walls[wid[0], wid[1]] + [img_data[wid[1]]]
                #painter.drawLine(img_data[wid[0]], img_data[wid[1]])
                painter.drawPolyline(*pts)
        # Then, draw the points
        pointSize = self.pointSize * min_scale
        pointLineColor = self.pointLineColor
        pointLineThickness = self.pointLineThickness * min_scale
        log_debug("pointSize = %g" % pointSize)
        for pid in img_data:
            color = pointColoring(imageid, pid)
            if color.alpha() > 0:
                pen = QPen(pointLineColor)
                pen.setWidthF(pointLineThickness)
                brush = QBrush(color)
                painter.setPen(pen)
                painter.setBrush(brush)
                pos = img_data[pid]
                rect = QRectF(pos.x() - pointSize,
                              pos.y() - pointSize, 2 * pointSize,
                              2 * pointSize)
                painter.drawEllipse(rect)
        if ellipsisDraw.plot:
            for cid in cells:
                pts = data.cellAtTime(cid, img_data.index)
                if pts:
                    pts.append(pts[0])
                    ppts = []
                    for p1, p2 in zip(pts[:-1], pts[1:]):
                        ppts.append(img_data[p1])
                        ppts.extend(img_data.walls[p1, p2])
                    ppts.append(ppts[0])
                    #poly = QPolygonF(ppts)
                    #painter.drawPolygon(poly)
                    ellipsisDraw(painter, imageid, cid, ppts, min_scale)
        # At last, draw the extra data
        for ed in extraDrawing:
            ed(painter, imageid)
        tr = painter.worldTransform()
        painter.restore()
        pic_w = wallColoring.finalizeImage(painter, imageid, tr, self.crop)
        pic_c = cellColoring.finalizeImage(painter, imageid, tr, self.crop)
        for ed in extraDrawing:
            ed.finalizeImage(painter, imageid, tr, self.crop)
        painter.end()
        return pix, pic_w, pic_c

    def start(self):
        if self.isRunning():
            assert not self.rendering_all, "Cannot run twice the rendering of all images with the same object."
            return
        if parameters.instance.use_thread:
            log_debug("Starting rendering thread.")
            QThread.start(self)
            return False
        else:
            self.run()
            return True

    def render_all(self):
        self.rendering_all = True
        return self.start()

    def render_single(self, img_id, retry=False):
        if retry:
            while self.isRunning():
                self.wait(10000)
        elif self.isRunning():
            return
        self.rendering_all = False
        self.current_image = img_id
        return self.start()

    def load(self, filename):
        self.loading = True
        self.result = filename
        return self.start()

    def run(self):
        if self.loading:
            self.run_loader()
        elif self.rendering_all:
            self.run_full()
        else:
            self.run_single()

    def run_single(self):
        img = self.current_image
        self.cellColoring.init()
        self.wallColoring.init()
        self.pointColoring.init()
        log_debug("Rendering image %d" % img)
        self.pix, self.pic_w, self.pic_c = self.drawImage(img)
        if self.pic_w is not None:
            log_debug("Has wall image")
        if self.pic_c is not None:
            log_debug("Has cell image")
        if self.pix is not None:
            log_debug("Pix correctly rendered")
        log_debug("Rendered image %d  = %s" % (img, self.pix))
        self.image_ready()

    def reload(self):
        if self.retryObject is None:
            return
        self._loading_arguments.update(self.retryObject.method_args)
        self.load(self.retryObject.filename)

    def run_loader(self):
        filename = self.result
        try:
            self.retryObject = None
            # First, prepare the data by getting the images and computing how big they
            # should be
            f = open(filename)
            first_line = f.readline()
            f.close()
            if first_line.startswith("TRKR_VERSION"):
                result = Result(None)
                result.load(self.result, **self._loading_arguments)
                result_type = "Growth"
            else:
                result = TrackingData()
                result.load(self.result, **self._loading_arguments)
                result_type = "Data"
            self.result = result
            self.result_type = result_type
            if result_type == "Data":
                data = result
                images = data.images_name
                if data.cells:
                    self.has_cells = True
                    self.has_walls = True
                else:
                    self.has_cells = False
                    self.has_walls = False
                self.has_points = bool(data.cell_points)
            else:
                data = result.data
                images = result.images
                self.has_cells = False
                self.has_walls = False
                self.has_points = False
            self.images = images
            cache = image_cache.cache
            self.update_nb_images(len(result))
            bbox = QRectF()
            ms = data.minScale()
            for i in range(len(result)):
                img_name = images[i]
                img_data = data[img_name]
                img = cache.image(data.image_path(img_name))
                matrix = QTransform()
                matrix = img_data.matrix()
                sc = QTransform()
                sc.scale(1.0 / ms, 1.0 / ms)
                matrix *= sc
                r = QRectF(img.rect())
                rbox = matrix.map(QPolygonF(r)).boundingRect()
                bbox |= rbox
                log_debug(
                    "Image '%s':\n\tSize = %gx%g\n\tTransformed = %gx%g %+g %+g\n\tGlobal bbox = %gx%g %+g %+g\n"
                    % (img_name, r.width(), r.height(), rbox.width(),
                       rbox.height(), rbox.left(), rbox.top(), bbox.width(),
                       bbox.height(), bbox.left(), bbox.top()))
                log_debug("Matrix:\n%g\t%g\t%g\n%g\t%g\t%g\n" %
                          (matrix.m11(), matrix.m12(), matrix.dx(),
                           matrix.m21(), matrix.m22(), matrix.dy()))
                if result_type == "Growth":
                    if result.cells[i]:
                        self.has_cells = True
                    if result.walls[i]:
                        self.has_walls = True
                    self.has_points = bool(result.data.cell_points)
                self.nextImage()
            translate = bbox.topLeft()
            translate *= -1
            self.translate = translate
            size = bbox.size().toSize()
            self.img_size = size
            self._crop = QRect(QPoint(0, 0), size)
            self.finished()
            self._loading_arguments = {
            }  # All done, we don't need that anymore
        except RetryTrackingDataException as ex:
            ex.filename = filename
            self.retryObject = ex
            self.finished()
            return
        except Exception as ex:
            _, _, exceptionTraceback = sys.exc_info()
            self.abort(ex, traceback=exceptionTraceback)
            raise

    def run_full(self):
        if not self.valid():
            self.abort("Object was not correctly initialized")
            return
        self.stop(False)
        painter = None
        try:
            result = self.result
            self.update_nb_images(len(result))
            #            if self.result_type == "Data":
            #                data = result
            #                images = result.images_name
            #            else:
            #                data = result.data
            #                images = result.images
            #            cache = image_cache.cache
            cellColoring = self.cellColoring
            wallColoring = self.wallColoring
            pointColoring = self.pointColoring
            file_format = self.fileFormat
            file_pattern = "%s%%0%dd.%s" % (self.filePrefix,
                                            len(str(len(result))), file_format)
            wall_file_pattern = "%s%%0%dd_wall.%s" % (
                self.filePrefix, len(str(len(result))), file_format)
            cell_file_pattern = "%s%%0%dd_cell.%s" % (
                self.filePrefix, len(str(len(result))), file_format)
            cellColoring.init()
            wallColoring.init()
            pointColoring.init()
            self.nextImage()
            for i in range(len(result)):
                if self.stopped():
                    self.abort("User interruption")
                    return
                pix, pic_w, pic_c = self.drawImage(i)
                pix.save(file_pattern % (i + 1), file_format)
                if pic_w is not None:
                    self.saveExtra(pic_w, wall_file_pattern % (i + 1),
                                   file_format)
                if pic_c is not None:
                    self.saveExtra(pic_c, cell_file_pattern % (i + 1),
                                   file_format)
                self.nextImage()
            self.finished()
        except Exception as ex:
            if painter is not None:
                painter.end()
            _, _, exceptionTraceback = sys.exc_info()
            self.abort(ex, traceback=exceptionTraceback)
            raise

    def saveExtra(self, picture, file_name, file_format):
        rect = picture.boundingRect()
        pix = QImage(rect.size(), QImage.Format_ARGB32)
        pix.fill(QColor(0, 0, 0, 0).rgba())
        paint = QPainter()
        paint.begin(pix)
        paint.drawPicture(rect.topLeft() * -1, picture)
        paint.end()
        pix.save(file_name, file_format)
Example #12
0
class Page(object):
    """Represents a page from a Poppler.Document.
    
    It maintains its own size and can draw itself using the cache.
    It also can maintain a list of links and return links at certain
    points or rectangles.
    
    The visible attribute (setVisible and visible) defaults to True but
    can be set to False to hide the page from a Surface (this is done by
    the Layout).
    
    """
    def __init__(self, document, pageNumber):
        self._document = document
        self._pageNumber = pageNumber
        self._pageSize = document.page(pageNumber).pageSize()
        self._rotation = popplerqt4.Poppler.Page.Rotate0
        self._rect = QRect()
        self._scale = 1.0
        self._visible = True
        self._layout = lambda: None
        self._waiting = True # whether image still needs to be generated
        
    def document(self):
        """Returns the document."""
        return self._document
        
    def pageNumber(self):
        """Returns the page number."""
        return self._pageNumber
    
    def pageSize(self):
        """The page size in points (1/72 inch), taking rotation into account."""
        return self._pageSize
        
    def layout(self):
        """Returns the Layout if we are part of one."""
        return self._layout()
    
    def visible(self):
        """Returns True if this page is visible (will be displayed)."""
        return self._visible
        
    def setVisible(self, visible):
        """Sets whether  this page is visible (will be displayed)."""
        self._visible = visible
        
    def rect(self):
        """Returns our QRect(), with position and size."""
        return self._rect
    
    def size(self):
        """Returns our size."""
        return self._rect.size()
    
    def height(self):
        """Returns our height."""
        return self._rect.height()
        
    def width(self):
        """Returns our width."""
        return self._rect.width()
        
    def pos(self):
        """Returns our position."""
        return self._rect.topLeft()
    
    def setPos(self, point):
        """Sets our position (affects the Layout)."""
        self._rect.moveTopLeft(point)
    
    def setRotation(self, rotation):
        """Sets our Poppler.Page.Rotation."""
        old, self._rotation = self._rotation, rotation
        if (old ^ rotation) & 1:
            self._pageSize.transpose()
            self.computeSize()
    
    def rotation(self):
        """Returns our rotation."""
        return self._rotation
    
    def computeSize(self):
        """Recomputes our size."""
        xdpi, ydpi = self.layout().dpi() if self.layout() else (72.0, 72.0)
        x = round(self._pageSize.width() * xdpi / 72.0 * self._scale)
        y = round(self._pageSize.height() * ydpi / 72.0 * self._scale)
        self._rect.setSize(QSize(x, y))
        
    def setScale(self, scale):
        """Changes the display scale."""
        self._scale = scale
        self.computeSize()
        
    def scale(self):
        """Returns our display scale."""
        return self._scale
    
    def scaleForWidth(self, width):
        """Returns the scale we need to display ourselves at the given width."""
        if self.layout():
            return width * 72.0 / self.layout().dpi()[0] / self._pageSize.width()
        else:
            return float(width) / self._pageSize.width()
        
    def scaleForHeight(self, height):
        """Returns the scale we need to display ourselves at the given height."""
        if self.layout():
            return height * 72.0 / self.layout().dpi()[1] / self._pageSize.height()
        else:
            return float(height) / self._pageSize.height()
        
    def setWidth(self, width):
        """Change our scale to force our width to the given value."""
        self.setScale(self.scaleForWidth(width))

    def setHeight(self, height):
        """Change our scale to force our height to the given value."""
        self.setScale(self.scaleForHeight(height))
        
    def paint(self, painter, rect):
        update_rect = rect & self.rect()
        if not update_rect:
            return
        image_rect = QRect(update_rect.topLeft() - self.rect().topLeft(), update_rect.size())
        image = cache.image(self)
        self._waiting = not image
        if image:
            painter.drawImage(update_rect, image, image_rect)
        else:
            # schedule an image to be generated, if done our update() method is called
            cache.generate(self)
            # find suitable image to be scaled from other size
            image = cache.image(self, False)
            if image:
                hscale = float(image.width()) / self.width()
                vscale = float(image.height()) / self.height()
                image_rect = QRectF(image_rect.x() * hscale, image_rect.y() * vscale,
                                    image_rect.width() * hscale, image_rect.height() * vscale)
                painter.drawImage(QRectF(update_rect), image, image_rect)
            else:
                # draw blank paper, using the background color of the cache rendering (if set)
                # or from the document itself.
                color = (cache.options(self.document()).paperColor()
                         or cache.options().paperColor() or self.document().paperColor())
                painter.fillRect(update_rect, color)

    def update(self):
        """Called when an image is drawn."""
        # only redraw when we were waiting for a correctly sized image.
        if self._waiting and self.layout():
            self.layout().updatePage(self)
    
    def repaint(self):
        """Call this to force a repaint (e.g. when the rendering options are changed)."""
        self._waiting = True
        cache.generate(self)
    
    def image(self, rect, xdpi=72.0, ydpi=None, options=None):
        """Returns a QImage of the specified rectangle (relative to our layout).
        
        xdpi defaults to 72.0 and ydpi defaults to xdpi.
        options may be a render.RenderOptions instance that will set some document
        rendering options just before rendering the image.
        """
        rect = rect.normalized().intersected(self.rect())
        if not rect:
            return
        rect.translate(-self.pos())
        if ydpi is None:
            ydpi = xdpi
        hscale = (xdpi * self.pageSize().width()) / (72.0 * self.width())
        vscale = (ydpi * self.pageSize().height()) / (72.0 * self.height())
        x = rect.x() * hscale
        y = rect.y() * vscale
        w = rect.width() * hscale
        h = rect.height() * vscale
        with lock(self.document()):
            options and options.write(self.document())
            page = self.document().page(self._pageNumber)
            image = page.renderToImage(xdpi, ydpi, x, y, w, h, self._rotation)
        image.setDotsPerMeterX(int(xdpi * 39.37))
        image.setDotsPerMeterY(int(ydpi * 39.37))
        return image
    
    def linksAt(self, point):
        """Returns a list() of zero or more links touched by point (relative to surface).
        
        The list is sorted with the smallest rectangle first.
        
        """
        # Poppler.Link objects have their linkArea() ranging in width and height
        # from 0.0 to 1.0, so divide by resp. height and width of the Page.
        point = point - self.pos()
        x = float(point.x()) / self.width()
        y = float(point.y()) / self.height()
        # rotate
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                x, y = y, 1-x
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                x, y = 1-x, 1-y
            else: # 270
                x, y = 1-y, x
        return list(sorted(cache.links(self).at(x, y), key=lambda link: link.linkArea().width()))
        
    def linksIn(self, rect):
        """Returns an unordered set() of links enclosed in rectangle (relative to surface)."""
        rect = rect.normalized()
        rect.translate(-self.pos())
        left   = float(rect.left())   / self.width()
        top    = float(rect.top())    / self.height()
        right  = float(rect.right())  / self.width()
        bottom = float(rect.bottom()) / self.height()
        # rotate
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                left, top, right, bottom = top, 1-right, bottom, 1-left
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                left, top, right, bottom = 1-right, 1-bottom, 1-left, 1-top
            else: # 270
                left, top, right, bottom = 1-bottom, left, 1-top, right
        return cache.links(self).inside(left, top, right, bottom)

    def linkRect(self, linkarea):
        """Returns a QRect encompassing the linkArea (of a link) in coordinates of our rect()."""
        left, top, right, bottom = linkarea.normalized().getCoords()
        # rotate
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                left, top, right, bottom = 1-bottom, left, 1-top, right
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                left, top, right, bottom = 1-right, 1-bottom, 1-left, 1-top
            else: # 270
                left, top, right, bottom = top, 1-right, bottom, 1-left
        rect = QRect()
        rect.setCoords(left * self.width(), top * self.height(), right * self.width(), bottom * self.height())
        rect.translate(self.pos())
        return rect
        
    def text(self, rect):
        """Returns text inside rectangle (relative to surface)."""
        rect = rect.normalized()
        rect.translate(-self.pos())
        w, h = self.pageSize().width(), self.pageSize().height()
        left   = float(rect.left())   / self.width()  * w
        top    = float(rect.top())    / self.height() * h
        right  = float(rect.right())  / self.width()  * w
        bottom = float(rect.bottom()) / self.height() * h
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                left, top, right, bottom = top, w-right, bottom, w-left
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                left, top, right, bottom = w-right, h-bottom, w-left, h-top
            else: # 270
                left, top, right, bottom = h-bottom, left, h-top, right
        rect = QRectF()
        rect.setCoords(left, top, right, bottom)
        with lock(self.document()):
            page = self.document().page(self._pageNumber)
            return page.text(rect)
        
    def searchRect(self, rectF):
        """Returns a QRect encompassing the given rect (in points) to our position, size and rotation."""
        rect = rectF.normalized()
        left, top, right, bottom = rect.getCoords()
        w, h = self.pageSize().width(), self.pageSize().height()
        hscale = self.width()  / float(w)
        vscale = self.height() / float(h)
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                left, top, right, bottom = w-bottom, left, w-top, right
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                left, top, right, bottom = w-right, h-bottom, w-left, h-top
            else: # 270
                left, top, right, bottom = top, h-right, bottom, h-left
        rect = QRect()
        rect.setCoords(left * hscale, top * vscale, right * hscale, bottom * vscale)
        return rect
Example #13
0
class SelectArea(QLabel):
    doneSignal = pyqtSignal(QPixmap)

    def __init__(self, *args, **kwargs):
        super(SelectArea, self).__init__(*args, **kwargs)

        self.pixmap = take_screenshot()

        self.setPixmap(self.pixmap)

        self.setCursor(Qt.CrossCursor)

        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        self.showFullScreen()
        self.move(0, 0)
        self.resize(self.pixmap.size())

        self.selected = QRect(0, 0, 0, 0)
        self.first_point = None

    def paintEvent(self, event):
        super(SelectArea, self).paintEvent(event)

        painter = QPainter(self)

        drawRegion = QRegion(self.pixmap.rect())
        drawRegion = drawRegion.subtract(QRegion(self.selected))

        painter.setClipRegion(drawRegion)

        painter.setBrush(QBrush(QColor(0, 0, 0, 120)))
        painter.drawRect(self.pixmap.rect())

        painter.setClipping(False)

        if self.selected:
            # направляющие линии

            painter.setPen(QPen(QColor(255, 255, 255)))
            painter.setBrush(QBrush(QColor(0, 0, 0, 0)))

            vertical = self.selected.normalized()
            vertical.setLeft(-1)
            vertical.setRight(self.pixmap.rect().right() + 1)

            horizontal = self.selected.normalized()
            horizontal.setTop(-1)
            horizontal.setBottom(self.pixmap.rect().bottom() + 1)

            painter.drawRects(
                vertical,
                horizontal
            )

            # координаты начала

            bound = self.pixmap.rect()
            bound.setBottomRight(self.selected.topLeft() - QPoint(5, 5))

            painter.drawText(
                bound,
                Qt.AlignBottom | Qt.AlignRight,
                '(%d, %d)' % (self.selected.topLeft().x(), self.selected.topLeft().y())
            )

            # ширина/высота

            bound = self.pixmap.rect()
            bound.setTopLeft(self.selected.bottomRight() + QPoint(5, 5))

            painter.drawText(
                bound,
                Qt.AlignTop | Qt.AlignLeft,
                '(%d, %d)' % (self.selected.width(), self.selected.height())
            )

    def keyPressEvent(self, event):
        key = event.key()
        if key == Qt.Key_Escape:
            self.close()

    def mousePressEvent(self, event):
        self.first_point = event.pos()
        self.selected.setTopLeft(self.first_point)

    def mouseMoveEvent(self, event):
        self.move_selection(event.pos())

    def mouseReleaseEvent(self, event):
        self.move_selection(event.pos())
        self.finish()

    def move_selection(self, now_point):
        self.selected = QRect(self.first_point, now_point).normalized()
        self.update()

    def finish(self):
        self.doneSignal.emit(self.pixmap.copy(self.selected))
        self.close()
Example #14
0
class StartupScreen(VispaWidget):

    # inherited parameters
    BACKGROUND_SHAPE = 'ROUNDRECT'
    SELECTABLE_FLAG = False
    AUTOSIZE = True
    AUTOSIZE_KEEP_ASPECT_RATIO = False

    PROTOTYPING_DESCRIPTION = """Prototyping"""

    EXECUTING_DESCRIPTION = """Executing"""

    VERIFYING_DESCRIPTION = """Verifying"""

    def __init__(self, parent):
        self._descriptionWidgets = []
        self._descriptionActiveRects = [
            QRect(), QRect(), QRect()
        ]  # descriptions will be visible if mouse cursor is in the rect
        VispaWidget.__init__(self, parent)
        self._filenewIcon = QIcon(QPixmap(":/resources/filenew.svg"))
        self._fileopenIcon = QIcon(QPixmap(":/resources/fileopen.svg"))
        self.setImage(
            QSvgRenderer(":/resources/startup_development_cycle.svg"))
        self.setDragable(False)
        self.setMouseTracking(
            True)  # receive mouse events even if no button is pressed
        self._hideDescriptions = False

        self.createPrototypingWidget()
        self.createExecutionWidget()
        self.createVerifyingWidget()

    def createDescriptionWidget(self, arrowDirection, description):
        widget = VispaWidget(self.parent())
        widget.enableAutosizing(True, False)
        widget.setSelectable(False)
        widget.setArrowShape(arrowDirection)
        widget.setVisible(not self._hideDescriptions)
        widget.setDragable(False)
        self._descriptionWidgets.append(widget)
        return widget

    def createPrototypingWidget(self):
        self._prototypingDescriptionWidget = self.createDescriptionWidget(
            VispaWidget.ARROW_SHAPE_BOTTOM, self.PROTOTYPING_DESCRIPTION)

        bodyWidget = QWidget(self._prototypingDescriptionWidget)
        bodyWidget.setLayout(QGridLayout())
        bodyWidget.layout().setContentsMargins(0, 0, 0, 0)

        bodyWidget.layout().addWidget(QLabel("Design physics analysis:"), 0, 0)
        analysisDesignerButton = QToolButton()
        analysisDesignerButton.setText("Analysis Designer")
        analysisDesignerButton.setIcon(self._filenewIcon)
        self.connect(analysisDesignerButton, SIGNAL("clicked(bool)"),
                     self.parent().newAnalysisDesignerSlot)
        bodyWidget.layout().addWidget(analysisDesignerButton, 0, 1)
        bodyWidget.layout().addWidget(QLabel("Create physics event:"), 1, 0)
        pxlButton = QToolButton()
        pxlButton.setText("PXL Editor")
        pxlButton.setIcon(self._filenewIcon)
        self.connect(pxlButton, SIGNAL("clicked(bool)"),
                     self.parent().newPxlSlot)
        bodyWidget.layout().addWidget(pxlButton, 1, 1)

        self._prototypingDescriptionWidget.setBodyWidget(bodyWidget)

    def createExecutionWidget(self):
        self._executionDescriptionWidget = self.createDescriptionWidget(
            VispaWidget.ARROW_SHAPE_RIGHT, self.EXECUTING_DESCRIPTION)

        bodyWidget = QWidget(self._executionDescriptionWidget)
        bodyWidget.setLayout(QGridLayout())
        bodyWidget.layout().setContentsMargins(0, 0, 0, 0)

        label = QLabel("Open and run existing analysis:")
        bodyWidget.layout().addWidget(label, 0, 0)
        analysisDesignerButton = QToolButton()
        analysisDesignerButton.setText("Open analysis file")
        analysisDesignerButton.setIcon(self._fileopenIcon)
        self.connect(analysisDesignerButton, SIGNAL("clicked(bool)"),
                     self.parent().openAnalysisFileSlot)
        bodyWidget.layout().addWidget(analysisDesignerButton, 0, 1)
        self._analysisDesignerRecentFilesList = QListWidget()
        self._analysisDesignerRecentFilesList.setFixedSize(
            label.sizeHint().width() +
            analysisDesignerButton.sizeHint().width(), 150)
        self.connect(self._analysisDesignerRecentFilesList,
                     SIGNAL("doubleClicked(QModelIndex)"),
                     self.parent().openAnalysisFileSlot)
        bodyWidget.layout().addWidget(self._analysisDesignerRecentFilesList, 1,
                                      0, 1, 2)

        self._executionDescriptionWidget.setBodyWidget(bodyWidget)

    def analysisDesignerRecentFilesList(self):
        return self._analysisDesignerRecentFilesList

    def createVerifyingWidget(self):
        self._verifyingDescriptionWidget = self.createDescriptionWidget(
            VispaWidget.ARROW_SHAPE_LEFT, self.VERIFYING_DESCRIPTION)

        bodyWidget = QWidget(self._verifyingDescriptionWidget)
        bodyWidget.setLayout(QGridLayout())
        bodyWidget.layout().setContentsMargins(0, 0, 0, 0)

        label = QLabel("Browse an existing PXL data file:")
        bodyWidget.layout().addWidget(label, 0, 0)
        analysisDesignerButton = QToolButton()
        analysisDesignerButton.setText("Open PXL file")
        analysisDesignerButton.setIcon(self._fileopenIcon)
        self.connect(analysisDesignerButton, SIGNAL("clicked(bool)"),
                     self.parent().openPxlFileSlot)
        bodyWidget.layout().addWidget(analysisDesignerButton, 0, 1)
        self._pxlEditorRecentFilesList = QListWidget()
        self._pxlEditorRecentFilesList.setFixedSize(
            label.sizeHint().width() +
            analysisDesignerButton.sizeHint().width(), 150)
        self.connect(self._pxlEditorRecentFilesList,
                     SIGNAL("doubleClicked(QModelIndex)"),
                     self.parent().openPxlFileSlot)
        bodyWidget.layout().addWidget(self._pxlEditorRecentFilesList, 1, 0, 1,
                                      2)

        self._verifyingDescriptionWidget.setBodyWidget(bodyWidget)

    def pxlEditorRecentFilesList(self):
        return self._pxlEditorRecentFilesList

    def mouseMoveEvent(self, event):
        if bool(event.buttons()):
            VispaWidget.mouseMoveEvent(self, event)
        elif self._hideDescriptions:
            for i in range(len(self._descriptionWidgets)):
                self._descriptionWidgets[i].setVisible(
                    self._descriptionActiveRects[i].contains(event.pos()))

    def moveEvent(self, event):
        VispaWidget.moveEvent(self, event)
        self.rearangeDescriptionWidgets()

    def rearangeContent(self):
        VispaWidget.rearangeContent(self)
        self.rearangeDescriptionWidgets()

    def rearangeDescriptionWidgets(self):
        self._activeSize = QSize(0.3 * self.width(), 0.1 * self.height())
        self._prototypingRect = QRect(
            QPoint(0.5 * (self.width() - self._activeSize.width()), 0),
            self._activeSize)
        self._executionRect = QRect(QPoint(0, 0.635 * self.height()),
                                    self._activeSize)
        self._verifyingRect = QRect(
            QPoint(self.width() - self._activeSize.width(),
                   0.635 * self.height()), self._activeSize)
        self._descriptionActiveRects[0] = self._prototypingRect
        self._descriptionActiveRects[1] = self._executionRect
        self._descriptionActiveRects[2] = self._verifyingRect

        self._prototypingDescriptionWidget.move(
            self.mapToParent(self._prototypingRect.topLeft()) +
            QPoint((self._prototypingRect.width() -
                    self._prototypingDescriptionWidget.width()) *
                   0.5, -self._prototypingDescriptionWidget.height()))
        self._executionDescriptionWidget.move(
            self.mapToParent(self._executionRect.topLeft()) - QPoint(
                self._executionDescriptionWidget.width(), -0.5 *
                (self._executionRect.height() -
                 self._executionDescriptionWidget.height())))
        self._verifyingDescriptionWidget.move(
            self.mapToParent(self._verifyingRect.topRight()) - QPoint(
                0, -0.5 * (self._verifyingRect.height() -
                           self._verifyingDescriptionWidget.height())))

    def boundingRect(self):
        br = VispaWidget.boundingRect(self)
        for w in self._descriptionWidgets:
            br = br.united(w.boundingRect())
        return br

    def setVisible(self, visible):
        VispaWidget.setVisible(self, visible)
        self._executionDescriptionWidget.setVisible(
            visible and not self._hideDescriptions)
        self._prototypingDescriptionWidget.setVisible(
            visible and not self._hideDescriptions)
        self._verifyingDescriptionWidget.setVisible(
            visible and not self._hideDescriptions)
Example #15
0
    def __layout(self):
        # position itself over `widget`
        widget = self.__widget
        if widget is None:
            return

        alignment = self.__alignment
        policy = self.sizePolicy()

        if widget.isWindow():
            bounds = widget.geometry()
        else:

            bounds = QRect(widget.mapToGlobal(QPoint(0, 0)),
                           widget.size())
        if self.isWindow():
            bounds = bounds
        else:
            bounds = QRect(self.parent().mapFromGlobal(bounds.topLeft()),
                           bounds.size())

        sh = self.sizeHint()
        minsh = self.minimumSizeHint()
        minsize = self.minimumSize()
        if minsize.isNull():
            minsize = minsh
        maxsize = bounds.size().boundedTo(self.maximumSize())
        minsize = minsize.boundedTo(maxsize)
        effectivesh = sh.expandedTo(minsize).boundedTo(maxsize)

        hpolicy = policy.horizontalPolicy()
        vpolicy = policy.verticalPolicy()

        def getsize(hint, minimum, maximum, policy):
            if policy == QSizePolicy.Ignored:
                return maximum
            elif policy & QSizePolicy.ExpandFlag:
                return maximum
            else:
                return max(hint, minimum)

        width = getsize(effectivesh.width(), minsize.width(),
                        maxsize.width(), hpolicy)

        heightforw = self.heightForWidth(width)
        if heightforw > 0:
            height = getsize(heightforw, minsize.height(),
                             maxsize.height(), vpolicy)
        else:
            height = getsize(effectivesh.height(), minsize.height(),
                             maxsize.height(), vpolicy)

        size = QSize(width, height)
        if alignment & Qt.AlignLeft:
            x = bounds.x()
        elif alignment & Qt.AlignRight:
            x = bounds.right() - size.width()
        else:
            x = bounds.x() + max(0, bounds.width() - size.width()) // 2

        if alignment & Qt.AlignTop:
            y = bounds.y()
        elif alignment & Qt.AlignBottom:
            y = bounds.bottom() - size.height()
        else:
            y = bounds.y() + max(0, bounds.height() - size.height()) // 2

        geom = QRect(QPoint(x, y), size)
        self.setGeometry(geom)
Example #16
0
class ImageAreaSelector (QtGui.QWidget):
    '''This widget provides means to visually crop a portion of
    an image by selecting it'''
     # pylint: disable=W0612
    NAME = 'ImageAreaSelector'
    DESCRIPTION = 'A widget used to select part of an image'
    AUTHOR = 'Gabriele "Whisky" Visconti'
    WEBSITE = ''
    # pylint: enable=W0612
    
    selection_changed = QtCore.pyqtSignal()
    
    def __init__(self, pixmap, parent=None):
        '''Constructor'''
        QtGui.QWidget.__init__(self, parent)
        self._pixmap = pixmap
        
        self._selection_rect = QRect() 
        self._image_origin = QPoint()
        self._resize_start = None
        self._drag_start = None
        self._handle_size = QSize(-10, -10)
        self._painter = QtGui.QPainter()
        self._hl_color1 = QtGui.QPalette().color(QtGui.QPalette.Highlight)
        self._hl_color2 = QtGui.QPalette().color(QtGui.QPalette.Highlight)
        self._hl_color2.setAlpha(150)
        self._zoom = 1.0
        
        self.adjust_minum_size()
        self.setBackgroundRole(QtGui.QPalette.Dark)
        self.setMouseTracking(True)
        self.setCursor(Qt.CrossCursor)
        
# -------------------- [BEGIN] QT_OVERRIDE

    def mousePressEvent (self, event):
        '''Overrides QWidget's mousePressEvent. Handles starting
        a new selection, starting a drag operation'''
        # pylint: disable=C0103
        mouse_pos = event.pos() / self._zoom
        sel_rect = self._selection_rect
        
        if not event.button() == Qt.LeftButton:
            return
        
        if (not sel_rect.isNull()) and sel_rect.contains(mouse_pos, True):
            handle_rect = QRect(sel_rect.bottomRight(), self._handle_size)
            if handle_rect.contains(mouse_pos):
                self._resize_start = mouse_pos
            else:
                self._drag_start = mouse_pos
        else:
            self._resize_start = mouse_pos
            sel_rect.setTopLeft    (mouse_pos)
            self._selection_rect.setSize(QSize(0, 0))
        

    
    def mouseMoveEvent (self, event):
        '''Overrides QWidget's mouseMoveEvent. Handles resizing
        and dragging operations on selection'''
        # pylint: disable=C0103
        sel_rect = self._selection_rect
        if self._resize_start:
            resize_end = event.pos() / self._zoom
            sel_rect.setBottomRight(sel_rect.bottomRight() +
                                    (resize_end - self._resize_start))
            self._resize_start = resize_end
            self.make_selection_square()
            self.update()
        elif self._drag_start is not None:
            drag_end = event.pos() / self._zoom
            sel_rect.translate(drag_end - self._drag_start)
            self._drag_start = drag_end
            self.update()
            
        # cursor shape:
        mouse_pos = event.pos() / self._zoom
        if (not sel_rect.isNull()) and sel_rect.contains(mouse_pos, True):
            handle_rect = QRect(sel_rect.bottomRight(), self._handle_size)
            if handle_rect.contains(mouse_pos):
                self.setCursor(Qt.SizeFDiagCursor)
            else:
                self.setCursor(Qt.OpenHandCursor)
                
        else:
            self.setCursor(Qt.CrossCursor)
        
            
    
    def mouseReleaseEvent (self, event):
        '''Overrides QWidget's mouseReleaseEvent. Handles ending a resizing or
        draggin operation on the selection'''
        # pylint: disable=C0103
        self._selection_rect = self._selection_rect.normalized()
        self._resize_start = None
        self._drag_start = None
        self.update()
        if not self._selection_rect.isNull():
            self.selection_changed.emit()
        
    
    def paintEvent(self, event):
        '''Overrides QWidtget's paintEvent.'''
        # pylint: disable=C0103
        QtGui.QWidget.paintEvent(self, event)
        self._painter.begin(self)
        pixmap_dest_rect = QRect(self._image_origin * self._zoom, 
                                 self._pixmap.size()*self._zoom)
        self._painter.drawPixmap(pixmap_dest_rect, self._pixmap)
        if not self._selection_rect.isNull():
            # preparing the darkened frame:
            sel_rect = self._selection_rect.normalized()
            frame = QtGui.QPixmap(event.rect().size())
            frame.fill(QtGui.QColor(0, 0, 0, 127))
            frame_painter = QtGui.QPainter(frame)
            # erase the selected area from the frame:
            frame_painter.setCompositionMode(
                                QtGui.QPainter.CompositionMode_DestinationIn)
            sel_rect_scaled = QRect(sel_rect.topLeft() * self._zoom,
                                    sel_rect.size() * self._zoom)
            frame_painter.fillRect(sel_rect_scaled, QtGui.QColor(0, 0, 0, 0))
            # draw selection border :
            frame_painter.setCompositionMode(
                                QtGui.QPainter.CompositionMode_SourceOver)
            frame_painter.setPen(self._hl_color1)
            frame_painter.drawRect(sel_rect_scaled)
            # draw the resize grip (if possible)
            if sel_rect_scaled.width() > 20 and sel_rect_scaled.height() > 20:
                handle_rect = QRect(sel_rect_scaled.bottomRight(), 
                                    self._handle_size)
                frame_painter.fillRect(handle_rect, self._hl_color2)
                frame_painter.drawRect(handle_rect)
            frame_painter.end()
            # painting the darkened frame:
            self._painter.drawPixmap(0, 0, frame)
            
        self._painter.end()
        
        
    def resizeEvent(self, event):
        '''Overrides QWidget's resizeEvent. Handles image centering.'''
        # pylint: disable=C0103
        self.adjust_image_origin()
        
# -------------------- [END] QT_OVERRIDE

    def adjust_image_origin(self):
        '''Recalculates the top left corner's image position, so the
        image is painted centered'''
        # pylint: disable=C0103
        new_size = self.size() / self._zoom
        pix_size = self._pixmap.size()
        
        dx = (new_size.width()  - pix_size.width() ) /2
        dy = (new_size.height() - pix_size.height()) /2
        
        new_image_origin = QPoint(dx, dy)
        self._selection_rect.translate(new_image_origin - self._image_origin)
        self._image_origin = new_image_origin
        log.info('image origin: %s' % new_image_origin)
        
    def select_unscaled(self):
        '''Selects, if possible, a 96 x 96 square centered around the original
        image. In this way the image won't be scaled but won't take up all the 
        96 x 96 area.'''
        # pylint: disable=C0103
        pix_size = self._pixmap.size()
        if pix_size.width() <= 96 and pix_size.height() <= 96:
            viewport_size = self.size()
            x =   (viewport_size.width () - 96) / 2
            y =   (viewport_size.height() - 96) / 2
            self._selection_rect.setTopLeft(QPoint(x, y))
            self._selection_rect.setSize(QSize(96, 96))
            self.update()
            self.selection_changed.emit()
            
            
    def select_all(self):
        '''Selects the whole image. Currently broken for images taller
        than wide.'''
        # TODO: make me work!
        self._selection_rect.setTopLeft(self._image_origin)
        self._selection_rect.setSize(self._pixmap.size())
        self.update()
        self.selection_changed.emit()
        
        
    def rotate_left(self):
        '''Rotates the image counterclockwise.'''
        self._pixmap = self._pixmap.transformed(QtGui.QTransform().rotate(-90))
        self.adjust_minum_size()
        self.adjust_image_origin()
        self.update()
        self.selection_changed.emit()


    def rotate_right(self):
        '''Rotates the image clockwise'''
        self._pixmap = self._pixmap.transformed(QtGui.QTransform().rotate(90))
        self.adjust_minum_size()
        self.adjust_image_origin()
        self.update()
        self.selection_changed.emit()
        
    
    def adjust_minum_size(self):
        '''Sets the new minimum size, calculated upon the image size
        and the _zoom factor.'''
        pixmap = self._pixmap
        if pixmap.width() < 96 or pixmap.height() < 96:
            min_size = QSize(96, 96)
        else:
            min_size = pixmap.size()
            
        self.setMinimumSize(min_size*self._zoom)


    def make_selection_square(self):
        '''Modify the selected area making it square'''
        wid = self._selection_rect.width ()
        self._selection_rect.setSize(QSize(wid, wid))
        
    
    def set_zoom(self, zoomlevel):
        '''Sets the specified zoomlevel'''
        self._zoom = zoomlevel
        self.adjust_minum_size()
        self.adjust_image_origin()
        self.update()
        self.selection_changed.emit()
        
        
    def fit_zoom(self):
        '''Chooses a zoomlevel that makes visible the entire image.
        Currently broken.'''
        widget_wid = self.size().width ()
        widget_hei = self.size().height()
        
        pixmap_wid = self._pixmap.width ()
        pixmap_hei = self._pixmap.height()
        
        self._zoom = (min(widget_wid, widget_hei) / 
                      min(pixmap_wid, pixmap_hei))
                      
        self.adjust_minum_size()
        self.adjust_image_origin()
        self.update()
        self.selection_changed.emit()
        
    def get_selected_pixmap(self):
        '''Returns the pixmap contained in the selection rect.
        Currently doesn't handle transparency correctly'''
        sel_rect_scaled = QRect(self._selection_rect.topLeft() * self._zoom,
                                self._selection_rect.size() * self._zoom)
        return QtGui.QPixmap.grabWidget(self, sel_rect_scaled)
Example #17
0
class StartupScreen(VispaWidget):
    
    # inherited parameters
    BACKGROUND_SHAPE = 'ROUNDRECT'
    SELECTABLE_FLAG = False
    AUTOSIZE = True
    AUTOSIZE_KEEP_ASPECT_RATIO = False
    
    PROTOTYPING_DESCRIPTION = """Prototyping"""
    
    EXECUTING_DESCRIPTION = """Executing"""
    
    VERIFYING_DESCRIPTION = """Verifying"""
        
    def __init__(self, parent):
        self._descriptionWidgets = []
        self._descriptionActiveRects = [QRect(), QRect(), QRect()]   # descriptions will be visible if mouse cursor is in the rect
        VispaWidget.__init__(self, parent)
        self._filenewIcon = QIcon(QPixmap(":/resources/filenew.svg"))
        self._fileopenIcon = QIcon(QPixmap(":/resources/fileopen.svg"))
        self.setImage(QSvgRenderer(":/resources/startup_development_cycle.svg"))
        self.setDragable(False)
        self.setMouseTracking(True)     # receive mouse events even if no button is pressed
        self._hideDescriptions = False
        
        self.createPrototypingWidget()
        self.createExecutionWidget()
        self.createVerifyingWidget()
        
    def createDescriptionWidget(self, arrowDirection, description):
        widget = VispaWidget(self.parent())
        widget.enableAutosizing(True, False)
        widget.setSelectable(False)
        widget.setArrowShape(arrowDirection)
        widget.setVisible(not self._hideDescriptions)
        widget.setDragable(False)
        self._descriptionWidgets.append(widget)
        return widget
    
    def createPrototypingWidget(self):
        self._prototypingDescriptionWidget = self.createDescriptionWidget(VispaWidget.ARROW_SHAPE_BOTTOM, self.PROTOTYPING_DESCRIPTION)
        
        bodyWidget = QWidget(self._prototypingDescriptionWidget)
        bodyWidget.setLayout(QGridLayout())
        bodyWidget.layout().setContentsMargins(0, 0, 0, 0)
        
        bodyWidget.layout().addWidget(QLabel("Design physics analysis:"), 0, 0)
        analysisDesignerButton = QToolButton()
        analysisDesignerButton.setText("Analysis Designer")
        analysisDesignerButton.setIcon(self._filenewIcon)
        self.connect(analysisDesignerButton, SIGNAL("clicked(bool)"), self.parent().newAnalysisDesignerSlot)
        bodyWidget.layout().addWidget(analysisDesignerButton, 0, 1)
        bodyWidget.layout().addWidget(QLabel("Create physics event:"), 1, 0)
        pxlButton = QToolButton()
        pxlButton.setText("PXL Editor")
        pxlButton.setIcon(self._filenewIcon)
        self.connect(pxlButton, SIGNAL("clicked(bool)"), self.parent().newPxlSlot)
        bodyWidget.layout().addWidget(pxlButton, 1, 1)
    
        self._prototypingDescriptionWidget.setBodyWidget(bodyWidget)
        
    def createExecutionWidget(self):
        self._executionDescriptionWidget = self.createDescriptionWidget(VispaWidget.ARROW_SHAPE_RIGHT, self.EXECUTING_DESCRIPTION)
        
        bodyWidget = QWidget(self._executionDescriptionWidget)
        bodyWidget.setLayout(QGridLayout())
        bodyWidget.layout().setContentsMargins(0, 0, 0, 0)
        
        label=QLabel("Open and run existing analysis:")
        bodyWidget.layout().addWidget(label, 0, 0)
        analysisDesignerButton = QToolButton()
        analysisDesignerButton.setText("Open analysis file")
        analysisDesignerButton.setIcon(self._fileopenIcon)
        self.connect(analysisDesignerButton, SIGNAL("clicked(bool)"), self.parent().openAnalysisFileSlot)
        bodyWidget.layout().addWidget(analysisDesignerButton, 0, 1)
        self._analysisDesignerRecentFilesList=QListWidget()
        self._analysisDesignerRecentFilesList.setFixedSize(label.sizeHint().width()+analysisDesignerButton.sizeHint().width(),150)
        self.connect(self._analysisDesignerRecentFilesList, SIGNAL("doubleClicked(QModelIndex)"), self.parent().openAnalysisFileSlot)
        bodyWidget.layout().addWidget(self._analysisDesignerRecentFilesList, 1, 0, 1, 2)
        
        self._executionDescriptionWidget.setBodyWidget(bodyWidget)

    def analysisDesignerRecentFilesList(self):
        return self._analysisDesignerRecentFilesList
        
    def createVerifyingWidget(self):
        self._verifyingDescriptionWidget = self.createDescriptionWidget(VispaWidget.ARROW_SHAPE_LEFT, self.VERIFYING_DESCRIPTION)
        
        bodyWidget = QWidget(self._verifyingDescriptionWidget)
        bodyWidget.setLayout(QGridLayout())
        bodyWidget.layout().setContentsMargins(0, 0, 0, 0)
        
        label=QLabel("Browse an existing PXL data file:")
        bodyWidget.layout().addWidget(label, 0, 0)
        analysisDesignerButton = QToolButton()
        analysisDesignerButton.setText("Open PXL file")
        analysisDesignerButton.setIcon(self._fileopenIcon)
        self.connect(analysisDesignerButton, SIGNAL("clicked(bool)"), self.parent().openPxlFileSlot)
        bodyWidget.layout().addWidget(analysisDesignerButton, 0, 1)
        self._pxlEditorRecentFilesList=QListWidget()
        self._pxlEditorRecentFilesList.setFixedSize(label.sizeHint().width()+analysisDesignerButton.sizeHint().width(),150)
        self.connect(self._pxlEditorRecentFilesList, SIGNAL("doubleClicked(QModelIndex)"), self.parent().openPxlFileSlot)
        bodyWidget.layout().addWidget(self._pxlEditorRecentFilesList, 1, 0, 1, 2)
        
        self._verifyingDescriptionWidget.setBodyWidget(bodyWidget)
        
    def pxlEditorRecentFilesList(self):
        return self._pxlEditorRecentFilesList
        
    def mouseMoveEvent(self, event):
        if bool(event.buttons()):
            VispaWidget.mouseMoveEvent(self, event)
        elif self._hideDescriptions:
            for i in range(len(self._descriptionWidgets)):
                self._descriptionWidgets[i].setVisible(self._descriptionActiveRects[i].contains(event.pos()))
                
    def moveEvent(self, event):
        VispaWidget.moveEvent(self, event)
        self.rearangeDescriptionWidgets()
        
    def rearangeContent(self):
        VispaWidget.rearangeContent(self)
        self.rearangeDescriptionWidgets()
        
    def rearangeDescriptionWidgets(self):
        self._activeSize = QSize(0.3 * self.width(), 0.1 * self.height())
        self._prototypingRect = QRect(QPoint(0.5 * (self.width() - self._activeSize.width()), 0), self._activeSize)
        self._executionRect = QRect(QPoint(0, 0.635 * self.height()), self._activeSize)
        self._verifyingRect = QRect(QPoint(self.width() -self._activeSize.width(), 0.635 * self.height()), self._activeSize)
        self._descriptionActiveRects[0] = self._prototypingRect
        self._descriptionActiveRects[1] = self._executionRect 
        self._descriptionActiveRects[2] = self._verifyingRect
        
        self._prototypingDescriptionWidget.move(self.mapToParent(self._prototypingRect.topLeft()) + QPoint((self._prototypingRect.width() - self._prototypingDescriptionWidget.width()) * 0.5, - self._prototypingDescriptionWidget.height()))
        self._executionDescriptionWidget.move(self.mapToParent(self._executionRect.topLeft()) - QPoint(self._executionDescriptionWidget.width(), - 0.5 * (self._executionRect.height() - self._executionDescriptionWidget.height())))
        self._verifyingDescriptionWidget.move(self.mapToParent(self._verifyingRect.topRight()) - QPoint(0, - 0.5 * (self._verifyingRect.height() - self._verifyingDescriptionWidget.height())))
        
    def boundingRect(self):
        br = VispaWidget.boundingRect(self)
        for w in self._descriptionWidgets:
            br = br.united(w.boundingRect())
        return br

    def setVisible(self, visible):
        VispaWidget.setVisible(self, visible)
        self._executionDescriptionWidget.setVisible(visible and not self._hideDescriptions)
        self._prototypingDescriptionWidget.setVisible(visible and not self._hideDescriptions)
        self._verifyingDescriptionWidget.setVisible(visible and not self._hideDescriptions)
Example #18
0
class ArkMapToolInteractive(QgsMapTool):

    _active = False

    _dragging = False
    _panningEnabled = False

    _zoomingEnabled = False
    _zoomRubberBand = None  #QgsRubberBand()
    _zoomRect = None # QRect()

    _snappingEnabled = False
    _snapper = None  #QgsMapCanvasSnapper()
    _snappingMarker = None  # QgsVertexMarker()

    _showSnappableVertices = False
    _snappableVertices = []  # [QgsPoint()]
    _snappableMarkers = []  # [QgsVertexMarker()]

    def __init__(self, canvas, snappingEnabled=False, showSnappableVertices=False):
        super(ArkMapToolInteractive, self).__init__(canvas)
        self._snappingEnabled = snappingEnabled
        self._showSnappableVertices = showSnappableVertices

    def __del__(self):
        if self._active:
            self.deactivate()

    def isActive(self):
        return self._active

    def activate(self):
        super(ArkMapToolInteractive, self).activate()
        self._active = True
        self._startSnapping()

    def deactivate(self):
        self._active = False
        if self._snappingEnabled:
            self._stopSnapping()
        if (self._zoomRubberBand is not None):
            self.canvas().scene().removeItem(self._zoomRubberBand)
            self._zoomRubberBand = None
        super(ArkMapToolInteractive, self).deactivate()

    def setAction(self, action):
        super(ArkMapToolInteractive, self).setAction(action)
        self.action().triggered.connect(self._activate)

    def _activate(self):
        self.canvas().setMapTool(self)

    def panningEnabled(self):
        return self._panningEnabled

    def setPanningEnabled(self, enabled):
        self._panningEnabled = enabled

    def zoomingEnabled(self):
        return self._zoomingEnabled

    def setZoomingEnabled(self, enabled):
        self._zoomingEnabled = enabled

    def snappingEnabled(self):
        return self._snappingEnabled

    def setSnappingEnabled(self, enabled):
        if (self._snappingEnabled == enabled):
            return
        self._snappingEnabled = enabled
        if not self._active:
            return
        if enabled:
            self._startSnapping()
        else:
            self._stopSnapping()

    def _startSnapping(self):
        self._snapper = QgsMapCanvasSnapper()
        self._snapper.setMapCanvas(self.canvas())
        if self._showSnappableVertices:
            self._startSnappableVertices()

    def _stopSnapping(self):
        self._deleteSnappingMarker()
        self._snapper = None
        if self._showSnappableVertices:
            self._stopSnappableVertices()

    def showSnappableVertices(self):
        return self._showSnappableVertices

    def setShowSnappableVertices(self, show):
        if (self._showSnappableVertices == show):
            return
        self._showSnappableVertices = show
        if not self._active:
            return
        if show:
            self._startSnappableVertices()
        else:
            self._stopSnappableVertices()

    def _startSnappableVertices(self):
        self.canvas().layersChanged.connect(self._layersChanged)
        self.canvas().extentsChanged.connect(self._redrawSnappableMarkers)
        QgsProject.instance().snapSettingsChanged.connect(self._layersChanged)
        self._layersChanged()

    def _stopSnappableVertices(self):
        self._deleteSnappableMarkers()
        self._snappableLayers = []
        self.canvas().layersChanged.disconnect(self._layersChanged)
        self.canvas().extentsChanged.disconnect(self._redrawSnappableMarkers)
        QgsProject.instance().snapSettingsChanged.disconnect(self._layersChanged)

    def canvasMoveEvent(self, e):
        super(ArkMapToolInteractive, self).canvasMoveEvent(e)
        if not self._active:
            return
        e.ignore()
        if (self._panningEnabled and e.buttons() & Qt.LeftButton):
            # Pan map mode
            if not self._dragging:
                self._dragging = True
                self.setCursor(QCursor(Qt.ClosedHandCursor))
            self.canvas().panAction(e)
            e.accept()
        elif (self._zoomingEnabled and e.buttons() & Qt.RightButton):
            # Zoom map mode
            if not self._dragging:
                self._dragging = True
                self.setCursor(QCursor(Qt.ClosedHandCursor))
                self._zoomRubberBand = QgsRubberBand(self.canvas(), QGis.Polygon)
                color = QColor(Qt.blue)
                color.setAlpha(63)
                self._zoomRubberBand.setColor(color)
                self._zoomRect = QRect(0, 0, 0, 0)
                self._zoomRect.setTopLeft(e.pos())
            self._zoomRect.setBottomRight(e.pos())
            if self._zoomRubberBand is not None:
                self._zoomRubberBand.setToCanvasRectangle(self._zoomRect)
                self._zoomRubberBand.show()
            e.accept()
        elif self._snappingEnabled:
            mapPoint, snapped = self._snapCursorPoint(e.pos())
            if (snapped):
                self._createSnappingMarker(mapPoint)
            else:
                self._deleteSnappingMarker()

    def canvasReleaseEvent(self, e):
        super(ArkMapToolInteractive, self).canvasReleaseEvent(e)
        e.ignore()
        if (e.button() == Qt.LeftButton):
            if self._dragging:
                # Pan map mode
                self.canvas().panActionEnd(e.pos())
                self.setCursor(capture_point_cursor)
                self._dragging = False
                e.accept()
        elif (e.button() == Qt.RightButton):
            if self._dragging:
                # Zoom mode
                self._zoomRect.setBottomRight(e.pos())
                if (self._zoomRect.topLeft() != self._zoomRect.bottomRight()):
                    coordinateTransform = self.canvas().getCoordinateTransform()
                    ll = coordinateTransform.toMapCoordinates(self._zoomRect.left(), self._zoomRect.bottom())
                    ur = coordinateTransform.toMapCoordinates(self._zoomRect.right(), self._zoomRect.top())
                    r = QgsRectangle()
                    r.setXMinimum(ll.x())
                    r.setYMinimum(ll.y())
                    r.setXMaximum(ur.x())
                    r.setYMaximum(ur.y())
                    r.normalize()
                    if (r.width() != 0 and r.height() != 0):
                        self.canvas().setExtent(r)
                        self.canvas().refresh()
                self._dragging = False
                if (self._zoomRubberBand is not None):
                    self.canvas().scene().removeItem(self._zoomRubberBand)
                    self._zoomRubberBand = None
                e.accept()

    def keyPressEvent(self, e):
        super(ArkMapToolInteractive, self).keyPressEvent(e)
        if (e.key() == Qt.Key_Escape):
            self.canvas().unsetMapTool(self)
            e.accept()

    def _snapCursorPoint(self, cursorPoint):
        res, snapResults = self._snapper.snapToBackgroundLayers(cursorPoint)
        if (res != 0 or len(snapResults) < 1):
            return self.toMapCoordinates(cursorPoint), False
        else:
            # Take a copy as QGIS will delete the result!
            snappedVertex = QgsPoint(snapResults[0].snappedVertex)
            return snappedVertex, True

    def _createSnappingMarker(self, snapPoint):
        if (self._snappingMarker is None):
            self._snappingMarker = QgsVertexMarker(self.canvas())
            self._snappingMarker.setIconType(QgsVertexMarker.ICON_CROSS)
            self._snappingMarker.setColor(Qt.magenta)
            self._snappingMarker.setPenWidth(3)
        self._snappingMarker.setCenter(snapPoint)

    def _deleteSnappingMarker(self):
        if (self._snappingMarker is not None):
            self.canvas().scene().removeItem(self._snappingMarker)
            self._snappingMarker = None

    def _createSnappableMarkers(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        extent = self.canvas().extent()
        for vertex in self._snappableVertices.asMultiPoint():
            if (extent.contains(vertex)):
                marker = QgsVertexMarker(self.canvas())
                marker.setIconType(QgsVertexMarker.ICON_X)
                marker.setColor(Qt.gray)
                marker.setPenWidth(1)
                marker.setCenter(vertex)
                self._snappableMarkers.append(marker)

    def _deleteSnappableMarkers(self):
        for marker in self._snappableMarkers:
            self.canvas().scene().removeItem(marker)
        del self._snappableMarkers[:]

    def _layersChanged(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        self._buildSnappableLayers()
        self._deleteSnappableMarkers()
        self._createSnappableMarkers()

    def _redrawSnappableMarkers(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        self._deleteSnappableMarkers()
        self._createSnappableMarkers()

    def _buildSnappableLayers(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        vertices = []
        for layer in self.canvas().layers():
            ok, enabled, type, units, tolerance, avoid = QgsProject.instance().snapSettingsForLayer(layer.id())
            if (ok and enabled and not layer.isEditable()):
                for feature in layer.getFeatures():
                    geometry = feature.geometry()
                    if geometry is None:
                        pass
                    elif geometry.type() == QGis.Point:
                        vertices.extend([geometry.asPoint()])
                    elif geometry.type() == QGis.Line:
                        vertices.extend(geometry.asPolyline())
                    elif geometry.type() == QGis.Polygon:
                        lines = geometry.asPolygon()
                        for line in lines:
                            vertices.extend(line)
        self._snappableVertices = QgsGeometry.fromMultiPoint(vertices)
        self._snappableVertices.simplify(0)
Example #19
0
class ArkMapToolInteractive(QgsMapTool):

    _active = False

    _dragging = False
    _panningEnabled = False

    _zoomingEnabled = False
    _zoomRubberBand = None  #QgsRubberBand()
    _zoomRect = None # QRect()

    _snappingEnabled = False
    _snapper = None  #QgsMapCanvasSnapper()
    _snappingMarker = None  # QgsVertexMarker()

    _showSnappableVertices = False
    _snappableVertices = []  # [QgsPoint()]
    _snappableMarkers = []  # [QgsVertexMarker()]

    def __init__(self, canvas, snappingEnabled=False, showSnappableVertices=False):
        super(ArkMapToolInteractive, self).__init__(canvas)
        self._snappingEnabled = snappingEnabled
        self._showSnappableVertices = showSnappableVertices

    def __del__(self):
        if self._active:
            self.deactivate()

    def isActive(self):
        return self._active

    def activate(self):
        super(ArkMapToolInteractive, self).activate()
        self._active = True
        self._startSnapping()

    def deactivate(self):
        self._active = False
        if self._snappingEnabled:
            self._stopSnapping()
        if (self._zoomRubberBand is not None):
            self.canvas().scene().removeItem(self._zoomRubberBand)
            self._zoomRubberBand = None
        super(ArkMapToolInteractive, self).deactivate()

    def setAction(self, action):
        super(ArkMapToolInteractive, self).setAction(action)
        self.action().triggered.connect(self._activate)

    def _activate(self):
        self.canvas().setMapTool(self)

    def panningEnabled(self):
        return self._panningEnabled

    def setPanningEnabled(self, enabled):
        self._panningEnabled = enabled

    def zoomingEnabled(self):
        return self._zoomingEnabled

    def setZoomingEnabled(self, enabled):
        self._zoomingEnabled = enabled

    def snappingEnabled(self):
        return self._snappingEnabled

    def setSnappingEnabled(self, enabled):
        if (self._snappingEnabled == enabled):
            return
        self._snappingEnabled = enabled
        if not self._active:
            return
        if enabled:
            self._startSnapping()
        else:
            self._stopSnapping()

    def _startSnapping(self):
        self._snapper = QgsMapCanvasSnapper()
        self._snapper.setMapCanvas(self.canvas())
        if self._showSnappableVertices:
            self._startSnappableVertices()

    def _stopSnapping(self):
        self._deleteSnappingMarker()
        self._snapper = None
        if self._showSnappableVertices:
            self._stopSnappableVertices()

    def showSnappableVertices(self):
        return self._showSnappableVertices

    def setShowSnappableVertices(self, show):
        if (self._showSnappableVertices == show):
            return
        self._showSnappableVertices = show
        if not self._active:
            return
        if show:
            self._startSnappableVertices()
        else:
            self._stopSnappableVertices()

    def _startSnappableVertices(self):
        self.canvas().layersChanged.connect(self._layersChanged)
        self.canvas().extentsChanged.connect(self._redrawSnappableMarkers)
        QgsProject.instance().snapSettingsChanged.connect(self._layersChanged)
        self._layersChanged()

    def _stopSnappableVertices(self):
        self._deleteSnappableMarkers()
        self._snappableLayers = []
        self.canvas().layersChanged.disconnect(self._layersChanged)
        self.canvas().extentsChanged.disconnect(self._redrawSnappableMarkers)
        QgsProject.instance().snapSettingsChanged.disconnect(self._layersChanged)

    def canvasMoveEvent(self, e):
        super(ArkMapToolInteractive, self).canvasMoveEvent(e)
        if not self._active:
            return
        e.ignore()
        if (self._panningEnabled and e.buttons() & Qt.LeftButton):
            # Pan map mode
            if not self._dragging:
                self._dragging = True
                self.setCursor(QCursor(Qt.ClosedHandCursor))
            self.canvas().panAction(e)
            e.accept()
        elif (self._zoomingEnabled and e.buttons() & Qt.RightButton):
            # Zoom map mode
            if not self._dragging:
                self._dragging = True
                self.setCursor(QCursor(Qt.ClosedHandCursor))
                self._zoomRubberBand = QgsRubberBand(self.canvas(), QGis.Polygon)
                color = QColor(Qt.blue)
                color.setAlpha(63)
                self._zoomRubberBand.setColor(color)
                self._zoomRect = QRect(0, 0, 0, 0)
                self._zoomRect.setTopLeft(e.pos())
            self._zoomRect.setBottomRight(e.pos())
            if self._zoomRubberBand is not None:
                self._zoomRubberBand.setToCanvasRectangle(self._zoomRect)
                self._zoomRubberBand.show()
            e.accept()
        elif self._snappingEnabled:
            mapPoint, snapped = self._snapCursorPoint(e.pos())
            if (snapped):
                self._createSnappingMarker(mapPoint)
            else:
                self._deleteSnappingMarker()

    def canvasReleaseEvent(self, e):
        super(ArkMapToolInteractive, self).canvasReleaseEvent(e)
        e.ignore()
        if (e.button() == Qt.LeftButton):
            if self._dragging:
                # Pan map mode
                self.canvas().panActionEnd(e.pos())
                self.setCursor(capture_point_cursor)
                self._dragging = False
                e.accept()
        elif (e.button() == Qt.RightButton):
            if self._dragging:
                # Zoom mode
                self._zoomRect.setBottomRight(e.pos())
                if (self._zoomRect.topLeft() != self._zoomRect.bottomRight()):
                    coordinateTransform = self.canvas().getCoordinateTransform()
                    ll = coordinateTransform.toMapCoordinates(self._zoomRect.left(), self._zoomRect.bottom())
                    ur = coordinateTransform.toMapCoordinates(self._zoomRect.right(), self._zoomRect.top())
                    r = QgsRectangle()
                    r.setXMinimum(ll.x())
                    r.setYMinimum(ll.y())
                    r.setXMaximum(ur.x())
                    r.setYMaximum(ur.y())
                    r.normalize()
                    if (r.width() != 0 and r.height() != 0):
                        self.canvas().setExtent(r)
                        self.canvas().refresh()
                self._dragging = False
                if (self._zoomRubberBand is not None):
                    self.canvas().scene().removeItem(self._zoomRubberBand)
                    self._zoomRubberBand = None
                e.accept()

    def keyPressEvent(self, e):
        super(ArkMapToolInteractive, self).keyPressEvent(e)
        if (e.key() == Qt.Key_Escape):
            self.canvas().unsetMapTool(self)
            e.accept()

    def _snapCursorPoint(self, cursorPoint):
        res, snapResults = self._snapper.snapToBackgroundLayers(cursorPoint)
        if (res != 0 or len(snapResults) < 1):
            return self.toMapCoordinates(cursorPoint), False
        else:
            # Take a copy as QGIS will delete the result!
            snappedVertex = QgsPoint(snapResults[0].snappedVertex)
            return snappedVertex, True

    def _createSnappingMarker(self, snapPoint):
        if (self._snappingMarker is None):
            self._snappingMarker = QgsVertexMarker(self.canvas())
            self._snappingMarker.setIconType(QgsVertexMarker.ICON_CROSS)
            self._snappingMarker.setColor(Qt.magenta)
            self._snappingMarker.setPenWidth(3)
        self._snappingMarker.setCenter(snapPoint)

    def _deleteSnappingMarker(self):
        if (self._snappingMarker is not None):
            self.canvas().scene().removeItem(self._snappingMarker)
            self._snappingMarker = None

    def _createSnappableMarkers(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        extent = self.canvas().extent()
        for vertex in self._snappableVertices.asMultiPoint():
            if (extent.contains(vertex)):
                marker = QgsVertexMarker(self.canvas())
                marker.setIconType(QgsVertexMarker.ICON_X)
                marker.setColor(Qt.gray)
                marker.setPenWidth(1)
                marker.setCenter(vertex)
                self._snappableMarkers.append(marker)

    def _deleteSnappableMarkers(self):
        for marker in self._snappableMarkers:
            self.canvas().scene().removeItem(marker)
        del self._snappableMarkers[:]

    def _layersChanged(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        self._buildSnappableLayers()
        self._deleteSnappableMarkers()
        self._createSnappableMarkers()

    def _redrawSnappableMarkers(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        self._deleteSnappableMarkers()
        self._createSnappableMarkers()

    def _buildSnappableLayers(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        vertices = []
        for layer in self.canvas().layers():
            ok, enabled, type, units, tolerance, avoid = QgsProject.instance().snapSettingsForLayer(layer.id())
            if (ok and enabled and not layer.isEditable()):
                for feature in layer.getFeatures():
                    geometry = feature.geometry()
                    if geometry is None:
                        pass
                    elif geometry.type() == QGis.Point:
                        vertices.extend([geometry.asPoint()])
                    elif geometry.type() == QGis.Line:
                        vertices.extend(geometry.asPolyline())
                    elif geometry.type() == QGis.Polygon:
                        lines = geometry.asPolygon()
                        for line in lines:
                            vertices.extend(line)
        self._snappableVertices = QgsGeometry.fromMultiPoint(vertices)
        self._snappableVertices.simplify(0)
Example #20
0
class ImageAreaSelector(QtGui.QWidget):
    '''This widget provides means to visually crop a portion of
    an image by selecting it'''
    # pylint: disable=W0612
    NAME = 'ImageAreaSelector'
    DESCRIPTION = 'A widget used to select part of an image'
    AUTHOR = 'Gabriele "Whisky" Visconti'
    WEBSITE = ''
    # pylint: enable=W0612

    selection_changed = QtCore.pyqtSignal()

    def __init__(self, pixmap, parent=None):
        '''Constructor'''
        QtGui.QWidget.__init__(self, parent)
        self._pixmap = pixmap

        self._selection_rect = QRect()
        self._image_origin = QPoint()
        self._resize_start = None
        self._drag_start = None
        self._handle_size = QSize(-10, -10)
        self._painter = QtGui.QPainter()
        self._hl_color1 = QtGui.QPalette().color(QtGui.QPalette.Highlight)
        self._hl_color2 = QtGui.QPalette().color(QtGui.QPalette.Highlight)
        self._hl_color2.setAlpha(150)
        self._zoom = 1.0

        self.adjust_minum_size()
        self.setBackgroundRole(QtGui.QPalette.Dark)
        self.setMouseTracking(True)
        self.setCursor(Qt.CrossCursor)

# -------------------- [BEGIN] QT_OVERRIDE

    def mousePressEvent(self, event):
        '''Overrides QWidget's mousePressEvent. Handles starting
        a new selection, starting a drag operation'''
        # pylint: disable=C0103
        mouse_pos = event.pos() / self._zoom
        sel_rect = self._selection_rect

        if not event.button() == Qt.LeftButton:
            return

        if (not sel_rect.isNull()) and sel_rect.contains(mouse_pos, True):
            handle_rect = QRect(sel_rect.bottomRight(), self._handle_size)
            if handle_rect.contains(mouse_pos):
                self._resize_start = mouse_pos
            else:
                self._drag_start = mouse_pos
        else:
            self._resize_start = mouse_pos
            sel_rect.setTopLeft(mouse_pos)
            self._selection_rect.setSize(QSize(0, 0))

    def mouseMoveEvent(self, event):
        '''Overrides QWidget's mouseMoveEvent. Handles resizing
        and dragging operations on selection'''
        # pylint: disable=C0103
        sel_rect = self._selection_rect
        if self._resize_start:
            resize_end = event.pos() / self._zoom
            sel_rect.setBottomRight(sel_rect.bottomRight() +
                                    (resize_end - self._resize_start))
            self._resize_start = resize_end
            self.make_selection_square()
            self.update()
        elif self._drag_start is not None:
            drag_end = event.pos() / self._zoom
            sel_rect.translate(drag_end - self._drag_start)
            self._drag_start = drag_end
            self.update()

        # cursor shape:
        mouse_pos = event.pos() / self._zoom
        if (not sel_rect.isNull()) and sel_rect.contains(mouse_pos, True):
            handle_rect = QRect(sel_rect.bottomRight(), self._handle_size)
            if handle_rect.contains(mouse_pos):
                self.setCursor(Qt.SizeFDiagCursor)
            else:
                self.setCursor(Qt.OpenHandCursor)

        else:
            self.setCursor(Qt.CrossCursor)

    def mouseReleaseEvent(self, event):
        '''Overrides QWidget's mouseReleaseEvent. Handles ending a resizing or
        draggin operation on the selection'''
        # pylint: disable=C0103
        self._selection_rect = self._selection_rect.normalized()
        self._resize_start = None
        self._drag_start = None
        self.update()
        if not self._selection_rect.isNull():
            self.selection_changed.emit()

    def paintEvent(self, event):
        '''Overrides QWidtget's paintEvent.'''
        # pylint: disable=C0103
        QtGui.QWidget.paintEvent(self, event)
        self._painter.begin(self)
        pixmap_dest_rect = QRect(self._image_origin * self._zoom,
                                 self._pixmap.size() * self._zoom)
        self._painter.drawPixmap(pixmap_dest_rect, self._pixmap)
        if not self._selection_rect.isNull():
            # preparing the darkened frame:
            sel_rect = self._selection_rect.normalized()
            frame = QtGui.QPixmap(event.rect().size())
            frame.fill(QtGui.QColor(0, 0, 0, 127))
            frame_painter = QtGui.QPainter(frame)
            # erase the selected area from the frame:
            frame_painter.setCompositionMode(
                QtGui.QPainter.CompositionMode_DestinationIn)
            sel_rect_scaled = QRect(sel_rect.topLeft() * self._zoom,
                                    sel_rect.size() * self._zoom)
            frame_painter.fillRect(sel_rect_scaled, QtGui.QColor(0, 0, 0, 0))
            # draw selection border :
            frame_painter.setCompositionMode(
                QtGui.QPainter.CompositionMode_SourceOver)
            frame_painter.setPen(self._hl_color1)
            frame_painter.drawRect(sel_rect_scaled)
            # draw the resize grip (if possible)
            if sel_rect_scaled.width() > 20 and sel_rect_scaled.height() > 20:
                handle_rect = QRect(sel_rect_scaled.bottomRight(),
                                    self._handle_size)
                frame_painter.fillRect(handle_rect, self._hl_color2)
                frame_painter.drawRect(handle_rect)
            frame_painter.end()
            # painting the darkened frame:
            self._painter.drawPixmap(0, 0, frame)

        self._painter.end()

    def resizeEvent(self, event):
        '''Overrides QWidget's resizeEvent. Handles image centering.'''
        # pylint: disable=C0103
        self.adjust_image_origin()

# -------------------- [END] QT_OVERRIDE

    def adjust_image_origin(self):
        '''Recalculates the top left corner's image position, so the
        image is painted centered'''
        # pylint: disable=C0103
        new_size = self.size() / self._zoom
        pix_size = self._pixmap.size()

        dx = (new_size.width() - pix_size.width()) / 2
        dy = (new_size.height() - pix_size.height()) / 2

        new_image_origin = QPoint(dx, dy)
        self._selection_rect.translate(new_image_origin - self._image_origin)
        self._image_origin = new_image_origin
        log.info('image origin: %s' % new_image_origin)

    def select_unscaled(self):
        '''Selects, if possible, a 96 x 96 square centered around the original
        image. In this way the image won't be scaled but won't take up all the 
        96 x 96 area.'''
        # pylint: disable=C0103
        pix_size = self._pixmap.size()
        if pix_size.width() <= 96 and pix_size.height() <= 96:
            viewport_size = self.size()
            x = (viewport_size.width() - 96) / 2
            y = (viewport_size.height() - 96) / 2
            self._selection_rect.setTopLeft(QPoint(x, y))
            self._selection_rect.setSize(QSize(96, 96))
            self.update()
            self.selection_changed.emit()

    def select_all(self):
        '''Selects the whole image. Currently broken for images taller
        than wide.'''
        # TODO: make me work!
        self._selection_rect.setTopLeft(self._image_origin)
        self._selection_rect.setSize(self._pixmap.size())
        self.update()
        self.selection_changed.emit()

    def rotate_left(self):
        '''Rotates the image counterclockwise.'''
        self._pixmap = self._pixmap.transformed(QtGui.QTransform().rotate(-90))
        self.adjust_minum_size()
        self.adjust_image_origin()
        self.update()
        self.selection_changed.emit()

    def rotate_right(self):
        '''Rotates the image clockwise'''
        self._pixmap = self._pixmap.transformed(QtGui.QTransform().rotate(90))
        self.adjust_minum_size()
        self.adjust_image_origin()
        self.update()
        self.selection_changed.emit()

    def adjust_minum_size(self):
        '''Sets the new minimum size, calculated upon the image size
        and the _zoom factor.'''
        pixmap = self._pixmap
        if pixmap.width() < 96 or pixmap.height() < 96:
            min_size = QSize(96, 96)
        else:
            min_size = pixmap.size()

        self.setMinimumSize(min_size * self._zoom)

    def make_selection_square(self):
        '''Modify the selected area making it square'''
        wid = self._selection_rect.width()
        self._selection_rect.setSize(QSize(wid, wid))

    def set_zoom(self, zoomlevel):
        '''Sets the specified zoomlevel'''
        self._zoom = zoomlevel
        self.adjust_minum_size()
        self.adjust_image_origin()
        self.update()
        self.selection_changed.emit()

    def fit_zoom(self):
        '''Chooses a zoomlevel that makes visible the entire image.
        Currently broken.'''
        widget_wid = self.size().width()
        widget_hei = self.size().height()

        pixmap_wid = self._pixmap.width()
        pixmap_hei = self._pixmap.height()

        self._zoom = (min(widget_wid, widget_hei) /
                      min(pixmap_wid, pixmap_hei))

        self.adjust_minum_size()
        self.adjust_image_origin()
        self.update()
        self.selection_changed.emit()

    def get_selected_pixmap(self):
        '''Returns the pixmap contained in the selection rect.
        Currently doesn't handle transparency correctly'''
        sel_rect_scaled = QRect(self._selection_rect.topLeft() * self._zoom,
                                self._selection_rect.size() * self._zoom)
        return QtGui.QPixmap.grabWidget(self, sel_rect_scaled)
Example #21
0
class Page(object):
    """Represents a page from a Poppler.Document.
    
    It maintains its own size and can draw itself using the cache.
    It also can maintain a list of links and return links at certain
    points or rectangles.
    
    The visible attribute (setVisible and visible) defaults to True but
    can be set to False to hide the page from a Surface (this is done by
    the Layout).
    
    """
    def __init__(self, document, pageNumber):
        self._document = document
        self._pageNumber = pageNumber
        self._pageSize = document.page(pageNumber).pageSize()
        self._rotation = popplerqt4.Poppler.Page.Rotate0
        self._rect = QRect()
        self._scale = 1.0
        self._visible = True
        self._layout = lambda: None
        self._waiting = True  # whether image still needs to be generated

    def document(self):
        """Returns the document."""
        return self._document

    def pageNumber(self):
        """Returns the page number."""
        return self._pageNumber

    def pageSize(self):
        """The page size in points (1/72 inch), taking rotation into account."""
        return self._pageSize

    def layout(self):
        """Returns the Layout if we are part of one."""
        return self._layout()

    def visible(self):
        """Returns True if this page is visible (will be displayed)."""
        return self._visible

    def setVisible(self, visible):
        """Sets whether  this page is visible (will be displayed)."""
        self._visible = visible

    def rect(self):
        """Returns our QRect(), with position and size."""
        return self._rect

    def size(self):
        """Returns our size."""
        return self._rect.size()

    def height(self):
        """Returns our height."""
        return self._rect.height()

    def width(self):
        """Returns our width."""
        return self._rect.width()

    def pos(self):
        """Returns our position."""
        return self._rect.topLeft()

    def setPos(self, point):
        """Sets our position (affects the Layout)."""
        self._rect.moveTopLeft(point)

    def setRotation(self, rotation):
        """Sets our Poppler.Page.Rotation."""
        old, self._rotation = self._rotation, rotation
        if (old ^ rotation) & 1:
            self._pageSize.transpose()
            self.computeSize()

    def rotation(self):
        """Returns our rotation."""
        return self._rotation

    def computeSize(self):
        """Recomputes our size."""
        xdpi, ydpi = self.layout().dpi() if self.layout() else (72.0, 72.0)
        x = round(self._pageSize.width() * xdpi / 72.0 * self._scale)
        y = round(self._pageSize.height() * ydpi / 72.0 * self._scale)
        self._rect.setSize(QSize(x, y))

    def setScale(self, scale):
        """Changes the display scale."""
        self._scale = scale
        self.computeSize()

    def scale(self):
        """Returns our display scale."""
        return self._scale

    def scaleForWidth(self, width):
        """Returns the scale we need to display ourselves at the given width."""
        if self.layout():
            return width * 72.0 / self.layout().dpi(
            )[0] / self._pageSize.width()
        else:
            return float(width) / self._pageSize.width()

    def scaleForHeight(self, height):
        """Returns the scale we need to display ourselves at the given height."""
        if self.layout():
            return height * 72.0 / self.layout().dpi(
            )[1] / self._pageSize.height()
        else:
            return float(height) / self._pageSize.height()

    def setWidth(self, width):
        """Change our scale to force our width to the given value."""
        self.setScale(self.scaleForWidth(width))

    def setHeight(self, height):
        """Change our scale to force our height to the given value."""
        self.setScale(self.scaleForHeight(height))

    def image(self):
        """Render the page as an image or our size. Return a QImage."""
        d = self._document
        w, h, r = self.width(), self.height(), self.rotation()
        page = d.page(self._pageNumber)
        pageSize = page.pageSize()
        if r & 1:
            pageSize.transpose()
        xres = 72.0 * w / pageSize.width()
        yres = 72.0 * h / pageSize.height()
        threshold = cache.options().oversampleThreshold() or cache.options(
            d).oversampleThreshold()
        multiplier = 2 if xres < threshold else 1
        with lock(d):
            cache.options().write(d)
            cache.options(d).write(d)
            image = page.renderToImage(xres * multiplier, yres * multiplier, 0,
                                       0, w * multiplier, h * multiplier, r)
        if multiplier == 2:
            image = image.scaledToWidth(w, Qt.SmoothTransformation)
        return image

    def paint(self, painter, rect):
        update_rect = rect & self.rect()
        if not update_rect:
            return
        image_rect = QRect(update_rect.topLeft() - self.rect().topLeft(),
                           update_rect.size())
        image = cache.image(self)
        self._waiting = not image
        if image:
            painter.drawImage(update_rect, image, image_rect)
        else:
            # schedule an image to be generated, if done our update() method is called
            cache.generate(self)
            # find suitable image to be scaled from other size
            image = cache.image(self, False)
            if image:
                hscale = float(image.width()) / self.width()
                vscale = float(image.height()) / self.height()
                image_rect = QRectF(image_rect.x() * hscale,
                                    image_rect.y() * vscale,
                                    image_rect.width() * hscale,
                                    image_rect.height() * vscale)
                painter.drawImage(QRectF(update_rect), image, image_rect)
            else:
                # draw blank paper, using the background color of the cache rendering (if set)
                # or from the document itself.
                color = (cache.options(self.document()).paperColor()
                         or cache.options().paperColor()
                         or self.document().paperColor())
                painter.fillRect(update_rect, color)

    def update(self):
        """Called when an image is drawn."""
        # only redraw when we were waiting for a correctly sized image.
        if self._waiting and self.layout():
            self.layout().updatePage(self)

    def repaint(self):
        """Call this to force a repaint (e.g. when the rendering options are changed)."""
        self._waiting = True
        cache.generate(self)

    def image(self, rect, xdpi=72.0, ydpi=None, options=None):
        """Returns a QImage of the specified rectangle (relative to our layout).
        
        xdpi defaults to 72.0 and ydpi defaults to xdpi.
        options may be a render.RenderOptions instance that will set some document
        rendering options just before rendering the image.
        """
        rect = rect.normalized().intersected(self.rect())
        if not rect:
            return
        rect.translate(-self.pos())
        if ydpi is None:
            ydpi = xdpi
        hscale = (xdpi * self.pageSize().width()) / (72.0 * self.width())
        vscale = (ydpi * self.pageSize().height()) / (72.0 * self.height())
        x = rect.x() * hscale
        y = rect.y() * vscale
        w = rect.width() * hscale
        h = rect.height() * vscale
        with lock(self.document()):
            options and options.write(self.document())
            page = self.document().page(self._pageNumber)
            image = page.renderToImage(xdpi, ydpi, x, y, w, h, self._rotation)
        image.setDotsPerMeterX(int(xdpi * 39.37))
        image.setDotsPerMeterY(int(ydpi * 39.37))
        return image

    def linksAt(self, point):
        """Returns a list() of zero or more links touched by point (relative to surface).
        
        The list is sorted with the smallest rectangle first.
        
        """
        # Poppler.Link objects have their linkArea() ranging in width and height
        # from 0.0 to 1.0, so divide by resp. height and width of the Page.
        point = point - self.pos()
        x = float(point.x()) / self.width()
        y = float(point.y()) / self.height()
        # rotate
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                x, y = y, 1 - x
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                x, y = 1 - x, 1 - y
            else:  # 270
                x, y = 1 - y, x
        return list(
            sorted(cache.links(self).at(x, y),
                   key=lambda link: link.linkArea().width()))

    def linksIn(self, rect):
        """Returns an unordered set() of links enclosed in rectangle (relative to surface)."""
        rect = rect.normalized()
        rect.translate(-self.pos())
        left = float(rect.left()) / self.width()
        top = float(rect.top()) / self.height()
        right = float(rect.right()) / self.width()
        bottom = float(rect.bottom()) / self.height()
        # rotate
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                left, top, right, bottom = top, 1 - right, bottom, 1 - left
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                left, top, right, bottom = 1 - right, 1 - bottom, 1 - left, 1 - top
            else:  # 270
                left, top, right, bottom = 1 - bottom, left, 1 - top, right
        return cache.links(self).inside(left, top, right, bottom)

    def linkRect(self, linkarea):
        """Returns a QRect encompassing the linkArea (of a link) in coordinates of our rect()."""
        left, top, right, bottom = linkarea.normalized().getCoords()
        # rotate
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                left, top, right, bottom = 1 - bottom, left, 1 - top, right
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                left, top, right, bottom = 1 - right, 1 - bottom, 1 - left, 1 - top
            else:  # 270
                left, top, right, bottom = top, 1 - right, bottom, 1 - left
        rect = QRect()
        rect.setCoords(left * self.width(), top * self.height(),
                       right * self.width(), bottom * self.height())
        rect.translate(self.pos())
        return rect

    def text(self, rect):
        """Returns text inside rectangle (relative to surface)."""
        rect = rect.normalized()
        rect.translate(-self.pos())
        w, h = self.pageSize().width(), self.pageSize().height()
        left = float(rect.left()) / self.width() * w
        top = float(rect.top()) / self.height() * h
        right = float(rect.right()) / self.width() * w
        bottom = float(rect.bottom()) / self.height() * h
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                left, top, right, bottom = top, w - right, bottom, w - left
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                left, top, right, bottom = w - right, h - bottom, w - left, h - top
            else:  # 270
                left, top, right, bottom = h - bottom, left, h - top, right
        rect = QRectF()
        rect.setCoords(left, top, right, bottom)
        with lock(self.document()):
            page = self.document().page(self._pageNumber)
            return page.text(rect)

    def searchRect(self, rectF):
        """Returns a QRect encompassing the given rect (in points) to our position, size and rotation."""
        rect = rectF.normalized()
        left, top, right, bottom = rect.getCoords()
        w, h = self.pageSize().width(), self.pageSize().height()
        hscale = self.width() / float(w)
        vscale = self.height() / float(h)
        if self._rotation:
            if self._rotation == popplerqt4.Poppler.Page.Rotate90:
                left, top, right, bottom = w - bottom, left, w - top, right
            elif self._rotation == popplerqt4.Poppler.Page.Rotate180:
                left, top, right, bottom = w - right, h - bottom, w - left, h - top
            else:  # 270
                left, top, right, bottom = top, h - right, bottom, h - left
        rect = QRect()
        rect.setCoords(left * hscale, top * vscale, right * hscale,
                       bottom * vscale)
        return rect
Example #22
0
class MapToolInteractive(QgsMapTool):
    """Tool to interact with map, including panning, zooming, and snapping"""
    def __init__(self, canvas, snappingEnabled=False):
        super(MapToolInteractive, self).__init__(canvas)
        self._active = False
        self._dragging = False
        self._panningEnabled = False
        self._zoomingEnabled = False
        self._zoomRubberBand = None  # QgsRubberBand()
        self._zoomRect = None  # QRect()
        self._snappingEnabled = snappingEnabled
        self._snapper = None  # QgsMapCanvasSnapper()
        self._snappingMarker = None  # QgsVertexMarker()

    def __del__(self):
        if self._active:
            self.deactivate()

    def isActive(self):
        return self._active

    def activate(self):
        super(MapToolInteractive, self).activate()
        self._active = True
        self._startSnapping()

    def deactivate(self):
        self._active = False
        if self._snappingEnabled:
            self._stopSnapping()
        if (self._zoomRubberBand is not None):
            self.canvas().scene().removeItem(self._zoomRubberBand)
            self._zoomRubberBand = None
        super(MapToolInteractive, self).deactivate()

    def setAction(self, action):
        super(MapToolInteractive, self).setAction(action)
        self.action().triggered.connect(self._activate)

    def _activate(self):
        self.canvas().setMapTool(self)

    def panningEnabled(self):
        return self._panningEnabled

    def setPanningEnabled(self, enabled):
        self._panningEnabled = enabled

    def zoomingEnabled(self):
        return self._zoomingEnabled

    def setZoomingEnabled(self, enabled):
        self._zoomingEnabled = enabled

    def snappingEnabled(self):
        return self._snappingEnabled

    def setSnappingEnabled(self, enabled):
        if (self._snappingEnabled == enabled):
            return
        self._snappingEnabled = enabled
        if not self._active:
            return
        if enabled:
            self._startSnapping()
        else:
            self._stopSnapping()

    def _startSnapping(self):
        self._snapper = QgsMapCanvasSnapper()
        self._snapper.setMapCanvas(self.canvas())

    def _stopSnapping(self):
        self._deleteSnappingMarker()
        self._snapper = None

    def canvasMoveEvent(self, e):
        super(MapToolInteractive, self).canvasMoveEvent(e)
        if not self._active:
            return
        e.ignore()
        if (self._panningEnabled and e.buttons() & Qt.LeftButton):
            # Pan map mode
            if not self._dragging:
                self._dragging = True
                self.setCursor(QCursor(Qt.ClosedHandCursor))
            self.canvas().panAction(e)
            e.accept()
        elif (self._zoomingEnabled and e.buttons() & Qt.RightButton):
            # Zoom map mode
            if not self._dragging:
                self._dragging = True
                self.setCursor(QCursor(Qt.ClosedHandCursor))
                self._zoomRubberBand = QgsRubberBand(self.canvas(),
                                                     QGis.Polygon)
                color = QColor(Qt.blue)
                color.setAlpha(63)
                self._zoomRubberBand.setColor(color)
                self._zoomRect = QRect(0, 0, 0, 0)
                self._zoomRect.setTopLeft(e.pos())
            self._zoomRect.setBottomRight(e.pos())
            if self._zoomRubberBand is not None:
                self._zoomRubberBand.setToCanvasRectangle(self._zoomRect)
                self._zoomRubberBand.show()
            e.accept()
        elif self._snappingEnabled:
            mapPoint, mapPointV2, snapped = self._snapCursorPoint(e.pos())
            if (snapped):
                self._createSnappingMarker(mapPoint)
            else:
                self._deleteSnappingMarker()

    def canvasReleaseEvent(self, e):
        super(MapToolInteractive, self).canvasReleaseEvent(e)
        e.ignore()
        if (e.button() == Qt.LeftButton):
            if self._dragging:
                # Pan map mode
                self.canvas().panActionEnd(e.pos())
                self.setCursor(CapturePointCursor)
                self._dragging = False
                e.accept()
        elif (e.button() == Qt.RightButton):
            if self._dragging:
                # Zoom mode
                self._zoomRect.setBottomRight(e.pos())
                if (self._zoomRect.topLeft() != self._zoomRect.bottomRight()):
                    coordinateTransform = self.canvas().getCoordinateTransform(
                    )
                    ll = coordinateTransform.toMapCoordinates(
                        self._zoomRect.left(), self._zoomRect.bottom())
                    ur = coordinateTransform.toMapCoordinates(
                        self._zoomRect.right(), self._zoomRect.top())
                    r = QgsRectangle()
                    r.setXMinimum(ll.x())
                    r.setYMinimum(ll.y())
                    r.setXMaximum(ur.x())
                    r.setYMaximum(ur.y())
                    r.normalize()
                    if (r.width() != 0 and r.height() != 0):
                        self.canvas().setExtent(r)
                        self.canvas().refresh()
                self._dragging = False
                if (self._zoomRubberBand is not None):
                    self.canvas().scene().removeItem(self._zoomRubberBand)
                    self._zoomRubberBand = None
                e.accept()

    def keyPressEvent(self, e):
        super(MapToolInteractive, self).keyPressEvent(e)
        if (e.key() == Qt.Key_Escape):
            self.canvas().unsetMapTool(self)
            e.accept()

    def _snapCursorPoint(self, cursorPoint):
        res, snapResults = self._snapper.snapToBackgroundLayers(cursorPoint)
        if (res != 0 or len(snapResults) < 1):
            clicked = self.toMapCoordinates(cursorPoint)
            clickedV2 = QgsPointV2(clicked)
            return clicked, clickedV2, False
        else:
            # Take a copy as QGIS will delete the result!
            snapped = QgsPoint(snapResults[0].snappedVertex)
            snappedV2 = QgsPointV2(snapped)
            return snapped, snappedV2, True

    def _createSnappingMarker(self, snapPoint):
        if (self._snappingMarker is None):
            self._snappingMarker = QgsVertexMarker(self.canvas())
            self._snappingMarker.setIconType(QgsVertexMarker.ICON_CROSS)
            self._snappingMarker.setColor(Qt.magenta)
            self._snappingMarker.setPenWidth(3)
        self._snappingMarker.setCenter(snapPoint)

    def _deleteSnappingMarker(self):
        if (self._snappingMarker is not None):
            self.canvas().scene().removeItem(self._snappingMarker)
            self._snappingMarker = None