示例#1
0
 def fitRect(rect, targetrect):
     size = rect.size().boundedTo(targetgeom.size())
     newrect = QRect(rect.topLeft(), size)
     dx, dy = 0, 0
     if newrect.left() < targetrect.left():
         dx = targetrect.left() - newrect.left()
     if newrect.top() < targetrect.top():
         dy = targetrect.top() - newrect.top()
     if newrect.right() > targetrect.right():
         dx = targetrect.right() - newrect.right()
     if newrect.bottom() > targetrect.bottom():
         dy = targetrect.bottom() - newrect.bottom()
     return newrect.translated(dx, dy)
示例#2
0
    def popup(self, pos=None, searchText=""):
        """
        Popup the menu at `pos` (in screen coordinates). 'Search' text field
        is initialized with `searchText` if provided.

        """
        if pos is None:
            pos = QPoint()

        self.__clearCurrentItems()

        self.__search.setText(searchText)
        patt = QRegExp("(^|\W)" + searchText)
        patt.setCaseSensitivity(False)
        self.__suggestPage.setFilterRegExp(patt)

        self.ensurePolished()

        if self.testAttribute(Qt.WA_Resized) and self.sizeGripEnabled():
            size = self.size()
        else:
            size = self.sizeHint()

        desktop = QApplication.desktop()
        screen_geom = desktop.availableGeometry(pos)

        # Adjust the size to fit inside the screen.
        if size.height() > screen_geom.height():
            size.setHeight(screen_geom.height())
        if size.width() > screen_geom.width():
            size.setWidth(screen_geom.width())

        geom = QRect(pos, size)

        if geom.top() < screen_geom.top():
            geom.setTop(screen_geom.top())

        if geom.left() < screen_geom.left():
            geom.setLeft(screen_geom.left())

        bottom_margin = screen_geom.bottom() - geom.bottom()
        right_margin = screen_geom.right() - geom.right()
        if bottom_margin < 0:
            # Falls over the bottom of the screen, move it up.
            geom.translate(0, bottom_margin)

        # TODO: right to left locale
        if right_margin < 0:
            # Falls over the right screen edge, move the menu to the
            # other side of pos.
            geom.translate(-size.width(), 0)

        self.setGeometry(geom)

        self.show()

        if searchText:
            self.setFocusProxy(self.__search)
        else:
            self.setFocusProxy(None)
    def __autoScrollAdvance(self):
        """Advance the auto scroll
        """
        pos = QCursor.pos()
        pos = self.mapFromGlobal(pos)
        margin = self.__autoScrollMargin

        vvalue = self.verticalScrollBar().value()
        hvalue = self.horizontalScrollBar().value()

        vrect = QRect(0, 0, self.width(), self.height())

        # What should be the speed
        advance = 10

        # We only do auto scroll if the mouse is inside the view.
        if vrect.contains(pos):
            if pos.x() < vrect.left() + margin:
                self.horizontalScrollBar().setValue(hvalue - advance)
            if pos.y() < vrect.top() + margin:
                self.verticalScrollBar().setValue(vvalue - advance)
            if pos.x() > vrect.right() - margin:
                self.horizontalScrollBar().setValue(hvalue + advance)
            if pos.y() > vrect.bottom() - margin:
                self.verticalScrollBar().setValue(vvalue + advance)

            if self.verticalScrollBar().value() == vvalue and \
                    self.horizontalScrollBar().value() == hvalue:
                self.__stopAutoScroll()
        else:
            self.__stopAutoScroll()

        log.debug("Auto scroll advance")
示例#4
0
def applyMargin(rect, margin):
    result = QRect(rect)
    result.setLeft(result.left()+margin)
    result.setRight(result.right()-margin)
    result.setTop(result.top()+margin)
    result.setBottom(result.bottom()-margin)
    return result
 def paintVerticalCell(self, painter: QPainter, hv: QHeaderView,
                       cellIndex: QModelIndex, leafIndex: QModelIndex,
                       logicalLeafIndex: int,
                       styleOptions: QStyleOptionHeader,
                       sectionRect: QRect, left: int):
     uniopt = QStyleOptionHeader(styleOptions)
     self.setForegroundBrush(uniopt, cellIndex)
     self.setBackgroundBrush(uniopt, cellIndex)
     width = self.cellSize(cellIndex, hv, uniopt).width()
     if cellIndex == leafIndex:
         width = sectionRect.width() - left
     top = self.currentCellLeft(cellIndex, leafIndex, logicalLeafIndex,
                                sectionRect.top(), hv)
     height = self.currentCellWidth(cellIndex, leafIndex,
                                    logicalLeafIndex, hv)
     r = QRect(left, top, width, height)
     uniopt.text = cellIndex.data(Qt.DisplayRole)
     painter.save()
     uniopt.rect = r
     if cellIndex.data(Qt.UserRole):
         hv.style().drawControl(QStyle.CE_HeaderSection, uniopt,
                                painter, hv)
         m = QMatrix()
         m.rotate(-90)
         painter.setWorldMatrix(m, True)
         new_r = QRect(0, 0, r.height(), r.width())
         new_r.moveCenter(QPoint(-r.center().y(), r.center().x()))
         uniopt.rect = new_r
         hv.style().drawControl(QStyle.CE_HeaderLabel, uniopt, painter,
                                hv)
     else:
         hv.style().drawControl(QStyle.CE_Header, uniopt, painter, hv)
     painter.restore()
     return left + width
示例#6
0
    def popup(self, pos=None, searchText=""):
        """
        Popup the menu at `pos` (in screen coordinates). 'Search' text field
        is initialized with `searchText` if provided.

        """
        if pos is None:
            pos = QPoint()

        self.__clearCurrentItems()

        self.__search.setText(searchText)
        patt = QRegExp("(^|\W)"+searchText)
        patt.setCaseSensitivity(False)
        self.__suggestPage.setFilterRegExp(patt)

        self.ensurePolished()

        if self.testAttribute(Qt.WA_Resized) and self.sizeGripEnabled():
            size = self.size()
        else:
            size = self.sizeHint()

        desktop = QApplication.desktop()
        screen_geom = desktop.availableGeometry(pos)

        # Adjust the size to fit inside the screen.
        if size.height() > screen_geom.height():
            size.setHeight(screen_geom.height())
        if size.width() > screen_geom.width():
            size.setWidth(screen_geom.width())

        geom = QRect(pos, size)

        if geom.top() < screen_geom.top():
            geom.setTop(screen_geom.top())

        if geom.left() < screen_geom.left():
            geom.setLeft(screen_geom.left())

        bottom_margin = screen_geom.bottom() - geom.bottom()
        right_margin = screen_geom.right() - geom.right()
        if bottom_margin < 0:
            # Falls over the bottom of the screen, move it up.
            geom.translate(0, bottom_margin)

        # TODO: right to left locale
        if right_margin < 0:
            # Falls over the right screen edge, move the menu to the
            # other side of pos.
            geom.translate(-size.width(), 0)

        self.setGeometry(geom)

        self.show()

        if searchText:
            self.setFocusProxy(self.__search)
        else:
            self.setFocusProxy(None)
示例#7
0
 def mouseReleaseEvent(self, event):
     if not self.rubberBand.size().isEmpty():
         rect = QRect(self.rubberBand.pos(), self.rubberBand.size())
         rect.moveLeft(rect.left() -
                       (self.width() - self.pixmap().width()) / 2.0)
         rect.moveTop(rect.top() -
                      (self.height() - self.pixmap().height()) / 2.0)
         self.currentSelection = rect
         self.copySelectionMenu.popup(QCursor.pos())
     else:
         self.copyMenu.popup(QCursor.pos())
     self.rubberBand.hide()
示例#8
0
 def paint(self, painter, option, index):
     painter.setFont(option.font)
     painter.setBrush(QBrush(Qt.black))
     if option.state & QStyle.State_Selected:
         painter.setPen(QPen(Qt.white))
     rect = QRect(option.rect)
     rect.setLeft(rect.left() + 4)
     rect.setTop(rect.top() + 4)
     rect.setWidth(rect.width() - 8)
     rect.setHeight(rect.height() - 8)
     painter.drawText(rect, Qt.Horizontal | Qt.TextWordWrap,
                      forceString(index.data()))
示例#9
0
def popup_position_from_source(popup, source, orientation=Qt.Vertical):
    popup.ensurePolished()
    source.ensurePolished()

    if popup.testAttribute(Qt.WA_Resized):
        size = popup.size()
    else:
        size = popup.sizeHint()

    desktop = QApplication.desktop()
    screen_geom = desktop.availableGeometry(source)
    source_rect = QRect(source.mapToGlobal(QPoint(0, 0)), source.size())

    if orientation == Qt.Vertical:
        if source_rect.right() + size.width() < screen_geom.right():
            x = source_rect.right()
        else:
            x = source_rect.left() - size.width()

        # bottom overflow
        dy = source_rect.top() + size.height() - screen_geom.bottom()
        if dy < 0:
            y = source_rect.top()
        else:
            y = source_rect.top() - dy
    else:
        # right overflow
        dx = source_rect.left() + size.width() - screen_geom.right()
        if dx < 0:
            x = source_rect.left()
        else:
            x = source_rect.left() - dx

        if source_rect.bottom() + size.height() < screen_geom.bottom():
            y = source_rect.bottom()
        else:
            y = source_rect.top() - size.height()

    return QPoint(x, y)
示例#10
0
def popup_position_from_source(popup, source, orientation=Qt.Vertical):
    popup.ensurePolished()
    source.ensurePolished()

    if popup.testAttribute(Qt.WA_Resized):
        size = popup.size()
    else:
        size = popup.sizeHint()

    desktop = QApplication.desktop()
    screen_geom = desktop.availableGeometry(source)
    source_rect = QRect(source.mapToGlobal(QPoint(0, 0)), source.size())

    if orientation == Qt.Vertical:
        if source_rect.right() + size.width() < screen_geom.right():
            x = source_rect.right()
        else:
            x = source_rect.left() - size.width()

        # bottom overflow
        dy = source_rect.top() + size.height() - screen_geom.bottom()
        if dy < 0:
            y = source_rect.top()
        else:
            y = source_rect.top() - dy
    else:
        # right overflow
        dx = source_rect.left() + size.width() - screen_geom.right()
        if dx < 0:
            x = source_rect.left()
        else:
            x = source_rect.left() - dx

        if source_rect.bottom() + size.height() < screen_geom.bottom():
            y = source_rect.bottom()
        else:
            y = source_rect.top() - size.height()

    return QPoint(x, y)
示例#11
0
    def render(self,fname):
      contentsSize = self.contentFrame.contentsSize()
      contentsSize -= QSize(self.m_scrollPosition.x(), self.m_scrollPosition.y())
      frameRect = QRect(QPoint(0, 0), contentsSize)
      #if not self.m_clipRect.isEmpty():
      #    frameRect = self.m_clipRect

      viewportSize = self.contentPage.viewportSize()
      self.contentPage.setViewportSize(contentsSize)

      image = QImage(frameRect.size(), QImage.Format_ARGB32)
      image.fill(qRgba(255, 255, 255, 0))

      painter = QPainter()

      # We use tiling approach to work-around Qt software rasterizer bug
      # when dealing with very large paint device.
      # See http://code.google.com/p/phantomjs/issues/detail?id=54.
      tileSize = 4096
      htiles = (image.width() + tileSize - 1) / tileSize
      vtiles = (image.height() + tileSize - 1) / tileSize
      for x in range(htiles):
          for y in range(vtiles):
              tileBuffer = QImage(tileSize, tileSize, QImage.Format_ARGB32)
              tileBuffer.fill(qRgba(255, 255, 255, 0))

              # Render the web page onto the small tile first
              painter.begin(tileBuffer)
              painter.setRenderHint(QPainter.Antialiasing, True)
              painter.setRenderHint(QPainter.TextAntialiasing, True)
              painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
              painter.translate(-frameRect.left(), -frameRect.top())
              painter.translate(-x * tileSize, -y * tileSize)
              self.contentFrame.render(painter, QRegion(frameRect))
              painter.end()

              # Copy the tile to the main buffer
              painter.begin(image)
              painter.setCompositionMode(QPainter.CompositionMode_Source)
              painter.drawImage(x * tileSize, y * tileSize, tileBuffer)
              painter.end()

      self.contentPage.setViewportSize(viewportSize)

      image.save(fname)

      return True
示例#12
0
    def paint(self, painter, option, index):
        """Performs custom painting of value of data in the model and decorations.

         Performs custom painting of value of data in the model at the specified index
         plus any decorations used in that column.

         Args:
            painter - QPainter
            option - QStyleOptionViewItemV4
            index - QModelIndex
        """
        self.initStyleOption(option, index)
        # I don't know why I have to do this. option.version returns 4, but still, when I try to
        # access option.features, boom-crash. The workaround is to force a V4.
        option = QStyleOptionViewItemV4(option)
        decorations = self._get_decorations(index, bool(option.state & QStyle.State_Selected))
        if decorations:
            option.decorationPosition = QStyleOptionViewItemV4.Right
            decorationWidth = sum(dec.pixmap.width() for dec in decorations)
            decorationHeight = max(dec.pixmap.height() for dec in decorations)
            option.decorationSize = QSize(decorationWidth, decorationHeight)
            option.features |= QStyleOptionViewItemV4.HasDecoration
        self._prepare_paint_options(option, index)

        xOffset = 0
        # First added for #15, the painting of custom amount information.  This can
        # be used as a pattern for painting any column of information.
        value_painter = self._get_value_painter(index)
        self._display_text = value_painter is None
        QStyledItemDelegate.paint(self, painter, option, index)
        if value_painter is not None:
            value_option = QStyleOptionViewItemV4(option)
            rect = value_option.rect
            rect = QRect(rect.left(), rect.top(), rect.width() - xOffset, rect.height())
            value_option.rect = rect
            value_painter.paint(painter, value_option, index)

        for dec in decorations:
            pixmap = dec.pixmap
            x = option.rect.right() - pixmap.width() - xOffset
            y = option.rect.center().y() - (pixmap.height() // 2)
            rect = QRect(x, y, pixmap.width(), pixmap.height())
            painter.drawPixmap(rect, pixmap)
            xOffset += pixmap.width()
示例#13
0
    def renderImage(self):
        contentsSize = self.m_mainFrame.contentsSize()
        contentsSize -= QSize(self.m_scrollPosition.x(), self.m_scrollPosition.y())
        frameRect = QRect(QPoint(0, 0), contentsSize)
        if not self.m_clipRect.isEmpty():
            frameRect = self.m_clipRect

        viewportSize = self.m_webPage.viewportSize()
        self.m_webPage.setViewportSize(contentsSize)

        image = QImage(frameRect.size(), QImage.Format_ARGB32)
        image.fill(qRgba(255, 255, 255, 0))

        painter = QPainter()

        # We use tiling approach to work-around Qt software rasterizer bug
        # when dealing with very large paint device.
        # See http://code.google.com/p/phantomjs/issues/detail?id=54.
        tileSize = 4096
        htiles = (image.width() + tileSize - 1) / tileSize
        vtiles = (image.height() + tileSize - 1) / tileSize
        for x in range(htiles):
            for y in range(vtiles):
                tileBuffer = QImage(tileSize, tileSize, QImage.Format_ARGB32)
                tileBuffer.fill(qRgba(255, 255, 255, 0))

                # Render the web page onto the small tile first
                painter.begin(tileBuffer)
                painter.setRenderHint(QPainter.Antialiasing, True)
                painter.setRenderHint(QPainter.TextAntialiasing, True)
                painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
                painter.translate(-frameRect.left(), -frameRect.top())
                painter.translate(-x * tileSize, -y * tileSize)
                self.m_mainFrame.render(painter, QRegion(frameRect))
                painter.end()

                # Copy the tile to the main buffer
                painter.begin(image)
                painter.setCompositionMode(QPainter.CompositionMode_Source)
                painter.drawImage(x * tileSize, y * tileSize, tileBuffer)
                painter.end()

        self.m_webPage.setViewportSize(viewportSize)
        return image
示例#14
0
def widget_popup_geometry(pos, widget):
    widget.ensurePolished()

    if widget.testAttribute(Qt.WA_Resized):
        size = widget.size()
    else:
        size = widget.sizeHint()

    desktop = QApplication.desktop()
    screen_geom = desktop.availableGeometry(pos)

    # Adjust the size to fit inside the screen.
    if size.height() > screen_geom.height():
        size.setHeight(screen_geom.height())
    if size.width() > screen_geom.width():
        size.setWidth(screen_geom.width())

    geom = QRect(pos, size)

    if geom.top() < screen_geom.top():
        geom.setTop(screen_geom.top())

    if geom.left() < screen_geom.left():
        geom.setLeft(screen_geom.left())

    bottom_margin = screen_geom.bottom() - geom.bottom()
    right_margin = screen_geom.right() - geom.right()
    if bottom_margin < 0:
        # Falls over the bottom of the screen, move it up.
        geom.translate(0, bottom_margin)

    # TODO: right to left locale
    if right_margin < 0:
        # Falls over the right screen edge, move the menu to the
        # other side of pos.
        geom.translate(-size.width(), 0)

    return geom
示例#15
0
def widget_popup_geometry(pos, widget):
    widget.ensurePolished()

    if widget.testAttribute(Qt.WA_Resized):
        size = widget.size()
    else:
        size = widget.sizeHint()

    desktop = QApplication.desktop()
    screen_geom = desktop.availableGeometry(pos)

    # Adjust the size to fit inside the screen.
    if size.height() > screen_geom.height():
        size.setHeight(screen_geom.height())
    if size.width() > screen_geom.width():
        size.setWidth(screen_geom.width())

    geom = QRect(pos, size)

    if geom.top() < screen_geom.top():
        geom.setTop(screen_geom.top())

    if geom.left() < screen_geom.left():
        geom.setLeft(screen_geom.left())

    bottom_margin = screen_geom.bottom() - geom.bottom()
    right_margin = screen_geom.right() - geom.right()
    if bottom_margin < 0:
        # Falls over the bottom of the screen, move it up.
        geom.translate(0, bottom_margin)

    # TODO: right to left locale
    if right_margin < 0:
        # Falls over the right screen edge, move the menu to the
        # other side of pos.
        geom.translate(-size.width(), 0)

    return geom
示例#16
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)
class ExtractRasterValue(ParentMapTool):
    """ Button 18. User select nodes and assign raster elevation or value """

    def __init__(self, iface, settings, action, index_action):
        """ Class constructor """

        # Call ParentMapTool constructor
        super(ExtractRasterValue, self).__init__(iface, settings, action, index_action)

        self.dragging = False

        # Vertex marker
        self.vertexMarker = QgsVertexMarker(self.canvas)
        self.vertexMarker.setColor(QColor(255, 25, 25))
        self.vertexMarker.setIconSize(11)
        self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        self.vertexMarker.setPenWidth(5)

        # Rubber band
        self.rubberBand = QgsRubberBand(self.canvas, True)
        mFillColor = QColor(100, 0, 0)
        self.rubberBand.setColor(mFillColor)
        self.rubberBand.setWidth(3)
        mBorderColor = QColor(254, 58, 29)
        self.rubberBand.setBorderColor(mBorderColor)

        # Select rectangle
        self.selectRect = QRect()

        # Init
        self.vectorLayer = None
        self.rasterLayer = None

    def reset(self):
        """ Clear selected features """

        layer = self.vectorLayer
        if layer is not None:
            layer.removeSelection()

        # Graphic elements
        self.rubberBand.reset()

    def set_config_action(self, action_99):
        """ Get the config form action"""
        self.configAction = action_99

    """ QgsMapTools inherited event functions """

    def canvasMoveEvent(self, event):
        """ With left click the digitizing is finished """

        if self.vectorLayer is None:
            return

        if event.buttons() == Qt.LeftButton:

            if not self.dragging:
                self.dragging = True
                self.selectRect.setTopLeft(event.pos())

            self.selectRect.setBottomRight(event.pos())
            self.set_rubber_band()

        else:

            # Hide highlight
            self.vertexMarker.hide()

            # Get the click
            x = event.pos().x()
            y = event.pos().y()
            eventPoint = QPoint(x, y)

            # Snapping
            (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint)  # @UnusedVariable

            # That's the snapped point
            if result <> []:

                # Check Arc or Node
                for snapPoint in result:

                    if snapPoint.layer == self.vectorLayer:

                        # Get the point
                        point = QgsPoint(result[0].snappedVertex)

                        # Add marker
                        self.vertexMarker.setCenter(point)
                        self.vertexMarker.show()

                        break

    def canvasPressEvent(self, event):

        self.selectRect.setRect(0, 0, 0, 0)
        self.rubberBand.reset()

    def canvasReleaseEvent(self, event):
        """ With left click the digitizing is finished """

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

            # Get the click
            x = event.pos().x()
            y = event.pos().y()
            eventPoint = QPoint(x, y)

            # Node layer
            layer = self.vectorLayer

            # Not dragging, just simple selection
            if not self.dragging:

                # Snap to node
                (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint)  # @UnusedVariable

                # That's the snapped point
                if result <> [] and (result[0].layer.name() == self.vectorLayer.name()):

                    point = QgsPoint(result[0].snappedVertex)  # @UnusedVariable
                    layer.removeSelection()
                    layer.select([result[0].snappedAtGeometry])

                    # Interpolate values
                    self.raster_interpolate()

                    # Hide highlight
                    self.vertexMarker.hide()

            else:

                # Set valid values for rectangle's width and height
                if self.selectRect.width() == 1:
                    self.selectRect.setLeft(self.selectRect.left() + 1)

                if self.selectRect.height() == 1:
                    self.selectRect.setBottom(self.selectRect.bottom() + 1)

                self.set_rubber_band()
                selectGeom = self.rubberBand.asGeometry()  # @UnusedVariable
                self.select_multiple_features(self.selectRectMapCoord)
                self.dragging = False

                # Interpolate values
                self.raster_interpolate()

        elif event.button() == Qt.RightButton:

            # Interpolate values
            self.raster_interpolate()

    def activate(self):

        # Check button
        self.action().setChecked(True)

        # Rubber band
        self.rubberBand.reset()

        # Store user snapping configuration
        self.snapperManager.storeSnappingOptions()

        # Clear snapping
        self.snapperManager.clearSnapping()

        # Get layers
        res = self.find_raster_layers()
        #        if res == 0:
        #            self.controller.show_warning("Raster configuration tool not properly configured.")
        #            return

        # Change cursor
        self.canvas.setCursor(self.cursor)

        # Show help message when action is activated
        if self.show_help:
            message = (
                "Right click to use current selection, select connec points by clicking or dragging (selection box)"
            )
            self.controller.show_info(message, context_name="ui_message")

        # Control current layer (due to QGIS bug in snapping system)
        try:
            if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer:
                self.canvas.setCurrentLayer(self.layer_arc)
        except:
            self.canvas.setCurrentLayer(self.layer_arc)

    def deactivate(self):

        # Check button
        self.action().setChecked(False)

        # Rubber band
        self.rubberBand.reset()

        # Restore previous snapping
        self.snapperManager.recoverSnappingOptions()

        # Recover cursor
        self.canvas.setCursor(self.stdCursor)

    def nearestNeighbor(self, thePoint):

        ident = self.dataProv.identify(thePoint, QgsRaster.IdentifyFormatValue)
        value = None
        if ident is not None:  # and ident.has_key(choosenBand+1):
            try:
                value = float(ident.results()[int(self.band)])
            except TypeError:
                value = None
        if value == self.noDataValue:
            return None
        return value

    def writeInterpolation(self, f, fieldIdx):

        thePoint = f.geometry().asPoint()
        value = self.nearestNeighbor(thePoint)
        self.vectorLayer.changeAttributeValue(f.id(), fieldIdx, value)

    def raster_interpolate(self):
        """ Interpolate features value from raster """

        # Interpolate values
        if self.vectorLayer is None and self.rasterLayer is None:
            return

        # Get data provider
        layer = self.vectorLayer
        self.dataProv = self.rasterLayer.dataProvider()
        if self.dataProv.srcNoDataValue(int(self.band)):
            self.noDataValue = self.dataProv.srcNoDataValue(int(self.band))
        else:
            self.noDataValue = None

        self.continueProcess = True

        self.fieldIdx = ""
        self.fieldIdx = layer.fieldNameIndex(self.fieldName)

        if self.band == 0:
            self.controller.show_warning("You must choose a band for the raster layer.")
            return
        if self.fieldName == "":
            self.controller.show_warning("You must choose a field to write values.")
            return
        if self.fieldIdx < 0:
            self.controller.show_warning("Selected field does not exist in feature layer.")
            return

        k = 0
        c = 0
        f = QgsFeature()

        # Check features selected
        if layer.selectedFeatureCount() == 0:
            message = "You have to select at least one feature!"
            self.controller.show_warning(message, context_name="ui_message")

            return

        # Check editable
        if not layer.isEditable():
            layer.startEditing()

        # Get selected id's
        ids = self.vectorLayer.selectedFeaturesIds()

        for fid in ids:

            k += 1
            layer.getFeatures(QgsFeatureRequest(fid)).nextFeature(f)
            c += 1
            self.writeInterpolation(f, self.fieldIdx)
            QCoreApplication.processEvents()

        self.controller.show_info(
            "%u values have been updated in layer %s.%s over %u points using %s raster"
            % (c, self.vectorLayer.name(), self.fieldName, k, self.table_raster)
        )

        # Check editable
        if layer.isEditable():
            layer.commitChanges()

        # Refresh map canvas
        self.rubberBand.reset()
        self.iface.mapCanvas().refresh()

    def set_rubber_band(self):

        # Coordinates transform
        transform = self.canvas.getCoordinateTransform()

        # Coordinates
        ll = transform.toMapCoordinates(self.selectRect.left(), self.selectRect.bottom())
        lr = transform.toMapCoordinates(self.selectRect.right(), self.selectRect.bottom())
        ul = transform.toMapCoordinates(self.selectRect.left(), self.selectRect.top())
        ur = transform.toMapCoordinates(self.selectRect.right(), self.selectRect.top())

        # Rubber band
        self.rubberBand.reset()
        self.rubberBand.addPoint(ll, False)
        self.rubberBand.addPoint(lr, False)
        self.rubberBand.addPoint(ur, False)
        self.rubberBand.addPoint(ul, False)
        self.rubberBand.addPoint(ll, True)

        self.selectRectMapCoord = QgsRectangle(ll, ur)

    def select_multiple_features(self, selectGeometry):

        # Default choice
        behaviour = QgsVectorLayer.SetSelection

        # Modifiers
        modifiers = QApplication.keyboardModifiers()

        if modifiers == Qt.ControlModifier:
            behaviour = QgsVectorLayer.AddToSelection
        elif modifiers == Qt.ShiftModifier:
            behaviour = QgsVectorLayer.RemoveFromSelection

        if self.vectorLayer is None:
            return

        # Change cursor
        QApplication.setOverrideCursor(Qt.WaitCursor)

        # Selection
        self.vectorLayer.selectByRect(selectGeometry, behaviour)

        # Old cursor
        QApplication.restoreOverrideCursor()

    def find_raster_layers(self):

        # Query database (form data)
        sql = "SELECT *"
        sql += " FROM " + self.schema_name + ".config_extract_raster_value"

        rows = self.controller.get_rows(sql)

        if not rows:
            self.controller.show_warning("Any data found in table config_extract_raster_value")
            self.configAction.activate(QAction.Trigger)
            return 0

        # Layers
        row = rows[0]
        self.table_raster = row[1]
        self.band = row[2]
        self.table_vector = row[3]
        self.fieldName = row[4]

        # Check if we have any layer loaded
        layers = self.iface.legendInterface().layers()
        if len(layers) == 0:
            return

        # Init layers
        self.rasterLayer = None
        self.vectorLayer = None

        # Iterate over all layers to get the ones specified in 'db' config section
        for cur_layer in layers:

            if cur_layer.name() == self.table_raster:
                self.rasterLayer = cur_layer

            (uri_schema, uri_table) = self.controller.get_layer_source(cur_layer)  # @UnusedVariable
            if uri_table is not None:
                if self.table_vector in uri_table:
                    self.vectorLayer = cur_layer

        # Check config
        if self.vectorLayer is None or self.rasterLayer is None:
            self.configAction.activate(QAction.Trigger)

        # Set snapping
        if self.vectorLayer <> None:
            self.snapperManager.snapToLayer(self.vectorLayer)
        else:
            self.controller.show_warning("Check vector_layer in config form, layer does not exist or is not defined.")
            return 0

        if self.rasterLayer == None:
            self.controller.show_warning("Check raster_layer in config form, layer does not exist or is not defined.")
            return 0

        return 1
示例#18
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
class MincutConnec(QgsMapTool):

    canvasClicked = pyqtSignal()

    def __init__(self, iface, controller):
        """ Class constructor """

        self.iface = iface
        self.canvas = self.iface.mapCanvas()
        self.controller = controller
        # Call superclass constructor and set current action
        QgsMapTool.__init__(self, self.canvas)

        self.dragging = False

        # Vertex marker
        color = QColor(255, 100, 255)
        self.vertex_marker = QgsVertexMarker(self.canvas)
        self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE)
        self.vertex_marker.setColor(color)
        self.vertex_marker.setIconSize(15)
        self.vertex_marker.setPenWidth(3)

        # Rubber band
        self.rubber_band = QgsRubberBand(self.canvas, QGis.Line)
        self.rubber_band.setColor(color)
        self.rubber_band.setWidth(1)

        # Select rectangle
        self.select_rect = QRect()

        # TODO: Parametrize
        self.connec_group = ["Wjoin", "Tap", "Fountain", "Greentap"]
        #self.snapperManager = SnappingConfigManager(self.iface)
        self.snapper = QgsMapCanvasSnapper(self.canvas)

    def activate(self):
        pass

    def canvasPressEvent(self, event):  #@UnusedVariable
        self.select_rect.setRect(0, 0, 0, 0)
        self.rubber_band.reset()

    def canvasMoveEvent(self, event):
        """ With left click the digitizing is finished """

        if event.buttons() == Qt.LeftButton:
            if not self.dragging:
                self.dragging = True
                self.select_rect.setTopLeft(event.pos())
            self.select_rect.setBottomRight(event.pos())
            self.set_rubber_band()

        else:
            # Hide highlight
            self.vertex_marker.hide()

            # Get the click
            x = event.pos().x()
            y = event.pos().y()
            event_point = QPoint(x, y)

            # Snapping
            (retval, result) = self.snapper.snapToBackgroundLayers(
                event_point)  # @UnusedVariable

            # That's the snapped point
            if result:
                # Check feature
                for snap_point in result:
                    element_type = snap_point.layer.name()
                    if element_type in self.connec_group:
                        # Get the point
                        point = QgsPoint(snap_point.snappedVertex)
                        # Add marker
                        self.vertex_marker.setCenter(point)
                        self.vertex_marker.show()
                        break

    def canvasReleaseEvent(self, event):
        """ With left click the digitizing is finished """

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

            # Get the click
            x = event.pos().x()
            y = event.pos().y()
            event_point = QPoint(x, y)

            # Not dragging, just simple selection
            if not self.dragging:

                # Snap to node
                (retval, result) = self.snapper.snapToBackgroundLayers(
                    event_point)  # @UnusedVariable

                # That's the snapped point
                if result:
                    # Check feature
                    for snapped_point in result:
                        element_type = snapped_point.layer.name()
                        if element_type in self.connec_group:
                            feat_type = 'connec'
                        else:
                            continue

                        point = QgsPoint(
                            snapped_point.snappedVertex)  # @UnusedVariable
                        # layer.removeSelection()
                        # layer.select([result[0].snappedAtGeometry])

                        #snapped_point.layer.removeSelection()
                        snapped_point.layer.select(
                            [snapped_point.snappedAtGeometry])

            else:

                # Set valid values for rectangle's width and height
                if self.select_rect.width() == 1:
                    self.select_rect.setLeft(self.select_rect.left() + 1)

                if self.select_rect.height() == 1:
                    self.select_rect.setBottom(self.select_rect.bottom() + 1)

                self.set_rubber_band()
                self.select_multiple_features(self.selected_rectangle)
                self.dragging = False

            # Refresh map canvas
            self.rubber_band.reset()
            self.refresh_map_canvas()

    def set_rubber_band(self):

        # Coordinates transform
        transform = self.canvas.getCoordinateTransform()

        # Coordinates
        ll = transform.toMapCoordinates(self.select_rect.left(),
                                        self.select_rect.bottom())
        lr = transform.toMapCoordinates(self.select_rect.right(),
                                        self.select_rect.bottom())
        ul = transform.toMapCoordinates(self.select_rect.left(),
                                        self.select_rect.top())
        ur = transform.toMapCoordinates(self.select_rect.right(),
                                        self.select_rect.top())

        # Rubber band
        self.rubber_band.reset()
        self.rubber_band.addPoint(ll, False)
        self.rubber_band.addPoint(lr, False)
        self.rubber_band.addPoint(ur, False)
        self.rubber_band.addPoint(ul, False)
        self.rubber_band.addPoint(ll, True)

        self.selected_rectangle = QgsRectangle(ll, ur)

    def select_multiple_features(self, rectangle):

        if self.connec_group is None:
            return

        if QGis.QGIS_VERSION_INT >= 21600:

            # Selection for all connec group layers
            for layer_name in self.connec_group:
                # Get layer by his name
                layer = self.controller.get_layer_by_layername(layer_name,
                                                               log_info=True)
                if layer:
                    self.group_pointers_connec.append(layer)
                    layer.selectByRect(rectangle)

        else:
            for layer_name in self.connec_group:
                self.iface.setActiveLayer(layer)
                layer.removeSelection()
                layer.select(rectangle, True)
示例#20
0
class ConnecMapTool(ParentMapTool):
    """ Button 20: User select connections from layer 'connec'
    Execute SQL function: 'gw_fct_connect_to_network' """
    def __init__(self, iface, settings, action, index_action):
        """ Class constructor """

        # Call ParentMapTool constructor
        super(ConnecMapTool, self).__init__(iface, settings, action,
                                            index_action)

        self.dragging = False

        # Select rectangle
        self.select_rect = QRect()

    """ QgsMapTools inherited event functions """

    def canvasMoveEvent(self, event):
        """ With left click the digitizing is finished """

        if event.buttons() == Qt.LeftButton:

            if not self.dragging:
                self.dragging = True
                self.select_rect.setTopLeft(event.pos())

            self.select_rect.setBottomRight(event.pos())
            self.set_rubber_band()

        else:

            # Hide marker
            self.vertex_marker.hide()

            # Get the click
            x = event.pos().x()
            y = event.pos().y()
            event_point = QPoint(x, y)

            # Snapping
            (retval, result) = self.snapper.snapToBackgroundLayers(
                event_point)  # @UnusedVariable

            # That's the snapped features
            if result:
                for snapped_feat in result:
                    # Check if it belongs to 'connec' or 'gully' group
                    exist_connec = self.snapper_manager.check_connec_group(
                        snapped_feat.layer)
                    exist_gully = self.snapper_manager.check_gully_group(
                        snapped_feat.layer)
                    if exist_connec or exist_gully:
                        # Get the point and add marker
                        point = QgsPoint(result[0].snappedVertex)
                        self.vertex_marker.setCenter(point)
                        self.vertex_marker.show()
                        break

    def canvasPressEvent(self, event):  #@UnusedVariable

        self.select_rect.setRect(0, 0, 0, 0)
        self.rubber_band.reset(QGis.Polygon)

    def canvasReleaseEvent(self, event):
        """ With left click the digitizing is finished """

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

            # Get the click
            x = event.pos().x()
            y = event.pos().y()
            event_point = QPoint(x, y)

            # Simple selection
            if not self.dragging:

                # Snap to connec or gully
                (retval, result) = self.snapper.snapToBackgroundLayers(
                    event_point)  # @UnusedVariable

                # That's the snapped features
                if result:
                    # Check if it belongs to 'connec' or 'gully' group
                    exist_connec = self.snapper_manager.check_connec_group(
                        result[0].layer)
                    exist_gully = self.snapper_manager.check_gully_group(
                        result[0].layer)
                    if exist_connec or exist_gully:
                        key = QApplication.keyboardModifiers()
                        # If Ctrl+Shift is clicked: deselect snapped feature
                        if key == (Qt.ControlModifier | Qt.ShiftModifier):
                            result[0].layer.deselect(
                                [result[0].snappedAtGeometry])
                        else:
                            # If Ctrl is not clicked: remove previous selection
                            if key != Qt.ControlModifier:
                                result[0].layer.removeSelection()
                            result[0].layer.select(
                                [result[0].snappedAtGeometry])

                        # Hide marker
                        self.vertex_marker.hide()

            # Multiple selection
            else:

                # Set valid values for rectangle's width and height
                if self.select_rect.width() == 1:
                    self.select_rect.setLeft(self.select_rect.left() + 1)

                if self.select_rect.height() == 1:
                    self.select_rect.setBottom(self.select_rect.bottom() + 1)

                self.set_rubber_band()
                selected_geom = self.rubber_band.asGeometry()  #@UnusedVariable
                self.select_multiple_features(self.selected_rectangle)
                self.dragging = False

                # Refresh map canvas
                self.rubber_band.reset()

        elif event.button() == Qt.RightButton:

            # Check selected records
            number_features = 0
            for layer in self.layer_connec_man:
                number_features += layer.selectedFeatureCount()

            if number_features > 0:
                message = ("Number of features selected in the 'connec' group")
                title = "Interpolate value - Do you want to update values"
                answer = self.controller.ask_question(
                    message, title, parameter=str(number_features))
                if answer:
                    # Create link
                    self.link_selected_features('connec',
                                                self.layer_connec_man)

            if self.layer_gully_man:
                # Check selected records
                number_features = 0
                for layer in self.layer_gully_man:
                    number_features += layer.selectedFeatureCount()

                if number_features > 0:
                    message = (
                        "Number of features selected in the 'gully' group")
                    title = "Interpolate value - Do you want to update values"
                    answer = self.controller.ask_question(
                        message, title, parameter=str(number_features))
                    if answer:
                        # Create link
                        self.link_selected_features('gully',
                                                    self.layer_gully_man)

    def activate(self):

        # Check button
        self.action().setChecked(True)

        # Rubber band
        self.rubber_band.reset()

        # Store user snapping configuration
        self.snapper_manager.store_snapping_options()

        # Clear snapping
        self.snapper_manager.clear_snapping()

        # Set snapping to 'connec' and 'gully'
        self.snapper_manager.snap_to_connec_gully()

        # Change cursor
        cursor = self.get_cursor_multiple_selection()
        self.canvas.setCursor(cursor)

        # Show help message when action is activated
        if self.show_help:
            message = "Right click to use current selection, select connec points by clicking or dragging (selection box)"
            self.controller.show_info(message)

    def deactivate(self):

        # Call parent method
        ParentMapTool.deactivate(self)

    def link_selected_features(self, geom_type, layers):
        """ Link selected @geom_type to the pipe """

        # Check features selected
        number_features = 0
        for layer in layers:
            number_features += layer.selectedFeatureCount()

        if number_features == 0:
            message = "You have to select at least one feature!"
            self.controller.show_warning(message)
            return

        # Get selected features from layers of selected @geom_type
        aux = "{"
        field_id = geom_type + "_id"

        # Iterate over all layers
        for layer in layers:
            if layer.selectedFeatureCount() > 0:
                # Get selected features of the layer
                features = layer.selectedFeatures()
                for feature in features:
                    feature_id = feature.attribute(field_id)
                    aux += str(feature_id) + ", "
                list_feature_id = aux[:-2] + "}"

                # Execute function
                function_name = "gw_fct_connect_to_network"
                sql = ("SELECT " + self.schema_name + "." + function_name + ""
                       "('" + list_feature_id + "', '" + geom_type.upper() +
                       "');")
                self.controller.execute_sql(sql, log_sql=True)
                layer.removeSelection()
        # Refresh map canvas
        self.rubber_band.reset()
        self.refresh_map_canvas()
        self.iface.actionPan().trigger()

    def set_rubber_band(self):

        # Coordinates transform
        transform = self.canvas.getCoordinateTransform()

        # Coordinates
        ll = transform.toMapCoordinates(self.select_rect.left(),
                                        self.select_rect.bottom())
        lr = transform.toMapCoordinates(self.select_rect.right(),
                                        self.select_rect.bottom())
        ul = transform.toMapCoordinates(self.select_rect.left(),
                                        self.select_rect.top())
        ur = transform.toMapCoordinates(self.select_rect.right(),
                                        self.select_rect.top())

        # Rubber band
        self.rubber_band.reset(QGis.Polygon)
        self.rubber_band.addPoint(ll, False)
        self.rubber_band.addPoint(lr, False)
        self.rubber_band.addPoint(ur, False)
        self.rubber_band.addPoint(ul, False)
        self.rubber_band.addPoint(ll, True)

        self.selected_rectangle = QgsRectangle(ll, ur)

    def select_multiple_features(self, selectGeometry):

        if self.layer_connec_man is None and self.layer_gully_man is None:
            return

        key = QApplication.keyboardModifiers()

        # If Ctrl+Shift clicked: remove features from selection
        if key == (Qt.ControlModifier | Qt.ShiftModifier):
            behaviour = QgsVectorLayer.RemoveFromSelection
        # If Ctrl clicked: add features to selection
        elif key == Qt.ControlModifier:
            behaviour = QgsVectorLayer.AddToSelection
        # If Ctrl not clicked: add features to selection
        else:
            behaviour = QgsVectorLayer.AddToSelection

        # Selection for all connec and gully layers
        for layer in self.layer_connec_man:
            layer.selectByRect(selectGeometry, behaviour)

        if self.layer_gully_man:
            for layer in self.layer_gully_man:
                layer.selectByRect(selectGeometry, behaviour)
示例#21
0
class Phantom(QObject):
    def __init__(self, args, parent=None):
        QObject.__init__(self, parent)

        # variable declarations
        self.m_loadStatus = self.m_state = ''
        self.m_var = self.m_paperSize = self.m_loadScript_cache = {}
        self.m_verbose = args.verbose
        self.m_page = WebPage(self)
        self.m_clipRect = QRect()
        # setup the values from args
        self.m_script = args.script.read()
        self.m_scriptFile = args.script.name
        self.m_scriptDir = os.path.dirname(args.script.name) + '/'
        self.m_args = args.script_args
        self.m_upload_file = args.upload_file
        autoLoadImages = False if args.load_images == 'no' else True
        pluginsEnabled = True if args.load_plugins == 'yes' else False

        args.script.close()

        do_action('PhantomInitPre', Bunch(locals()))

        palette = self.m_page.palette()
        palette.setBrush(QPalette.Base, Qt.transparent)
        self.m_page.setPalette(palette)

        if not args.proxy:
            QNetworkProxyFactory.setUseSystemConfiguration(True)
        else:
            proxy = QNetworkProxy(QNetworkProxy.HttpProxy, args.proxy[0],
                                  int(args.proxy[1]))
            QNetworkProxy.setApplicationProxy(proxy)

        self.m_page.settings().setAttribute(QWebSettings.AutoLoadImages,
                                            autoLoadImages)
        self.m_page.settings().setAttribute(QWebSettings.PluginsEnabled,
                                            pluginsEnabled)
        self.m_page.settings().setAttribute(
            QWebSettings.FrameFlatteningEnabled, True)
        self.m_page.settings().setAttribute(
            QWebSettings.OfflineStorageDatabaseEnabled, True)
        self.m_page.settings().setAttribute(QWebSettings.LocalStorageEnabled,
                                            True)
        self.m_page.settings().setLocalStoragePath(
            QDesktopServices.storageLocation(QDesktopServices.DataLocation))
        self.m_page.settings().setOfflineStoragePath(
            QDesktopServices.storageLocation(QDesktopServices.DataLocation))

        # Ensure we have a document.body.
        self.m_page.mainFrame().setHtml('<html><body></body></html>')

        self.m_page.mainFrame().setScrollBarPolicy(Qt.Horizontal,
                                                   Qt.ScrollBarAlwaysOff)
        self.m_page.mainFrame().setScrollBarPolicy(Qt.Vertical,
                                                   Qt.ScrollBarAlwaysOff)

        m_netAccessMan = NetworkAccessManager(args.disk_cache,
                                              args.ignore_ssl_errors, self)
        self.m_page.setNetworkAccessManager(m_netAccessMan)

        # inject our properties and slots into javascript
        self.m_page.mainFrame().javaScriptWindowObjectCleared.connect(
            self.inject)
        self.m_page.loadFinished.connect(self.finish)

        do_action('PhantomInitPost', Bunch(locals()))

    def execute(self):
        if self.m_script.startswith('#!'):
            self.m_script = '//' + self.m_script

        if self.m_scriptFile.lower().endswith('.coffee'):
            coffee = CSConverter(self)
            self.m_script = coffee.convert(self.m_script)

        self.m_page.mainFrame().evaluateJavaScript(self.m_script)

    def finish(self, success):
        self.m_loadStatus = 'success' if success else 'fail'
        self.m_page.mainFrame().evaluateJavaScript(self.m_script)

    def inject(self):
        self.m_page.mainFrame().addToJavaScriptWindowObject('phantom', self)

    def renderPdf(self, fileName):
        p = QPrinter()
        p.setOutputFormat(QPrinter.PdfFormat)
        p.setOutputFileName(fileName)
        p.setResolution(pdf_dpi)
        paperSize = self.m_paperSize

        if not len(paperSize):
            pageSize = QSize(self.m_page.mainFrame().contentsSize())
            paperSize['width'] = str(pageSize.width()) + 'px'
            paperSize['height'] = str(pageSize.height()) + 'px'
            paperSize['border'] = '0px'

        if paperSize.get('width') and paperSize.get('height'):
            sizePt = QSizeF(ceil(self.stringToPointSize(paperSize['width'])),
                            ceil(self.stringToPointSize(paperSize['height'])))
            p.setPaperSize(sizePt, QPrinter.Point)
        elif 'format' in paperSize:
            orientation = QPrinter.Landscape if paperSize.get(
                'orientation') and paperSize['orientation'].lower(
                ) == 'landscape' else QPrinter.Portrait
            orientation = QPrinter.Orientation(orientation)
            p.setOrientation(orientation)

            formats = {
                'A3': QPrinter.A3,
                'A4': QPrinter.A4,
                'A5': QPrinter.A5,
                'Legal': QPrinter.Legal,
                'Letter': QPrinter.Letter,
                'Tabloid': QPrinter.Tabloid
            }

            p.setPaperSize(QPrinter.A4)  # fallback
            for format, size in formats.items():
                if format.lower() == paperSize['format'].lower():
                    p.setPaperSize(size)
                    break
        else:
            return False

        border = floor(self.stringToPointSize(
            paperSize['border'])) if paperSize.get('border') else 0
        p.setPageMargins(border, border, border, border, QPrinter.Point)

        self.m_page.mainFrame().print_(p)
        return True

    def returnValue(self):
        return self.m_returnValue

    def stringToPointSize(self, string):
        units = (('mm', 72 / 25.4), ('cm', 72 / 2.54), ('in', 72.0),
                 ('px', 72.0 / pdf_dpi / 2.54), ('', 72.0 / pdf_dpi / 2.54))

        for unit, format in units:
            if string.endswith(unit):
                value = string.rstrip(unit)
                return float(value) * format
        return 0

    ##
    # Properties and methods exposed to JavaScript
    ##

    @pyqtProperty('QStringList')
    def args(self):
        return self.m_args

    @pyqtProperty('QVariantMap')
    def clipRect(self):
        result = {
            'width': self.m_clipRect.width(),
            'height': self.m_clipRect.height(),
            'top': self.m_clipRect.top(),
            'left': self.m_clipRect.left()
        }
        return result

    @clipRect.setter
    def clipRect(self, size):
        names = ('width', 'height', 'top', 'left')
        for item in names:
            try:
                globals()[item] = int(size[item])
                if globals()[item] < 0:
                    if item not in ('top', 'left'):
                        globals()[item] = 0
            except KeyError:
                globals()[item] = getattr(self.m_clipRect, item)()

        self.m_clipRect = QRect(left, top, width, height)

    @pyqtProperty(str)
    def content(self):
        return self.m_page.mainFrame().toHtml()

    @content.setter
    def content(self, content):
        self.m_page.mainFrame().setHtml(content)

    @pyqtSlot()
    @pyqtSlot(int)
    def exit(self, code=0):
        self.m_returnValue = code
        self.m_page.loadFinished.disconnect(self.finish)
        QTimer.singleShot(0, qApp, SLOT('quit()'))

    @pyqtProperty(str)
    def loadStatus(self):
        return self.m_loadStatus

    @pyqtSlot(str, result=bool)
    def loadScript(self, script):
        if script in self.m_loadScript_cache:
            self.m_page.mainFrame().evaluateJavaScript(
                self.m_loadScript_cache[script])
            return True

        scriptFile = script
        try:
            script = codecs.open(self.m_scriptDir + script, encoding='utf-8')
            script = script.read()
        except IOError:
            return False

        if script.startswith('#!'):
            script = '//' + script

        if scriptFile.lower().endswith('.coffee'):
            coffee = CSConverter(self)
            script = coffee.convert(script)

        self.m_loadScript_cache[scriptFile] = script
        self.m_page.mainFrame().evaluateJavaScript(script)
        return True

    @pyqtSlot(str, name='open')
    def open_(self, address):
        qDebug('Opening address %s' % address)
        self.m_page.triggerAction(QWebPage.Stop)
        self.m_loadStatus = 'loading'
        self.m_page.mainFrame().setUrl(QUrl(address))

    @pyqtProperty('QVariantMap')
    def paperSize(self):
        return self.m_paperSize

    @paperSize.setter
    def paperSize(self, size):
        self.m_paperSize = size

    @pyqtSlot(str, result=bool)
    def render(self, fileName):
        fileInfo = QFileInfo(fileName)
        path = QDir()
        path.mkpath(fileInfo.absolutePath())

        if fileName.lower().endswith('.pdf'):
            return self.renderPdf(fileName)

        viewportSize = QSize(self.m_page.viewportSize())
        pageSize = QSize(self.m_page.mainFrame().contentsSize())

        bufferSize = QSize()
        if not self.m_clipRect.isEmpty():
            bufferSize = self.m_clipRect.size()
        else:
            bufferSize = self.m_page.mainFrame().contentsSize()

        if pageSize == '':
            return False

        image = QImage(bufferSize, QImage.Format_ARGB32)
        image.fill(qRgba(255, 255, 255, 0))
        p = QPainter(image)

        p.setRenderHint(QPainter.Antialiasing, True)
        p.setRenderHint(QPainter.TextAntialiasing, True)
        p.setRenderHint(QPainter.SmoothPixmapTransform, True)

        self.m_page.setViewportSize(pageSize)

        if not self.m_clipRect.isEmpty():
            p.translate(-self.m_clipRect.left(), -self.m_clipRect.top())
            self.m_page.mainFrame().render(p, QRegion(self.m_clipRect))
        else:
            self.m_page.mainFrame().render(p)

        p.end()
        self.m_page.setViewportSize(viewportSize)
        return image.save(fileName)

    @pyqtSlot('QWebElement', str)
    def setFormInputFile(self, el, fileTag):
        self.m_page.m_nextFileTag = fileTag
        el.evaluateJavaScript('''(function(target){
                              var evt = document.createEvent('MouseEvents');
                              evt.initMouseEvent("click", true, true, window,
                              0, 0, 0, 0, 0, false, false, false, false, 0, null);
                              target.dispatchEvent(evt);})(this);''')

    @pyqtSlot(int)
    def sleep(self, ms):
        startTime = QTime.currentTime()
        while True:
            QApplication.processEvents(QEventLoop.AllEvents, 25)
            if startTime.msecsTo(QTime.currentTime()) > ms:
                break
            usleep(0.005)

    @pyqtProperty(str)
    def state(self):
        return self.m_state

    @state.setter
    def state(self, value):
        self.m_state = value

    @pyqtProperty(str)
    def userAgent(self):
        return self.m_page.m_userAgent

    @userAgent.setter
    def userAgent(self, ua):
        self.m_page.m_userAgent = ua

    @pyqtSlot(str, result='QVariant')
    @pyqtSlot(int, result='QVariant')
    @pyqtSlot(str, 'QVariant')
    @pyqtSlot(int, 'QVariant')
    def ctx(self, name, value=None):
        if not value:
            return self.m_var.get(name)
        self.m_var[name] = value

    @pyqtProperty('QVariantMap')
    def version(self):
        version = {
            'major': version_major,
            'minor': version_minor,
            'patch': version_patch
        }
        return version

    @pyqtProperty('QVariantMap')
    def viewportSize(self):
        size = self.m_page.viewportSize()
        result = {'width': size.width(), 'height': size.height()}
        return result

    @viewportSize.setter
    def viewportSize(self, size):
        names = ('width', 'height')
        for item in names:
            try:
                globals()[item] = int(size[item])
                if globals()[item] < 0:
                    globals()[item] = 0
            except KeyError:
                globals()[item] = getattr(self.m_page.viewportSize(), item)()

        self.m_page.setViewportSize(QSize(width, height))

    do_action('Phantom', Bunch(locals()))
示例#22
0
class ImageView(QtGui.QScrollArea):
    def __init__(self,parent):
        '''constructor of ImageView'''
        QtGui.QWidget.__init__(self)
        self.parent = parent
        self.selection = None
        self.image = None
        self.zoom = 100
        #if (!ic) ic=new IconCache();
        self.setFocusPolicy(QtCore.Qt.WheelFocus)
        self.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
        self.d =ImageViewPrivate(self)
        self.setWidget(self.d)
        self.setWidgetResizable(True)
        self.setBackgroundRole(QtGui.QPalette.Dark)
        self.setMouseTracking(True)
        self.mouse_x = -1
        self.mouse_y = -1
        self.mouse_ex = 0
        self.mouse_ey = 0
        self.data_rect = QtCore.QRect(0,0,1,1)
        #self.setMode(view)
     
    def saveImage(self,name):
        '''Save image in viewer under given name'''
        if self.image: 
            save_success = self.image.saveImage(name)
            if save_success:
                return True 
            else :
                return False
        else:
            print("[ImageView][Save Image Failed]:No image to save")
            return False
        
    def selectionFromImageCoords(self,selectionRect):
        '''Return selection rectangle converted from image coordinate system to window coordinate syste'''
        x= (selectionRect.left())*self.data_rect.width()/self.image.x()+self.data_rect.left()
        y= (selectionRect.top())*self.data_rect.height()/self.image.y()+self.data_rect.top()
        xr=(selectionRect.right()+1)*self.data_rect.width()/self.image.x()+self.data_rect.left()-1
        yr=(selectionRect.bottom()+1)*self.data_rect.height()/self.image.y()+self.data_rect.top()-1
        return QRect(x,y,xr,yr)
    
    def selectPointToImageCoords(self,point):
        '''Return selected point converted to image coordinate system'''
        dw=self.data_rect.width()
        dh=self.data_rect.height()
        x=int(floor((point.x()-self.data_rect.left())* self.image.x()/dw))-1
        y=int(floor((point.y()-self.data_rect.top())* self.image.y()/dh))-1
        return QPoint(x,y)
        
    def selectionToImageCoords(self,selectionRect):
        '''Return selection rectangle converted to image coordinate system@param selectionRect rectangle to convert'''
        dw=self.data_rect.width()
        dh=self.data_rect.height()
        x=int(floor((selectionRect.left()-self.data_rect.left())* self.image.x()/dw))
        y=int(floor((selectionRect.top()-self.data_rect.top())* self.image.y()/dh))
        xr=int(floor((selectionRect.right()-self.data_rect.left()) * self.image.x()/dw))
        yr=int(floor((selectionRect.bottom()-self.data_rect.top())*self.image.y()/dh))
        #print(QRect(x,y,xr,yr))
        return QRect(x,y,xr,yr)

    def setDataRect(self):
        '''Update data_rect'''
        #Get image dimensions
        dx=self.image.x()
        dy=self.image.y()
        if dx == 0 or dy == 0:
            print("Null Image")
            return
        #printInfo ="setDataRect %f_%f" %(dx,dy)
        #print(printInfo)
        #Get image screen dimensions (may differ if zoom is set)
        screen_dx=dx*self.zoom/100.0
        screen_dy=dy*self.zoom/100.0
        x=self.d.width()
        y=self.d.height()
        wx=x
        wy=dy*wx/dx
        if wy > y:
            wy=y
            wx=dx*wy/dy
        if self.isSet("center"):#居中画图而不拉伸
            self.data_rect=QRect(max((x-screen_dx)/2,0),max((y-screen_dy)/2,0),screen_dx,screen_dy)
        else:
            self.data_rect=QRect(max((x-wx)/2,0),max((y-wy)/2,0),wx,wy)
            
    def getSelection(self):
        '''return the selection rectangle from (x0,y0,x1,y1) to (x,y,width,height)'''
        if not self.selection:
            return None
        width = self.selection.width() - self.selection.left()
        height = self.selection.height() - self.selection.top()
        return QRect(self.selection.left(),self.selection.top(),width,height)
    
    def repaint(self,x,y,p,src):
        '''Repaint the image using given painter and size of output window
        @param x width of output window
        @param y height of output window
        @param p QPainter to use
        @param src Which part of image to repaint (when not in fullscreen)'''
        '''TypeError: QPainter.setRenderHint(QPainter.RenderHint, bool on=True): first argument of unbound method must have type 'QPainter'''
        #p.setRenderHint(QtGui.QPainter.SmoothPixmapTransform,True)
        p.setRenderHint(QtGui.QPainter.Antialiasing,True)
        p.setPen = Qt.blue
        black = QtGui.QColor(0,0,0)
        if not self.image:
            #No image is loaded
            p.drawRect(0,0,x-1,y-1)
            p.fillRect(1,1,x-2,y-2,black)
            p.drawText(0,0,x,y,Qt.AlignVCenter | Qt.AlignCenter,QString(NOIMAGELOAD))
            return

        if self.image.x<=0 and self.image.y()<=0:
            #Invalid image is loaded
            p.drawRect(0,0,x-1,y-1)
            p.fillRect(1,1,x-2,y-2,black)
            p.drawText(0,0,x,y,Qt.AlignVCenter | Qt.AlignCenter,QString("Invalid image"))
            return
        #Rectangle specifying entire window
        rect = QRect(0,0,x,y)
        #Determine target rectangle at screen
        #Update data rectangle for mouse navigation
        self.setDataRect()
        #Repaint stripes around image if needed
        if self.data_rect.left() > src.left():
            #repaint stripe on left
            tmp = QRect(0,0,self.data_rect.left(),y)
            p.fillRect(tmp & src,black)
        if self.data_rect.right()<src.right():
            #repaint on right
            tmp = QRect(self.data_rect.right(),0,x-self.data_rect.right(),y)
            p.fillRect(tmp & src,black)
        if self.data_rect.top()>src.top():
            #repaint on top
            tmp = QRect(self.data_rect.left(),0,self.data_rect.width(),self.data_rect.top())
            p.fillRect(tmp & src,black)
        if self.data_rect.bottom()<src.bottom():
            #repaint on bottom
            tmp = QRect(self.data_rect.left(),self.data_rect.bottom(),self.data_rect.width(),y-self.data_rect.bottom())
            p.fillRect(tmp & src,black)
        #Show scaled to window
        #Set target to data rectangle
        target=self.data_rect
        #Draw entire image scaled
        source = QRect(0,0,self.image.x(),self.image.y())
        self.image.draw(p,source,target)#Stop Here
          
    def getImage(self):
        ''' Return image shown in the widget (or NULL if nothing is shown)
        If the image is modified, update() should be called to redraw the new image'''
        return self.image
    
    def moveRubberBand(self,newGeom):
        '''Move the rubberband after updating the zoom level'''
        if not rb: return
        self.rb.setGeometry(newGeom)  
        
    def rectCheck(self):
        ''' Called when selection is finished (on releasing the mouse button) and on start'''
        pass
        #if self.selection.isValid():
             ##"Snap rect to grid"
            #self.setDataRect()
            #self.d.moveRubberBand(self.selectionFromImageCoords(self.selection))
   

    def cancelRect(self):
        self.d.cancelRect()
        self.rectCheck()
    
    def flushImage(self):
        '''Use after new image is loaded to flush/redraw various data'''
        self.update()
        self.cancelRect()
        #emit zoomChanged(zoom);
        #emit info("");


    def loadImage(self,name):
        '''Open image with given name in viewer'''    
        if self.image: del self.image
        self.image = Image(name)
        if not self.image:
            return False
        
        self.flushImage()
        return True
        
    
    def sizeHint(self):
        '''sizeHint() 方法给出一个推荐的大小。这里我们重写这个方法'''
        '''Return size hint for the widget'''
        if self.image:
            #Size of image
            return QtCore.QSize(self.image.x(),self.image.y())
        else:
            #Some default,very Important
            return QtCore.QSize(840,630)

    #Load state of specified optional feature from settings (default value is false)
    def isSet(self,name):
        globalSettings = QtCore.QSettings("JiZhe","CTAnalysis")
        return globalSettings.value("view/"+name).toBool()
    
    #Return minimum size neede to display the widget
    def minSize(self):
        if self.image :
        #if self.image and self.isSet("scale"):
            #Size of image
            x = self.image.x()
            y = self.image.y()
            x = x * self.zoom / 100.0
            y = y * self.zoom / 100.0
            return QtCore.QSize(x,y)
        else:
            #No minimum
            return QtCore.QSize(0,0)
        

    #Change internal widget's minimum size.Schedule this and internal widget to repaint.
    def update(self):
        self.d.setMinimumSize(self.minSize())
        self.d.update()
        #QtGui.QScrollArea.update()?

    def mouseCoordEvent(self,e):
        pass
    
    def selPoint(self,point):
        '''Select a point and then do region grow'''
        region_grow_seed_point = self.selectPointToImageCoords(point)
        self.parent.region_grow(region_grow_seed_point)
    
    def selRect(self,r):
        '''Update selection rectangle position'''
        if r.width()<=0 or r.height()<=0:
            self.selection=QRect()
        else:
            self.selection = self.selectionToImageCoords(r)
            #print(self.selection)
            x1=self.selection.left()
            x2=self.selection.right()
            y1=self.selection.top()
            y2=self.selection.bottom()
            w=self.selection.width()
            h=self.selection.height()
示例#23
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)
示例#24
0
class BrushingModel(QObject):
    brushSizeChanged     = pyqtSignal(int)
    brushColorChanged    = pyqtSignal(QColor)
    brushStrokeAvailable = pyqtSignal(QPointF, object)
    drawnNumberChanged   = pyqtSignal(int)
    
    minBrushSize       = 1
    maxBrushSize       = 61
    defaultBrushSize   = 3
    defaultDrawnNumber = 1
    defaultColor       = Qt.white
    erasingColor       = Qt.black
    erasingNumber      = 100
    
    def __init__(self):
        QObject.__init__(self)
        self.sliceRect = None
        self.bb    = QRect() #bounding box enclosing the drawing
        self.brushSize = self.defaultBrushSize
        self.drawColor = self.defaultColor
        self._temp_color = None
        self._temp_number = None
        self.drawnNumber = self.defaultDrawnNumber

        self.pos = None
        self.erasing = False
        
        self.drawOnto = None
        
        #an empty scene, where we add all drawn line segments
        #a QGraphicsLineItem, and which we can use to then
        #render to an image
        self.scene = QGraphicsScene()

    def toggleErase(self):
        self.erasing = not(self.erasing)
        if self.erasing:
            self.setErasing()
        else:
            self.disableErasing()

    def setErasing(self):
        self.erasing = True
        self._temp_color = self.drawColor
        self._temp_number = self.drawnNumber
        self.setBrushColor(self.erasingColor)
        self.brushColorChanged.emit(self.erasingColor)
        self.setDrawnNumber(self.erasingNumber)
    
    def disableErasing(self):
        self.erasing = False
        self.setBrushColor(self._temp_color)
        self.brushColorChanged.emit(self.drawColor)
        self.setDrawnNumber(self._temp_number)

    def setBrushSize(self, size):
        self.brushSize = size
        self.brushSizeChanged.emit(self.brushSize)
    
    def setDrawnNumber(self, num):
        print "Setting Drawnnumer", num
        self.drawnNumber = num
        self.drawnNumberChanged.emit(num)
        
    def getBrushSize(self):
        return self.brushSize
    
    def brushSmaller(self):
        b = self.brushSize
        if b > self.minBrushSize:
            self.setBrushSize(b-1)
        
    def brushBigger(self):
        b = self.brushSize
        if self.brushSize < self.maxBrushSize:
            self.setBrushSize(b+1)
        
    def setBrushColor(self, color):
        self.drawColor = color
        self.brushColorChanged.emit(self.drawColor)
    
    def beginDrawing(self, pos, sliceRect):
        self.sliceRect = sliceRect
        self.scene.clear()
        self.bb = QRect()
        self.pos = QPointF(pos.x()+0.0001, pos.y()+0.0001)
        line = self.moveTo(pos)
        return line

    def endDrawing(self, pos):
        self.moveTo(pos)

        tempi = QImage(QSize(self.bb.width(), self.bb.height()), QImage.Format_ARGB32_Premultiplied) #TODO: format
        tempi.fill(0)
        painter = QPainter(tempi)
        self.scene.render(painter, target=QRectF(), source=QRectF(QPointF(self.bb.x(), self.bb.y()), QSizeF(self.bb.width(), self.bb.height())))
        painter.end()
        
        ndarr = qimage2ndarray.rgb_view(tempi)[:,:,0]
        labels = numpy.where(ndarr>0,numpy.uint8(self.drawnNumber),numpy.uint8(0))
        labels = labels.swapaxes(0,1)
        assert labels.shape[0] == self.bb.width()
        assert labels.shape[1] == self.bb.height()

        self.brushStrokeAvailable.emit(QPointF(self.bb.x(), self.bb.y()), labels)

    def dumpDraw(self, pos):
        res = self.endDrawing(pos)
        self.beginDrawing(pos, self.sliceRect)
        return res

    def moveTo(self, pos):
        oldX, oldY = self.pos.x(), self.pos.y()
        x,y = pos.x(), pos.y()
        
        #print "BrushingModel.moveTo(pos=%r)" % (pos) 
        line = QGraphicsLineItem(oldX, oldY, x, y)
        line.setPen(QPen( QBrush(Qt.white), self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        self.scene.addItem(line)

        #update bounding Box 
        if not self.bb.isValid():
            self.bb = QRect(QPoint(x,y), QSize(1,1))
        #grow bounding box
        self.bb.setLeft(  min(self.bb.left(),   max(0,                   x-self.brushSize/2-1) ) )
        self.bb.setRight( max(self.bb.right(),  min(self.sliceRect[0]-1, x+self.brushSize/2+1) ) )
        self.bb.setTop(   min(self.bb.top(),    max(0,                   y-self.brushSize/2-1) ) )
        self.bb.setBottom(max(self.bb.bottom(), min(self.sliceRect[1]-1, y+self.brushSize/2+1) ) )
        
        #update/move position
        self.pos = pos
示例#25
0
    def paintInterface(self,painter,option,contentRect):
        # define some parameters
        padding = contentRect.height()/50
        fontSize = contentRect.height()/35
        
        txtFieldWidth = contentRect.width()/2 - 2*padding
        txtFieldHeight = contentRect.height()/20
        
        
        current_img_width = contentRect.height()/3
        current_img_height = contentRect.height()/3
        
        whiteBrush10p = QBrush(QColor.fromCmyk(0,0,0,0,5))
        whiteBrush20p = QBrush(QColor.fromCmyk(0,0,0,0,40))
        
        fcRectWidth = (contentRect.width() - 4 * padding)/3
        fcRectHeight = current_img_height + 2 * padding + 2 * txtFieldHeight
        
        
        
        textColor = Plasma.Theme.defaultTheme().color(Plasma.Theme.TextColor)
        bgColor = Plasma.Theme.defaultTheme().color(Plasma.Theme.BackgroundColor)
        textFont = Plasma.Theme.defaultTheme().font(Plasma.Theme.DefaultFont)
        
        # create text rects
        rect_text_location = QRect(contentRect.left() + padding ,contentRect.top() + 2*padding, 2*txtFieldWidth, txtFieldHeight)
        rect_text_temperature = QRect(contentRect.left() + padding ,contentRect.top() + 3* padding + 1 * txtFieldHeight , txtFieldWidth, txtFieldHeight)
        rect_text_condition = QRect(contentRect.left() + padding ,contentRect.top() + 4* padding + 2 * txtFieldHeight , txtFieldWidth, txtFieldHeight)
        rect_text_humidity = QRect(contentRect.left() + padding ,contentRect.top() + 5* padding + 3 * txtFieldHeight , txtFieldWidth, txtFieldHeight)
        rect_text_wind = QRect(contentRect.left() + padding , contentRect.top() + 6* padding + 4 * txtFieldHeight , txtFieldWidth, txtFieldHeight)
        
        painter.save()
        
        painter.setPen(textColor)
        textFont.setPointSize(fontSize)
        painter.setFont(textFont)
        
        painter.drawText(rect_text_location,Qt.Alignment(Qt.AlignLeft),"Location: " + self._weather.location)
        painter.drawText(rect_text_temperature,Qt.Alignment(Qt.AlignLeft),"Temperature: " + self._weather.current_temperature)
        painter.drawText(rect_text_condition,Qt.Alignment(Qt.AlignLeft),"Condition: " + self._weather.current_condition)
        painter.drawText(rect_text_humidity,Qt.Alignment(Qt.AlignLeft),self._weather.current_humidity)
        painter.drawText(rect_text_wind,Qt.Alignment(Qt.AlignLeft),self._weather.current_wind)
        
        svg_current = Plasma.Svg(self)
        curImgName = self._mapper.getMappedImageName(self._weather.current_condition)
        svg_current.setImagePath(self._image_prefix + curImgName)
        svg_current.resize(current_img_width,current_img_height)
        xOffset = contentRect.width()/2 + contentRect.width()/2 - current_img_width
        yOffset = contentRect.top() + txtFieldHeight + 2 * padding
        svg_current.paint(painter,contentRect.left() + xOffset, yOffset)
        
        # create forecast blocks
        fc_rect1 = QRect(contentRect.left() + padding, contentRect.bottom() - fcRectHeight, fcRectWidth, fcRectHeight)
        fc_rect2 = QRect(fc_rect1.right() + padding, contentRect.bottom() - fcRectHeight, fcRectWidth, fcRectHeight)
        fc_rect3 = QRect(fc_rect2.right() + padding, contentRect.bottom() - fcRectHeight, fcRectWidth, fcRectHeight)
        
        painter.setPen(bgColor)
        painter.setBrush(whiteBrush20p)
        painter.drawRect(fc_rect1)
        painter.drawRect(fc_rect3)
        painter.setBrush(whiteBrush10p)
        painter.drawRect(fc_rect2)
        
        # text rects for day list
        rect_text_dl1 = QRect(fc_rect1.left(),fc_rect1.top() + padding,fcRectWidth,txtFieldHeight)
        rect_text_dl2 = QRect(fc_rect2.left(),fc_rect2.top() + padding,fcRectWidth,txtFieldHeight)
        rect_text_dl3 = QRect(fc_rect3.left(),fc_rect3.top() + padding,fcRectWidth,txtFieldHeight)
        
        painter.setPen(textColor)
        painter.drawText(rect_text_dl1,Qt.Alignment(Qt.AlignCenter),self._weather.fc_dl[0])
        painter.drawText(rect_text_dl2,Qt.Alignment(Qt.AlignCenter),self._weather.fc_dl[1])
        painter.drawText(rect_text_dl3,Qt.Alignment(Qt.AlignCenter),self._weather.fc_dl[2])
        
        fc_svg1 = Plasma.Svg()
        fc_svg2 = Plasma.Svg()
        fc_svg3 = Plasma.Svg()
        
        fc_svg1.setImagePath(self._image_prefix + self._mapper.getMappedImageName(self._weather.fc_conditions[0]))
        fc_svg2.setImagePath(self._image_prefix + self._mapper.getMappedImageName(self._weather.fc_conditions[1]))
        fc_svg3.setImagePath(self._image_prefix + self._mapper.getMappedImageName(self._weather.fc_conditions[2]))
       

        fc_svg1.resize(current_img_width,current_img_height)
        fc_svg2.resize(current_img_width,current_img_height)
        fc_svg3.resize(current_img_width,current_img_height)
        
        xOffSet = fc_rect1.left() + (fc_rect1.width() - current_img_width)/2
        fc_svg1.paint(painter,xOffSet, fc_rect1.top() + txtFieldHeight + 2* padding)
        xOffSet = fc_rect2.left() + (fc_rect2.width() - current_img_width)/2
        fc_svg2.paint(painter,xOffSet, fc_rect2.top() + txtFieldHeight + 2* padding)
        xOffSet = fc_rect3.left() + (fc_rect3.width() - current_img_width)/2
        fc_svg3.paint(painter,xOffSet, fc_rect3.top() + txtFieldHeight + 2* padding)
        
        # text rects for high/low temperatures
        
        rect_text_temp1 = QRect(fc_rect1.left(),fc_rect1.bottom() - 3 * padding,fcRectWidth,txtFieldHeight)
        rect_text_temp2 = QRect(fc_rect2.left(),fc_rect2.bottom() - 3 * padding,fcRectWidth,txtFieldHeight)
        rect_text_temp3 = QRect(fc_rect3.left(),fc_rect3.bottom() - 3 * padding,fcRectWidth,txtFieldHeight)
        painter.setPen(textColor)
        painter.drawText(rect_text_temp1,Qt.Alignment(Qt.AlignCenter),self._weather.fc_low_high[0])
        painter.drawText(rect_text_temp2,Qt.Alignment(Qt.AlignCenter),self._weather.fc_low_high[1])
        painter.drawText(rect_text_temp3,Qt.Alignment(Qt.AlignCenter),self._weather.fc_low_high[2])
        
        
        painter.restore()
示例#26
0
class BrushingModel(QObject):
    brushSizeChanged = pyqtSignal(int)
    brushColorChanged = pyqtSignal(QColor)
    brushStrokeAvailable = pyqtSignal(QPointF, object)
    drawnNumberChanged = pyqtSignal(int)

    minBrushSize = 1
    maxBrushSize = 61
    defaultBrushSize = 3
    defaultDrawnNumber = 1
    defaultColor = Qt.white
    erasingColor = Qt.black
    erasingNumber = 100

    def __init__(self, parent=None):
        QObject.__init__(self, parent=parent)
        self.sliceRect = None
        self.bb = QRect()  #bounding box enclosing the drawing
        self.brushSize = self.defaultBrushSize
        self.drawColor = self.defaultColor
        self._temp_color = None
        self._temp_number = None
        self.drawnNumber = self.defaultDrawnNumber

        self.pos = None
        self.erasing = False
        self._hasMoved = False

        self.drawOnto = None

        #an empty scene, where we add all drawn line segments
        #a QGraphicsLineItem, and which we can use to then
        #render to an image
        self.scene = QGraphicsScene()

    def toggleErase(self):
        self.erasing = not (self.erasing)
        if self.erasing:
            self.setErasing()
        else:
            self.disableErasing()

    def setErasing(self):
        self.erasing = True
        self._temp_color = self.drawColor
        self._temp_number = self.drawnNumber
        self.setBrushColor(self.erasingColor)
        self.brushColorChanged.emit(self.erasingColor)
        self.setDrawnNumber(self.erasingNumber)

    def disableErasing(self):
        self.erasing = False
        self.setBrushColor(self._temp_color)
        self.brushColorChanged.emit(self.drawColor)
        self.setDrawnNumber(self._temp_number)

    def setBrushSize(self, size):
        self.brushSize = size
        self.brushSizeChanged.emit(self.brushSize)

    def setDrawnNumber(self, num):
        self.drawnNumber = num
        self.drawnNumberChanged.emit(num)

    def getBrushSize(self):
        return self.brushSize

    def brushSmaller(self):
        b = self.brushSize
        if b > self.minBrushSize:
            self.setBrushSize(b - 1)

    def brushBigger(self):
        b = self.brushSize
        if self.brushSize < self.maxBrushSize:
            self.setBrushSize(b + 1)

    def setBrushColor(self, color):
        self.drawColor = color
        self.brushColorChanged.emit(self.drawColor)

    def beginDrawing(self, pos, sliceRect):
        '''

        pos -- QPointF-like
        '''
        self.sliceRect = sliceRect
        self.scene.clear()
        self.bb = QRect()
        self.pos = QPointF(pos.x(), pos.y())
        self._hasMoved = False

    def endDrawing(self, pos):
        has_moved = self._hasMoved  # _hasMoved will change after calling moveTo
        if has_moved:
            self.moveTo(pos)
        else:
            assert (self.pos == pos)
            self.moveTo(QPointF(pos.x() + 0.0001,
                                pos.y() + 0.0001))  # move a little

        # Qt seems to use strange rules for determining which pixels to set when rendering a brush stroke to a QImage.
        # We seem to get better results if we do the following:
        # 1) Slightly offset the source window because apparently there is a small shift in the data
        # 2) Render the scene to an image that is MUCH larger than the scene resolution (4x by 4x)
        # 3) Downsample each 4x4 patch from the large image back to a single pixel in the final image,
        #     applying some threshold to determine if the final pixel is on or off.

        tempi = QImage(QSize(4 * self.bb.width(), 4 * self.bb.height()),
                       QImage.Format_ARGB32_Premultiplied)  #TODO: format
        tempi.fill(0)
        painter = QPainter(tempi)
        # Offset the source window.  At first I thought the right offset was 0.5, because
        #  that would seem to make sure points are rounded to pixel CENTERS, but
        #  experimentation indicates that 0.25 is slightly better for some reason...
        source_rect = QRectF(QPointF(self.bb.x() + 0.25,
                                     self.bb.y() + 0.25),
                             QSizeF(self.bb.width(), self.bb.height()))
        target_rect = QRectF(QPointF(0, 0),
                             QSizeF(4 * self.bb.width(), 4 * self.bb.height()))
        self.scene.render(painter, target=target_rect, source=source_rect)
        painter.end()

        # Now downsample: convert each 4x4 patch into a single pixel by summing and dividing
        ndarr = qimage2ndarray.rgb_view(tempi)[:, :, 0].astype(int)
        ndarr = ndarr.reshape((ndarr.shape[0], ) + (ndarr.shape[1] // 4, ) +
                              (4, ))
        ndarr = ndarr.sum(axis=-1)
        ndarr = ndarr.transpose()
        ndarr = ndarr.reshape((ndarr.shape[0], ) + (ndarr.shape[1] // 4, ) +
                              (4, ))
        ndarr = ndarr.sum(axis=-1)
        ndarr = ndarr.transpose()
        ndarr //= 4 * 4

        downsample_threshold = (7. / 16) * 255
        labels = numpy.where(ndarr >= downsample_threshold,
                             numpy.uint8(self.drawnNumber), numpy.uint8(0))
        labels = labels.swapaxes(0, 1)
        assert labels.shape[0] == self.bb.width()
        assert labels.shape[1] == self.bb.height()

        ##
        ## ensure that at least one pixel is label when the brush size is 1
        ##
        ## this happens when the user just clicked without moving
        ## in that case the lineitem will be so tiny, that it won't be rendered
        ## into a single pixel by the code above
        if not has_moved and self.brushSize <= 1 and numpy.count_nonzero(
                labels) == 0:
            labels[labels.shape[0] // 2,
                   labels.shape[1] // 2] = self.drawnNumber

        self.brushStrokeAvailable.emit(QPointF(self.bb.x(), self.bb.y()),
                                       labels)

    def dumpDraw(self, pos):
        res = self.endDrawing(pos)
        self.beginDrawing(pos, self.sliceRect)
        return res

    def moveTo(self, pos):
        #data coordinates
        oldX, oldY = self.pos.x(), self.pos.y()
        x, y = pos.x(), pos.y()

        line = QGraphicsLineItem(oldX, oldY, x, y)
        line.setPen(
            QPen(QBrush(Qt.white), self.brushSize, Qt.SolidLine, Qt.RoundCap,
                 Qt.RoundJoin))
        self.scene.addItem(line)
        self._hasMoved = True

        #update bounding Box
        if not self.bb.isValid():
            self.bb = QRect(QPoint(oldX, oldY), QSize(1, 1))
        #grow bounding box
        self.bb.setLeft(
            min(self.bb.left(), max(0, x - self.brushSize // 2 - 1)))
        self.bb.setRight(
            max(self.bb.right(),
                min(self.sliceRect[0] - 1, x + self.brushSize // 2 + 1)))
        self.bb.setTop(min(self.bb.top(), max(0, y - self.brushSize // 2 - 1)))
        self.bb.setBottom(
            max(self.bb.bottom(),
                min(self.sliceRect[1] - 1, y + self.brushSize // 2 + 1)))

        #update/move position
        self.pos = pos
示例#27
0
    def paint (self, painter, option, index):
        '''
        QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index
        '''

        orntnDelegate = self.chooseOrientationDelegate()
        
        painter.save()

        if option.state & QStyle.State_Selected:
            borderPen = QPen(Qt.blue)
            borderPen.setWidth(3)
            nameBgColors = [Qt.white, Qt.yellow]
        else:
            borderPen = QPen(Qt.lightGray)
            lightBlue = QColor(0, 0, 255).lighter(180)
            nameBgColors = [Qt.white, lightBlue]

        # Set default font and color
        itemRect = option.rect
        painter.fillRect(itemRect, Qt.white)
        painter.setFont(orntnDelegate.getNameFont())
        painter.setPen(Qt.black)

        m = index.model()

        ticker = m.data(index, PositionsModel.ROLE_TICKER)
        companyName = m.data(index, Qt.DisplayRole)
        line2 = m.data(index, PositionsModel.ROLE_CURRENT_PRICE)
        line3 = m.data(index, PositionsModel.ROLE_CHANGE)

        fontMetricsCompanyName = QFontMetrics(orntnDelegate.getNameFont())
        fontMetricsTicker = QFontMetrics(orntnDelegate.getTickerFont())
        fontMetricsCurrentPrice = QFontMetrics(orntnDelegate.getCurrentPriceFont())
        fontMetricsChange = QFontMetrics(orntnDelegate.getChangeFont())
        lineSp1 = fontMetricsCompanyName.lineSpacing()
        lineSp2 = fontMetricsCurrentPrice.lineSpacing()
        lineSp3 = fontMetricsChange.lineSpacing()

        # Company Name    (EXCHANGE:SYMBOL)
        textRectShade = QRect(itemRect.left(),
                              itemRect.top(),
                              itemRect.width(),
                              self.MARGIN + lineSp1 + self.MARGIN)
        gradient = self.createLinearGradient(textRectShade,
                                             nameBgColors[0],
                                             nameBgColors[1])
        painter.fillRect(textRectShade, gradient)
        textRect = QRect(itemRect.left() + self.MARGIN, itemRect.top() + self.MARGIN,
                         itemRect.width() - 2 * self.MARGIN, lineSp1 + self.MARGIN)
        painter.setFont(orntnDelegate.getNameFont())
        # Shorten the company name such that long company names are not written on top of the ticker
        tickerTextW = fontMetricsTicker.width(ticker)
        companyNameTextW = textRect.width() - tickerTextW - 2 * self.MARGIN
        companyName = fontMetricsCompanyName.elidedText(companyName, Qt.ElideRight, companyNameTextW)
        painter.drawText(textRect, Qt.AlignVCenter | Qt.AlignLeft, companyName)
        painter.setFont(orntnDelegate.getTickerFont())
        painter.drawText(textRect, Qt.AlignVCenter | Qt.AlignRight, "(%s)" % (ticker))

        # Current price
        painter.setFont(orntnDelegate.getCurrentPriceFont())
        textRect.adjust(0, lineSp1 + self.MARGIN, 0, lineSp2 + self.MARGIN)
        painter.drawText(textRect, Qt.AlignTop | Qt.AlignLeft, line2)

        rightColRect = QRect(textRect.left(), textRect.top(),
                             textRect.width(), textRect.height())

        # Change
        ccol = m.data(index, PositionsModel.ROLE_CHANGE_COLOR)
        if ccol == "chg":
            painter.setPen(QPen(Qt.darkGreen))
        elif ccol == "chr":
            painter.setPen(QPen(Qt.red))
        painter.setFont(orntnDelegate.getChangeFont())
        textRect.adjust(0, lineSp2 + self.MARGIN, 0, lineSp3 + self.MARGIN)
        painter.drawText(textRect, Qt.AlignTop | Qt.AlignLeft, line3)

        '''
        Right Column
        '''

        orntnDelegate.paintRightCol(index, painter, rightColRect)

        painter.setPen(borderPen)
        painter.drawRect(itemRect)

        painter.restore()
示例#28
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)
示例#29
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)
示例#30
0
def paintVerticalCell(self, painter: QPainter, hv: QHeaderView, cellIndex: QModelIndex, leafIndex: QModelIndex, logicalLeafIndex: int, styleOptions: QStyleOptionHeader, sectionRect: QRect, left: int):
uniopt = QStyleOptionHeader(styleOptions)
self.setForegroundBrush(uniopt, cellIndex)
self.setBackgroundBrush(uniopt, cellIndex)
width=self.cellSize(cellIndex, hv, uniopt).width()
if cellIndex==leafIndex:
    width=sectionRect.width()-left
top=self.currentCellLeft(cellIndex, leafIndex, logicalLeafIndex, sectionRect.top(), hv)
height=self.currentCellWidth(cellIndex, leafIndex, logicalLeafIndex, hv)
r = QRect(left, top, width, height)
uniopt.text = cellIndex.data(Qt.DisplayRole)
painter.save()
uniopt.rect = r
if cellIndex.data(Qt.UserRole):
    hv.style().drawControl(QStyle.CE_HeaderSection, uniopt, painter, hv)
    m = QMatrix()
    m.rotate(-90)
    painter.setWorldMatrix(m, True)
    new_r = QRect(0, 0,  r.height(), r.width())
    new_r.moveCenter(QPoint(-r.center().y(), r.center().x()))
    uniopt.rect = new_r
    hv.style().drawControl(QStyle.CE_HeaderLabel, uniopt, painter, hv)
else:
    hv.style().drawControl(QStyle.CE_Header, uniopt, painter, hv)
painter.restore()
return left+width

def paintVerticalSection(self, painter: QPainter, sectionRect: QRect, logicalLeafIndex: int, hv: QHeaderView, styleOptions: QStyleOptionHeader, leafIndex: QModelIndex):
oldBO = painter.brushOrigin()
left = sectionRect.x()
indexes = QModelIndexList(self.parentIndexes(leafIndex))
for i in range(indexes.size()):
    realStyleOptions = QStyleOptionHeader(styleOptions)
    if i<indexes.size()-1 and (realStyleOptions.state&QStyle.State_Sunken or realStyleOptions.state&QStyle.State_On):
        t = QStyle.State(QStyle.State_Sunken | QStyle.State_On)
        realStyleOptions.state = realStyleOptions.state&~t #FIXME: parent items are not highlighted
    left=self.paintVerticalCell(painter, hv, indexes[i], leafIndex, logicalLeafIndex, realStyleOptions, sectionRect, left)
painter.setBrushOrigin(oldBO)

def __init__(self, orientation: Qt.Orientation, parent: QWidget):
super().__init__(orientation, parent)
self._pd = self.private_data()
self.sectionResized.connect(self.on_sectionResized)
self.setHighlightSections(self.options.get("highlightSections"))
self.setClickable(self.options.get("clickable"))
self.show() #force to be visible
getattr(parent, "set%sHeader"%("Horizontal", "Vertical")[orientation!=Qt.Horizontal])(self)
self.sectionMoved.connect(self.on_sectionMoved)

def on_sectionMoved(self, logicalIndex, oldVisualIndex, newVisualIndex):
    view, model = self.parent(), self.parent().model()
    if not hasattr(model, "reorder"):
        return #reorder underlying data of models with /reorder/ def only
    if getattr(self, "manual_move", False):
        self.manual_move=False
        return
    self.manual_move=True
    self.moveSection(newVisualIndex, oldVisualIndex) #cancel move
    if model.reorder(oldVisualIndex, newVisualIndex, self.orientation()):
        #Reorder column widths / row heights
        horizontal = self.orientation()==Qt.Horizontal
        itemSize = (view.rowHeight, view.columnWidth)[horizontal]
        setItemSize = (view.setRowHeight, view.setColumnWidth)[horizontal]
        rng = sorted((oldVisualIndex, newVisualIndex))
        options = [(itemSize(i), i) for i in range(rng[0], rng[1]+1)]
        options.insert(newVisualIndex-rng[0], options.pop(oldVisualIndex-rng[0]))
        for i, col in enumerate(range(rng[0], rng[1]+1)):
            setItemSize(col, options[i][0])
        getattr(view, "select"+("Row", "Column")[horizontal])(newVisualIndex) #FIXME: don't select if sorting is enable?
        if self.isSortIndicatorShown():
            sortIndIndex = next((i for i, o in enumerate(options) if o[1]==self.sortIndicatorSection()), None)
            if sortIndIndex is not None: #sort indicator is among sections being reordered
                self.setSortIndicator(sortIndIndex+rng[0], self.sortIndicatorOrder()) #FIXME: does unnecessary sorting
        model.layoutChanged.emit() #update view

def styleOptionForCell(self, logicalInd: int)->QStyleOptionHeader:
opt = QStyleOptionHeader()
self.initStyleOption(opt)
if self.isSortIndicatorShown() and self.sortIndicatorSection()==logicalInd:
    opt.sortIndicator = (QStyleOptionHeader.SortUp, QStyleOptionHeader.SortDown)[self.sortIndicatorOrder()==Qt.AscendingOrder]
if self.window().isActiveWindow():
    opt.state = opt.state|QStyle.State_Active
opt.textAlignment = Qt.AlignCenter
opt.iconAlignment = Qt.AlignVCenter
opt.section = logicalInd
visual = self.visualIndex(logicalInd)
if self.count() == 1:
    opt.position = QStyleOptionHeader.OnlyOneSection
else:
    if visual == 0:
        opt.position = QStyleOptionHeader.Beginning
    else:
        opt.position = QStyleOptionHeader.End if visual==self.count()-1 else QStyleOptionHeader.Middle
if self.isClickable():
    #            if logicalIndex == d.hover:
    #            ...
    if self.highlightSections() and self.selectionModel():
        if self.orientation()==Qt.Horizontal:
            if self.selectionModel().columnIntersectsSelection(logicalInd, self.rootIndex()):
                opt.state = opt.state|QStyle.State_On
            if self.selectionModel().isColumnSelected(logicalInd, self.rootIndex()):
                opt.state = opt.state|QStyle.State_Sunken
        else:
            if self.selectionModel().rowIntersectsSelection(logicalInd, self.rootIndex()):
                opt.state = opt.state|QStyle.State_On
            if self.selectionModel().isRowSelected(logicalInd, self.rootIndex()):
                opt.state = opt.state|QStyle.State_Sunken
if self.selectionModel():
    previousSelected=False
    if self.orientation()==Qt.Horizontal:
        previousSelected = self.selectionModel().isColumnSelected(self.logicalIndex(visual - 1), self.rootIndex())
    else:
        previousSelected = self.selectionModel().isRowSelected(self.logicalIndex(visual - 1), self.rootIndex())
    nextSelected=False
    if self.orientation()==Qt.Horizontal:
        nextSelected = self.selectionModel().isColumnSelected(self.logicalIndex(visual + 1), self.rootIndex())
    else:
        nextSelected = self.selectionModel().isRowSelected(self.logicalIndex(visual + 1), self.rootIndex())
    if previousSelected and nextSelected:
        opt.selectedPosition = QStyleOptionHeader.NextAndPreviousAreSelected
    else:
        if previousSelected:
            opt.selectedPosition = QStyleOptionHeader.PreviousIsSelected
        else:
            if nextSelected:
                opt.selectedPosition = QStyleOptionHeader.NextIsSelected
            else:
                opt.selectedPosition = QStyleOptionHeader.NotAdjacent
return opt

def sectionSizeFromContents(self, logicalIndex: int)->QSize:
if self._pd.headerModel:
    curLeafIndex = QModelIndex(self._pd.leafIndex(logicalIndex))
    if curLeafIndex.isValid():
        styleOption = QStyleOptionHeader(self.styleOptionForCell(logicalIndex))
        s = QSize(self._pd.cellSize(curLeafIndex, self, styleOption))
        curLeafIndex=curLeafIndex.parent()
        while curLeafIndex.isValid():
            if self.orientation() == Qt.Horizontal:
                s.setHeight(s.height()+self._pd.cellSize(curLeafIndex, self, styleOption).height())
            else:
                s.setWidth(s.width()+self._pd.cellSize(curLeafIndex, self, styleOption).width())
            curLeafIndex=curLeafIndex.parent()
        return s
return super().sectionSizeFromContents(logicalIndex)

def paintSection(self, painter: QPainter, rect: QRect, logicalIndex: int):
if rect.isValid():
    leafIndex = QModelIndex(self._pd.leafIndex(logicalIndex))
    if leafIndex.isValid():
        if self.orientation() == Qt.Horizontal:
            self._pd.paintHorizontalSection(painter, rect, logicalIndex, self, self.styleOptionForCell(logicalIndex), leafIndex)
        else:
            self._pd.paintVerticalSection(painter, rect, logicalIndex, self, self.styleOptionForCell(logicalIndex), leafIndex)
        return
super().paintSection(painter, rect, logicalIndex)

def on_sectionResized(self, logicalIndex: int):
if self.isSectionHidden(logicalIndex):
    return
leafIndex = QModelIndex(self._pd.leafIndex(logicalIndex))
if leafIndex.isValid():
    leafsList = QModelIndexList(self._pd.leafs(self._pd.findRootIndex(leafIndex)))
    for n in range(leafsList.indexOf(leafIndex), 0, -1):
        logicalIndex-=1
        w = self.viewport().width()
        h = self.viewport().height()
        pos = self.sectionViewportPosition(logicalIndex)
        r = QRect(pos, 0, w - pos, h)
        if self.orientation() == Qt.Horizontal:
            if self.isRightToLeft():
                r.setRect(0, 0, pos + self.sectionSize(logicalIndex), h)
        else:
            r.setRect(0, pos, w, h - pos)
        self.viewport().update(r.normalized())

def setModel(self, model):
    super().setModel(model)
    model.layoutChanged.connect(self.layoutChanged)
    self.layoutChanged()

def layoutChanged(self):
    if self.model():
        self._pd.initFromNewModel(self.orientation(), self.model())
        axis = ("column", "row")[self.orientation()!=Qt.Horizontal]
        cnt = getattr(self.model(), axis+"Count")(QModelIndex())
        if cnt:
            self.initializeSections(0, cnt-1)
MultiIndexHeaderView=HierarchicalHeaderView

class DataFrameModel(QtCore.QAbstractTableModel):
    #na_values:least|greatest - for sorting
    options = {"striped": True, "stripesColor": "#fafafa", "na_values": "least",
               "tooltip_min_len": 21}
    def __init__(self, dataframe=None):
        super().__init__()
        self.setDataFrame(dataframe if dataframe is not None else pd.DataFrame())

    def setDataFrame(self, dataframe):
        self.df = dataframe.copy()
        #        self.df_full = self.df
        self.layoutChanged.emit()

    def rowCount(self, parent):
        return len(self.df)

    def columnCount(self, parent):
        return len(self.df.columns)

    def readLevel(self, y=0, xs=0, xe=None, orient=None):
        c = getattr(self.df, ("columns", "index")[orient!=HorizontalHeaderDataRole])
        if not hasattr(c, "levels"): #not MultiIndex
            return [QtGui.QStandardItem(str(i)) for i in c]
        sibl = []
        section_start, v, xe = xs, None, xe or len(c)
        for i in range(xs, xe):
            label = c.labels[y][i]
            if label!=v:
                if y+1<len(c.levels) and i>xs:
                    children = self.readLevel(y+1, section_start, i, orient=orient)
                    sibl[-1].appendRow(children)
                item = QtGui.QStandardItem(str(c.levels[y][label]))
                sibl.append(item)
                section_start = i
                v=label
        if y+1<len(c.levels):
            children = self.readLevel(y+1, section_start, orient=orient)
            sibl[-1].appendRow(children)
        return sibl

    def data(self, index, role):
        row, col = index.row(), index.column()
        if role in (Qt.DisplayRole, Qt.ToolTipRole):
            ret = self.df.iat[row, col]
            if ret is not None and ret==ret: #convert to str except for None, NaN, NaT
                if isinstance(ret, float):
                    ret = "{:n}".format(ret)
                elif isinstance(ret, datetime.date):
                    #FIXME: show microseconds optionally
                    ret = ret.strftime(("%x", "%c")[isinstance(ret, datetime.datetime)])
                else: ret = str(ret)
                if role == Qt.ToolTipRole:
                    if len(ret)<self.options["tooltip_min_len"]: ret = ""
                return ret
        elif role == Qt.BackgroundRole:
            if self.options["striped"] and row%2:
                return QBrush(QColor(self.options["stripesColor"]))
        elif role in (HorizontalHeaderDataRole, VerticalHeaderDataRole):
            hm = QtGui.QStandardItemModel()
            hm.appendRow(self.readLevel(orient=role))
            return hm

    def reorder(self, oldIndex, newIndex, orientation):
        "Reorder columns / rows"
        horizontal = orientation==Qt.Horizontal
        cols = list(self.df.columns if horizontal else self.df.index)
        cols.insert(newIndex, cols.pop(oldIndex))
        self.df = self.df[cols] if horizontal else self.df.T[cols].T
        return True

    #    def filter(self, filt=None):
    #        self.df = self.df_full if filt is None else self.df[filt]
    #        self.layoutChanged.emit()

    def headerData(self, section, orientation, role):
        if role != Qt.DisplayRole: return
        label = getattr(self.df, ("columns", "index")[orientation!=Qt.Horizontal])[section]
        #        return label if type(label) is tuple else label
        return ("\n", " | ")[orientation!=Qt.Horizontal].join(str(i) for i in label) if type(label) is tuple else str(label)

    def dataFrame(self):
        return self.df

    def sort(self, column, order):
        #        print("sort", column, order) #FIXME: double sort after setSortingEnabled(True)
        if len(self.df):
            asc = order==Qt.AscendingOrder
            na_pos = 'first' if (self.options["na_values"]=="least")==asc else 'last'
            self.df.sort_values(self.df.columns[column], ascending=asc,
                                inplace=True, na_position=na_pos)
            self.layoutChanged.emit()

if __name__=="__main__":
    import sys, locale
    locale.setlocale(locale.LC_ALL, '') #system locale settings
    app = QtGui.QApplication(sys.argv)
    form = QtGui.QWidget()
    form.setAttribute(Qt.WA_DeleteOnClose) #http://stackoverflow.com/a/27178019/1119602
    form.setMinimumSize(700, 260)
    view = QtGui.QTableView()
    QtGui.QVBoxLayout(form).addWidget(view)
    form.show()

    #Prepare data
    tuples=[('bar', 'one', 'q'), ('bar', 'two', 'q'), ('baz', 'one', 'q'), ('baz', 'two', 'q'),
            ('foo', 'one', 'q'), ('foo', 'two', 'q'), ('qux', 'one', 'q'), ('qux', 'two', 'q')]
    index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second', 'third'])
    df=pd.DataFrame(pd.np.random.randn(6, 6), index=index[:6], columns=index[:6])
    print("DataFrame:\n%s"%df)

    #Prepare view
    #    oldh, oldv = view.horizontalHeader(), view.verticalHeader()
    #    oldh.setParent(form), oldv.setParent(form) #Save old headers for some reason
    MultiIndexHeaderView(Qt.Horizontal, view)
    MultiIndexHeaderView(Qt.Vertical, view)
    view.horizontalHeader().setMovable(True) #reorder DataFrame columns manually

    #Set data
    view.setModel(DataFrameModel(df))
    view.resizeColumnsToContents()
    view.resizeRowsToContents()

    #Set sorting enabled (after setting model)
    view.setSortingEnabled(True)
    sys.exit(app.exec())
示例#31
0
class WebPage(QObject):
    javaScriptAlertSent = pyqtSignal(str)
    javaScriptConsoleMessageSent = pyqtSignal(str, int, str)
    loadStarted = pyqtSignal()
    loadFinished = pyqtSignal(str)
    resourceReceived = pyqtSignal('QVariantMap')
    resourceRequested = pyqtSignal('QVariantMap')

    def __init__(self, parent=None):
        QObject.__init__(self, parent)

        # variable declarations
        self.m_paperSize = {}
        self.m_clipRect = QRect()
        self.m_libraryPath = ''

        self.setObjectName('WebPage')
        self.m_webPage = CustomPage(self)
        self.m_mainFrame = self.m_webPage.mainFrame()

        self.m_webPage.loadStarted.connect(self.loadStarted)
        self.m_webPage.loadFinished.connect(self.finish)

        # Start with transparent background
        palette = self.m_webPage.palette()
        palette.setBrush(QPalette.Base, Qt.transparent)
        self.m_webPage.setPalette(palette)

        # Page size does not need to take scrollbars into account
        self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Horizontal,
                                                      Qt.ScrollBarAlwaysOff)
        self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Vertical,
                                                      Qt.ScrollBarAlwaysOff)

        self.m_webPage.settings().setAttribute(
            QWebSettings.OfflineStorageDatabaseEnabled, True)
        self.m_webPage.settings().setOfflineStoragePath(
            QDesktopServices.storageLocation(QDesktopServices.DataLocation))
        self.m_webPage.settings().setAttribute(
            QWebSettings.LocalStorageDatabaseEnabled, True)
        self.m_webPage.settings().setAttribute(
            QWebSettings.OfflineWebApplicationCacheEnabled, True)
        self.m_webPage.settings().setOfflineWebApplicationCachePath(
            QDesktopServices.storageLocation(QDesktopServices.DataLocation))
        self.m_webPage.settings().setAttribute(
            QWebSettings.FrameFlatteningEnabled, True)
        self.m_webPage.settings().setAttribute(
            QWebSettings.LocalStorageEnabled, True)
        self.m_webPage.settings().setLocalStoragePath(
            QDesktopServices.storageLocation(QDesktopServices.DataLocation))

        # Ensure we have a document.body.
        self.m_webPage.mainFrame().setHtml('<html><body></body></html>')

        self.m_webPage.setViewportSize(QSize(400, 300))

        do_action('WebPageInit', Bunch(locals()))

    def applySettings(self, defaults):
        opt = self.m_webPage.settings()

        opt.setAttribute(QWebSettings.AutoLoadImages, defaults['loadImages'])
        opt.setAttribute(QWebSettings.PluginsEnabled, defaults['loadPlugins'])
        opt.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls,
                         defaults['localAccessRemote'])
        if 'userAgent' in defaults:
            self.m_webPage.m_userAgent = defaults['userAgent']

    def finish(self, ok):
        status = 'success' if ok else 'fail'
        self.loadFinished.emit(status)

    def mainFrame(self):
        return self.m_mainFrame

    def renderImage(self):
        frameRect = QRect(QPoint(0, 0), self.m_mainFrame.contentsSize())
        if not self.m_clipRect.isEmpty():
            frameRect = self.m_clipRect

        viewportSize = self.m_webPage.viewportSize()
        self.m_webPage.setViewportSize(self.m_mainFrame.contentsSize())

        image = QImage(frameRect.size(), QImage.Format_ARGB32)
        image.fill(qRgba(255, 255, 255, 0))

        painter = QPainter()

        # We use tiling approach to work-around Qt software rasterizer bug
        # when dealing with very large paint device.
        # See http://code.google.com/p/phantomjs/issues/detail?id=54.
        tileSize = 4096
        htiles = (image.width() + tileSize - 1) / tileSize
        vtiles = (image.height() + tileSize - 1) / tileSize
        for x in range(htiles):
            for y in range(vtiles):
                tileBuffer = QImage(tileSize, tileSize, QImage.Format_ARGB32)
                tileBuffer.fill(qRgba(255, 255, 255, 0))

                # Render the web page onto the small tile first
                painter.begin(tileBuffer)
                painter.setRenderHint(QPainter.Antialiasing, True)
                painter.setRenderHint(QPainter.TextAntialiasing, True)
                painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
                painter.translate(-frameRect.left(), -frameRect.top())
                painter.translate(-x * tileSize, -y * tileSize)
                self.m_mainFrame.render(painter, QRegion(frameRect))
                painter.end()

                # Copy the tile to the main buffer
                painter.begin(image)
                painter.setCompositionMode(QPainter.CompositionMode_Source)
                painter.drawImage(x * tileSize, y * tileSize, tileBuffer)
                painter.end()

        self.m_webPage.setViewportSize(viewportSize)
        return image

    def renderPdf(self, fileName):
        p = QPrinter()
        p.setOutputFormat(QPrinter.PdfFormat)
        p.setOutputFileName(fileName)
        p.setResolution(pdf_dpi)
        paperSize = self.m_paperSize

        if not len(paperSize):
            pageSize = QSize(self.m_webPage.mainFrame().contentsSize())
            paperSize['width'] = str(pageSize.width()) + 'px'
            paperSize['height'] = str(pageSize.height()) + 'px'
            paperSize['border'] = '0px'

        if paperSize.get('width') and paperSize.get('height'):
            sizePt = QSizeF(ceil(self.stringToPointSize(paperSize['width'])),
                            ceil(self.stringToPointSize(paperSize['height'])))
            p.setPaperSize(sizePt, QPrinter.Point)
        elif 'format' in paperSize:
            orientation = QPrinter.Landscape if paperSize.get(
                'orientation') and paperSize['orientation'].lower(
                ) == 'landscape' else QPrinter.Portrait
            orientation = QPrinter.Orientation(orientation)
            p.setOrientation(orientation)

            formats = {
                'A0': QPrinter.A0,
                'A1': QPrinter.A1,
                'A2': QPrinter.A2,
                'A3': QPrinter.A3,
                'A4': QPrinter.A4,
                'A5': QPrinter.A5,
                'A6': QPrinter.A6,
                'A7': QPrinter.A7,
                'A8': QPrinter.A8,
                'A9': QPrinter.A9,
                'B0': QPrinter.B0,
                'B1': QPrinter.B1,
                'B2': QPrinter.B2,
                'B3': QPrinter.B3,
                'B4': QPrinter.B4,
                'B5': QPrinter.B5,
                'B6': QPrinter.B6,
                'B7': QPrinter.B7,
                'B8': QPrinter.B8,
                'B9': QPrinter.B9,
                'B10': QPrinter.B10,
                'C5E': QPrinter.C5E,
                'Comm10E': QPrinter.Comm10E,
                'DLE': QPrinter.DLE,
                'Executive': QPrinter.Executive,
                'Folio': QPrinter.Folio,
                'Ledger': QPrinter.Ledger,
                'Legal': QPrinter.Legal,
                'Letter': QPrinter.Letter,
                'Tabloid': QPrinter.Tabloid
            }

            p.setPaperSize(QPrinter.A4)  # fallback
            for format, size in formats.items():
                if format.lower() == paperSize['format'].lower():
                    p.setPaperSize(size)
                    break
        else:
            return False

        border = floor(self.stringToPointSize(
            paperSize['border'])) if paperSize.get('border') else 0
        p.setPageMargins(border, border, border, border, QPrinter.Point)

        self.m_webPage.mainFrame().print_(p)
        return True

    def setNetworkAccessManager(self, networkAccessManager):
        self.m_webPage.setNetworkAccessManager(networkAccessManager)
        networkAccessManager.resourceRequested.connect(self.resourceRequested)
        networkAccessManager.resourceReceived.connect(self.resourceReceived)

    def stringToPointSize(self, string):
        units = (('mm', 72 / 25.4), ('cm', 72 / 2.54), ('in', 72.0),
                 ('px', 72.0 / pdf_dpi / 2.54), ('', 72.0 / pdf_dpi / 2.54))

        for unit, format in units:
            if string.endswith(unit):
                value = string.rstrip(unit)
                return float(value) * format
        return 0

    def userAgent(self):
        return self.m_webPage.m_userAgent

    ##
    # Properties and methods exposed to JavaScript
    ##

    @pyqtSlot(str)
    def _appendScriptElement(self, scriptUrl):
        self.m_mainFrame.evaluateJavaScript('''
            var el = document.createElement('script');
            el.onload = function() { alert('%(scriptUrl)s'); };
            el.src = '%(scriptUrl)s';
            document.body.appendChild(el);
        ''' % {'scriptUrl': scriptUrl})

    @pyqtProperty('QVariantMap')
    def clipRect(self):
        result = {
            'width': self.m_clipRect.width(),
            'height': self.m_clipRect.height(),
            'top': self.m_clipRect.top(),
            'left': self.m_clipRect.left()
        }
        return result

    @clipRect.setter
    def clipRect(self, size):
        names = ('width', 'height', 'top', 'left')
        for item in names:
            try:
                globals()[item] = int(size[item])
                if globals()[item] < 0:
                    if item not in ('top', 'left'):
                        globals()[item] = 0
            except KeyError:
                globals()[item] = getattr(self.m_clipRect, item)()

        self.m_clipRect = QRect(left, top, width, height)

    @pyqtProperty(str)
    def content(self):
        return self.m_mainFrame.toHtml()

    @content.setter
    def content(self, content):
        self.m_mainFrame.setHtml(content)

    @pyqtSlot(str, result='QVariant')
    def evaluate(self, code):
        function = '(%s)()' % code
        return self.m_mainFrame.evaluateJavaScript(function)

    @pyqtSlot(str, result=bool)
    def injectJs(self, filePath):
        return injectJsInFrame(filePath, self.m_libraryPath, self.m_mainFrame)

    @pyqtSlot(str, str, 'QVariantMap')
    @pyqtSlot(str, 'QVariantMap', 'QVariantMap')
    def openUrl(self, address, op, settings):
        operation = op
        body = QByteArray()

        self.applySettings(settings)
        self.m_webPage.triggerAction(QWebPage.Stop)

        if type(op) is dict:
            operation = op.get('operation')
            body = QByteArray(op.get('data', ''))

        if operation == '':
            operation = 'get'

        networkOp = QNetworkAccessManager.CustomOperation
        operation = operation.lower()
        if operation == 'get':
            networkOp = QNetworkAccessManager.GetOperation
        elif operation == 'head':
            networkOp = QNetworkAccessManager.HeadOperation
        elif operation == 'put':
            networkOp = QNetworkAccessManager.PutOperation
        elif operation == 'post':
            networkOp = QNetworkAccessManager.PostOperation
        elif operation == 'delete':
            networkOp = QNetworkAccessManager.DeleteOperation

        if networkOp == QNetworkAccessManager.CustomOperation:
            self.m_mainFrame.evaluateJavaScript(
                'console.error("Unknown network operation: %s");' % operation)
            return

        self.m_mainFrame.load(QNetworkRequest(QUrl(address)), networkOp, body)

    @pyqtProperty('QVariantMap')
    def paperSize(self):
        return self.m_paperSize

    @paperSize.setter
    def paperSize(self, size):
        self.m_paperSize = size

    @pyqtSlot(str, result=bool)
    def render(self, fileName):
        if self.m_mainFrame.contentsSize() == '':
            return False

        fileInfo = QFileInfo(fileName)
        path = QDir()
        path.mkpath(fileInfo.absolutePath())

        if fileName.lower().endswith('.pdf'):
            return self.renderPdf(fileName)

        image = self.renderImage()

        return image.save(fileName)

    @pyqtProperty(str)
    def libraryPath(self):
        return self.m_libraryPath

    @libraryPath.setter
    def libraryPath(self, dirPath):
        self.m_libraryPath = dirPath

    @pyqtSlot(str, str)
    def uploadFile(self, selector, fileName):
        el = self.m_mainFrame.findFirstElement(selector)
        if el.isNull():
            return

        self.m_webPage.m_uploadFile = fileName
        el.evaluateJavaScript('''
            (function (el) {
                var ev = document.createEvent('MouseEvents');
                ev.initEvent('click', true, true);
                el.dispatchEvent(ev);
            })(this)
        ''')

    @pyqtProperty('QVariantMap')
    def viewportSize(self):
        size = self.m_webPage.viewportSize()
        result = {'width': size.width(), 'height': size.height()}
        return result

    @viewportSize.setter
    def viewportSize(self, size):
        names = ('width', 'height')
        for item in names:
            try:
                globals()[item] = int(size[item])
                if globals()[item] < 0:
                    globals()[item] = 0
            except KeyError:
                globals()[item] = getattr(self.m_webPage.viewportSize(),
                                          item)()

        self.m_webPage.setViewportSize(QSize(width, height))

    do_action('WebPage', Bunch(locals()))
示例#32
0
class BrushingModel(QObject):
    brushSizeChanged     = pyqtSignal(int)
    brushColorChanged    = pyqtSignal(QColor)
    brushStrokeAvailable = pyqtSignal(QPointF, object)
    drawnNumberChanged   = pyqtSignal(int)

    minBrushSize       = 1
    maxBrushSize       = 61
    defaultBrushSize   = 3
    defaultDrawnNumber = 1
    defaultColor       = Qt.white
    erasingColor       = Qt.black
    erasingNumber      = 100

    def __init__(self, parent=None):
        QObject.__init__(self, parent=parent)
        self.sliceRect = None
        self.bb    = QRect() #bounding box enclosing the drawing
        self.brushSize = self.defaultBrushSize
        self.drawColor = self.defaultColor
        self._temp_color = None
        self._temp_number = None
        self.drawnNumber = self.defaultDrawnNumber

        self.pos = None
        self.erasing = False
        self._hasMoved = False

        self.drawOnto = None

        #an empty scene, where we add all drawn line segments
        #a QGraphicsLineItem, and which we can use to then
        #render to an image
        self.scene = QGraphicsScene()

    def toggleErase(self):
        self.erasing = not(self.erasing)
        if self.erasing:
            self.setErasing()
        else:
            self.disableErasing()

    def setErasing(self):
        self.erasing = True
        self._temp_color = self.drawColor
        self._temp_number = self.drawnNumber
        self.setBrushColor(self.erasingColor)
        self.brushColorChanged.emit(self.erasingColor)
        self.setDrawnNumber(self.erasingNumber)

    def disableErasing(self):
        self.erasing = False
        self.setBrushColor(self._temp_color)
        self.brushColorChanged.emit(self.drawColor)
        self.setDrawnNumber(self._temp_number)

    def setBrushSize(self, size):
        self.brushSize = size
        self.brushSizeChanged.emit(self.brushSize)

    def setDrawnNumber(self, num):
        self.drawnNumber = num
        self.drawnNumberChanged.emit(num)

    def getBrushSize(self):
        return self.brushSize

    def brushSmaller(self):
        b = self.brushSize
        if b > self.minBrushSize:
            self.setBrushSize(b-1)

    def brushBigger(self):
        b = self.brushSize
        if self.brushSize < self.maxBrushSize:
            self.setBrushSize(b+1)

    def setBrushColor(self, color):
        self.drawColor = color
        self.brushColorChanged.emit(self.drawColor)

    def beginDrawing(self, pos, sliceRect):
        '''

        pos -- QPointF-like
        '''
        self.sliceRect = sliceRect
        self.scene.clear()
        self.bb = QRect()
        self.pos = QPointF(pos.x(), pos.y())
        self._hasMoved = False

    def endDrawing(self, pos):
        has_moved = self._hasMoved # _hasMoved will change after calling moveTo
        if has_moved:
            self.moveTo(pos)
        else:
            assert(self.pos == pos)
            self.moveTo(QPointF(pos.x()+0.0001, pos.y()+0.0001)) # move a little

        tempi = QImage(QSize(self.bb.width(), self.bb.height()), QImage.Format_ARGB32_Premultiplied) #TODO: format
        tempi.fill(0)
        painter = QPainter(tempi)
        self.scene.render(painter, target=QRectF(), source=QRectF(QPointF(self.bb.x(), self.bb.y()), QSizeF(self.bb.width(), self.bb.height())))
        painter.end()

        ndarr = qimage2ndarray.rgb_view(tempi)[:,:,0]
        labels = numpy.where(ndarr>0,numpy.uint8(self.drawnNumber),numpy.uint8(0))
        labels = labels.swapaxes(0,1)
        assert labels.shape[0] == self.bb.width()
        assert labels.shape[1] == self.bb.height()

        ##
        ## ensure that at least one pixel is label when the brush size is 1
        ##
        ## this happens when the user just clicked without moving
        ## in that case the lineitem will be so tiny, that it won't be rendered
        ## into a single pixel by the code above
        if not has_moved and self.brushSize <= 1 and numpy.count_nonzero(labels) == 0:
            labels[labels.shape[0]//2, labels.shape[1]//2] = self.drawnNumber

        self.brushStrokeAvailable.emit(QPointF(self.bb.x(), self.bb.y()), labels)

    def dumpDraw(self, pos):
        res = self.endDrawing(pos)
        self.beginDrawing(pos, self.sliceRect)
        return res

    def moveTo(self, pos):
        #data coordinates
        oldX, oldY = self.pos.x(), self.pos.y()
        x,y = pos.x(), pos.y()

        line = QGraphicsLineItem(oldX, oldY, x, y)
        line.setPen(QPen( QBrush(Qt.white), self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        self.scene.addItem(line)
        self._hasMoved = True

        #update bounding Box
        if not self.bb.isValid():
            self.bb = QRect(QPoint(oldX,oldY), QSize(1,1))
        #grow bounding box
        self.bb.setLeft(  min(self.bb.left(),   max(0,                   x-self.brushSize/2-1) ) )
        self.bb.setRight( max(self.bb.right(),  min(self.sliceRect[0]-1, x+self.brushSize/2+1) ) )
        self.bb.setTop(   min(self.bb.top(),    max(0,                   y-self.brushSize/2-1) ) )
        self.bb.setBottom(max(self.bb.bottom(), min(self.sliceRect[1]-1, y+self.brushSize/2+1) ) )

        #update/move position
        self.pos = pos
示例#33
0
class PoseWidget(QLabel):
    default_colors = (Qt.red, Qt.blue, Qt.yellow, Qt.green)

    def __init__(self,
                 ressource_directory,
                 dim: QSize,
                 player_size: QSize,
                 player_ratio_spacing: float = 0.33,
                 alpha_color="#FF00FF",
                 vertical_random_ratio=0.1,
                 key_press_event_callback=None,
                 dev_mode=False):
        """
        :param ressource_directory: String, chemin du dossier ressource.
        :param dim: QSize, taille alloué au widget.
        :param dev_mode: boolean [facultatif (defaut False)], active le mode developpement.
        """
        # Setup widget
        super().__init__()
        if dev_mode:
            self.setStyleSheet("background-color:blue")
        else:
            self.setStyleSheet("background-color:" + alpha_color)
        self.setMaximumSize(dim)
        self.setMinimumSize(dim)

        # Setup poses
        self.current_silhouette = None
        self.colors = list(self.default_colors)
        self.player_rect = QRect((dim.width() - player_size.width()) * 0.5,
                                 (dim.height() - player_size.height()) * 0.5,
                                 player_size.width(), player_size.height())
        self.player_ratio_spacing = player_ratio_spacing
        self.vertical_random_ratio = vertical_random_ratio
        self.player_pixel_spacing = 0
        self.pose_dict = {}
        self._load_all_poses(ressource_directory,
                             load_all_images=True,
                             verbose=False,
                             filter_valid=True,
                             dev_mode=dev_mode)

        # Init constant for pose drawing
        exemple_sil_pixmap = self.pose_dict[list(
            self.pose_dict.keys())[1]].get_silhouette().pixmap
        vertical_scale = self.player_rect.height() / exemple_sil_pixmap.height(
        )
        self.pose_hauteur = round(exemple_sil_pixmap.height() * vertical_scale)
        self.pose_largeur = round(exemple_sil_pixmap.width() * vertical_scale)
        self.player_pixel_spacing = self.player_ratio_spacing * self.pose_largeur

        # Event callback
        self.key_press_event_callback = key_press_event_callback

    def _load_all_poses(self,
                        ressource_directory,
                        load_all_images=False,
                        verbose=False,
                        filter_valid=True,
                        dev_mode=False):
        """Essai de charger toutes les poses ayant un fichier de conf dans le sous-dossier 'pose_configs' du dossier
        ressource fourni
        PARAMS: - ressource_directory : path-like = le chemin du dossier de ressource du jeu
                -[load_all_images]: boolean = [False] si True les images (.png/.jpg) présentent dans le dossiers
                'silhouette_storage' mais ne comportant pas de ficher de conf associées seront aussi chargées et une
                configuration automatique leur sera créé.
                -[verbose]: boolean = [False] si True, la liste des fonds trouvés sera affichée ainsi que leur validité
                (contient tous les fonds valides où non)
                -[filter_valid]: boolean = [True] si True seul les fonds correctment chargés seront revoyés.
        Postcondition : le dictionnaire pose_dict des fonds est chargé"""
        if dev_mode:
            print("   Loading Poses...", end="")
        self.pose_dict = {}
        # lister tous les nom de poses (c'est a dire tous les fichier d'extension '.conf' du dossier
        # background_storage en coupant l'extension en question)
        name_list_c = [
            path[:-5]
            for path in os.listdir(ressource_directory + "pose_configs")
            if path.find(".conf") > -1
        ]
        # Tenter de charger tous les poses listées
        for pose_name in name_list_c:
            self.pose_dict[pose_name] = Pose(self,
                                             ressource_directory,
                                             pose_name,
                                             force_conf_generation=False)
            if verbose:
                print("Loading pose", pose_name, "from config; succès :",
                      self.pose_dict[pose_name].valide)

        # ajouter les nom des fichers .png si l'option est activée, en évitant les doublons
        if load_all_images:
            name_list_i = [
                path[:-4] for path in os.listdir(ressource_directory +
                                                 "silhouette_storage")
                if path.find(".png") > -1 and not (path[:-4] in name_list_c)
            ]
            # Tenter de charger tous les fond listées en générant toujours les config
            for pose_name in name_list_i:
                self.pose_dict[pose_name] = Pose(self,
                                                 ressource_directory,
                                                 pose_name,
                                                 except_if_inexistant=False,
                                                 force_conf_generation=True)
                if verbose:
                    print("Loading background", pose_name,
                          "from image; succès :",
                          self.pose_dict[pose_name].valide)
        # ajout de l'image vide
        self.pose_dict[""] = Pose(self,
                                  ressource_directory,
                                  None,
                                  except_if_inexistant=True,
                                  force_conf_generation=False)
        if filter_valid:
            r_list = []
            for pose_name in self.pose_dict.keys():
                if not self.pose_dict[pose_name].valide:
                    r_list.append(pose_name)
            for r_pose_name in r_list:
                self.pose_dict.pop(r_pose_name, None)

        if dev_mode:
            print("OK")

    def set_poses(self, pose_names):
        """Set la pose affichée à l'écran enla designant par son nom"""
        self.current_silhouette = ()
        for pose_name in pose_names:
            if pose_name in self.pose_dict.keys():
                self.current_silhouette = self.current_silhouette + \
                                          (self.pose_dict[pose_name].get_silhouette(),)
            else:
                print("Warning: Set_Pose d'une pose inconnue :", pose_name)
        self.colors = list(self.default_colors)
        self.update()

    def paintEvent(self, *args):
        #TODO OPTIMISE FOR MULTIPLE REPETITIVE CALL
        super().paintEvent(*args)
        if self.current_silhouette is not None:
            # Génération du QPainter
            print("update started")
            painter = QPainter(self)
            # Effacer la pose précédente
            painter.eraseRect(self.rect())

            # get x axis coordonate of the center of the player 1st on the left
            left_center = self.player_rect.left(
            ) + 0.5 * self.player_rect.width() - self.player_pixel_spacing * (
                len(self.current_silhouette) - 1) * 0.5

            n = len(self.current_silhouette)
            for k in random.sample(range(n), n):
                vertical_random = self.vertical_random_ratio * self.pose_hauteur * random.random(
                )
                new_pixmap = QPixmap(self.pose_largeur, self.pose_hauteur)
                new_pixmap.fill(self.colors[k])
                new_pixmap.setMask(self.current_silhouette[k].pixmap.scaled(
                    self.pose_largeur, self.pose_hauteur).mask())
                painter.drawPixmap(
                    int(left_center + k * self.player_pixel_spacing -
                        (self.pose_largeur * 0.5)),
                    self.player_rect.top() + vertical_random,
                    self.pose_largeur, self.pose_hauteur, new_pixmap)
                #self.setPixmap(silhouette.sil_pixmap.scaled(self.width(), self.height(), Qt.KeepAspectRatio))

    def keyPressEvent(self, e):
        self.key_press_event_callback(e)
示例#34
0
class BrushingModel(QObject):
    brushSizeChanged = pyqtSignal(int)
    brushColorChanged = pyqtSignal(QColor)
    brushStrokeAvailable = pyqtSignal(QPointF, object)
    drawnNumberChanged = pyqtSignal(int)

    minBrushSize = 1
    maxBrushSize = 61
    defaultBrushSize = 3
    defaultDrawnNumber = 1
    defaultColor = Qt.white
    erasingColor = Qt.black
    erasingNumber = 100

    def __init__(self, parent=None):
        QObject.__init__(self, parent=parent)
        self.sliceRect = None
        self.bb = QRect()  #bounding box enclosing the drawing
        self.brushSize = self.defaultBrushSize
        self.drawColor = self.defaultColor
        self._temp_color = None
        self._temp_number = None
        self.drawnNumber = self.defaultDrawnNumber

        self.pos = None
        self.erasing = False
        self._hasMoved = False

        self.drawOnto = None

        #an empty scene, where we add all drawn line segments
        #a QGraphicsLineItem, and which we can use to then
        #render to an image
        self.scene = QGraphicsScene()

    def toggleErase(self):
        self.erasing = not (self.erasing)
        if self.erasing:
            self.setErasing()
        else:
            self.disableErasing()

    def setErasing(self):
        self.erasing = True
        self._temp_color = self.drawColor
        self._temp_number = self.drawnNumber
        self.setBrushColor(self.erasingColor)
        self.brushColorChanged.emit(self.erasingColor)
        self.setDrawnNumber(self.erasingNumber)

    def disableErasing(self):
        self.erasing = False
        self.setBrushColor(self._temp_color)
        self.brushColorChanged.emit(self.drawColor)
        self.setDrawnNumber(self._temp_number)

    def setBrushSize(self, size):
        self.brushSize = size
        self.brushSizeChanged.emit(self.brushSize)

    def setDrawnNumber(self, num):
        self.drawnNumber = num
        self.drawnNumberChanged.emit(num)

    def getBrushSize(self):
        return self.brushSize

    def brushSmaller(self):
        b = self.brushSize
        if b > self.minBrushSize:
            self.setBrushSize(b - 1)

    def brushBigger(self):
        b = self.brushSize
        if self.brushSize < self.maxBrushSize:
            self.setBrushSize(b + 1)

    def setBrushColor(self, color):
        self.drawColor = color
        self.brushColorChanged.emit(self.drawColor)

    def beginDrawing(self, pos, sliceRect):
        '''

        pos -- QPointF-like
        '''
        self.sliceRect = sliceRect
        self.scene.clear()
        self.bb = QRect()
        self.pos = QPointF(pos.x(), pos.y())
        self._hasMoved = False

    def endDrawing(self, pos):
        has_moved = self._hasMoved  # _hasMoved will change after calling moveTo
        if has_moved:
            self.moveTo(pos)
        else:
            assert (self.pos == pos)
            self.moveTo(QPointF(pos.x() + 0.0001,
                                pos.y() + 0.0001))  # move a little

        tempi = QImage(QSize(self.bb.width(), self.bb.height()),
                       QImage.Format_ARGB32_Premultiplied)  #TODO: format
        tempi.fill(0)
        painter = QPainter(tempi)
        self.scene.render(painter,
                          target=QRectF(),
                          source=QRectF(
                              QPointF(self.bb.x(), self.bb.y()),
                              QSizeF(self.bb.width(), self.bb.height())))
        painter.end()

        ndarr = qimage2ndarray.rgb_view(tempi)[:, :, 0]
        labels = numpy.where(ndarr > 0, numpy.uint8(self.drawnNumber),
                             numpy.uint8(0))
        labels = labels.swapaxes(0, 1)
        assert labels.shape[0] == self.bb.width()
        assert labels.shape[1] == self.bb.height()

        ##
        ## ensure that at least one pixel is label when the brush size is 1
        ##
        ## this happens when the user just clicked without moving
        ## in that case the lineitem will be so tiny, that it won't be rendered
        ## into a single pixel by the code above
        if not has_moved and self.brushSize <= 1 and numpy.count_nonzero(
                labels) == 0:
            labels[labels.shape[0] // 2,
                   labels.shape[1] // 2] = self.drawnNumber

        self.brushStrokeAvailable.emit(QPointF(self.bb.x(), self.bb.y()),
                                       labels)

    def dumpDraw(self, pos):
        res = self.endDrawing(pos)
        self.beginDrawing(pos, self.sliceRect)
        return res

    def moveTo(self, pos):
        #data coordinates
        oldX, oldY = self.pos.x(), self.pos.y()
        x, y = pos.x(), pos.y()

        line = QGraphicsLineItem(oldX, oldY, x, y)
        line.setPen(
            QPen(QBrush(Qt.white), self.brushSize, Qt.SolidLine, Qt.RoundCap,
                 Qt.RoundJoin))
        self.scene.addItem(line)
        self._hasMoved = True

        #update bounding Box
        if not self.bb.isValid():
            self.bb = QRect(QPoint(oldX, oldY), QSize(1, 1))
        #grow bounding box
        self.bb.setLeft(min(self.bb.left(), max(0,
                                                x - self.brushSize / 2 - 1)))
        self.bb.setRight(
            max(self.bb.right(),
                min(self.sliceRect[0] - 1, x + self.brushSize / 2 + 1)))
        self.bb.setTop(min(self.bb.top(), max(0, y - self.brushSize / 2 - 1)))
        self.bb.setBottom(
            max(self.bb.bottom(),
                min(self.sliceRect[1] - 1, y + self.brushSize / 2 + 1)))

        #update/move position
        self.pos = pos
示例#35
0
class BrushingModel(QObject):
    brushSizeChanged     = pyqtSignal(int)
    brushColorChanged    = pyqtSignal(QColor)
    brushStrokeAvailable = pyqtSignal(QPointF, object)
    drawnNumberChanged   = pyqtSignal(int)

    minBrushSize       = 1
    maxBrushSize       = 61
    defaultBrushSize   = 3
    defaultDrawnNumber = 1
    defaultColor       = Qt.white
    erasingColor       = Qt.black
    erasingNumber      = 100

    def __init__(self, parent=None):
        QObject.__init__(self, parent=parent)
        self.sliceRect = None
        self.bb    = QRect() #bounding box enclosing the drawing
        self.brushSize = self.defaultBrushSize
        self.drawColor = self.defaultColor
        self._temp_color = None
        self._temp_number = None
        self.drawnNumber = self.defaultDrawnNumber

        self.pos = None
        self.erasing = False
        self._hasMoved = False

        self.drawOnto = None

        #an empty scene, where we add all drawn line segments
        #a QGraphicsLineItem, and which we can use to then
        #render to an image
        self.scene = QGraphicsScene()

    def toggleErase(self):
        self.erasing = not(self.erasing)
        if self.erasing:
            self.setErasing()
        else:
            self.disableErasing()

    def setErasing(self):
        self.erasing = True
        self._temp_color = self.drawColor
        self._temp_number = self.drawnNumber
        self.setBrushColor(self.erasingColor)
        self.brushColorChanged.emit(self.erasingColor)
        self.setDrawnNumber(self.erasingNumber)

    def disableErasing(self):
        self.erasing = False
        self.setBrushColor(self._temp_color)
        self.brushColorChanged.emit(self.drawColor)
        self.setDrawnNumber(self._temp_number)

    def setBrushSize(self, size):
        self.brushSize = size
        self.brushSizeChanged.emit(self.brushSize)

    def setDrawnNumber(self, num):
        self.drawnNumber = num
        self.drawnNumberChanged.emit(num)

    def getBrushSize(self):
        return self.brushSize

    def brushSmaller(self):
        b = self.brushSize
        if b > self.minBrushSize:
            self.setBrushSize(b-1)

    def brushBigger(self):
        b = self.brushSize
        if self.brushSize < self.maxBrushSize:
            self.setBrushSize(b+1)

    def setBrushColor(self, color):
        self.drawColor = color
        self.brushColorChanged.emit(self.drawColor)

    def beginDrawing(self, pos, sliceRect):
        '''

        pos -- QPointF-like
        '''
        self.sliceRect = sliceRect
        self.scene.clear()
        self.bb = QRect()
        self.pos = QPointF(pos.x(), pos.y())
        self._hasMoved = False

    def endDrawing(self, pos):
        has_moved = self._hasMoved # _hasMoved will change after calling moveTo
        if has_moved:
            self.moveTo(pos)
        else:
            assert(self.pos == pos)
            self.moveTo(QPointF(pos.x()+0.0001, pos.y()+0.0001)) # move a little

        # Qt seems to use strange rules for determining which pixels to set when rendering a brush stroke to a QImage.
        # We seem to get better results if we do the following:
        # 1) Slightly offset the source window because apparently there is a small shift in the data
        # 2) Render the scene to an image that is MUCH larger than the scene resolution (4x by 4x)
        # 3) Downsample each 4x4 patch from the large image back to a single pixel in the final image,
        #     applying some threshold to determine if the final pixel is on or off. 

        tempi = QImage(QSize(4*self.bb.width(), 4*self.bb.height()), QImage.Format_ARGB32_Premultiplied) #TODO: format
        tempi.fill(0)
        painter = QPainter(tempi)
        # Offset the source window.  At first I thought the right offset was 0.5, because 
        #  that would seem to make sure points are rounded to pixel CENTERS, but 
        #  experimentation indicates that 0.25 is slightly better for some reason...
        source_rect = QRectF( QPointF(self.bb.x()+0.25, self.bb.y()+0.25), 
                              QSizeF(self.bb.width(), self.bb.height()) )
        target_rect = QRectF( QPointF(0,0),
                             QSizeF(4*self.bb.width(), 4*self.bb.height()) )
        self.scene.render(painter, target=target_rect, source=source_rect)
        painter.end()

        # Now downsample: convert each 4x4 patch into a single pixel by summing and dividing
        ndarr = qimage2ndarray.rgb_view(tempi)[:,:,0].astype(int)
        ndarr = ndarr.reshape( (ndarr.shape[0],) + (ndarr.shape[1]//4,) + (4,) )
        ndarr = ndarr.sum(axis=-1)
        ndarr = ndarr.transpose()
        ndarr = ndarr.reshape( (ndarr.shape[0],) + (ndarr.shape[1]//4,) + (4,) )
        ndarr = ndarr.sum(axis=-1)
        ndarr = ndarr.transpose()
        ndarr //= 4*4

        downsample_threshold = (7./16)*255
        labels = numpy.where(ndarr>=downsample_threshold, numpy.uint8(self.drawnNumber), numpy.uint8(0))
        labels = labels.swapaxes(0,1)
        assert labels.shape[0] == self.bb.width()
        assert labels.shape[1] == self.bb.height()

        ##
        ## ensure that at least one pixel is label when the brush size is 1
        ##
        ## this happens when the user just clicked without moving
        ## in that case the lineitem will be so tiny, that it won't be rendered
        ## into a single pixel by the code above
        if not has_moved and self.brushSize <= 1 and numpy.count_nonzero(labels) == 0:
            labels[labels.shape[0]//2, labels.shape[1]//2] = self.drawnNumber

        self.brushStrokeAvailable.emit(QPointF(self.bb.x(), self.bb.y()), labels)

    def dumpDraw(self, pos):
        res = self.endDrawing(pos)
        self.beginDrawing(pos, self.sliceRect)
        return res

    def moveTo(self, pos):
        #data coordinates
        oldX, oldY = self.pos.x(), self.pos.y()
        x,y = pos.x(), pos.y()

        line = QGraphicsLineItem(oldX, oldY, x, y)
        line.setPen(QPen( QBrush(Qt.white), self.brushSize, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
        self.scene.addItem(line)
        self._hasMoved = True

        #update bounding Box
        if not self.bb.isValid():
            self.bb = QRect(QPoint(oldX,oldY), QSize(1,1))
        #grow bounding box
        self.bb.setLeft(  min(self.bb.left(),   max(0,                   x-self.brushSize//2-1) ) )
        self.bb.setRight( max(self.bb.right(),  min(self.sliceRect[0]-1, x+self.brushSize//2+1) ) )
        self.bb.setTop(   min(self.bb.top(),    max(0,                   y-self.brushSize//2-1) ) )
        self.bb.setBottom(max(self.bb.bottom(), min(self.sliceRect[1]-1, y+self.brushSize//2+1) ) )

        #update/move position
        self.pos = pos
示例#36
0
    def paint(self, painter, option, index):
        self.initStyleOption(option, index)
        if hasattr(option, "checkState"):
            if option.checkState == Qt.Unchecked:
                option.checkState = Qt.PartiallyChecked
            elif option.checkState == Qt.PartiallyChecked:
                option.checkState = Qt.Unchecked
            elif option.checkState == Qt.Checked:
                option.font = QFont(option.font)
                option.font.setStrikeOut(True)
        # ref: qt4-x11-4.6.2/src/gui/styles/qcommonstyle.cpp
        painter.save()
        painter.setClipRect(option.rect)
        # QApplication.style().drawControl(
        #     QStyle.CE_ItemViewItem, option, painter,
        #     getattr(option, "widget", None))
        style = QApplication.style()
        widget = getattr(option, "widget", None)
        # log.debug("widget: %r style: %r" % (widget, style.metaObject().className()))
        style.drawPrimitive(
            QStyle.PE_PanelItemViewItem, option, painter, widget)

        if option.checkState == Qt.Checked:
            painter.setOpacity(0.3)
        text_rect = style.subElementRect(
            QStyle.SE_ItemViewItemText, option, widget)
        item_text = option.fontMetrics.elidedText(
            option.text, option.textElideMode, text_rect.width())
        painter.setFont(option.font)
        style.drawItemText(painter, text_rect, option.displayAlignment,
                           option.palette, True, item_text, QPalette.Text)

        check_rect = style.subElementRect(
            QStyle.SE_ItemViewItemCheckIndicator, option, widget)
        if option.checkState == Qt.PartiallyChecked:
            brush = option.palette.brush(QPalette.Base)
            painter.fillRect(check_rect, brush)
            bullet_rect = QRect(check_rect)
            if bullet_rect.width() > BULLET_SIZE:
                bullet_rect.setLeft(
                    bullet_rect.left() +
                    (bullet_rect.width() - BULLET_SIZE) / 2)
                bullet_rect.setWidth(BULLET_SIZE)
            if bullet_rect.height() > BULLET_SIZE:
                bullet_rect.setTop(
                    bullet_rect.top() +
                    (bullet_rect.height() - BULLET_SIZE) / 2)
                bullet_rect.setHeight(BULLET_SIZE)
            painter.setPen(QPen(option.palette.color(QPalette.Text)))
            painter.setBrush(option.palette.brush(QPalette.Text))
            painter.drawEllipse(bullet_rect)
        else:
            check_opt = QStyleOptionButton()
            check_opt.rect = check_rect
            check_opt.state = option.state & ~QStyle.State_HasFocus
            if option.checkState == Qt.Checked:
                check_opt.state |= QStyle.State_On
            else:
                check_opt.state |= QStyle.State_Off
            style.drawPrimitive(
                QStyle.PE_IndicatorItemViewItemCheck, check_opt, painter,
                widget)
        painter.restore()
示例#37
0
class Phantom(QObject):
    def __init__(self, args, parent=None):
        QObject.__init__(self, parent)

        # variable declarations
        self.m_loadStatus = self.m_state = ''
        self.m_var = self.m_paperSize = self.m_loadScript_cache = {}
        self.m_verbose = args.verbose
        self.m_page = WebPage(self)
        self.m_clipRect = QRect()
        # setup the values from args
        self.m_script = args.script.read()
        self.m_scriptFile = args.script.name
        self.m_scriptDir = os.path.dirname(args.script.name) + '/'
        self.m_args = args.script_args
        self.m_upload_file = args.upload_file
        autoLoadImages = False if args.load_images == 'no' else True
        pluginsEnabled = True if args.load_plugins == 'yes' else False

        args.script.close()

        do_action('PhantomInitPre', Bunch(locals()))

        palette = self.m_page.palette()
        palette.setBrush(QPalette.Base, Qt.transparent)
        self.m_page.setPalette(palette)

        if not args.proxy:
            QNetworkProxyFactory.setUseSystemConfiguration(True)
        else:
            proxy = QNetworkProxy(QNetworkProxy.HttpProxy, args.proxy[0], int(args.proxy[1]))
            QNetworkProxy.setApplicationProxy(proxy)

        self.m_page.settings().setAttribute(QWebSettings.AutoLoadImages, autoLoadImages)
        self.m_page.settings().setAttribute(QWebSettings.PluginsEnabled, pluginsEnabled)
        self.m_page.settings().setAttribute(QWebSettings.FrameFlatteningEnabled, True)
        self.m_page.settings().setAttribute(QWebSettings.OfflineStorageDatabaseEnabled, True)
        self.m_page.settings().setAttribute(QWebSettings.LocalStorageEnabled, True)
        self.m_page.settings().setLocalStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation))
        self.m_page.settings().setOfflineStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation))

        # Ensure we have a document.body.
        self.m_page.mainFrame().setHtml('<html><body></body></html>')

        self.m_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
        self.m_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)

        m_netAccessMan = NetworkAccessManager(args.disk_cache, args.ignore_ssl_errors, self)
        self.m_page.setNetworkAccessManager(m_netAccessMan)

        # inject our properties and slots into javascript
        self.m_page.mainFrame().javaScriptWindowObjectCleared.connect(self.inject)
        self.m_page.loadFinished.connect(self.finish)

        do_action('PhantomInitPost', Bunch(locals()))

    def execute(self):
        if self.m_script.startswith('#!'):
            self.m_script = '//' + self.m_script

        if self.m_scriptFile.lower().endswith('.coffee'):
            coffee = CSConverter(self)
            self.m_script = coffee.convert(self.m_script)

        self.m_page.mainFrame().evaluateJavaScript(self.m_script)

    def finish(self, success):
        self.m_loadStatus = 'success' if success else 'fail'
        self.m_page.mainFrame().evaluateJavaScript(self.m_script)

    def inject(self):
        self.m_page.mainFrame().addToJavaScriptWindowObject('phantom', self)

    def renderPdf(self, fileName):
        p = QPrinter()
        p.setOutputFormat(QPrinter.PdfFormat)
        p.setOutputFileName(fileName)
        p.setResolution(pdf_dpi)
        paperSize = self.m_paperSize

        if not len(paperSize):
            pageSize = QSize(self.m_page.mainFrame().contentsSize())
            paperSize['width'] = str(pageSize.width()) + 'px'
            paperSize['height'] = str(pageSize.height()) + 'px'
            paperSize['border'] = '0px'

        if paperSize.get('width') and paperSize.get('height'):
            sizePt = QSizeF(ceil(self.stringToPointSize(paperSize['width'])),
                            ceil(self.stringToPointSize(paperSize['height'])))
            p.setPaperSize(sizePt, QPrinter.Point)
        elif 'format' in paperSize:
            orientation = QPrinter.Landscape if paperSize.get('orientation') and paperSize['orientation'].lower() == 'landscape' else QPrinter.Portrait
            orientation = QPrinter.Orientation(orientation)
            p.setOrientation(orientation)

            formats = {
                'A3': QPrinter.A3,
                'A4': QPrinter.A4,
                'A5': QPrinter.A5,
                'Legal': QPrinter.Legal,
                'Letter': QPrinter.Letter,
                'Tabloid': QPrinter.Tabloid
            }

            p.setPaperSize(QPrinter.A4) # fallback
            for format, size in formats.items():
                if format.lower() == paperSize['format'].lower():
                    p.setPaperSize(size)
                    break
        else:
            return False

        border = floor(self.stringToPointSize(paperSize['border'])) if paperSize.get('border') else 0
        p.setPageMargins(border, border, border, border, QPrinter.Point)

        self.m_page.mainFrame().print_(p)
        return True

    def returnValue(self):
        return self.m_returnValue

    def stringToPointSize(self, string):
        units = (
            ('mm', 72 / 25.4),
            ('cm', 72 / 2.54),
            ('in', 72.0),
            ('px', 72.0 / pdf_dpi / 2.54),
            ('', 72.0 / pdf_dpi / 2.54)
        )

        for unit, format in units:
            if string.endswith(unit):
                value = string.rstrip(unit)
                return float(value) * format
        return 0

    ##
    # Properties and methods exposed to JavaScript
    ##

    @pyqtProperty('QStringList')
    def args(self):
        return self.m_args

    @pyqtProperty('QVariantMap')
    def clipRect(self):
        result = {
            'width': self.m_clipRect.width(),
            'height': self.m_clipRect.height(),
            'top': self.m_clipRect.top(),
            'left': self.m_clipRect.left()
        }
        return result

    @clipRect.setter
    def clipRect(self, size):
        names = ('width', 'height', 'top', 'left')
        for item in names:
            try:
                globals()[item] = int(size[item])
                if globals()[item] < 0:
                    if item not in ('top', 'left'):
                        globals()[item] = 0
            except KeyError:
                globals()[item] = getattr(self.m_clipRect, item)()

        self.m_clipRect = QRect(left, top, width, height)

    @pyqtProperty(str)
    def content(self):
        return self.m_page.mainFrame().toHtml()

    @content.setter
    def content(self, content):
        self.m_page.mainFrame().setHtml(content)

    @pyqtSlot()
    @pyqtSlot(int)
    def exit(self, code=0):
        self.m_returnValue = code
        self.m_page.loadFinished.disconnect(self.finish)
        QTimer.singleShot(0, qApp, SLOT('quit()'))

    @pyqtProperty(str)
    def loadStatus(self):
        return self.m_loadStatus

    @pyqtSlot(str, result=bool)
    def loadScript(self, script):
        if script in self.m_loadScript_cache:
            self.m_page.mainFrame().evaluateJavaScript(self.m_loadScript_cache[script])
            return True

        scriptFile = script
        try:
            script = codecs.open(self.m_scriptDir + script, encoding='utf-8')
            script = script.read()
        except IOError:
            return False

        if script.startswith('#!'):
            script = '//' + script

        if scriptFile.lower().endswith('.coffee'):
            coffee = CSConverter(self)
            script = coffee.convert(script)

        self.m_loadScript_cache[scriptFile] = script
        self.m_page.mainFrame().evaluateJavaScript(script)
        return True

    @pyqtSlot(str, name='open')
    def open_(self, address):
        qDebug('Opening address %s' % address)
        self.m_page.triggerAction(QWebPage.Stop)
        self.m_loadStatus = 'loading'
        self.m_page.mainFrame().setUrl(QUrl(address))

    @pyqtProperty('QVariantMap')
    def paperSize(self):
        return self.m_paperSize

    @paperSize.setter
    def paperSize(self, size):
        self.m_paperSize = size

    @pyqtSlot(str, result=bool)
    def render(self, fileName):
        fileInfo = QFileInfo(fileName)
        path = QDir()
        path.mkpath(fileInfo.absolutePath())

        if fileName.lower().endswith('.pdf'):
            return self.renderPdf(fileName)

        viewportSize = QSize(self.m_page.viewportSize())
        pageSize = QSize(self.m_page.mainFrame().contentsSize())

        bufferSize = QSize()
        if not self.m_clipRect.isEmpty():
            bufferSize = self.m_clipRect.size()
        else:
            bufferSize = self.m_page.mainFrame().contentsSize()

        if pageSize == '':
            return False

        image = QImage(bufferSize, QImage.Format_ARGB32)
        image.fill(qRgba(255, 255, 255, 0))
        p = QPainter(image)

        p.setRenderHint(QPainter.Antialiasing, True)
        p.setRenderHint(QPainter.TextAntialiasing, True)
        p.setRenderHint(QPainter.SmoothPixmapTransform, True)

        self.m_page.setViewportSize(pageSize)

        if not self.m_clipRect.isEmpty():
            p.translate(-self.m_clipRect.left(), -self.m_clipRect.top())
            self.m_page.mainFrame().render(p, QRegion(self.m_clipRect))
        else:
            self.m_page.mainFrame().render(p)

        p.end()
        self.m_page.setViewportSize(viewportSize)
        return image.save(fileName)

    @pyqtSlot('QWebElement', str)
    def setFormInputFile(self, el, fileTag):
        self.m_page.m_nextFileTag = fileTag
        el.evaluateJavaScript('''(function(target){
                              var evt = document.createEvent('MouseEvents');
                              evt.initMouseEvent("click", true, true, window,
                              0, 0, 0, 0, 0, false, false, false, false, 0, null);
                              target.dispatchEvent(evt);})(this);''')

    @pyqtSlot(int)
    def sleep(self, ms):
        startTime = QTime.currentTime()
        while True:
            QApplication.processEvents(QEventLoop.AllEvents, 25)
            if startTime.msecsTo(QTime.currentTime()) > ms:
                break
            usleep(0.005)

    @pyqtProperty(str)
    def state(self):
        return self.m_state

    @state.setter
    def state(self, value):
        self.m_state = value

    @pyqtProperty(str)
    def userAgent(self):
        return self.m_page.m_userAgent

    @userAgent.setter
    def userAgent(self, ua):
        self.m_page.m_userAgent = ua

    @pyqtSlot(str, result='QVariant')
    @pyqtSlot(int, result='QVariant')
    @pyqtSlot(str, 'QVariant')
    @pyqtSlot(int, 'QVariant')
    def ctx(self, name, value=None):
        if not value:
            return self.m_var.get(name)
        self.m_var[name] = value

    @pyqtProperty('QVariantMap')
    def version(self):
        version = {
            'major': version_major,
            'minor': version_minor,
            'patch': version_patch
        }
        return version

    @pyqtProperty('QVariantMap')
    def viewportSize(self):
        size = self.m_page.viewportSize()
        result = {
            'width': size.width(),
            'height': size.height()
        }
        return result

    @viewportSize.setter
    def viewportSize(self, size):
        names = ('width', 'height')
        for item in names:
            try:
                globals()[item] = int(size[item])
                if globals()[item] < 0:
                    globals()[item] = 0
            except KeyError:
                globals()[item] = getattr(self.m_page.viewportSize(), item)()

        self.m_page.setViewportSize(QSize(width, height))

    do_action('Phantom', Bunch(locals()))
示例#38
0
class WebPage(QObject):
    javaScriptAlertSent = pyqtSignal(str)
    javaScriptConsoleMessageSent = pyqtSignal(str, int, str)
    loadStarted = pyqtSignal()
    loadFinished = pyqtSignal(str)
    resourceReceived = pyqtSignal('QVariantMap')
    resourceRequested = pyqtSignal('QVariantMap')

    def __init__(self, parent=None):
        QObject.__init__(self, parent)

        # variable declarations
        self.m_paperSize = {}
        self.m_clipRect = QRect()
        self.m_libraryPath = ''

        self.setObjectName('WebPage')
        self.m_webPage = CustomPage(self)
        self.m_mainFrame = self.m_webPage.mainFrame()

        self.m_webPage.loadStarted.connect(self.loadStarted)
        self.m_webPage.loadFinished.connect(self.finish)

        # Start with transparent background
        palette = self.m_webPage.palette()
        palette.setBrush(QPalette.Base, Qt.transparent)
        self.m_webPage.setPalette(palette)

        # Page size does not need to take scrollbars into account
        self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
        self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)

        self.m_webPage.settings().setAttribute(QWebSettings.OfflineStorageDatabaseEnabled, True)
        self.m_webPage.settings().setOfflineStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation))
        self.m_webPage.settings().setAttribute(QWebSettings.LocalStorageDatabaseEnabled, True)
        self.m_webPage.settings().setAttribute(QWebSettings.OfflineWebApplicationCacheEnabled, True)
        self.m_webPage.settings().setOfflineWebApplicationCachePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation))
        self.m_webPage.settings().setAttribute(QWebSettings.FrameFlatteningEnabled, True)
        self.m_webPage.settings().setAttribute(QWebSettings.LocalStorageEnabled, True)
        self.m_webPage.settings().setLocalStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation))

        # Ensure we have a document.body.
        self.m_webPage.mainFrame().setHtml('<html><body></body></html>')

        self.m_webPage.setViewportSize(QSize(400, 300))

        do_action('WebPageInit')

    def applySettings(self, defaults):
        opt = self.m_webPage.settings()

        opt.setAttribute(QWebSettings.AutoLoadImages, defaults['loadImages'])
        opt.setAttribute(QWebSettings.PluginsEnabled, defaults['loadPlugins'])
        opt.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, defaults['localAccessRemote'])
        if 'userAgent' in defaults:
            self.m_webPage.m_userAgent = defaults['userAgent']

    def finish(self, ok):
        status = 'success' if ok else 'fail'
        self.loadFinished.emit(status)

    def mainFrame(self):
        return self.m_mainFrame

    def renderImage(self):
        viewportSize = self.m_webPage.viewportSize()
        frameRect = QRect(QPoint(0, 0), viewportSize)
        if not self.m_clipRect.isEmpty():
            frameRect = self.m_clipRect

        if self.m_webPage.scrollPosition:
            self.m_webPage.mainFrame().\
                setScrollPosition(QPoint(self.m_webPage.scrollPosition.x(),
                                         self.m_webPage.scrollPosition.y() ))

        image = QImage(frameRect.size(), QImage.Format_ARGB32)
        image.fill(qRgba(255, 255, 255, 0))

        painter = QPainter()

        # We use tiling approach to work-around Qt software rasterizer bug
        # when dealing with very large paint device.
        # See http://code.google.com/p/phantomjs/issues/detail?id=54.
        tileSize = 4096
        htiles = (image.width() + tileSize - 1) / tileSize
        vtiles = (image.height() + tileSize - 1) / tileSize
        for x in range(htiles):
            for y in range(vtiles):
                tileBuffer = QImage(tileSize, tileSize, QImage.Format_ARGB32)
                tileBuffer.fill(qRgba(255, 255, 255, 0))

                # Render the web page onto the small tile first
                painter.begin(tileBuffer)
                painter.setRenderHint(QPainter.Antialiasing, True)
                painter.setRenderHint(QPainter.TextAntialiasing, True)
                painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
                painter.translate(-frameRect.left(), -frameRect.top())
                painter.translate(-x * tileSize, -y * tileSize)
                self.m_mainFrame.render(painter, QRegion(frameRect))
                painter.end()

                # Copy the tile to the main buffer
                painter.begin(image)
                painter.setCompositionMode(QPainter.CompositionMode_Source)
                painter.drawImage(x * tileSize, y * tileSize, tileBuffer)
                painter.end()

        self.m_webPage.setViewportSize(viewportSize)
        return image

    def renderPdf(self, fileName):
        p = QPrinter()
        p.setOutputFormat(QPrinter.PdfFormat)
        p.setOutputFileName(fileName)
        p.setResolution(pdf_dpi)
        paperSize = self.m_paperSize

        if not len(paperSize):
            pageSize = QSize(self.m_webPage.mainFrame().contentsSize())
            paperSize['width'] = str(pageSize.width()) + 'px'
            paperSize['height'] = str(pageSize.height()) + 'px'
            paperSize['border'] = '0px'

        if paperSize.get('width') and paperSize.get('height'):
            sizePt = QSizeF(ceil(self.stringToPointSize(paperSize['width'])),
                            ceil(self.stringToPointSize(paperSize['height'])))
            p.setPaperSize(sizePt, QPrinter.Point)
        elif 'format' in paperSize:
            orientation = QPrinter.Landscape if paperSize.get('orientation') and paperSize['orientation'].lower() == 'landscape' else QPrinter.Portrait
            orientation = QPrinter.Orientation(orientation)
            p.setOrientation(orientation)

            formats = {
                'A0': QPrinter.A0,
                'A1': QPrinter.A1,
                'A2': QPrinter.A2,
                'A3': QPrinter.A3,
                'A4': QPrinter.A4,
                'A5': QPrinter.A5,
                'A6': QPrinter.A6,
                'A7': QPrinter.A7,
                'A8': QPrinter.A8,
                'A9': QPrinter.A9,
                'B0': QPrinter.B0,
                'B1': QPrinter.B1,
                'B2': QPrinter.B2,
                'B3': QPrinter.B3,
                'B4': QPrinter.B4,
                'B5': QPrinter.B5,
                'B6': QPrinter.B6,
                'B7': QPrinter.B7,
                'B8': QPrinter.B8,
                'B9': QPrinter.B9,
                'B10': QPrinter.B10,
                'C5E': QPrinter.C5E,
                'Comm10E': QPrinter.Comm10E,
                'DLE': QPrinter.DLE,
                'Executive': QPrinter.Executive,
                'Folio': QPrinter.Folio,
                'Ledger': QPrinter.Ledger,
                'Legal': QPrinter.Legal,
                'Letter': QPrinter.Letter,
                'Tabloid': QPrinter.Tabloid
            }

            p.setPaperSize(QPrinter.A4) # fallback
            for format, size in formats.items():
                if format.lower() == paperSize['format'].lower():
                    p.setPaperSize(size)
                    break
        else:
            return False

        border = floor(self.stringToPointSize(paperSize['border'])) if paperSize.get('border') else 0
        p.setPageMargins(border, border, border, border, QPrinter.Point)

        self.m_webPage.mainFrame().print_(p)
        return True

    def setNetworkAccessManager(self, networkAccessManager):
        self.m_webPage.setNetworkAccessManager(networkAccessManager)
        networkAccessManager.resourceRequested.connect(self.resourceRequested)
        networkAccessManager.resourceReceived.connect(self.resourceReceived)

    def stringToPointSize(self, string):
        units = (
            ('mm', 72 / 25.4),
            ('cm', 72 / 2.54),
            ('in', 72.0),
            ('px', 72.0 / pdf_dpi / 2.54),
            ('', 72.0 / pdf_dpi / 2.54)
        )

        for unit, format in units:
            if string.endswith(unit):
                value = string.rstrip(unit)
                return float(value) * format
        return 0

    def userAgent(self):
        return self.m_webPage.m_userAgent

    ##
    # Properties and methods exposed to JavaScript
    ##

    @pyqtSlot(str)
    def _appendScriptElement(self, scriptUrl):
        self.m_mainFrame.evaluateJavaScript('''
            var el = document.createElement('script');
            el.onload = function() { alert('%(scriptUrl)s'); };
            el.src = '%(scriptUrl)s';
            document.body.appendChild(el);
        ''' % {'scriptUrl': scriptUrl})

    @pyqtProperty('QVariantMap')
    def clipRect(self):
        result = {
            'width': self.m_clipRect.width(),
            'height': self.m_clipRect.height(),
            'top': self.m_clipRect.top(),
            'left': self.m_clipRect.left()
        }
        return result

    @clipRect.setter
    def clipRect(self, size):
        sizes = {'width': 0, 'height': 0, 'top': 0, 'left': 0}
        for item in sizes.keys():
            try:
                sizes[item] = int(size[item])
                if sizes[item] < 0:
                    if item not in ('top', 'left'):
                        sizes[item] = 0
            except (KeyError, ValueError):
                sizes[item] = self.clipRect[item]

        self.m_clipRect = QRect(sizes['left'], sizes['top'], sizes['width'], sizes['height'])

    @pyqtProperty(str)
    def content(self):
        return self.m_mainFrame.toHtml()

    @content.setter
    def content(self, content):
        self.m_mainFrame.setHtml(content)

    @pyqtSlot(str, result='QVariant')
    def evaluate(self, code):
        function = '(%s)()' % code
        return self.m_mainFrame.evaluateJavaScript(function)

    @pyqtSlot(str, result=bool)
    def injectJs(self, filePath):
        return injectJsInFrame(filePath, self.m_libraryPath, self.m_mainFrame)

    @pyqtSlot(str, str, 'QVariantMap')
    @pyqtSlot(str, 'QVariantMap', 'QVariantMap')
    def openUrl(self, address, op, settings):
        operation = op
        body = QByteArray()

        self.applySettings(settings)
        self.m_webPage.triggerAction(QWebPage.Stop)

        if type(op) is dict:
            operation = op.get('operation')
            body = QByteArray(op.get('data', ''))

        if operation == '':
            operation = 'get'

        networkOp = QNetworkAccessManager.CustomOperation
        operation = operation.lower()
        if operation == 'get':
            networkOp = QNetworkAccessManager.GetOperation
        elif operation == 'head':
            networkOp = QNetworkAccessManager.HeadOperation
        elif operation == 'put':
            networkOp = QNetworkAccessManager.PutOperation
        elif operation == 'post':
            networkOp = QNetworkAccessManager.PostOperation
        elif operation == 'delete':
            networkOp = QNetworkAccessManager.DeleteOperation

        if networkOp == QNetworkAccessManager.CustomOperation:
            self.m_mainFrame.evaluateJavaScript('console.error("Unknown network operation: %s");' % operation)
            return

        self.m_mainFrame.load(QNetworkRequest(QUrl(address)), networkOp, body)

    @pyqtProperty('QVariantMap')
    def paperSize(self):
        return self.m_paperSize

    @paperSize.setter
    def paperSize(self, size):
        self.m_paperSize = size

    @pyqtSlot(str, result=bool)
    def render(self, fileName):
        if self.m_mainFrame.contentsSize() == '':
            return False

        fileInfo = QFileInfo(fileName)
        path = QDir()
        path.mkpath(fileInfo.absolutePath())

        if fileName.lower().endswith('.pdf'):
            return self.renderPdf(fileName)

        image = self.renderImage()

        return image.save(fileName)

    @pyqtProperty(str)
    def libraryPath(self):
        return self.m_libraryPath

    @libraryPath.setter
    def libraryPath(self, dirPath):
        self.m_libraryPath = dirPath

    @pyqtSlot(str, str)
    def uploadFile(self, selector, fileName):
        el = self.m_mainFrame.findFirstElement(selector)
        if el.isNull():
            return

        self.m_webPage.m_uploadFile = fileName
        el.evaluateJavaScript('''
            (function (el) {
                var ev = document.createEvent('MouseEvents');
                ev.initEvent('click', true, true);
                el.dispatchEvent(ev);
            })(this)
        ''')

    @pyqtProperty('QVariantMap')
    def viewportSize(self):
        size = self.m_webPage.viewportSize()
        result = {
            'width': size.width(),
            'height': size.height()
        }
        return result

    @viewportSize.setter
    def viewportSize(self, size):
        sizes = {'width': 0, 'height': 0}
        for item in sizes.keys():
            try:
                sizes[item] = int(size[item])
                if sizes[item] < 0:
                    sizes[item] = 0
            except (KeyError, ValueError):
                sizes[item] = self.viewportSize[item]

        self.m_webPage.setViewportSize(QSize(sizes['width'], sizes['height']))

    @pyqtProperty('QVariantMap')
    def scrollPosition(self):
        scroll = self.m_webPage.scrollPosition
        result = {
            'left': scroll.x(),
            'top': scroll.y()
        }
        return result

    @scrollPosition.setter
    def scrollPosition(self, size):
        names = ('left', 'top')
        for item in names:
            try:
                globals()[item] = int(size[item])
                if globals()[item] < 0:
                    globals()[item] = 0
            except KeyError:
                globals()[item] = getattr(self.m_webPage.scrollPosition, item)()
        self.m_webPage.scrollPosition = QPoint(left, top)

    do_action('WebPage')
示例#39
0
class ConnecMapTool(ParentMapTool):
    """ Button 20. User select connections from layer 'connec'
    Execute SQL function: 'gw_fct_connect_to_network' """

    def __init__(self, iface, settings, action, index_action):
        """ Class constructor """

        # Call ParentMapTool constructor
        super(ConnecMapTool, self).__init__(iface, settings, action, index_action)

        self.dragging = False

        # Vertex marker
        self.vertexMarker = QgsVertexMarker(self.canvas)
        self.vertexMarker.setColor(QColor(255, 25, 25))
        self.vertexMarker.setIconSize(11)
        self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX)  # or ICON_CROSS, ICON_X
        self.vertexMarker.setPenWidth(5)

        # Rubber band
        self.rubberBand = QgsRubberBand(self.canvas, True)
        mFillColor = QColor(100, 0, 0)
        self.rubberBand.setColor(mFillColor)
        self.rubberBand.setWidth(3)
        mBorderColor = QColor(254, 58, 29)
        self.rubberBand.setBorderColor(mBorderColor)

        # Select rectangle
        self.selectRect = QRect()

    def reset(self):
        """ Clear selected features """

        layer = self.layer_connec
        if layer is not None:
            layer.removeSelection()

        # Graphic elements
        self.rubberBand.reset()

    """ QgsMapTools inherited event functions """

    def canvasMoveEvent(self, event):
        """ With left click the digitizing is finished """

        if event.buttons() == Qt.LeftButton:

            if not self.dragging:
                self.dragging = True
                self.selectRect.setTopLeft(event.pos())

            self.selectRect.setBottomRight(event.pos())
            self.set_rubber_band()

        else:

            # Hide highlight
            self.vertexMarker.hide()

            # Get the click
            x = event.pos().x()
            y = event.pos().y()
            eventPoint = QPoint(x, y)

            # Snapping
            (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint)  # @UnusedVariable

            # That's the snapped point
            if result <> []:

                # Check Arc or Node
                for snapPoint in result:

                    if snapPoint.layer == self.layer_connec:

                        # Get the point
                        point = QgsPoint(result[0].snappedVertex)

                        # Add marker
                        self.vertexMarker.setCenter(point)
                        self.vertexMarker.show()

                        break

    def canvasPressEvent(self, event):

        self.selectRect.setRect(0, 0, 0, 0)
        self.rubberBand.reset()

    def canvasReleaseEvent(self, event):
        """ With left click the digitizing is finished """

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

            # Get the click
            x = event.pos().x()
            y = event.pos().y()
            eventPoint = QPoint(x, y)

            # Node layer
            layer = self.layer_connec

            # Not dragging, just simple selection
            if not self.dragging:

                # Snap to node
                (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint)  # @UnusedVariable

                # That's the snapped point
                if result <> [] and (result[0].layer.name() == self.layer_connec.name()):

                    point = QgsPoint(result[0].snappedVertex)  # @UnusedVariable
                    layer.removeSelection()
                    layer.select([result[0].snappedAtGeometry])

                    # Create link
                    self.link_connec()

                    # Hide highlight
                    self.vertexMarker.hide()

            else:

                # Set valid values for rectangle's width and height
                if self.selectRect.width() == 1:
                    self.selectRect.setLeft(self.selectRect.left() + 1)

                if self.selectRect.height() == 1:
                    self.selectRect.setBottom(self.selectRect.bottom() + 1)

                self.set_rubber_band()
                selectGeom = self.rubberBand.asGeometry()  # @UnusedVariable
                self.select_multiple_features(self.selectRectMapCoord)
                self.dragging = False

                # Create link
                self.link_connec()

        elif event.button() == Qt.RightButton:

            # Create link
            self.link_connec()

    def activate(self):

        # Check button
        self.action().setChecked(True)

        # Rubber band
        self.rubberBand.reset()

        # Store user snapping configuration
        self.snapperManager.storeSnappingOptions()

        # Clear snapping
        self.snapperManager.clearSnapping()

        # Set snapping to arc and node
        self.snapperManager.snapToConnec()

        # Change cursor
        self.canvas.setCursor(self.cursor)

        # Show help message when action is activated
        if self.show_help:
            message = (
                "Right click to use current selection, select connec points by clicking or dragging (selection box)"
            )
            self.controller.show_info(message, context_name="ui_message")

        # Control current layer (due to QGIS bug in snapping system)
        try:
            if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer:
                self.canvas.setCurrentLayer(self.layer_connec)
        except:
            self.canvas.setCurrentLayer(self.layer_connec)

    def deactivate(self):

        # Check button
        self.action().setChecked(False)

        # Rubber band
        self.rubberBand.reset()

        # Restore previous snapping
        self.snapperManager.recoverSnappingOptions()

        # Recover cursor
        self.canvas.setCursor(self.stdCursor)

    def link_connec(self):
        """ Link selected connec to the pipe """

        # Get selected features (from layer 'connec')
        aux = "{"
        layer = self.layer_connec
        if layer.selectedFeatureCount() == 0:
            message = "You have to select at least one feature!"
            self.controller.show_warning(message, context_name="ui_message")

            return
        features = layer.selectedFeatures()
        for feature in features:
            connec_id = feature.attribute("connec_id")
            aux += str(connec_id) + ", "
        connec_array = aux[:-2] + "}"

        # Execute function
        function_name = "gw_fct_connect_to_network"
        sql = "SELECT " + self.schema_name + "." + function_name + "('" + connec_array + "');"
        self.controller.execute_sql(sql)

        # Refresh map canvas
        self.rubberBand.reset()
        self.iface.mapCanvas().refresh()

    def set_rubber_band(self):

        # Coordinates transform
        transform = self.canvas.getCoordinateTransform()

        # Coordinates
        ll = transform.toMapCoordinates(self.selectRect.left(), self.selectRect.bottom())
        lr = transform.toMapCoordinates(self.selectRect.right(), self.selectRect.bottom())
        ul = transform.toMapCoordinates(self.selectRect.left(), self.selectRect.top())
        ur = transform.toMapCoordinates(self.selectRect.right(), self.selectRect.top())

        # Rubber band
        self.rubberBand.reset()
        self.rubberBand.addPoint(ll, False)
        self.rubberBand.addPoint(lr, False)
        self.rubberBand.addPoint(ur, False)
        self.rubberBand.addPoint(ul, False)
        self.rubberBand.addPoint(ll, True)

        self.selectRectMapCoord = QgsRectangle(ll, ur)

    def select_multiple_features(self, selectGeometry):

        # Default choice
        behaviour = QgsVectorLayer.SetSelection

        # Modifiers
        modifiers = QApplication.keyboardModifiers()

        if modifiers == Qt.ControlModifier:
            behaviour = QgsVectorLayer.AddToSelection
        elif modifiers == Qt.ShiftModifier:
            behaviour = QgsVectorLayer.RemoveFromSelection

        if self.layer_connec is None:
            return

        # Change cursor
        QApplication.setOverrideCursor(Qt.WaitCursor)

        # Selection
        self.layer_connec.selectByRect(selectGeometry, behaviour)

        # Old cursor
        QApplication.restoreOverrideCursor()