Пример #1
0
    def _get_icon_rect(self, opt, text_rect):
        """Get a QRect for the icon to draw.

        Args:
            opt: QStyleOptionTab
            text_rect: The QRect for the text.

        Return:
            A QRect.
        """
        icon_size = opt.iconSize
        if not icon_size.isValid():
            icon_extent = self.pixelMetric(QStyle.PM_SmallIconSize)
            icon_size = QSize(icon_extent, icon_extent)
        icon_mode = (QIcon.Normal if opt.state & QStyle.State_Enabled
                     else QIcon.Disabled)
        icon_state = (QIcon.On if opt.state & QStyle.State_Selected
                      else QIcon.Off)
        tab_icon_size = opt.icon.actualSize(icon_size, icon_mode, icon_state)
        tab_icon_size = QSize(min(tab_icon_size.width(), icon_size.width()),
                              min(tab_icon_size.height(), icon_size.height()))
        icon_rect = QRect(text_rect.left(),
                          text_rect.center().y() - tab_icon_size.height() / 2,
                          tab_icon_size.width(), tab_icon_size.height())
        icon_rect = self._style.visualRect(opt.direction, opt.rect, icon_rect)
        qtutils.ensure_valid(icon_rect)
        return icon_rect
Пример #2
0
    def _get_icon_rect(self, opt, text_rect):
        """Get a QRect for the icon to draw.

        Args:
            opt: QStyleOptionTab
            text_rect: The QRect for the text.

        Return:
            A QRect.
        """
        icon_size = opt.iconSize
        if not icon_size.isValid():
            icon_extent = self.pixelMetric(QStyle.PM_SmallIconSize)
            icon_size = QSize(icon_extent, icon_extent)
        icon_mode = (QIcon.Normal if opt.state & QStyle.State_Enabled
                     else QIcon.Disabled)
        icon_state = (QIcon.On if opt.state & QStyle.State_Selected
                      else QIcon.Off)
        # reserve space for favicon when tab bar is vertical (issue #1968)
        position = config.val.tabs.position
        if (position in [QTabWidget.East, QTabWidget.West] and
                config.val.tabs.favicons.show):
            tab_icon_size = icon_size
        else:
            actual_size = opt.icon.actualSize(icon_size, icon_mode, icon_state)
            tab_icon_size = QSize(
                min(actual_size.width(), icon_size.width()),
                min(actual_size.height(), icon_size.height()))

        icon_top = text_rect.center().y() + 1 - tab_icon_size.height() / 2
        icon_rect = QRect(QPoint(text_rect.left(), icon_top), tab_icon_size)
        icon_rect = self._style.visualRect(opt.direction, opt.rect, icon_rect)
        return icon_rect
Пример #3
0
 def maxSize(self):
     size = QSize()
     for variation in self.d.variations:
         size.setWidth(max(size.width(), variation.map.width()))
         size.setHeight(max(size.height(), variation.map.height()))
     
     return size
Пример #4
0
    def get_favicon(self):
        u"""
        Get favicon for the site.

        This is called when the site_url can’t be loaded or when that
        page doesn’t contain a link tag with rel set to icon (the new
        way of doing site icons.)
        """
        if self.site_icon:
            return
        if not with_pyqt:
            self.site_icon = None
            return
        ico_url = urllib.parse.urljoin(self.icon_url, "/favicon.ico")
        ico_request = urllib.request.Request(ico_url)
        if self.user_agent:
            ico_request.add_header('User-agent', self.user_agent)
        ico_response = urllib.request.urlopen(ico_request)
        if 200 != ico_response.code:
            self.site_icon = None
            return
        self.site_icon = QImage.fromData(ico_response.read())
        max_size = QSize(self.max_icon_size, self.max_icon_size)
        ico_size = self.site_icon.size()
        if ico_size.width() > max_size.width() \
                or ico_size.height() > max_size.height():
            self.site_icon = self.site_icon.scaled(
                max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
Пример #5
0
def calculate_relative_position(parent_rect: QtCore.QRect, own_size: QtCore.QSize,
                                constraint: RelativeLayoutConstraint):
    """
    Calculates the position of the element, given its size, the position and size of the parent and a relative layout
    constraint. The position is the position of the parent plus the weighted size of the parent, the weighted size of
    the element and an offset. The weights and the offset are given by the constraint for each direction.

    :param parent_rect: parent coordinates and size as rectangle
    :param own_size: size of the element (width and height)
    :param constraint: relative layout constraint to apply
    :return: tuple of recommended x and y positions of the element
    """
    """
        Returns the left, upper corner of an object if the parent rectangle (QRect) is given and our own size (QSize)
        and a relative layout constraint (see RelativeLayoutConstraint).
    """
    x = (parent_rect.x()
         + constraint.x[0] * parent_rect.width()
         + constraint.x[1] * own_size.width()
         + constraint.x[2])
    y = (parent_rect.y()
         + constraint.y[0] * parent_rect.height()
         + constraint.y[1] * own_size.height()
         + constraint.y[2])
    return x, y
Пример #6
0
    def renderToTexture(self, levelOfDetail = 1.0):
        # Determine the fbo size we will need.
        size = (self.sceneRect().size() * levelOfDetail).toSize()
        fboSize = nextPowerOfTwo(size)
        if fboSize.isEmpty():
            fboSize = QSize(16, 16)

        # Create or re-create the fbo.
        if self.fbo is None or self.fbo.size() != fboSize:
            #del self.fbo
            self.fbo = QGLFramebufferObject(fboSize, self.format)
            if not self.fbo.isValid():
                #del self.fbo
                self.fbo = None
                return 0
            self.dirty = True

        # Return the previous texture contents if the scene hasn't changed.
        if self.fbo is not None and not self.dirty:
            return self.fbo.texture()

        # Render the scene into the fbo, scaling the QPainter's view
        # transform up to the power-of-two fbo size.
        painter = QPainter(self.fbo)
        painter.setWindow(0, 0, size.width(), size.height())
        painter.setViewport(0, 0, fboSize.width(), fboSize.height())
        self.render(painter)
        painter.end()
        self.dirty = False
        return self.fbo.texture()
Пример #7
0
    def set_viewport(self, size, raise_if_empty=False):
        """
        Set viewport size.
        If size is "full" viewport size is detected automatically.
        If can also be "<width>x<height>".

        .. note::

           This will update all JS geometry variables, but window resize event
           is delivered asynchronously and so ``window.resize`` will not be
           invoked until control is yielded to the event loop.

        """
        if size == 'full':
            size = self.web_page.mainFrame().contentsSize()
            self.logger.log("Contents size: %s" % size, min_level=2)
            if size.isEmpty():
                if raise_if_empty:
                    raise RuntimeError("Cannot detect viewport size")
                else:
                    size = defaults.VIEWPORT_SIZE
                    self.logger.log("Viewport is empty, falling back to: %s" %
                                    size)

        if not isinstance(size, QSize):
            validate_size_str(size)
            w, h = map(int, size.split('x'))
            size = QSize(w, h)
        self.web_page.setViewportSize(size)
        self._force_relayout()
        w, h = int(size.width()), int(size.height())
        self.logger.log("viewport size is set to %sx%s" % (w, h), min_level=2)
        return w, h
Пример #8
0
    def sizeHint(self):
        hint = QLabel.sizeHint(self)

        if self.maximum_size_hint != None:
            hint = QSize(max(hint.width(), self.maximum_size_hint.width()),
                         max(hint.height(), self.maximum_size_hint.height()))

        self.maximum_size_hint = hint

        return hint
Пример #9
0
    def calculateSize(self, sizeType):
        totalSize = QSize()

        for wrapper in self.list:
            position = wrapper.position
            itemSize = QSize()

            if sizeType == self.MinimumSize:
                itemSize = wrapper.item.minimumSize()
            else: # sizeType == self.SizeHint
                itemSize = wrapper.item.sizeHint()

            if position in (self.North, self.South, self.Center):
                totalSize.setHeight(totalSize.height() + itemSize.height())

            if position in (self.West, self.East, self.Center):
                totalSize.setWidth(totalSize.width() + itemSize.width())

        return totalSize
Пример #10
0
class FeatureTableWidgetHHeader(QTableWidgetItem):
    sub_trans = str.maketrans('0123456789', '₀₁₂₃₄₅₆₇₈₉')

    def __init__(self, column, sigma=None, window_size=3.5):
        QTableWidgetItem.__init__(self)
        # init
        # ------------------------------------------------
        self.column = column
        self.sigma = sigma
        self.window_size = window_size
        self.pixmapSize = QSize(61, 61)
        self.setNameAndBrush(self.sigma)

    @property
    def brushSize(self):
        if self.sigma is None:
            return 0
        else:
            return int(3.0 * self.sigma + 0.5) + 1

    def setNameAndBrush(self, sigma, color=Qt.black):
        self.sigma = sigma
        self.setText(f'σ{self.column}'.translate(self.sub_trans))
        if self.sigma is not None:
            total_window = (1 + 2 * int(self.sigma * self.window_size + 0.5))
            self.setToolTip(f'sigma = {sigma:.1f} pixels, window diameter = {total_window:.1f}')
        font = QFont()
        font.setPointSize(10)
        # font.setBold(True)
        self.setFont(font)
        self.setForeground(color)

        pixmap = QPixmap(self.pixmapSize)
        pixmap.fill(Qt.transparent)
        painter = QPainter()
        painter.begin(pixmap)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setPen(color)
        brush = QBrush(color)
        painter.setBrush(brush)
        painter.drawEllipse(QRect(old_div(self.pixmapSize.width(), 2) - old_div(self.brushSize, 2),
                                  old_div(self.pixmapSize.height(), 2) - old_div(self.brushSize, 2),
                                  self.brushSize, self.brushSize))
        painter.end()
        self.setIcon(QIcon(pixmap))
        self.setTextAlignment(Qt.AlignVCenter)

    def setIconAndTextColor(self, color):
        self.setNameAndBrush(self.sigma, color)
Пример #11
0
 def mapSize(self):
     p = RenderParams(self.map())
     # The map size is the same regardless of which indexes are shifted.
     if (p.staggerX):
         size = QSize(self.map().width() * p.columnWidth + p.sideOffsetX,
                    self.map().height() * (p.tileHeight + p.sideLengthY))
         if (self.map().width() > 1):
             size.setHeight(size.height() + p.rowHeight)
         return size
     else:
         size = QSize(self.map().width() * (p.tileWidth + p.sideLengthX),
                    self.map().height() * p.rowHeight + p.sideOffsetY)
         if (self.map().height() > 1):
             size.setWidth(size.width() + p.columnWidth)
         return size
Пример #12
0
    def calculate_size(self):
        """Determine size by calculating the space of the visible items"""

        visible_items = min(self.model().rowCount(), self.MAX_VISIBLE_ITEMS)
        first_visible_row = self.verticalScrollBar().value()
        option = self.viewOptions()
        size_hint = QSize()
        for index in range(visible_items):
            tmp_size = self.itemDelegate().sizeHint(
                option, self.model().index(index + first_visible_row, 0))
            if size_hint.width() < tmp_size.width():
                size_hint = tmp_size

        height = size_hint.height()
        height *= visible_items
        size_hint.setHeight(height)
        return size_hint
Пример #13
0
    def maybe_get_icon(self):
        u"""
        Get icon for the site as a QImage if we haven’t already.

        Get the site icon, either the 'rel="icon"' or the favicon, for
        the web page at url or passed in as page_html and store it as
        a QImage. This function can be called repeatedly and loads the
        icon only once.
        """
        if self.site_icon:
            return
        if not with_pyqt:
            self.site_icon = None
            return
        page_request = urllib.request.Request(self.icon_url)
        if self.user_agent:
            page_request.add_header('User-agent', self.user_agent)
        page_response = urllib.request.urlopen(page_request)
        if 200 != page_response.code:
            self.get_favicon()
            return
        page_soup = soup(page_response, 'html.parser')
        try:
            icon_url = page_soup.find(
                name='link', attrs={'rel': 'icon'})['href']
        except (TypeError, KeyError):
            self.get_favicon()
            return
        # The url may be absolute or relative.
        if not urllib.parse.urlsplit(icon_url).netloc:
            icon_url = urllib.parse.urljoin(
                self.url, urllib.parse.quote(icon_url.encode('utf-8')))
        icon_request = urllib.request.Request(icon_url)
        if self.user_agent:
            icon_request.add_header('User-agent', self.user_agent)
        icon_response = urllib.request.urlopen(icon_request)
        if 200 != icon_response.code:
            self.site_icon = None
            return
        self.site_icon = QImage.fromData(icon_response.read())
        max_size = QSize(self.max_icon_size, self.max_icon_size)
        icon_size = self.site_icon.size()
        if icon_size.width() > max_size.width() \
                or icon_size.height() > max_size.height():
            self.site_icon = self.site_icon.scaled(
                max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
Пример #14
0
    def __init__(self, parent):
        super().__init__(parent)

        self.setFocusPolicy(Qt.NoFocus)

        self.setBackgroundRole(QPalette.Window)
        self.setFrameShape(QFrame.NoFrame)
        self.setBackgroundBrush(QBrush(Qt.NoBrush))

        self._scene = QGraphicsScene(self)
        self.setScene(self._scene)

        self._keys = {}
        self._midi_to_key = {}
        for octave in range(2, 7):
            for idx, note in enumerate(['C', 'D', 'E', 'F', 'G', 'A', 'B']):
                pitch_name = '%s%d' % (note, octave)
                key = PianoKey(
                    parent,
                    140 * octave + 20 * idx,
                    pitch_name,
                    PianoKey.WHITE)
                self._keys[pitch_name] = key
                self._midi_to_key[music.NOTE_TO_MIDI[pitch_name]] = key
                self._scene.addItem(key)

            for idx, note in enumerate(['C#', 'D#', '', 'F#', 'G#', 'A#', '']):
                if not note:
                    continue
                pitch_name = '%s%d' % (note, octave)
                key = PianoKey(
                    parent,
                    140 * octave + 20 * idx + 10,
                    pitch_name,
                    PianoKey.BLACK)
                self._keys[pitch_name] = key
                self._midi_to_key[music.NOTE_TO_MIDI[pitch_name]] = key
                self._scene.addItem(key)


        size = self._scene.sceneRect().size()
        size = QSize(int(math.ceil(size.width())) + 1,
                     int(math.ceil(size.height())) + 10)
        self.setMinimumSize(size)
        self.setMaximumSize(size)
Пример #15
0
 def maybe_get_icon(self):
     if self.site_icon:
         return
     if not with_pyqt:
         self.site_icon = None
         return
     try:
         icon_data = self.get_data_from_url(self.full_icon_url)
     except:
         AudioDownloader.maybe_get_icon(self)
     else:
         self.site_icon = QImage.fromData(icon_data)
         max_size = QSize(self.max_icon_size, self.max_icon_size)
         ico_size = self.site_icon.size()
         if ico_size.width() > max_size.width() \
                 or ico_size.height() > max_size.height():
             self.site_icon = self.site_icon.scaled(
                 max_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
Пример #16
0
 def setColor(self, color):
     if type(color)!=QColor:
         color = QColor(color)
     if (self.mColor == color or not color.isValid()):
         return
     self.mColor = color
     size = QSize(self.iconSize())
     size.setWidth(size.width()-2)
     size.setHeight(size.height()-2)
     pixmap = QPixmap(size)
     pixmap.fill(self.mColor)
     painter = QPainter(pixmap)
     border = QColor(Qt.black)
     border.setAlpha(128)
     painter.setPen(border)
     painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1)
     painter.end()
     self.setIcon(QIcon(pixmap))
     self.colorChanged.emit(color)
Пример #17
0
class ResizeMap(QUndoCommand):
    def __init__(self, mapDocument, size):
        super().__init__(QCoreApplication.translate("Undo Commands", "Resize Map"))
        
        self.mMapDocument = mapDocument
        self.mSize = QSize(size)

    def undo(self):
        self.swapSize()

    def redo(self):
        self.swapSize()

    def swapSize(self):
        map = self.mMapDocument.map()
        oldSize = QSize(map.width(), map.height())
        map.setWidth(self.mSize.width())
        map.setHeight(self.mSize.height())
        self.mSize = oldSize
        self.mMapDocument.emitMapChanged()
Пример #18
0
    def paintEvent(self, event, *args):
        """ Custom paint event """
        self.mutex.lock()

        # Paint custom frame image on QWidget
        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing, True)

        # Fill background black
        painter.fillRect(event.rect(), self.palette().window())

        if self.current_image:
            # DRAW FRAME
            # Calculate new frame image size, maintaining aspect ratio
            pixSize = self.current_image.size()
            pixSize.scale(event.rect().size(), Qt.KeepAspectRatio)

            # Scale image
            scaledPix = self.current_image.scaled(pixSize, Qt.KeepAspectRatio, Qt.SmoothTransformation)

            # Calculate center of QWidget and Draw image
            center = self.centeredViewport(self.width(), self.height())
            painter.drawImage(center, scaledPix)

        if self.transforming_clip:
            # Draw transform handles on top of video preview
            # Get framerate
            fps = get_app().project.get(["fps"])
            fps_float = float(fps["num"]) / float(fps["den"])

            # Determine frame # of clip
            start_of_clip = round(float(self.transforming_clip.data["start"]) * fps_float)
            position_of_clip = (float(self.transforming_clip.data["position"]) * fps_float) + 1
            playhead_position = float(get_app().window.preview_thread.current_frame)
            clip_frame_number = round(playhead_position - position_of_clip) + start_of_clip + 1

            # Get properties of clip at current frame
            raw_properties = json.loads(self.transforming_clip_object.PropertiesJSON(clip_frame_number))

            # Get size of current video player
            player_width = self.rect().width()
            player_height = self.rect().height()

            # Determine original size of clip's reader
            source_width = self.transforming_clip.data['reader']['width']
            source_height = self.transforming_clip.data['reader']['height']
            source_size = QSize(source_width, source_height)

            # Determine scale of clip
            scale = self.transforming_clip.data['scale']
            if scale == openshot.SCALE_FIT:
                source_size.scale(player_width, player_height, Qt.KeepAspectRatio)

            elif scale == openshot.SCALE_STRETCH:
                source_size.scale(player_width, player_height, Qt.IgnoreAspectRatio)

            elif scale == openshot.SCALE_CROP:
                width_size = QSize(player_width, round(player_width / (float(source_width) / float(source_height))))
                height_size = QSize(round(player_height / (float(source_height) / float(source_width))), player_height)
                if width_size.width() >= player_width and width_size.height() >= player_height:
                    source_size.scale(width_size.width(), width_size.height(), Qt.KeepAspectRatio)
                else:
                    source_size.scale(height_size.width(), height_size.height(), Qt.KeepAspectRatio)

            # Get new source width / height (after scaling mode applied)
            source_width = source_size.width()
            source_height = source_size.height()

            # Init X/Y
            x = 0.0
            y = 0.0

            # Get scaled source image size (scale_x, scale_y)
            sx = max(float(raw_properties.get('scale_x').get('value')), 0.001)
            sy = max(float(raw_properties.get('scale_y').get('value')), 0.001)
            scaled_source_width = source_width * sx
            scaled_source_height = source_height * sy

            # Determine gravity of clip
            gravity = self.transforming_clip.data['gravity']
            if gravity == openshot.GRAVITY_TOP_LEFT:
                x += self.centeredViewport(self.width(), self.height()).x()  # nudge right
                y += self.centeredViewport(self.width(), self.height()).y()  # nudge down
            elif gravity == openshot.GRAVITY_TOP:
                x = (player_width - scaled_source_width) / 2.0 # center
                y += self.centeredViewport(self.width(), self.height()).y()  # nudge down
            elif gravity == openshot.GRAVITY_TOP_RIGHT:
                x = player_width - scaled_source_width # right
                x -= self.centeredViewport(self.width(), self.height()).x()  # nudge left
                y += self.centeredViewport(self.width(), self.height()).y()  # nudge down
            elif gravity == openshot.GRAVITY_LEFT:
                y = (player_height - scaled_source_height) / 2.0 # center
                x += self.centeredViewport(self.width(), self.height()).x()  # nudge right
            elif gravity == openshot.GRAVITY_CENTER:
                x = (player_width - scaled_source_width) / 2.0 # center
                y = (player_height - scaled_source_height) / 2.0 # center
            elif gravity == openshot.GRAVITY_RIGHT:
                x = player_width - scaled_source_width # right
                y = (player_height - scaled_source_height) / 2.0 # center
                x -= self.centeredViewport(self.width(), self.height()).x()  # nudge left
            elif gravity == openshot.GRAVITY_BOTTOM_LEFT:
                y = (player_height - scaled_source_height) # bottom
                x += self.centeredViewport(self.width(), self.height()).x()  # nudge right
                y -= self.centeredViewport(self.width(), self.height()).y()  # nudge up
            elif gravity == openshot.GRAVITY_BOTTOM:
                x = (player_width - scaled_source_width) / 2.0 # center
                y = (player_height - scaled_source_height) # bottom
                y -= self.centeredViewport(self.width(), self.height()).y()  # nudge up
            elif gravity == openshot.GRAVITY_BOTTOM_RIGHT:
                x = player_width - scaled_source_width # right
                y = (player_height - scaled_source_height) # bottom
                x -= self.centeredViewport(self.width(), self.height()).x()  # nudge left
                y -= self.centeredViewport(self.width(), self.height()).y()  # nudge up

            # Track gravity starting coordinate
            self.gravity_point = QPointF(x, y)

            # Scale to fit in widget
            final_size = QSize(source_width, source_height)

            # Adjust x,y for location
            x_offset = raw_properties.get('location_x').get('value')
            y_offset = raw_properties.get('location_y').get('value')
            x += (scaledPix.width() * x_offset)
            y += (scaledPix.height() * y_offset)

            self.transform = QTransform()

            # Apply translate/move
            if x or y:
                self.transform.translate(x, y)

            # Apply scale
            if sx or sy:
                self.transform.scale(sx, sy)

            # Apply shear
            shear_x = raw_properties.get('shear_x').get('value')
            shear_y = raw_properties.get('shear_y').get('value')
            if shear_x or shear_y:
                self.transform.shear(shear_x, shear_y)

            # Apply rotation
            rotation = raw_properties.get('rotation').get('value')
            if rotation:
                origin_x = x - self.centeredViewport(self.width(), self.height()).x() + (scaled_source_width / 2.0)
                origin_y = y - self.centeredViewport(self.width(), self.height()).y() + (scaled_source_height / 2.0)
                self.transform.translate(origin_x, origin_y)
                self.transform.rotate(rotation)
                self.transform.translate(-origin_x, -origin_y)

            # Apply transform
            painter.setTransform(self.transform)

            # Draw transform corners and center origin circle
            # Corner size
            cs = 6.0
            os = 12.0

            # Calculate 4 corners coordinates
            self.topLeftHandle = QRectF(0.0, 0.0, cs/sx, cs/sy)
            self.topRightHandle = QRectF(source_width - (cs/sx), 0, cs/sx, cs/sy)
            self.bottomLeftHandle = QRectF(0.0, source_height - (cs/sy), cs/sx, cs/sy)
            self.bottomRightHandle = QRectF(source_width - (cs/sx), source_height - (cs/sy), cs/sx, cs/sy)

            # Draw 4 corners
            painter.fillRect(self.topLeftHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.topRightHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.bottomLeftHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.bottomRightHandle, QBrush(QColor("#53a0ed")))

            # Calculate 4 side coordinates
            self.topHandle = QRectF(0.0 + (source_width / 2.0) - (cs/sx/2.0), 0, cs/sx, cs/sy)
            self.bottomHandle = QRectF(0.0 + (source_width / 2.0) - (cs/sx/2.0), source_height - (cs/sy), cs/sx, cs/sy)
            self.leftHandle = QRectF(0.0, (source_height / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)
            self.rightHandle = QRectF(source_width - (cs/sx), (source_height / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)

            # Draw 4 sides (centered)
            painter.fillRect(self.topHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.bottomHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.leftHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.rightHandle, QBrush(QColor("#53a0ed")))

            # Calculate center coordinate
            self.centerHandle = QRectF((source_width / 2.0) - (os/sx), (source_height / 2.0) - (os/sy), os/sx*2.0, os/sy*2.0)

            # Draw origin
            painter.setBrush(QColor(83, 160, 237, 122))
            painter.setPen(Qt.NoPen)
            painter.drawEllipse(self.centerHandle)

            # Draw translucent rectangle
            self.clipRect = QRectF(0, 0, final_size.width(), final_size.height())

            # Remove transform
            painter.resetTransform()

        # End painter
        painter.end()

        self.mutex.unlock()
Пример #19
0
class Settings(object):

    l = logging.getLogger('Settings')


    def __init__(self, fileName = 'notepad.ini'):
        self.fileName = fileName
        self.notepads = []
        self.mainWindowSize = QSize(1200, 700)
        self.mainWindowPos = QPoint(240, 200)
        self.browserPath = []

    
    def load(self):
        self.l.debug('Loading local settings ...')
        config = configparser.ConfigParser()
        config.read(self.fileName)

        # load dropbox configuration
        self.dropboxToken = ''
        if config.has_section('dropbox'):
            self.dropboxToken = config['dropbox'].get('dropboxToken')

        # load browser state
        if config.has_section('browser'):
            browserSettings = config['browser']
            path = browserSettings.get('path')
            self.browserPath = ast.literal_eval(path)

        # load main window position and size
        if config.has_section('mainWindow'):
            windowSettings = config['mainWindow']

            # read position
            pos = windowSettings.get('pos', '{},{}'.format(self.mainWindowPos.x(), self.mainWindowPos.y())).split(',')
            self.mainWindowPos = QPoint(int(pos[0]), int(pos[1]))

            # read size
            size = windowSettings.get('size', '{},{}'.format(self.mainWindowSize.width(), self.mainWindowSize.height())).split(',')
            self.mainWindowSize = QSize(int(size[0]), int(size[1]))

        # load notepads
        self.notepads = []
        for s in config.sections():
            if s.startswith('notepad_'):
                npDef = None
                npType = config.get(s, 'type')
                if npType == 'local':
                    npDef = {'name' : config.get(s, 'name'),
                             'type'  : npType,
                             'path'   : config.get(s, 'path') }
                elif npType == 'dropbox':
                    npDef = {'name' : config.get(s, 'name'),
                             'type'  : npType }
                if npDef is not None:
                    self.notepads.append(npDef)


    def dump(self, channel):
        self.l.debug('Saving local settings ...')
        config = configparser.ConfigParser()

        config.add_section('dropbox')
        config.set('dropbox', 'dropboxToken', self.dropboxToken)

        config.add_section('browser')
        config.set('browser', 'path', str(self.browserPath))

        config.add_section('mainWindow')
        config.set('mainWindow', 'pos', '{},{}'.format(self.mainWindowPos.x(), self.mainWindowPos.y()))
        config.set('mainWindow', 'size', '{},{}'.format(self.mainWindowSize.width(), self.mainWindowSize.height()))

        idx = 0
        for s in self.notepads:
            sectName = "notepad_{}".format(idx)
            idx += 1
            
            config.add_section(sectName)
            config.set(sectName, 'name', s['name'])
            config.set(sectName, 'type', s['type'])
            if s['type'] == 'local':
                config.set(sectName, 'path', s['path'])

        config.write(channel)

    
    def save(self):
        with open(self.fileName, 'w') as configfile:
            self.dump(configfile)


    def getNotepads(self):
        return self.notepads

    def addNotepad(self, npDef):
        self.notepads.append(npDef)

    def setMainWindowPos(self, pos):
        self.mainWindowPos = pos    

    def setBrowserPath(self, path):
        self.browserPath = path

    def setMainWindowSize(self, size):
        self.mainWindowSize = size

    def getMainWindowPos(self):
        return self.mainWindowPos

    def getMainWindowSize(self):
        return self.mainWindowSize

    def getBrowserPath(self):
        return self.browserPath

    def getDropboxToken(self):
        return self.dropboxToken 

    def setDropboxToken(self, token):
        self.dropboxToken = token 
Пример #20
0
class ResizeHelper(QWidget):
    offsetChanged = pyqtSignal(QPoint)
    offsetXChanged = pyqtSignal(int)
    offsetYChanged = pyqtSignal(int)
    offsetBoundsChanged = pyqtSignal(QRect)

    def __init__(self, parent = None):
        super().__init__(parent)
        
        self.mMouseAnchorPoint = QPoint()
        self.mOffset = QPoint()
        self.mOldSize = QSize()
        self.mDragging = False
        self.mOffsetBounds = QRect()
        self.mScale = 0.0
        self.mNewSize = QSize()
        self.mOrigOffset = QPoint()
        
        self.setMinimumSize(20, 20)
        self.setOldSize(QSize(1, 1))

    def oldSize(self):
        return self.mOldSize

    def newSize(self):
        return self.mNewSize

    def offset(self):
        return self.mOffset

    def offsetBounds(self):
        return self.mOffsetBounds

    def setOldSize(self, size):
        self.mOldSize = size
        self.recalculateMinMaxOffset()
        self.recalculateScale()

    def setNewSize(self, size):
        self.mNewSize = size
        self.recalculateMinMaxOffset()
        self.recalculateScale()

    def setOffset(self, offset):
        # Clamp the offset within the offset bounds
        newOffset = QPoint(min(self.mOffsetBounds.right(),
                            max(self.mOffsetBounds.left(), offset.x())),
                                min(self.mOffsetBounds.bottom(),
                                    max(self.mOffsetBounds.top(), offset.y())))
        if (self.mOffset != newOffset):
            xChanged = self.mOffset.x() != newOffset.x()
            yChanged = self.mOffset.y() != newOffset.y()
            self.mOffset = newOffset
            if (xChanged):
                self.offsetXChanged.emit(self.mOffset.x())
            if (yChanged):
                self.offsetYChanged.emit(self.mOffset.y())
            self.offsetChanged.emit(self.mOffset)
            self.update()

    ## Method to set only the X offset, provided for convenience. */
    def setOffsetX(self, x):
        self.setOffset(QPoint(x, self.mOffset.y()))

    ## Method to set only the Y offset, provided for convenience. */
    def setOffsetY(self, y):
        self.setOffset(QPoint(self.mOffset.x(), y))

    ## Method to set only new width, provided for convenience. */
    def setNewWidth(self, width):
        self.mNewSize.setWidth(width)
        self.recalculateMinMaxOffset()
        self.recalculateScale()

    ## Method to set only new height, provided for convenience. */
    def setNewHeight(self, height):
        self.mNewSize.setHeight(height)
        self.recalculateMinMaxOffset()
        self.recalculateScale()

    def paintEvent(self, event):
        _size = self.size() - QSize(2, 2)
        if (_size.isEmpty()):
            return
        origX = (_size.width() - self.mNewSize.width() * self.mScale) / 2 + 0.5
        origY = (_size.height() - self.mNewSize.height() * self.mScale) / 2 + 0.5
        oldRect = QRect(self.mOffset, self.mOldSize)
        painter = QPainter(self)
        painter.translate(origX, origY)
        painter.scale(self.mScale, self.mScale)
        pen = QPen(Qt.black)
        pen.setCosmetic(True)
        painter.setPen(pen)
        painter.drawRect(QRect(QPoint(0, 0), self.mNewSize))
        pen.setColor(Qt.white)
        painter.setPen(pen)
        painter.setBrush(Qt.white)
        painter.setOpacity(0.5)
        painter.drawRect(oldRect)
        pen.setColor(Qt.black)
        pen.setStyle(Qt.DashLine)
        painter.setOpacity(1.0)
        painter.setBrush(Qt.NoBrush)
        painter.setPen(pen)
        painter.drawRect(oldRect)
        painter.end()

    def mousePressEvent(self, event):
        self.mMouseAnchorPoint = event.pos()
        self.mOrigOffset = self.mOffset
        self.mDragging = event.button() == Qt.LeftButton

    def mouseMoveEvent(self, event):
        if (not self.mDragging):
            return
        pos = event.pos()
        if (pos != self.mMouseAnchorPoint):
            self.setOffset(self.mOrigOffset + (pos - self.mMouseAnchorPoint) / self.mScale)
            self.offsetChanged.emit(self.mOffset)

    def resizeEvent(self, event):
        self.recalculateScale()

    def recalculateScale(self):
        _size = self.size() - QSize(2, 2)
        if (_size.isEmpty()):
            return
        if self.mOldSize.width() < self.mNewSize.width():
            width = self.mNewSize.width()
        else:
            width = 2 * self.mOldSize.width() - self.mNewSize.width()
        if self.mOldSize.height() < self.mNewSize.height():
            height = self.mNewSize.height()
        else:
            height = 2 * self.mOldSize.height() - self.mNewSize.height()

        # Pick the smallest scale
        scaleW = _size.width() / width
        scaleH = _size.height() / height
        if scaleW < scaleH:
            self.mScale = scaleW
        else:
            self.mScale = scaleH

        self.update()

    def recalculateMinMaxOffset(self):
        offsetBounds = self.mOffsetBounds
        if (self.mOldSize.width() <= self.mNewSize.width()):
            offsetBounds.setLeft(0)
            offsetBounds.setRight(self.mNewSize.width() - self.mOldSize.width())
        else:
            offsetBounds.setLeft(self.mNewSize.width() - self.mOldSize.width())
            offsetBounds.setRight(0)

        if (self.mOldSize.height() <= self.mNewSize.height()):
            offsetBounds.setTop(0)
            offsetBounds.setBottom(self.mNewSize.height() - self.mOldSize.height())
        else:
            offsetBounds.setTop(self.mNewSize.height() - self.mOldSize.height())
            offsetBounds.setBottom(0)

        if (self.mOffsetBounds != offsetBounds):
            self.mOffsetBounds = offsetBounds
            self.offsetBoundsChanged.emit(self.mOffsetBounds)
Пример #21
0
oppsite = {
    Qt.Key_Left:    Qt.Key_Right,
    Qt.Key_Right:   Qt.Key_Left,
    Qt.Key_Up:      Qt.Key_Down,
    Qt.Key_Down:    Qt.Key_Up
}

# Return current time in seconds as a int number.
def now():
    return int(round(time.time()*1000))



# Return a tuple of block size
def bsize()
    return block.width()-1, block.height()-1

# return the scale of the window
def scale_sz(sz)
    return sz.width() * block.width(), sz.height()* block.height()

# return the size of snake size
def scaled_pt(p):
    return p.x() * block.width(), p.y() * block.height()


# Snake controller Game
class Game(ojbect):
    def __init__(self):
        super(Snake, self).__init__()
Пример #22
0
class AbstractLayout(QObject):
    """Manages page.Page instances with a list-like api.

    You can iterate over the layout itself, which yields all Page instances.
    You can also iterate over pages(), which only yields the Page instances
    that are visible().

    """

    redraw = pyqtSignal(QRect)
    changed = pyqtSignal()
    scaleChanged = pyqtSignal(float)

    def __init__(self):
        super(AbstractLayout, self).__init__()
        self._pages = []
        self._size = QSize()
        self._margin = 4
        self._spacing = 8
        self._scale = 1.0
        self._scaleChanged = False
        self._dpi = (72, 72)

    def own(self, page):
        """(Internal) Makes the page have ourselves as layout."""
        if page.layout():
            page.layout().remove(page)
        page._layout = weakref.ref(self)
        page.computeSize()

    def disown(self, page):
        """(Internal) Removes ourselves as owner of the page."""
        page._layout = lambda: None

    def append(self, page):
        self.own(page)
        self._pages.append(page)

    def insert(self, position, page):
        self.own(page)
        self._pages.insert(position, page)

    def extend(self, pages):
        for page in pages:
            self.append(page)

    def remove(self, page):
        self._pages.remove(page)
        self.disown(page)

    def pop(self, index=None):
        page = self._pages.pop(index)
        self.disown(page)
        return page

    def clear(self):
        del self[:]

    def count(self):
        return len(self._pages)

    def __len__(self):
        return len(self._pages)

    def __bool__(self):
        return True

    def __contains__(self, page):
        return page in self._pages

    def __getitem__(self, item):
        return self._pages[item]

    def __delitem__(self, item):
        if isinstance(item, slice):
            for page in self._pages[item]:
                self.disown(page)
        else:
            self.disown(self._pages[item])
        del self._pages[item]

    def __setitem__(self, item, new):
        if isinstance(item, slice):
            old = self._pages[item]
            self._pages[item] = new
            for page in self._pages[item]:
                self.own(page)
            for page in old:
                self.disown(page)
        else:
            self.disown(self._pages[item])
            self._pages[item] = new
            self.own(new)

    def index(self, page):
        """Returns the index at which the given Page can be found in our Layout."""
        return self._pages.index(page)

    def setSize(self, size):
        """Sets our size. Mainly done after layout."""
        self._size = size

    def size(self):
        """Returns our size as QSize()."""
        return self._size

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

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

    def setDPI(self, xdpi, ydpi=None):
        """Sets our DPI in X and Y direction. If Y isn't given, uses the X value."""
        self._dpi = xdpi, ydpi or xdpi
        for page in self:
            page.computeSize()

    def dpi(self):
        """Returns our DPI as a tuple(XDPI, YDPI)."""
        return self._dpi

    def scale(self):
        """Returns the scale (1.0 == 100%)."""
        return self._scale

    def setScale(self, scale):
        """Sets the scale (1.0 == 100%) of all our Pages."""
        if scale != self._scale:
            self._scale = scale
            for page in self:
                page.setScale(scale)
            self._scaleChanged = True

    def setPageWidth(self, width, sameScale=True):
        """Sets the width of all pages.

        If sameScale is True (default), the largest page will be scaled to the given
        width (minus margin).  All pages will then be scaled to that scale.
        If sameScale is False all pages will be scaled individually to the same width.

        """
        if sameScale and any(self.pages()):
            self.setScale(self.widest().scaleForWidth(width))
        else:
            for page in self:
                page.setWidth(width)

    def setPageHeight(self, height, sameScale=True):
        """Sets the height of all pages.

        If sameScale is True (default), the largest page will be scaled to the given
        height (minus margin).  All pages will then be scaled to that scale.
        If sameScale is False all pages will be scaled individually to the same height.

        """
        if sameScale and any(self.pages()):
            self.setScale(self.highest().scaleForWidth(height))
        else:
            for page in self:
                page.setHeight(height)

    def setMargin(self, margin):
        """Sets the margin around the pages in pixels."""
        self._margin = margin

    def margin(self):
        """Returns the margin around the pages in pixels."""
        return self._margin

    def setSpacing(self, spacing):
        """Sets the space between the pages in pixels."""
        self._spacing = spacing

    def spacing(self):
        """Returns the space between the pages in pixels."""
        return self._spacing

    def fit(self, size, mode):
        """Fits the layout in the given ViewMode."""
        if mode and any(self.pages()):
            scales = []
            if mode & FitWidth:
                scales.append(self.scaleFitWidth(size.width()))
            if mode & FitHeight:
                scales.append(self.scaleFitHeight(size.height()))
            self.setScale(min(scales))

    def scaleFitHeight(self, height):
        """Return the scale this layout would need to fit in the height.

        This method is called by fit().
        The default implementation returns a suitable scale for the highest Page.

        """
        return self.highest().scaleForHeight(height - self.margin() * 2)

    def scaleFitWidth(self, width):
        """Return the scale this layout would need to fit in the width.

        This method is called by fit().
        The default implementation returns a suitable scale for the widest Page.

        """
        return self.widest().scaleForWidth(width - self.margin() * 2)

    def update(self):
        """Performs the layout (positions the Pages and adjusts our size)."""
        self.reLayout()
        if self._scaleChanged:
            self.scaleChanged.emit(self._scale)
            self._scaleChanged = False
        self.changed.emit()

    def reLayout(self):
        """This is called by update().

        You must implement this method to position the Pages and adjust our size.
        See Layout for a possible implementation.

        """
        pass

    def updatePage(self, page):
        """Called by the Page when an image has been generated."""
        self.redraw.emit(page.rect())

    def page(self, document, pageNumber):
        """Returns the page (visible or not) from a Poppler.Document with page number.

        Returns None if that page is not available.

        """
        # Specific layouts may use faster algorithms to find the page.
        try:
            page = self[pageNumber]
        except IndexError:
            pass
        else:
            if page.document() == document:
                return page
        for page in self:
            if page.document() == document and page.pageNumber() == pageNumber:
                return page

    def pages(self):
        """Yields our pages that are visible()."""
        for page in self:
            if page.visible():
                yield page

    def pageAt(self, point):
        """Returns the page that contains the given QPoint."""
        # Specific layouts may use faster algorithms to find the page.
        for page in self.pages():
            if page.rect().contains(point):
                return page

    def pagesAt(self, rect):
        """Yields the pages touched by the given QRect."""
        # Specific layouts may use faster algorithms to find the pages.
        for page in self.pages():
            if page.rect().intersects(rect):
                yield page

    def linkAt(self, point):
        """Returns (page, link) if pos points to a Poppler.Link in a Page, else (None, None)."""
        page = self.pageAt(point)
        if page:
            links = page.linksAt(point)
            if links:
                return page, links[0]
        return None, None

    def widest(self):
        """Returns the widest visible page (in its natural page size)."""
        pages = list(self.pages())
        if pages:
            return max(pages, key = lambda p: p.pageSize().width())

    def highest(self):
        """Returns the highest visible page (in its natural page size)."""
        pages = list(self.pages())
        if pages:
            return max(pages, key = lambda p: p.pageSize().height())

    def maxWidth(self):
        """Returns the width of the widest visible page."""
        page = self.widest()
        return page.width() if page else 0

    def maxHeight(self):
        """Returns the height of the highest visible page."""
        page = self.highest()
        return page.height() if page else 0

    def load(self, document):
        """Convenience method to load all the pages of the given Poppler.Document using page.Page()."""
        self.clear()
        for num in range(document.numPages()):
            p = page.Page(document, num)
            p.setScale(self._scale)
            self.append(p)
Пример #23
0
class PreviewWindow(QFrame):
    minsize = (32, 32)
    maxsize = None
    last_save_dir = ""
    raise_window = False

    key_signal = pyqtSignal(int, int, int)
    move_signal = pyqtSignal()

    def __init__(self,
                 manager,
                 name,
                 image=None,
                 message=None,
                 position=None,
                 size=None,
                 high_quality=False):
        super(PreviewWindow, self).__init__()
        self.setObjectName("Preview window {}".format(name))
        self.setWindowTitle(name)

        self.manager = manager

        desktop = QApplication.instance().desktop()
        if self.maxsize:
            self.maxsize = QSize(*self.maxsize)
        else:
            self.maxsize = desktop.screenGeometry(
                desktop.screenNumber(self)).size() * 0.95

        self.setMinimumSize(*self.minsize)
        self.setMaximumSize(self.maxsize)

        self.image = None
        self.original = None
        self.message = message
        self.scale = 1.
        self.rotation = 0
        self.quality = Qt.SmoothTransformation if high_quality else Qt.FastTransformation
        self.fixed_size = size

        self.scrollarea = PreviewScrollArea()
        self.scrollarea.setFrameStyle(0)
        self.scrollarea.setFocusPolicy(Qt.NoFocus)

        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)
        layout.addWidget(self.scrollarea, 0, 0)

        self.preview = QLabel()
        self.preview.setMouseTracking(False)
        self.preview.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.scrollarea.setWidget(self.preview)

        self.message_label = QLabel(" ")
        self.layout().addWidget(self.message_label, 0, 0, Qt.AlignTop)
        self.message_label.setStyleSheet(
            "QLabel {color:black;background:rgba(255,255,255,32)}")
        self.message_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.message_label.setText("")
        shadow = QGraphicsDropShadowEffect()
        shadow.setBlurRadius(4)
        shadow.setColor(Qt.white)
        shadow.setOffset(0, 0)
        self.message_label.setGraphicsEffect(shadow)

        self.blink_widget = QWidget()
        self.blink_widget.hide()
        self.blink_widget.setStyleSheet("border:3px solid red")
        self.blink_timer = QTimer()
        self.blink_timer.setInterval(1000)
        self.blink_timer.timeout.connect(self.blink_)
        layout.addWidget(self.blink_widget, 0, 0)

        self.setImage(image, show=False)

        if image is not None and not size:
            size = self.autoSize()

        if size:
            self.resize(*size)

        if position == 'cursor':
            position = (QCursor.pos().x() - self.size().width() // 2,
                        QCursor.pos().y() - self.size().height() // 2)

        if position:
            self.move(*position)

        self.showNormal()

    def setImage(self, image, show=True, scale=None, blink=False):
        if image is None: return
        self.original = image
        if isinstance(image, QImage):
            image = QPixmap.fromImage(image)
        elif isinstance(image, QPixmap):
            pass
        else:
            image = array_to_pixmap(image)
        self.image = image
        if not scale:
            scale = self.scale
            if image.width() * scale > self.maxsize.width():
                scale = self.maxsize.width() / image.width()
            if image.height() * scale > self.maxsize.height():
                scale = self.maxsize.height() / image.height()
        self.setZoom(scale)
        if self.message is not None:
            self.message_label.setText(self.message)
        if blink:
            self.blink(True)
        if show:
            self.show()
            if self.raise_window:
                self.raise_()

    def closeEvent(self, event):
        self.scale = 1.0
        self.fixed_size = None

    def setImageAndParams(self,
                          image,
                          show=True,
                          scale=None,
                          position=None,
                          size=None,
                          hq=None,
                          message=None,
                          blink=False):
        if size:
            self.fixed_size = size
        if position:
            self.move(*position)
        if hq is not None:
            if hq: self.quality = Qt.SmoothTransformation
            else: self.quality = Qt.FastTransformation
        if message is not None:
            self.message = message
        self.setImage(image, show=show, scale=scale, blink=blink)

    def setZoom(self, scale):
        self.setParams(scale=scale)

    def setRotation(self, rotation):
        self.setParams(rotation=rotation)

    def setParams(self, scale=None, rotation=None):
        assert isinstance(self.image, QPixmap)

        if scale is None: scale = self.scale
        if rotation is None: rotation = self.rotation

        if scale != 1.0 or rotation:
            transform = QTransform().rotate(rotation).scale(scale, scale)
            pixmap = self.image.transformed(transform, self.quality)
        else:
            pixmap = self.image

        w = pixmap.width()
        h = pixmap.height()

        self.scale = scale
        self.rotation = rotation
        self.preview.setPixmap(pixmap)
        self.preview.setFixedSize(pixmap.size())

        if not self.fixed_size:
            self.resize(w, h)

    def autoSize(self):
        size = self.image.size()
        w = size.width()
        h = size.height()
        return w, h

    def wheelEvent(self, event):
        assert isinstance(event, QWheelEvent)
        event.accept()

        if event.angleDelta().y() > 0:
            s = 1.1
        else:
            s = 1 / 1.1
        self.setZoom(s * self.scale)

        scrollX = self.scrollarea.horizontalScrollBar().value()
        posX = event.x()
        newX = s * (scrollX + posX) - posX
        self.scrollarea.horizontalScrollBar().setValue(int(newX))

        scrollY = self.scrollarea.verticalScrollBar().value()
        posY = event.y()
        newY = s * (scrollY + posY) - posY
        self.scrollarea.verticalScrollBar().setValue(int(newY))

        self.blink(False)

    def mousePressEvent(self, event):
        assert isinstance(event, QMouseEvent)
        self.key_signal.emit(KEY_MOUSE, event.x(), event.y())
        self.blink(False)
        event.accept()

    def keyPressEvent(self, event):
        assert isinstance(event, QKeyEvent)
        self.key_signal.emit(int(event.key()), 0, 0)
        self.blink(False)
        if event.key() == Qt.Key_Escape:
            self.close()
            event.accept()

    def moveEvent(self, event):
        self.move_signal.emit()

    def contextMenuEvent(self, event):
        assert isinstance(event, QContextMenuEvent)

        self.blink(False)

        menu = QMenu()

        copy = menu.addAction("Copy to clipboard")

        reset = menu.addAction("Reset view")

        hq = menu.addAction("High quality")
        hq.setCheckable(True)
        if self.quality == Qt.SmoothTransformation:
            hq.setChecked(True)

        fixed = menu.addAction("Fixed size")
        fixed.setCheckable(True)
        if self.fixed_size:
            fixed.setChecked(True)

        rotate_right = menu.addAction("Rotate +")
        rotate_left = menu.addAction("Rotate -")

        save = menu.addAction("Save...")

        quit = menu.addAction("Close")

        action = menu.exec_(self.mapToGlobal(event.pos()))

        if action == quit:
            self.close()
        elif action == reset:
            self.setParams(1, 0)
        elif action == hq:
            if self.quality == Qt.SmoothTransformation:
                self.quality = Qt.FastTransformation
            else:
                self.quality = Qt.SmoothTransformation
            self.setZoom(self.scale)
        elif action == rotate_right:
            rotation = (self.rotation + 90) % 360
            self.setRotation(rotation)
        elif action == rotate_left:
            rotation = (self.rotation + 270) % 360
            self.setRotation(rotation)
        elif action == save:
            filename, filter = QFileDialog.getSaveFileNameAndFilter(
                self,
                "Save image...",
                filter="*.png;;*.jpg;;*.bmp;;*.tiff;;*.gif",
                directory=self.last_save_dir)
            if filename:
                try:
                    if not str(filename).endswith(filter[1:]):
                        filename = filename + filter[1:]
                    PreviewWindow.last_save_dir = path.dirname(str(filename))
                    success = self.image.save(filename, quality=100)
                    if not success: raise Exception("unknown error")
                except Exception as e:
                    QMessageBox.critical(
                        self, "Saving error",
                        "Cannot save.\nError: {}".format(e.message))
                    print("Saving error:", e)
        elif action == fixed:
            if self.fixed_size:
                self.fixed_size = None
            else:
                self.fixed_size = self.size()
        elif action == copy:
            print("copy")
            clipboard = QApplication.instance().clipboard()
            clipboard.setPixmap(self.image)

    def blink(self, enable):
        if enable:
            self.blink_timer.start()
        else:
            self.blink_timer.stop()
            self.blink_widget.hide()

    @pyqtSlot()
    def blink_(self):
        if self.blink_widget.isHidden():
            self.blink_widget.show()
        else:
            self.blink_widget.hide()
Пример #24
0
class SquircleRenderer(QObject):  #QOpenGLFunctions
    """docstring for SquircleRenderer"""
    def __init__(self, parent=None):
        super(SquircleRenderer, self).__init__(parent)
        self.m_t = 0.0
        self.m_program = None
        self.m_viewportSize = QSize()

    def setT(self, t):
        self.m_t = t

    def setViewportSize(self, size):
        self.m_viewportSize = size

    def setWin(self, win):
        self.win = win

        ver = QOpenGLVersionProfile()
        ver.setVersion(2, 1)

        self.m_context = self.win.openglContext()
        self.gl = self.m_context.versionFunctions(ver)

    @pyqtSlot()
    def paint(self):
        if not self.m_program:
            self.gl.initializeOpenGLFunctions()

            self.m_program = QOpenGLShaderProgram(self)
            self.m_program.addShaderFromSourceCode(
                QOpenGLShader.Vertex, "attribute highp vec4 vertices;"
                "varying highp vec2 coords;"
                "void main() {"
                "    gl_Position = vertices;"
                "    coords = vertices.xy;"
                "}")
            self.m_program.addShaderFromSourceCode(
                QOpenGLShader.Fragment, "uniform lowp float t;"
                "varying highp vec2 coords;"
                "void main() {"
                "    lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));"
                "    i = smoothstep(t - 0.8, t + 0.8, i);"
                "    i = floor(i * 20.) / 20.;"
                "    gl_FragColor = vec4(coords * .5 + .5, i, i);"
                "}")

            self.m_program.bindAttributeLocation("vertices", 0)

            self.m_program.link()

        self.m_program.bind()

        self.m_program.enableAttributeArray(0)

        values = [(-1, -1), (1, -1), (-1, 1), (1, 1)]

        self.m_program.setAttributeArray(0, values)

        self.m_program.setUniformValue("t", self.m_t)

        #print("DATA:",self.m_viewportSize.width(), self.m_viewportSize.height(), self.m_t)#, self.gl.glViewport)

        self.gl.glViewport(0, 0, self.m_viewportSize.width(),
                           self.m_viewportSize.height())

        self.gl.glDisable(self.gl.GL_DEPTH_TEST)

        self.gl.glClearColor(0, 0, 0, 1)

        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT)

        self.gl.glEnable(self.gl.GL_BLEND)

        self.gl.glBlendFunc(self.gl.GL_SRC_ALPHA, self.gl.GL_ONE)

        self.gl.glDrawArrays(self.gl.GL_TRIANGLE_STRIP, 0, 4)

        self.m_program.disableAttributeArray(0)

        self.m_program.release()
Пример #25
0
    def __init__(self, pages: AllPages, windowSize: QSize):
        super().__init__(pages, windowSize)

        mainLayout = QVBoxLayout()
        mainLayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(mainLayout)

        #Titel
        mainLayout.addWidget(
            self.getTitleAsQLabel(TextKey.PAGE_SYSTEMPICTUREMANAGER_TITLE))

        scroll_area_content_widget = QWidget()
        self.scroll_area = QScrollArea(self)
        self.scroll_area.setGeometry(0, 0, windowSize.width(),
                                     windowSize.height())
        self.scroll_area.setWidgetResizable(True)
        self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll_area.setWidget(scroll_area_content_widget)

        mainContentLabel = QVBoxLayout()
        scroll_area_content_widget.setLayout(mainContentLabel)
        mainLayout.addWidget(self.scroll_area)

        #Funny pictures
        funnyTitle = QLabel(
            textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_FUNNY_TITEL])
        funnyTitleFont = QFont()
        funnyTitleFont.setUnderline(True)
        funnyTitle.setFont(funnyTitleFont)
        mainContentLabel.addWidget(funnyTitle)

        mainContentLabel.addWidget(
            QLabel(textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_SOURCELABEL]))
        funnySource = QLineEdit()
        funnySource.setEnabled(False)
        funnySource.setText(
            CfgService.get(
                CfgKey.PAGE_SYSTEMPICTUREMANAGER_FUNNY_PICTURE_SOURCE))
        mainContentLabel.addWidget(funnySource)

        mainContentLabel.addWidget(
            QLabel(textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_TARGETLABEL]))
        funnyTarget = QLineEdit()
        funnyTarget.setEnabled(False)
        funnyTarget.setText(
            CfgService.get(CfgKey.PAGE_CAPTUREPHOTO_LAST_IMAGE_FOLDER))
        mainContentLabel.addWidget(funnyTarget)

        funnyPictureNavigation = QHBoxLayout()
        mainContentLabel.addLayout(funnyPictureNavigation)

        self.funnyDeleteButton = QPushButton(
            textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_DELETEBUTTON])
        self.funnyDeleteButton.clicked.connect(self.deleteFunnyPictureFolder)
        funnyPictureNavigation.addWidget(self.funnyDeleteButton)

        self.funnyUpdateButton = QPushButton(
            textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_UPDATEBUTTON])
        self.funnyUpdateButton.clicked.connect(self.updateFunnyPictures)
        funnyPictureNavigation.addWidget(self.funnyUpdateButton)

        #Loading Gifs
        loadingGifTitle = QLabel(
            textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_LOADINGGIFS_TITEL])
        loadingGifFont = QFont()
        loadingGifFont.setUnderline(True)
        loadingGifTitle.setFont(funnyTitleFont)
        mainContentLabel.addWidget(loadingGifTitle)

        mainContentLabel.addWidget(
            QLabel(textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_SOURCELABEL]))
        loadingGifSource = QLineEdit()
        loadingGifSource.setEnabled(False)
        loadingGifSource.setText(
            CfgService.get(
                CfgKey.PAGE_SYSTEMPICTUREMANAGER_LOADINGGIFS_PICTURE_SOURCE))
        mainContentLabel.addWidget(loadingGifSource)

        mainContentLabel.addWidget(
            QLabel(textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_TARGETLABEL]))
        loadingGifTarget = QLineEdit()
        loadingGifTarget.setEnabled(False)
        loadingGifTarget.setText(
            CfgService.get(CfgKey.PAGE_CAPTUREPHOTO_LOADING_GIF_FOLDER))
        mainContentLabel.addWidget(loadingGifTarget)

        loadingGifNavigation = QHBoxLayout()
        mainContentLabel.addLayout(loadingGifNavigation)

        self.loadingGifDeleteButton = QPushButton(
            textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_DELETEBUTTON])
        self.loadingGifDeleteButton.clicked.connect(
            self.deleteLoadingGifFolder)
        loadingGifNavigation.addWidget(self.loadingGifDeleteButton)

        self.loadingGifUpdateButton = QPushButton(
            textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_UPDATEBUTTON])
        self.loadingGifUpdateButton.clicked.connect(self.updateLoadingGifs)
        loadingGifNavigation.addWidget(self.loadingGifUpdateButton)

        mainContentLabel.addStretch()
        #progressbar
        self.progressbar = QProgressBar()
        self.progressbar.setValue(0)
        mainLayout.addWidget(self.progressbar)
        #Navigationbuttons
        navigationBox = QHBoxLayout()
        mainLayout.addLayout(navigationBox)

        self.pictureManagerButton = QPushButton(
            textValue[TextKey.PAGE_SYSTEMPICTUREMANAGER_NEXTBUTTON])
        self.pictureManagerButton.clicked.connect(self.nextPageEvent)
        self.setNavigationbuttonStyle(self.pictureManagerButton)
        navigationBox.addWidget(self.pictureManagerButton)

        #timer
        self.timer = QTimer()
        self.timer.timeout.connect(self.timerUpdate)
Пример #26
0
import sys

from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QScreen
from PyQt5.QtCore import QSize

from mainwindow import MainWindow

windowSize = QSize(1000, 800)

if __name__ == "__main__":
    qapp = QApplication.instance()
    if not qapp:
        qapp = QApplication(sys.argv)

    app = MainWindow()
    screen: QScreen = app.screen()
    size: QSize = screen.size()

    app.setGeometry(
        size.width()//2 - windowSize.width()//2,
        size.height()//2 - windowSize.height()//2,
        windowSize.width(),
        windowSize.height(),
    )
    app.show()
    app.activateWindow()
    app.raise_()
    qapp.exec_()

Пример #27
0
def pixmapForLegendNode(legend_node):

    # handles only symbol nodes
    if not isinstance(legend_node, QgsSymbolLegendNode):
        return

    # If size is default, use default implementation
    size = iface.layerTreeView().iconSize()
    if size.width() in (-1, 16):
        size = QSize(18, 18)

    symbol = legend_node.symbol()
    if not symbol:
        return

    # Compute minimum width
    model = iface.layerTreeView().layerTreeModel()
    if not legend_node.layerNode():
        return

    text = legend_node.textOnSymbolLabel()

    minimum_width = max(
        max(
            l_node.minimumIconSize().width() + (8 if text else 0)
            for l_node in model.layerLegendNodes(legend_node.layerNode())
            if isinstance(l_node, QgsSymbolLegendNode)
        ),
        size.width(),
    )

    symbol_size = QSize(minimum_width, size.height())
    context = QgsRenderContext.fromMapSettings(iface.mapCanvas().mapSettings())
    pixmap = QgsSymbolLayerUtils.symbolPreviewPixmap(symbol, symbol_size, 0, context)

    if text:
        painter = QPainter(pixmap)
        text_format = legend_node.textOnSymbolTextFormat()

        try:
            text_context = createTemporaryRenderContext()
            if text_context:
                painter.setRenderHint(QPainter.Antialiasing)
                text_context.setPainter(painter)

                font_metrics = QFontMetricsF(text_format.scaledFont(context))
                y_baseline_v_center = (
                    symbol_size.height()
                    + font_metrics.ascent()
                    - font_metrics.descent()
                ) / 2

                QgsTextRenderer.drawText(
                    QPointF(symbol_size.width() / 2, y_baseline_v_center),
                    0,
                    QgsTextRenderer.AlignCenter,
                    [text],
                    text_context,
                    text_format,
                )
                text_context.setPainter(None)

        except Exception as e:
            QgsMessageLog.logMessage(str(e))

    return pixmap
Пример #28
0
 def check_res(res: QSize):
     return res.width() >= min_size and res.height() >= min_size
Пример #29
0
 def sizeHint(self) -> QSize:
     size = QSize(100,100)
     for item in self._items:
         size.expandedTo(item.minimumSize())
     return QSize(size.width() * 3, size.height() * 2)
Пример #30
0
    def getBalloonImage(self, size: QSize, flip=False, soul_id=-1):
        if self._balloon is None:
            logging.warning("getBalloonImage: balloon is None")
            return kikka.helper.getDefaultImage()

        drect = []
        # calculate destination rect
        if len(self._balloon.clipW) == 3:
            dw = [
                self._balloon.clipW[0],
                size.width() - self._balloon.clipW[0] - self._balloon.clipW[2],
                self._balloon.clipW[2]
            ]
        elif len(self._balloon.clipW) == 5:
            sw = size.width() - self._balloon.clipW[0] - self._balloon.clipW[
                2] - self._balloon.clipW[4]
            dw = [
                self._balloon.clipW[0], sw // 2, self._balloon.clipW[2],
                sw - sw // 2, self._balloon.clipW[4]
            ]
        else:
            sw = size.width() // 3
            dw = [sw, size.width() - sw * 2, sw]

        if len(self._balloon.clipH) == 3:
            dh = [
                self._balloon.clipH[0],
                size.height() - self._balloon.clipH[0] -
                self._balloon.clipH[2], self._balloon.clipH[2]
            ]
        elif len(self._balloon.clipH) == 5:
            sh = size.height() - self._balloon.clipH[0] - self._balloon.clipH[
                2] - self._balloon.clipH[4]
            dh = [
                self._balloon.clipH[0], sh // 2, self._balloon.clipH[2],
                sh - sh // 2, self._balloon.clipH[4]
            ]
        else:
            sh = size.height() // 3
            dh = [sh, size.height() - sh * 2, sh]

        for y in range(len(self._balloon.clipH)):
            dr = []
            for x in range(len(self._balloon.clipW)):
                pt = QPoint(0, 0)
                if x > 0: pt.setX(dr[x - 1].x() + dw[x - 1])
                if y > 0: pt.setY(drect[y - 1][0].y() + dh[y - 1])
                sz = QSize(dw[x], dh[y])
                dr.append(QRect(pt, sz))
            drect.append(dr)
        pass  # exit for

        # paint balloon image
        img = QImage(size, QImage.Format_ARGB32)
        pixmap = QPixmap().fromImage(self._balloon_image_cache, Qt.AutoColor)
        painter = QPainter(img)
        painter.setCompositionMode(QPainter.CompositionMode_Source)

        for y in range(len(self._balloon.clipH)):
            for x in range(len(self._balloon.clipW)):
                painter.drawPixmap(drect[y][x], pixmap,
                                   self._balloon.bgRect[y][x])
        painter.end()

        # flip or not
        if self._balloon.flipBackground is True and flip is True:
            img = img.mirrored(True, False)
            if self._balloon.noFlipCenter is True and len(
                    self._balloon.clipW) == 5 and len(
                        self._balloon.clipH) == 5:
                painter = QPainter(img)
                painter.setCompositionMode(QPainter.CompositionMode_Source)
                painter.drawPixmap(drect[2][2], pixmap,
                                   self._balloon.bgRect[2][2])
                painter.end()

        # debug draw
        if kikka.shell.isDebug is True:
            painter = QPainter(img)
            painter.fillRect(QRect(0, 0, 200, 64), QColor(0, 0, 0, 64))
            painter.setPen(Qt.red)
            for y in range(len(self._balloon.clipH)):
                for x in range(len(self._balloon.clipW)):
                    if x in (0, 2, 4) and y in (0, 2, 4):
                        continue
                    rectf = QRect(drect[y][x])
                    text = "(%d, %d)\n%d x %d" % (
                        rectf.x(), rectf.y(), rectf.width(), rectf.height())
                    painter.drawText(rectf, Qt.AlignCenter, text)
                if y > 0:
                    painter.drawLine(drect[y][0].x(), drect[y][0].y(),
                                     drect[y][0].x() + img.width(),
                                     drect[y][0].y())

            for x in range(1, len(self._balloon.clipW)):
                painter.drawLine(drect[0][x].x(), drect[0][x].y(),
                                 drect[0][x].x(),
                                 drect[0][x].y() + img.height())

            painter.setPen(Qt.green)
            painter.drawRect(QRect(0, 0, img.width() - 1, img.height() - 1))
            painter.drawText(3, 12, "DialogWindow")
            painter.drawText(3, 24, "Ghost: %d" % self.ID)
            painter.drawText(3, 36, "Name: %s" % self.name)
            painter.drawText(3, 48, "soul_id: %d" % soul_id)
        return img
Пример #31
0
class MainApp(QWidget):
    STRANGER_DANGER = 350
    IMAGE_SIZE = (100, 100)

    stranger_color = (179, 20, 20)
    recognized_color = (59, 235, 62)

    def __init__(self, fps=30, parent=None):
        # type: (int, Optional[QWidget]) -> None
        super().__init__(parent=parent)

        self.pkg_path = path.dirname(path.dirname(path.abspath(__file__)))
        self.training_data_dir = path.join(self.pkg_path, 'train')
        self.models_dir = path.join(self.pkg_path, 'models')
        self.model_fname = 'fisherfaces.p'

        try:
            self.model = data_provider.load_model(
                path.join(self.models_dir, self.model_fname))
        except AssertionError:
            self.model = None

        self.existing_labels = QStringListModel(self.get_existing_labels())

        self.fps = fps
        self.video_size = QSize(640, 480)

        self.gray_image = None
        self.detected_faces = []

        # Setup the UI
        self.main_layout = QHBoxLayout()
        self.setLayout(self.main_layout)

        self.control_layout = QVBoxLayout()
        self.control_layout.setSpacing(8)
        self.main_layout.addItem(self.control_layout)

        # Setup the existing label view
        self.labels_view = QListView(parent=self)
        self.labels_view.setModel(self.existing_labels)
        self.labels_view.setSelectionMode(QListView.SingleSelection)
        self.labels_view.setItemDelegate(CapitalizeDelegate(self))
        self.control_layout.addWidget(self.labels_view)

        self.new_label_txt = QLineEdit(self)
        self.new_label_txt.returnPressed.connect(self.add_new_label)
        self.new_label_txt.returnPressed.connect(self.new_label_txt.clear)
        self.control_layout.addWidget(self.new_label_txt)

        self.add_button = QPushButton('Add Label', self)
        self.add_button.clicked.connect(self.add_new_label)
        self.control_layout.addWidget(self.add_button)

        # Setup the training area
        train_box = QGroupBox('Train', self)
        train_box_layout = QVBoxLayout()
        train_box.setLayout(train_box_layout)
        self.control_layout.addWidget(train_box)
        self.train_btn = QPushButton('Train', self)
        self.train_btn.clicked.connect(self.train)
        train_box_layout.addWidget(self.train_btn)

        self.control_layout.addStretch(0)

        # Add take picture shortcut
        self.take_picture_btn = QPushButton('Take picture', self)
        self.take_picture_btn.clicked.connect(self.take_picture)
        self.control_layout.addWidget(self.take_picture_btn)
        shortcut = QShortcut(QKeySequence('Space'), self, self.take_picture)
        shortcut.setWhatsThis('Take picture and add to training data.')

        # Add quit shortcut
        shortcut = QShortcut(QKeySequence('Esc'), self, self.close)
        shortcut.setWhatsThis('Quit')

        # Setup the main camera area
        self.image_label = QLabel(self)
        self.image_label.setFixedSize(self.video_size)
        self.main_layout.addWidget(self.image_label)

        # Setup the camera
        self.capture = cv2.VideoCapture(0)
        self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, self.video_size.width())
        self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, self.video_size.height())

        self.timer = QTimer()
        self.timer.timeout.connect(self.display_video_stream)
        self.timer.start(int(1000 / self.fps))

    def classify_face(self, image):
        if self.model is None:
            return

        label_idx, distances = self.model.predict(image.ravel(), True)
        label_idx, distance = label_idx[0], distances[0][label_idx]

        labels = self.existing_labels.stringList()
        return labels[label_idx], distance

    def get_training_data(self):
        """Read the images from disk into an n*(w*h) matrix."""
        return data_provider.get_image_data_from_directory(
            self.training_data_dir)

    def train(self):
        X, y, mapping = self.get_training_data()
        # Inspect scree plot to determine appropriate number of PCA components
        classifier = PCALDAClassifier(
            n_components=2, pca_components=200, metric='euclidean',
        ).fit(X, y)

        # Replace the existing running model
        self.model = classifier

        # Save the classifier to file
        data_provider.save_model(
            classifier, path.join(self.models_dir, self.model_fname))

    def add_new_label(self):
        new_label = self.new_label_txt.text()
        new_label = new_label.lower()

        # Prevent empty entries
        if len(new_label) < 3:
            return

        string_list = self.existing_labels.stringList()

        if new_label not in string_list:
            string_list.append(new_label)
            string_list.sort()
            self.existing_labels.setStringList(string_list)

            # Automatically select the added label
            selection_model = self.labels_view.selectionModel()
            index = self.existing_labels.index(string_list.index(new_label))
            selection_model.setCurrentIndex(index, QItemSelectionModel.Select)

    def display_video_stream(self):
        """Read frame from camera and repaint QLabel widget."""
        _, frame = self.capture.read()
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        frame = cv2.flip(frame, 1)

        # Use the Viola-Jones face detector to detect faces to classify
        face_cascade = cv2.CascadeClassifier(path.join(
            self.pkg_path, 'resources', 'haarcascade_frontalface_default.xml'))
        self.gray_image = gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        self.detected_faces = face_cascade.detectMultiScale(gray, 1.3, 5)
        for x, y, w, h in self.detected_faces:
            # Label the detected face as per the model
            face = gray[y:y + h, x:x + w]
            face = cv2.resize(face, self.IMAGE_SIZE)

            result = self.classify_face(face)
            # If a model is loaded, we can predict
            if result:
                predicted, distance = self.classify_face(face)

                if distance > self.STRANGER_DANGER:
                    predicted = 'Stranger danger!'
                    color = self.stranger_color
                else:
                    predicted = predicted.capitalize()
                    color = self.recognized_color

                # Draw a rectangle around the detected face
                cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
                text = '%s (%.1f)' % (predicted, distance)
                cv2.putText(frame, text, (x, y + h + 15),
                            cv2.FONT_HERSHEY_TRIPLEX, 0.5, color)
            else:
                # Draw a rectangle around the detected face
                cv2.rectangle(frame, (x, y), (x + w, y + h),
                              self.stranger_color, 2)
                cv2.putText(frame, 'Stranger danger!', (x, y + h + 15),
                            cv2.FONT_HERSHEY_TRIPLEX, 0.5, self.stranger_color)

        # Display the image in the image area
        image = QImage(frame, frame.shape[1], frame.shape[0],
                       frame.strides[0], QImage.Format_RGB888)
        self.image_label.setPixmap(QPixmap.fromImage(image))

    @contextmanager
    def stop_camera_feed(self):
        """Temporarly stop the feed and face detection."""
        try:
            self.timer.stop()
            yield
        finally:
            self.timer.start(int(1000 / self.fps))

    def take_picture(self):
        # Notify the user there were no faces detected
        if self.detected_faces is None or len(self.detected_faces) < 1:
            return
            raise NoFacesError()

        if len(self.detected_faces) > 1:
            return
            raise MultipleFacesError()

        with self.stop_camera_feed():
            x, y, w, h = self.detected_faces[0]

            face = self.gray_image[y:y + h, x:x + w]
            face = cv2.resize(face, self.IMAGE_SIZE)
            denoised_image = cv2.fastNlMeansDenoising(face)

            if not self.selected_label:
                return

            self.save_image(denoised_image, self.selected_label)

    @property
    def selected_label(self):
        index = self.labels_view.selectedIndexes()
        if len(index) < 1:
            return None

        label = self.existing_labels.data(index[0], Qt.DisplayRole)

        return label

    def get_existing_labels(self):
        """Get a list of the currently existing labels"""
        return data_provider.get_folder_names(self.training_data_dir)

    def save_image(self, image: np.ndarray, label: str) -> None:
        """Save an image to disk in the appropriate directory."""
        if not path.exists(self.training_data_dir):
            mkdir(self.training_data_dir)

        label_path = path.join(self.training_data_dir, label)
        if not path.exists(label_path):
            mkdir(label_path)

        existing_files = listdir(label_path)
        existing_files = map(lambda p: path.splitext(p)[0], existing_files)
        existing_files = list(map(int, existing_files))
        last_fname = sorted(existing_files)[-1] if len(existing_files) else 0

        fname = path.join(label_path, '%03d.png' % (last_fname + 1))
        cv2.imwrite(fname, image)
Пример #32
0
class IconEditorGrid(QWidget):
    """
    Class implementing the icon editor grid.
    
    @signal canRedoChanged(bool) emitted after the redo status has changed
    @signal canUndoChanged(bool) emitted after the undo status has changed
    @signal clipboardImageAvailable(bool) emitted to signal the availability
        of an image to be pasted
    @signal colorChanged(QColor) emitted after the drawing color was changed
    @signal imageChanged(bool) emitted after the image was modified
    @signal positionChanged(int, int) emitted after the cursor poition was
        changed
    @signal previewChanged(QPixmap) emitted to signal a new preview pixmap
    @signal selectionAvailable(bool) emitted to signal a change of the
        selection
    @signal sizeChanged(int, int) emitted after the size has been changed
    @signal zoomChanged(int) emitted to signal a change of the zoom value
    """
    canRedoChanged = pyqtSignal(bool)
    canUndoChanged = pyqtSignal(bool)
    clipboardImageAvailable = pyqtSignal(bool)
    colorChanged = pyqtSignal(QColor)
    imageChanged = pyqtSignal(bool)
    positionChanged = pyqtSignal(int, int)
    previewChanged = pyqtSignal(QPixmap)
    selectionAvailable = pyqtSignal(bool)
    sizeChanged = pyqtSignal(int, int)
    zoomChanged = pyqtSignal(int)

    Pencil = 1
    Rubber = 2
    Line = 3
    Rectangle = 4
    FilledRectangle = 5
    Circle = 6
    FilledCircle = 7
    Ellipse = 8
    FilledEllipse = 9
    Fill = 10
    ColorPicker = 11

    RectangleSelection = 20
    CircleSelection = 21

    MarkColor = QColor(255, 255, 255, 255)
    NoMarkColor = QColor(0, 0, 0, 0)

    ZoomMinimum = 100
    ZoomMaximum = 10000
    ZoomStep = 100
    ZoomDefault = 1200
    ZoomPercent = True

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(IconEditorGrid, self).__init__(parent)

        self.setAttribute(Qt.WA_StaticContents)
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        self.__curColor = Qt.black
        self.__zoom = 12
        self.__curTool = self.Pencil
        self.__startPos = QPoint()
        self.__endPos = QPoint()
        self.__dirty = False
        self.__selecting = False
        self.__selRect = QRect()
        self.__isPasting = False
        self.__clipboardSize = QSize()
        self.__pasteRect = QRect()

        self.__undoStack = QUndoStack(self)
        self.__currentUndoCmd = None

        self.__image = QImage(32, 32, QImage.Format_ARGB32)
        self.__image.fill(qRgba(0, 0, 0, 0))
        self.__markImage = QImage(self.__image)
        self.__markImage.fill(self.NoMarkColor.rgba())

        self.__compositingMode = QPainter.CompositionMode_SourceOver
        self.__lastPos = (-1, -1)

        self.__gridEnabled = True
        self.__selectionAvailable = False

        self.__initCursors()
        self.__initUndoTexts()

        self.setMouseTracking(True)

        self.__undoStack.canRedoChanged.connect(self.canRedoChanged)
        self.__undoStack.canUndoChanged.connect(self.canUndoChanged)
        self.__undoStack.cleanChanged.connect(self.__cleanChanged)

        self.imageChanged.connect(self.__updatePreviewPixmap)
        QApplication.clipboard().dataChanged.connect(self.__checkClipboard)

        self.__checkClipboard()

    def __initCursors(self):
        """
        Private method to initialize the various cursors.
        """
        self.__normalCursor = QCursor(Qt.ArrowCursor)

        pix = QPixmap(":colorpicker-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__colorPickerCursor = QCursor(pix, 1, 21)

        pix = QPixmap(":paintbrush-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__paintCursor = QCursor(pix, 0, 19)

        pix = QPixmap(":fill-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__fillCursor = QCursor(pix, 3, 20)

        pix = QPixmap(":aim-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__aimCursor = QCursor(pix, 10, 10)

        pix = QPixmap(":eraser-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__rubberCursor = QCursor(pix, 1, 16)

    def __initUndoTexts(self):
        """
        Private method to initialize texts to be associated with undo commands
        for the various drawing tools.
        """
        self.__undoTexts = {
            self.Pencil: self.tr("Set Pixel"),
            self.Rubber: self.tr("Erase Pixel"),
            self.Line: self.tr("Draw Line"),
            self.Rectangle: self.tr("Draw Rectangle"),
            self.FilledRectangle: self.tr("Draw Filled Rectangle"),
            self.Circle: self.tr("Draw Circle"),
            self.FilledCircle: self.tr("Draw Filled Circle"),
            self.Ellipse: self.tr("Draw Ellipse"),
            self.FilledEllipse: self.tr("Draw Filled Ellipse"),
            self.Fill: self.tr("Fill Region"),
        }

    def isDirty(self):
        """
        Public method to check the dirty status.
        
        @return flag indicating a modified status (boolean)
        """
        return self.__dirty

    def setDirty(self, dirty, setCleanState=False):
        """
        Public slot to set the dirty flag.
        
        @param dirty flag indicating the new modification status (boolean)
        @param setCleanState flag indicating to set the undo stack to clean
            (boolean)
        """
        self.__dirty = dirty
        self.imageChanged.emit(dirty)

        if not dirty and setCleanState:
            self.__undoStack.setClean()

    def sizeHint(self):
        """
        Public method to report the size hint.
        
        @return size hint (QSize)
        """
        size = self.__zoom * self.__image.size()
        if self.__zoom >= 3 and self.__gridEnabled:
            size += QSize(1, 1)
        return size

    def setPenColor(self, newColor):
        """
        Public method to set the drawing color.
        
        @param newColor reference to the new color (QColor)
        """
        self.__curColor = QColor(newColor)
        self.colorChanged.emit(QColor(newColor))

    def penColor(self):
        """
        Public method to get the current drawing color.
        
        @return current drawing color (QColor)
        """
        return QColor(self.__curColor)

    def setCompositingMode(self, mode):
        """
        Public method to set the compositing mode.
        
        @param mode compositing mode to set (QPainter.CompositionMode)
        """
        self.__compositingMode = mode

    def compositingMode(self):
        """
        Public method to get the compositing mode.
        
        @return compositing mode (QPainter.CompositionMode)
        """
        return self.__compositingMode

    def setTool(self, tool):
        """
        Public method to set the current drawing tool.
        
        @param tool drawing tool to be used
            (IconEditorGrid.Pencil ... IconEditorGrid.CircleSelection)
        """
        self.__curTool = tool
        self.__lastPos = (-1, -1)

        if self.__curTool in [self.RectangleSelection, self.CircleSelection]:
            self.__selecting = True
        else:
            self.__selecting = False

        if self.__curTool in [
                self.RectangleSelection, self.CircleSelection, self.Line,
                self.Rectangle, self.FilledRectangle, self.Circle,
                self.FilledCircle, self.Ellipse, self.FilledEllipse
        ]:
            self.setCursor(self.__aimCursor)
        elif self.__curTool == self.Fill:
            self.setCursor(self.__fillCursor)
        elif self.__curTool == self.ColorPicker:
            self.setCursor(self.__colorPickerCursor)
        elif self.__curTool == self.Pencil:
            self.setCursor(self.__paintCursor)
        elif self.__curTool == self.Rubber:
            self.setCursor(self.__rubberCursor)
        else:
            self.setCursor(self.__normalCursor)

    def tool(self):
        """
        Public method to get the current drawing tool.
        
        @return current drawing tool
            (IconEditorGrid.Pencil ... IconEditorGrid.CircleSelection)
        """
        return self.__curTool

    def setIconImage(self, newImage, undoRedo=False, clearUndo=False):
        """
        Public method to set a new icon image.
        
        @param newImage reference to the new image (QImage)
        @keyparam undoRedo flag indicating an undo or redo operation (boolean)
        @keyparam clearUndo flag indicating to clear the undo stack (boolean)
        """
        if newImage != self.__image:
            self.__image = newImage.convertToFormat(QImage.Format_ARGB32)
            self.update()
            self.updateGeometry()
            self.resize(self.sizeHint())

            self.__markImage = QImage(self.__image)
            self.__markImage.fill(self.NoMarkColor.rgba())

            if undoRedo:
                self.setDirty(not self.__undoStack.isClean())
            else:
                self.setDirty(False)

            if clearUndo:
                self.__undoStack.clear()

            self.sizeChanged.emit(*self.iconSize())

    def iconImage(self):
        """
        Public method to get a copy of the icon image.
        
        @return copy of the icon image (QImage)
        """
        return QImage(self.__image)

    def iconSize(self):
        """
        Public method to get the size of the icon.
        
        @return width and height of the image as a tuple (integer, integer)
        """
        return self.__image.width(), self.__image.height()

    def setZoomFactor(self, newZoom):
        """
        Public method to set the zoom factor in percent.
        
        @param newZoom zoom factor (integer >= 100)
        """
        newZoom = max(100, newZoom)  # must not be less than 100
        if newZoom != self.__zoom:
            self.__zoom = newZoom // 100
            self.update()
            self.updateGeometry()
            self.resize(self.sizeHint())
            self.zoomChanged.emit(int(self.__zoom * 100))

    def zoomFactor(self):
        """
        Public method to get the current zoom factor in percent.
        
        @return zoom factor (integer)
        """
        return self.__zoom * 100

    def setGridEnabled(self, enable):
        """
        Public method to enable the display of grid lines.
        
        @param enable enabled status of the grid lines (boolean)
        """
        if enable != self.__gridEnabled:
            self.__gridEnabled = enable
            self.update()

    def isGridEnabled(self):
        """
        Public method to get the grid lines status.
        
        @return enabled status of the grid lines (boolean)
        """
        return self.__gridEnabled

    def paintEvent(self, evt):
        """
        Protected method called to repaint some of the widget.
        
        @param evt reference to the paint event object (QPaintEvent)
        """
        painter = QPainter(self)

        if self.__zoom >= 3 and self.__gridEnabled:
            painter.setPen(self.palette().windowText().color())
            i = 0
            while i <= self.__image.width():
                painter.drawLine(self.__zoom * i, 0, self.__zoom * i,
                                 self.__zoom * self.__image.height())
                i += 1
            j = 0
            while j <= self.__image.height():
                painter.drawLine(0, self.__zoom * j,
                                 self.__zoom * self.__image.width(),
                                 self.__zoom * j)
                j += 1

        col = QColor("#aaa")
        painter.setPen(Qt.DashLine)
        for i in range(0, self.__image.width()):
            for j in range(0, self.__image.height()):
                rect = self.__pixelRect(i, j)
                if evt.region().intersects(rect):
                    color = QColor.fromRgba(self.__image.pixel(i, j))
                    painter.fillRect(rect, QBrush(Qt.white))
                    painter.fillRect(QRect(rect.topLeft(), rect.center()), col)
                    painter.fillRect(QRect(rect.center(), rect.bottomRight()),
                                     col)
                    painter.fillRect(rect, QBrush(color))

                    if self.__isMarked(i, j):
                        painter.drawRect(rect.adjusted(0, 0, -1, -1))

        painter.end()

    def __pixelRect(self, i, j):
        """
        Private method to determine the rectangle for a given pixel coordinate.
        
        @param i x-coordinate of the pixel in the image (integer)
        @param j y-coordinate of the pixel in the image (integer)
        @return rectangle for the given pixel coordinates (QRect)
        """
        if self.__zoom >= 3 and self.__gridEnabled:
            return QRect(self.__zoom * i + 1, self.__zoom * j + 1,
                         self.__zoom - 1, self.__zoom - 1)
        else:
            return QRect(self.__zoom * i, self.__zoom * j, self.__zoom,
                         self.__zoom)

    def mousePressEvent(self, evt):
        """
        Protected method to handle mouse button press events.
        
        @param evt reference to the mouse event object (QMouseEvent)
        """
        if evt.button() == Qt.LeftButton:
            if self.__isPasting:
                self.__isPasting = False
                self.editPaste(True)
                self.__markImage.fill(self.NoMarkColor.rgba())
                self.update(self.__pasteRect)
                self.__pasteRect = QRect()
                return

            if self.__curTool == self.Pencil:
                cmd = IconEditCommand(self, self.__undoTexts[self.__curTool],
                                      self.__image)
                self.__setImagePixel(evt.pos(), True)
                self.setDirty(True)
                self.__undoStack.push(cmd)
                self.__currentUndoCmd = cmd
            elif self.__curTool == self.Rubber:
                cmd = IconEditCommand(self, self.__undoTexts[self.__curTool],
                                      self.__image)
                self.__setImagePixel(evt.pos(), False)
                self.setDirty(True)
                self.__undoStack.push(cmd)
                self.__currentUndoCmd = cmd
            elif self.__curTool == self.Fill:
                i, j = self.__imageCoordinates(evt.pos())
                col = QColor()
                col.setRgba(self.__image.pixel(i, j))
                cmd = IconEditCommand(self, self.__undoTexts[self.__curTool],
                                      self.__image)
                self.__drawFlood(i, j, col)
                self.setDirty(True)
                self.__undoStack.push(cmd)
                cmd.setAfterImage(self.__image)
            elif self.__curTool == self.ColorPicker:
                i, j = self.__imageCoordinates(evt.pos())
                col = QColor()
                col.setRgba(self.__image.pixel(i, j))
                self.setPenColor(col)
            else:
                self.__unMark()
                self.__startPos = evt.pos()
                self.__endPos = evt.pos()

    def mouseMoveEvent(self, evt):
        """
        Protected method to handle mouse move events.
        
        @param evt reference to the mouse event object (QMouseEvent)
        """
        self.positionChanged.emit(*self.__imageCoordinates(evt.pos()))

        if self.__isPasting and not (evt.buttons() & Qt.LeftButton):
            self.__drawPasteRect(evt.pos())
            return

        if evt.buttons() & Qt.LeftButton:
            if self.__curTool == self.Pencil:
                self.__setImagePixel(evt.pos(), True)
                self.setDirty(True)
            elif self.__curTool == self.Rubber:
                self.__setImagePixel(evt.pos(), False)
                self.setDirty(True)
            elif self.__curTool in [self.Fill, self.ColorPicker]:
                pass  # do nothing
            else:
                self.__drawTool(evt.pos(), True)

    def mouseReleaseEvent(self, evt):
        """
        Protected method to handle mouse button release events.
        
        @param evt reference to the mouse event object (QMouseEvent)
        """
        if evt.button() == Qt.LeftButton:
            if self.__curTool in [self.Pencil, self.Rubber]:
                if self.__currentUndoCmd:
                    self.__currentUndoCmd.setAfterImage(self.__image)
                    self.__currentUndoCmd = None

            if self.__curTool not in [
                    self.Pencil, self.Rubber, self.Fill, self.ColorPicker,
                    self.RectangleSelection, self.CircleSelection
            ]:
                cmd = IconEditCommand(self, self.__undoTexts[self.__curTool],
                                      self.__image)
                if self.__drawTool(evt.pos(), False):
                    self.__undoStack.push(cmd)
                    cmd.setAfterImage(self.__image)
                    self.setDirty(True)

    def __setImagePixel(self, pos, opaque):
        """
        Private slot to set or erase a pixel.
        
        @param pos position of the pixel in the widget (QPoint)
        @param opaque flag indicating a set operation (boolean)
        """
        i, j = self.__imageCoordinates(pos)

        if self.__image.rect().contains(i, j) and (i, j) != self.__lastPos:
            if opaque:
                painter = QPainter(self.__image)
                painter.setPen(self.penColor())
                painter.setCompositionMode(self.__compositingMode)
                painter.drawPoint(i, j)
            else:
                self.__image.setPixel(i, j, qRgba(0, 0, 0, 0))
            self.__lastPos = (i, j)

            self.update(self.__pixelRect(i, j))

    def __imageCoordinates(self, pos):
        """
        Private method to convert from widget to image coordinates.
        
        @param pos widget coordinate (QPoint)
        @return tuple with the image coordinates (tuple of two integers)
        """
        i = pos.x() // self.__zoom
        j = pos.y() // self.__zoom
        return i, j

    def __drawPasteRect(self, pos):
        """
        Private slot to draw a rectangle for signaling a paste operation.
        
        @param pos widget position of the paste rectangle (QPoint)
        """
        self.__markImage.fill(self.NoMarkColor.rgba())
        if self.__pasteRect.isValid():
            self.__updateImageRect(
                self.__pasteRect.topLeft(),
                self.__pasteRect.bottomRight() + QPoint(1, 1))

        x, y = self.__imageCoordinates(pos)
        isize = self.__image.size()
        if x + self.__clipboardSize.width() <= isize.width():
            sx = self.__clipboardSize.width()
        else:
            sx = isize.width() - x
        if y + self.__clipboardSize.height() <= isize.height():
            sy = self.__clipboardSize.height()
        else:
            sy = isize.height() - y

        self.__pasteRect = QRect(QPoint(x, y), QSize(sx - 1, sy - 1))

        painter = QPainter(self.__markImage)
        painter.setPen(self.MarkColor)
        painter.drawRect(self.__pasteRect)
        painter.end()

        self.__updateImageRect(self.__pasteRect.topLeft(),
                               self.__pasteRect.bottomRight() + QPoint(1, 1))

    def __drawTool(self, pos, mark):
        """
        Private method to perform a draw operation depending of the current
        tool.
        
        @param pos widget coordinate to perform the draw operation at (QPoint)
        @param mark flag indicating a mark operation (boolean)
        @return flag indicating a successful draw (boolean)
        """
        self.__unMark()

        if mark:
            self.__endPos = QPoint(pos)
            drawColor = self.MarkColor
            img = self.__markImage
        else:
            drawColor = self.penColor()
            img = self.__image

        start = QPoint(*self.__imageCoordinates(self.__startPos))
        end = QPoint(*self.__imageCoordinates(pos))

        painter = QPainter(img)
        painter.setPen(drawColor)
        painter.setCompositionMode(self.__compositingMode)

        if self.__curTool == self.Line:
            painter.drawLine(start, end)

        elif self.__curTool in [
                self.Rectangle, self.FilledRectangle, self.RectangleSelection
        ]:
            left = min(start.x(), end.x())
            top = min(start.y(), end.y())
            right = max(start.x(), end.x())
            bottom = max(start.y(), end.y())
            if self.__curTool == self.RectangleSelection:
                painter.setBrush(QBrush(drawColor))
            if self.__curTool == self.FilledRectangle:
                for y in range(top, bottom + 1):
                    painter.drawLine(left, y, right, y)
            else:
                painter.drawRect(left, top, right - left, bottom - top)
            if self.__selecting:
                self.__selRect = QRect(left, top, right - left + 1,
                                       bottom - top + 1)
                self.__selectionAvailable = True
                self.selectionAvailable.emit(True)

        elif self.__curTool in [
                self.Circle, self.FilledCircle, self.CircleSelection
        ]:
            deltaX = abs(start.x() - end.x())
            deltaY = abs(start.y() - end.y())
            r = max(deltaX, deltaY)
            if self.__curTool in [self.FilledCircle, self.CircleSelection]:
                painter.setBrush(QBrush(drawColor))
            painter.drawEllipse(start, r, r)
            if self.__selecting:
                self.__selRect = QRect(start.x() - r,
                                       start.y() - r, 2 * r + 1, 2 * r + 1)
                self.__selectionAvailable = True
                self.selectionAvailable.emit(True)

        elif self.__curTool in [self.Ellipse, self.FilledEllipse]:
            r1 = abs(start.x() - end.x())
            r2 = abs(start.y() - end.y())
            if r1 == 0 or r2 == 0:
                return False
            if self.__curTool == self.FilledEllipse:
                painter.setBrush(QBrush(drawColor))
            painter.drawEllipse(start, r1, r2)

        painter.end()

        if self.__curTool in [
                self.Circle, self.FilledCircle, self.Ellipse,
                self.FilledEllipse
        ]:
            self.update()
        else:
            self.__updateRect(self.__startPos, pos)

        return True

    def __drawFlood(self, i, j, oldColor, doUpdate=True):
        """
        Private method to perform a flood fill operation.
        
        @param i x-value in image coordinates (integer)
        @param j y-value in image coordinates (integer)
        @param oldColor reference to the color at position i, j (QColor)
        @param doUpdate flag indicating an update is requested (boolean)
            (used for speed optimizations)
        """
        if (not self.__image.rect().contains(i, j)
                or self.__image.pixel(i, j) != oldColor.rgba()
                or self.__image.pixel(i, j) == self.penColor().rgba()):
            return

        self.__image.setPixel(i, j, self.penColor().rgba())

        self.__drawFlood(i, j - 1, oldColor, False)
        self.__drawFlood(i, j + 1, oldColor, False)
        self.__drawFlood(i - 1, j, oldColor, False)
        self.__drawFlood(i + 1, j, oldColor, False)

        if doUpdate:
            self.update()

    def __updateRect(self, pos1, pos2):
        """
        Private slot to update parts of the widget.
        
        @param pos1 top, left position for the update in widget coordinates
            (QPoint)
        @param pos2 bottom, right position for the update in widget
            coordinates (QPoint)
        """
        self.__updateImageRect(QPoint(*self.__imageCoordinates(pos1)),
                               QPoint(*self.__imageCoordinates(pos2)))

    def __updateImageRect(self, ipos1, ipos2):
        """
        Private slot to update parts of the widget.
        
        @param ipos1 top, left position for the update in image coordinates
            (QPoint)
        @param ipos2 bottom, right position for the update in image
            coordinates (QPoint)
        """
        r1 = self.__pixelRect(ipos1.x(), ipos1.y())
        r2 = self.__pixelRect(ipos2.x(), ipos2.y())

        left = min(r1.x(), r2.x())
        top = min(r1.y(), r2.y())
        right = max(r1.x() + r1.width(), r2.x() + r2.width())
        bottom = max(r1.y() + r1.height(), r2.y() + r2.height())
        self.update(left, top, right - left + 1, bottom - top + 1)

    def __unMark(self):
        """
        Private slot to remove the mark indicator.
        """
        self.__markImage.fill(self.NoMarkColor.rgba())
        if self.__curTool in [
                self.Circle, self.FilledCircle, self.Ellipse,
                self.FilledEllipse, self.CircleSelection
        ]:
            self.update()
        else:
            self.__updateRect(self.__startPos, self.__endPos)

        if self.__selecting:
            self.__selRect = QRect()
            self.__selectionAvailable = False
            self.selectionAvailable.emit(False)

    def __isMarked(self, i, j):
        """
        Private method to check, if a pixel is marked.
        
        @param i x-value in image coordinates (integer)
        @param j y-value in image coordinates (integer)
        @return flag indicating a marked pixel (boolean)
        """
        return self.__markImage.pixel(i, j) == self.MarkColor.rgba()

    def __updatePreviewPixmap(self):
        """
        Private slot to generate and signal an updated preview pixmap.
        """
        p = QPixmap.fromImage(self.__image)
        self.previewChanged.emit(p)

    def previewPixmap(self):
        """
        Public method to generate a preview pixmap.
        
        @return preview pixmap (QPixmap)
        """
        p = QPixmap.fromImage(self.__image)
        return p

    def __checkClipboard(self):
        """
        Private slot to check, if the clipboard contains a valid image, and
        signal the result.
        """
        ok = self.__clipboardImage()[1]
        self.__clipboardImageAvailable = ok
        self.clipboardImageAvailable.emit(ok)

    def canPaste(self):
        """
        Public slot to check the availability of the paste operation.
        
        @return flag indicating availability of paste (boolean)
        """
        return self.__clipboardImageAvailable

    def __clipboardImage(self):
        """
        Private method to get an image from the clipboard.
        
        @return tuple with the image (QImage) and a flag indicating a
            valid image (boolean)
        """
        img = QApplication.clipboard().image()
        ok = not img.isNull()
        if ok:
            img = img.convertToFormat(QImage.Format_ARGB32)

        return img, ok

    def __getSelectionImage(self, cut):
        """
        Private method to get an image from the selection.
        
        @param cut flag indicating to cut the selection (boolean)
        @return image of the selection (QImage)
        """
        if cut:
            cmd = IconEditCommand(self, self.tr("Cut Selection"), self.__image)

        img = QImage(self.__selRect.size(), QImage.Format_ARGB32)
        img.fill(qRgba(0, 0, 0, 0))
        for i in range(0, self.__selRect.width()):
            for j in range(0, self.__selRect.height()):
                if self.__image.rect().contains(self.__selRect.x() + i,
                                                self.__selRect.y() + j):
                    if self.__isMarked(self.__selRect.x() + i,
                                       self.__selRect.y() + j):
                        img.setPixel(
                            i, j,
                            self.__image.pixel(self.__selRect.x() + i,
                                               self.__selRect.y() + j))
                        if cut:
                            self.__image.setPixel(self.__selRect.x() + i,
                                                  self.__selRect.y() + j,
                                                  qRgba(0, 0, 0, 0))

        if cut:
            self.__undoStack.push(cmd)
            cmd.setAfterImage(self.__image)

        self.__unMark()

        if cut:
            self.update(self.__selRect)

        return img

    def editCopy(self):
        """
        Public slot to copy the selection.
        """
        if self.__selRect.isValid():
            img = self.__getSelectionImage(False)
            QApplication.clipboard().setImage(img)

    def editCut(self):
        """
        Public slot to cut the selection.
        """
        if self.__selRect.isValid():
            img = self.__getSelectionImage(True)
            QApplication.clipboard().setImage(img)

    @pyqtSlot()
    def editPaste(self, pasting=False):
        """
        Public slot to paste an image from the clipboard.
        
        @param pasting flag indicating part two of the paste operation
            (boolean)
        """
        img, ok = self.__clipboardImage()
        if ok:
            if (img.width() > self.__image.width()
                    or img.height() > self.__image.height()):
                res = E5MessageBox.yesNo(
                    self, self.tr("Paste"),
                    self.tr("""<p>The clipboard image is larger than the"""
                            """ current image.<br/>Paste as new image?</p>"""))
                if res:
                    self.editPasteAsNew()
                return
            elif not pasting:
                self.__isPasting = True
                self.__clipboardSize = img.size()
            else:
                cmd = IconEditCommand(self, self.tr("Paste Clipboard"),
                                      self.__image)
                self.__markImage.fill(self.NoMarkColor.rgba())
                painter = QPainter(self.__image)
                painter.setPen(self.penColor())
                painter.setCompositionMode(self.__compositingMode)
                painter.drawImage(self.__pasteRect.x(), self.__pasteRect.y(),
                                  img, 0, 0,
                                  self.__pasteRect.width() + 1,
                                  self.__pasteRect.height() + 1)

                self.__undoStack.push(cmd)
                cmd.setAfterImage(self.__image)

                self.__updateImageRect(
                    self.__pasteRect.topLeft(),
                    self.__pasteRect.bottomRight() + QPoint(1, 1))
        else:
            E5MessageBox.warning(
                self, self.tr("Pasting Image"),
                self.tr("""Invalid image data in clipboard."""))

    def editPasteAsNew(self):
        """
        Public slot to paste the clipboard as a new image.
        """
        img, ok = self.__clipboardImage()
        if ok:
            cmd = IconEditCommand(self,
                                  self.tr("Paste Clipboard as New Image"),
                                  self.__image)
            self.setIconImage(img)
            self.setDirty(True)
            self.__undoStack.push(cmd)
            cmd.setAfterImage(self.__image)

    def editSelectAll(self):
        """
        Public slot to select the complete image.
        """
        self.__unMark()

        self.__startPos = QPoint(0, 0)
        self.__endPos = QPoint(self.rect().bottomRight())
        self.__markImage.fill(self.MarkColor.rgba())
        self.__selRect = self.__image.rect()
        self.__selectionAvailable = True
        self.selectionAvailable.emit(True)

        self.update()

    def editClear(self):
        """
        Public slot to clear the image.
        """
        self.__unMark()

        cmd = IconEditCommand(self, self.tr("Clear Image"), self.__image)
        self.__image.fill(qRgba(0, 0, 0, 0))
        self.update()
        self.setDirty(True)
        self.__undoStack.push(cmd)
        cmd.setAfterImage(self.__image)

    def editResize(self):
        """
        Public slot to resize the image.
        """
        from .IconSizeDialog import IconSizeDialog
        dlg = IconSizeDialog(self.__image.width(), self.__image.height())
        res = dlg.exec_()
        if res == QDialog.Accepted:
            newWidth, newHeight = dlg.getData()
            if (newWidth != self.__image.width()
                    or newHeight != self.__image.height()):
                cmd = IconEditCommand(self, self.tr("Resize Image"),
                                      self.__image)
                img = self.__image.scaled(newWidth, newHeight,
                                          Qt.IgnoreAspectRatio,
                                          Qt.SmoothTransformation)
                self.setIconImage(img)
                self.setDirty(True)
                self.__undoStack.push(cmd)
                cmd.setAfterImage(self.__image)

    def editNew(self):
        """
        Public slot to generate a new, empty image.
        """
        from .IconSizeDialog import IconSizeDialog
        dlg = IconSizeDialog(self.__image.width(), self.__image.height())
        res = dlg.exec_()
        if res == QDialog.Accepted:
            width, height = dlg.getData()
            img = QImage(width, height, QImage.Format_ARGB32)
            img.fill(qRgba(0, 0, 0, 0))
            self.setIconImage(img)

    def grayScale(self):
        """
        Public slot to convert the image to gray preserving transparency.
        """
        cmd = IconEditCommand(self, self.tr("Convert to Grayscale"),
                              self.__image)
        for x in range(self.__image.width()):
            for y in range(self.__image.height()):
                col = self.__image.pixel(x, y)
                if col != qRgba(0, 0, 0, 0):
                    gray = qGray(col)
                    self.__image.setPixel(x, y,
                                          qRgba(gray, gray, gray, qAlpha(col)))
        self.update()
        self.setDirty(True)
        self.__undoStack.push(cmd)
        cmd.setAfterImage(self.__image)

    def editUndo(self):
        """
        Public slot to perform an undo operation.
        """
        if self.__undoStack.canUndo():
            self.__undoStack.undo()

    def editRedo(self):
        """
        Public slot to perform a redo operation.
        """
        if self.__undoStack.canRedo():
            self.__undoStack.redo()

    def canUndo(self):
        """
        Public method to return the undo status.
        
        @return flag indicating the availability of undo (boolean)
        """
        return self.__undoStack.canUndo()

    def canRedo(self):
        """
        Public method to return the redo status.
        
        @return flag indicating the availability of redo (boolean)
        """
        return self.__undoStack.canRedo()

    def __cleanChanged(self, clean):
        """
        Private slot to handle the undo stack clean state change.
        
        @param clean flag indicating the clean state (boolean)
        """
        self.setDirty(not clean)

    def shutdown(self):
        """
        Public slot to perform some shutdown actions.
        """
        self.__undoStack.canRedoChanged.disconnect(self.canRedoChanged)
        self.__undoStack.canUndoChanged.disconnect(self.canUndoChanged)
        self.__undoStack.cleanChanged.disconnect(self.__cleanChanged)

    def isSelectionAvailable(self):
        """
        Public method to check the availability of a selection.
        
        @return flag indicating the availability of a selection (boolean)
        """
        return self.__selectionAvailable
Пример #33
0
    def __init__(self, pages: AllPages, windowsize: QSize,
                 globalPagesVariable: GlobalPagesVariableService):
        super().__init__(pages, windowsize)
        self.globalPagesVariable = globalPagesVariable
        self.greenscreenColorRangeService = GreenscreenColorRangeService()
        self.camera = CameraService.initGreenscreenCalibrationCam(
            QSize(windowsize.width() / 2,
                  windowsize.height() / 2))
        mainLayout = QVBoxLayout()
        mainLayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(mainLayout)

        #Titel
        mainLayout.addWidget(
            self.getTitleAsQLabel(TextKey.PAGE_GREENSCREEN_COLOR_PICKER_TITLE))

        #Monitoring:
        colorLayout = QHBoxLayout()
        mainLayout.addLayout(colorLayout)

        self.averageColorLabel = QLineEdit()
        self.averageColorLabel.setReadOnly(True)
        colorLayout.addWidget(self.averageColorLabel)

        self.minColorLabel = QLineEdit()
        self.minColorLabel.setReadOnly(True)
        colorLayout.addWidget(self.minColorLabel)

        self.maxColorLabel = QLineEdit()
        self.maxColorLabel.setReadOnly(True)
        colorLayout.addWidget(self.maxColorLabel)

        #Hinweis:
        self.hintLabel = QLabel()
        mainLayout.addWidget(self.hintLabel)

        #Picture
        mainLayout.addStretch()
        self.picture = QLabel()
        self.picture.setAlignment(Qt.AlignCenter)
        mainLayout.addWidget(self.picture)

        #Buttons   ##################################################################################################
        mainLayout.addStretch()
        navigationTopLayout = QHBoxLayout()
        mainLayout.addLayout(navigationTopLayout)

        toleranceButton = QPushButton(
            textValue[TextKey.PAGE_GREENSCREEN_COLOR_PICKER_TOLERANCE_BUTTON])
        toleranceButton.clicked.connect(self._toleranceButtonEvent)
        self.setNavigationbuttonStyle(toleranceButton)
        navigationTopLayout.addWidget(toleranceButton)

        self.capturePhotoButton = QPushButton(textValue[
            TextKey.PAGE_GREENSCREEN_COLOR_PICKER_CAPTURE_PHOTO_BUTTON])
        self.capturePhotoButton.clicked.connect(self._capturePhotoEvent)
        self.setNavigationbuttonStyle(self.capturePhotoButton)
        navigationTopLayout.addWidget(self.capturePhotoButton)

        #Bottom ------------------------------------------------
        navigationBottomLayout = QHBoxLayout()
        mainLayout.addLayout(navigationBottomLayout)

        backButton = QPushButton(textValue[TextKey.PAGE_CONFIG_BACKBUTTON])
        backButton.clicked.connect(self._backPageSelectEvent)
        self.setNavigationbuttonStyle(backButton)
        navigationBottomLayout.addWidget(backButton)

        self.saveButton = QPushButton(
            textValue[TextKey.PAGE_GREENSCREEN_COLOR_PICKER_SAVE_BUTTON])
        self.saveButton.clicked.connect(self._saveEvent)
        self.setNavigationbuttonStyle(self.saveButton)
        navigationBottomLayout.addWidget(self.saveButton)
Пример #34
0
class MainWindow(QMainWindow, Ui_MainWindow):
    resized = QtCore.pyqtSignal()

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.pushButton_bubble.clicked.connect(self.on_click)
        self.pushButton_dbsetting.clicked.connect(self.on_click)
        self.pushButton_shortcuts.clicked.connect(self.on_click)
        self.pushButton_calibrate.clicked.connect(self.on_click)
        self.pushButton_start.clicked.connect(self.on_click)
        self.pushButton_ok.clicked.connect(self.on_click)
        self.pushButton_cancel.clicked.connect(self.on_click)
        self.pushButton_apply.clicked.connect(self.on_click)
        self.edit_ratioValue.returnPressed.connect(self.on_enter)
        self.edit_id.returnPressed.connect(self.on_enter)
        self.check_id.stateChanged.connect(self.on_check)
        self.frame_board.installEventFilter(self)
        self.image = QImage()
        self.image_size = QSize()
        self.stack.setCurrentWidget(self.page_main)
        self.url = ""
        self.customConnected = False

    def on_calibration_click(self, name):
        if eq(name, "close"):
            self.calibration_window.destroy()
        if eq(name, "cancel"):
            self.calibration_window.destroy()
        if eq(name, "calibrate"):
            if eq(self.calibration_window.url, ""): return
            self.url = self.calibration_window.url
            self.setImageSize(self.calibration_window.image_size)
            self.calibration_window.destroy()
            self.setBoardBackground()

    def on_check(self):
        if self.check_id.isChecked():
            if self.isExistingID():
                self.warning("Duplicated ID!")
                self.check_id.setChecked(False)

    def on_enter(self):
        sending_edit = self.sender()
        if eq(sending_edit.objectName(), "edit_ratioValue"):
            if self.isFloat(self.edit_ratioValue.displayText()) is not True:
                return
            if eq(self.url, "") is True:
                self.edit_ratioValue.setText("")
                return
            width = (float(self.edit_ratioValue.displayText()) *
                     self.image.size().width()) / 100
            if width <= 0:
                self.setImageSize(self.image_size)
                return
            self.image_size.setWidth(width)
            self.image_size.setHeight(self.getScaledHeight(width))
            self.setImageSize(self.image_size)
        if eq(sending_edit.objectName(), "edit_id"):
            if self.isExistingID():
                self.warning("Duplicated ID!")
            elif self.isValidID() is not True:
                self.edit_id.setText("")
            else:
                self.check_id.setChecked(True)

    def warning(self, warning):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Critical)
        msg.setText(warning)
        msg.setWindowTitle("Error")
        msg.show()
        msg.exec_()

    def isValidID(self):
        if eq(self.edit_id.displayText(), "") is True: return False
        if len(self.edit_id.displayText()) > 255: return False
        return True

    def isExistingID(self):
        if self.customConnected is True:
            dbconn = MYSQL(dbhost=self.table.item(0, 0).text(),
                           dbuser=self.table.item(1, 0).text(),
                           dbpwd=self.table.item(2, 0).text(),
                           dbname=self.table.item(3, 0).text(),
                           dbcharset=self.table.item(4, 0).text())
        else:
            dbconn = MYSQL(dbhost=dbconstant.HOST,
                           dbuser=dbconstant.USER,
                           dbpwd=dbconstant.PASSWORD,
                           dbname=dbconstant.DB_NAME,
                           dbcharset=dbconstant.CHARSET)

        condition = {'id': self.edit_id.displayText()}
        count = dbconn.count(table=dbconstant.TABLE, condition=condition)
        dbconn.close()

        return True if count > 0 else False

    def isFloat(self, str):
        try:
            float(str)
            return True
        except:
            return False

    @pyqtSlot()
    def on_click(self):
        sending_button = self.sender()
        if eq(sending_button.objectName(), "pushButton_bubble"):
            self.toggleName(sending_button)
        if eq(sending_button.objectName(), "pushButton_dbsetting"):
            self.stack.setCurrentWidget(self.page_database)
        if eq(sending_button.objectName(), "pushButton_shortcuts"):
            self.stack.setCurrentWidget(self.page_shortcuts)
        if eq(sending_button.objectName(), "pushButton_calibrate"):
            if self.image_size.width() > pyautogui.size(
            ).width or self.image_size.height() > pyautogui.size().height:
                self.warning("Image size is too big")
                return
            self.calibration_window = Calibration(self, self.url,
                                                  self.image_size)
            self.calibration_window.show()
        if eq(sending_button.objectName(), "pushButton_start"):
            isPlotting = True if eq(self.pushButton_calibrate.text(),
                                    "ok") else False
            # 2. db 체크
            if self.check_id.isChecked():
                if eq(self.edit_id.text(), ""):
                    self.warning("there is no id")
                    return
                if self.isExistingID():
                    self.warning("Database ID Duplicated!")
                    return
                id = self.edit_id.text()
            else:
                id = ""
            # 3. 이미지 체크
            if eq(self.url, ""):
                self.warning("There is no image!")
                return
            if self.image_size.width() > pyautogui.size(
            ).width or self.image_size.height() > pyautogui.size().height:
                self.warning("Image size is too big")
                return
            self.tracking_window = Tracker(self.url, self.image_size,
                                           isPlotting, id, self.table,
                                           self.customConnected)
            self.tracking_window.showFullScreen()
            self.tracking_window.setFixedSize(self.tracking_window.size())
        if eq(sending_button.objectName(), "pushButton_ok"):
            self.stack.setCurrentWidget(self.page_main)
        if eq(sending_button.objectName(), "pushButton_cancel"):
            filled = self.checkFilled()
            if filled is False:
                self.table.clearContents()
                self.stack.setCurrentWidget(self.page_main)
                return
            if self.checkConnect() is False:
                self.table.clearContents()
                self.stack.setCurrentWidget(self.page_main)
        if eq(sending_button.objectName(), "pushButton_apply"):
            if self.checkFilled() is not True:
                self.warning("Not Filled!")
                return
            if self.checkConnect() is True:
                self.stack.setCurrentWidget(self.page_main)
            else:
                self.warning("Not Valid Information!")

    def checkFilled(self):
        for row in range(5):
            if self.table.item(row, 0) is None:
                self.customConnected = False
                return False
        return True

    def checkConnect(self):
        dbconn = MYSQL(dbhost=self.table.item(0, 0).text(),
                       dbuser=self.table.item(1, 0).text(),
                       dbpwd=self.table.item(2, 0).text(),
                       dbname=self.table.item(3, 0).text(),
                       dbcharset=self.table.item(4, 0).text())

        if dbconn.session() is None:
            dbconn.close()
            self.customConnected = False
            return False
        else:
            dbconn.close()
            self.customConnected = True
            return True

    def eventFilter(self, object, event):
        if object is self.frame_board:
            if event.type() == QtCore.QEvent.Drop:
                if event.mimeData().hasUrls():
                    event.accept()
                    self.url = event.mimeData().urls()[0].toLocalFile()
                    self.setBoardBackground()
                    self.setImageSize(self.image.size())
                else:
                    event.ignore()
        return False

    def setBoardBackground(self):
        self.image = QImage(self.url)
        pixmap = QPixmap(self.url)
        pixmap = pixmap.scaled(self.frame_board.width(),
                               self.getScaledHeight(self.frame_board.width()))
        self.frame_board.setPixmap(pixmap)

    def getScaledHeight(self, width):
        height = math.floor(
            (width * self.image.size().height()) / self.image.size().width())
        return height

    def setImageSize(self, size):
        self.image_size = size
        self.ratio = (100 *
                      self.image_size.width()) / self.image.size().width()
        self.label_widthValue.setText("%d" % self.image_size.width())
        self.label_heightValue.setText("%d" % self.image_size.height())
        self.edit_ratioValue.setText("%0.2f" % self.ratio)
Пример #35
0
class ThumbnailExtractor(LoadBalancerWorker):

    # Exif rotation constants
    rotate_0 = "1"
    rotate_90 = "6"
    rotate_180 = "3"
    rotate_270 = "8"

    maxStandardSize = QSize(
        max(ThumbnailSize.width, ThumbnailSize.height),
        max(ThumbnailSize.width, ThumbnailSize.height),
    )

    def __init__(self) -> None:
        self.thumbnailSizeNeeded = QSize(ThumbnailSize.width, ThumbnailSize.height)
        self.thumbnail_cache = ThumbnailCacheSql(create_table_if_not_exists=False)
        self.fdo_cache_large = FdoCacheLarge()
        self.fdo_cache_normal = FdoCacheNormal()

        super().__init__("Thumbnail Extractor")

    def rotate_thumb(self, thumbnail: QImage, orientation: str) -> QImage:
        """
        If required return a rotated copy the thumbnail
        :param thumbnail: thumbnail to rotate
        :param orientation: EXIF orientation tag
        :return: possibly rotated thumbnail
        """

        if orientation == self.rotate_90:
            thumbnail = thumbnail.transformed(QTransform().rotate(90))
        elif orientation == self.rotate_270:
            thumbnail = thumbnail.transformed(QTransform().rotate(270))
        elif orientation == self.rotate_180:
            thumbnail = thumbnail.transformed(QTransform().rotate(180))
        return thumbnail

    def image_large_enough(self, size: QSize) -> bool:
        """Check if image is equal or bigger than thumbnail size."""

        return (
            size.width() >= self.thumbnailSizeNeeded.width()
            or size.height() >= self.thumbnailSizeNeeded.height()
        )

    def _extract_256_thumb(
        self,
        rpd_file: RPDFile,
        processing: Set[ExtractionProcessing],
        orientation: Optional[str],
    ) -> PhotoDetails:

        thumbnail = None
        data = rpd_file.metadata.get_preview_256()
        if isinstance(data, bytes):
            thumbnail = QImage.fromData(data)
            if thumbnail.isNull():
                thumbnail = None
            else:
                if thumbnail.width() > 160 or thumbnail.height() > 120:
                    processing.add(ExtractionProcessing.resize)

        return PhotoDetails(thumbnail, orientation)

    def _extract_metadata(
        self, rpd_file: RPDFile, processing: Set[ExtractionProcessing]
    ) -> PhotoDetails:

        thumbnail = orientation = None
        try:
            orientation = rpd_file.metadata.orientation()
        except Exception:
            pass

        rpd_file.mdatatime = rpd_file.metadata.timestamp(missing=0.0)

        # Not all files have an exif preview, but some do
        # (typically CR2, ARW, PEF, RW2).
        # If they exist, they are (almost!) always 160x120

        # TODO how about thumbnail_cache_status?
        if self.write_fdo_thumbnail and rpd_file.fdo_thumbnail_256 is None:
            photo_details = self._extract_256_thumb(
                rpd_file=rpd_file, processing=processing, orientation=orientation
            )
            if photo_details.thumbnail is not None:
                return photo_details
            # if no valid preview found, fall back to the code below and make do with
            # the best we can get

        preview = rpd_file.metadata.get_small_thumbnail_or_first_indexed_preview()
        if preview:
            thumbnail = QImage.fromData(preview)
            if thumbnail.isNull():
                thumbnail = None
            else:
                if thumbnail.width() < thumbnail.height() and orientation in (
                    self.rotate_270,
                    self.rotate_90,
                ):
                    # The orientation has already been applied to the thumbnail
                    logging.debug(
                        "Already rotated: %s", rpd_file.get_current_full_file_name()
                    )
                    orientation = self.rotate_0

                if max(thumbnail.width(), thumbnail.height()) > 160:
                    logging.debug("Resizing: %s", rpd_file.get_current_full_file_name())
                    processing.add(ExtractionProcessing.resize)
                elif not rpd_file.is_jpeg():
                    processing.add(ExtractionProcessing.strip_bars_photo)

        return PhotoDetails(thumbnail, orientation)

    def get_disk_photo_thumb(
        self,
        rpd_file: Photo,
        full_file_name: str,
        processing: Set[ExtractionProcessing],
        force_exiftool: bool,
    ) -> PhotoDetails:
        """
        Get the photo's thumbnail from a file that is on disk.

        Sets rpd_file's mdatatime.

        :param rpd_file: file details
        :param full_file_name: full name of the file from which to get the metadata
        :param processing: processing extraction tasks to complete,
        :param force_exiftool: whether to force the use of ExifTool to load the metadata
        :return: thumbnail and its orientation
        """

        orientation = None
        thumbnail = None
        photo_details = PhotoDetails(thumbnail, orientation)
        if rpd_file.load_metadata(
            full_file_name=full_file_name,
            et_process=self.exiftool_process,
            force_exiftool=force_exiftool,
        ):

            photo_details = self._extract_metadata(rpd_file, processing)
            thumbnail = photo_details.thumbnail

        if thumbnail is not None:
            return photo_details

        if rpd_file.is_loadable():
            thumbnail = QImage(full_file_name)
            processing.add(ExtractionProcessing.resize)
            if not rpd_file.from_camera:
                processing.remove(ExtractionProcessing.orient)
            if thumbnail.isNull():
                thumbnail = None
                logging.warning(
                    "Unable to create a thumbnail out of the file: {}".format(
                        full_file_name
                    )
                )

        return PhotoDetails(thumbnail, orientation)

    def get_from_buffer(
        self,
        rpd_file: Photo,
        raw_bytes: bytearray,
        processing: Set[ExtractionProcessing],
    ) -> PhotoDetails:
        if not rpd_file.load_metadata(
            raw_bytes=raw_bytes, et_process=self.exiftool_process
        ):
            return PhotoDetails(None, None)
        else:
            return self._extract_metadata(rpd_file, processing)

    def get_photo_orientation(
        self,
        rpd_file: Photo,
        force_exiftool: bool,
        full_file_name: Optional[str] = None,
        raw_bytes: Optional[bytearray] = None,
    ) -> Optional[str]:

        if rpd_file.metadata is None:
            self.load_photo_metadata(
                rpd_file=rpd_file,
                full_file_name=full_file_name,
                raw_bytes=raw_bytes,
                force_exiftool=force_exiftool,
            )

        if rpd_file.metadata is not None:
            try:
                return rpd_file.metadata.orientation()
            except Exception:
                pass
        return None

    def assign_mdatatime(
        self,
        rpd_file: Union[Photo, Video],
        force_exiftool: bool,
        full_file_name: Optional[str] = None,
        raw_bytes: Optional[bytearray] = None,
    ) -> None:
        """
        Load the file's metadata and assign the metadata time to the rpd file
        """

        if rpd_file.file_type == FileType.photo:
            self.assign_photo_mdatatime(
                rpd_file=rpd_file,
                full_file_name=full_file_name,
                raw_bytes=raw_bytes,
                force_exiftool=force_exiftool,
            )
        else:
            self.assign_video_mdatatime(
                rpd_file=rpd_file, full_file_name=full_file_name
            )

    def assign_photo_mdatatime(
        self,
        rpd_file: Photo,
        force_exiftool: bool,
        full_file_name: Optional[str] = None,
        raw_bytes: Optional[bytearray] = None,
    ) -> None:
        """
        Load the photo's metadata and assign the metadata time to the rpd file
        """

        self.load_photo_metadata(
            rpd_file=rpd_file,
            full_file_name=full_file_name,
            raw_bytes=raw_bytes,
            force_exiftool=force_exiftool,
        )
        if rpd_file.metadata is not None and rpd_file.date_time() is None:
            rpd_file.mdatatime = 0.0

    def load_photo_metadata(
        self,
        rpd_file: Photo,
        force_exiftool: bool,
        full_file_name: Optional[str] = None,
        raw_bytes: Optional[bytearray] = None,
    ) -> None:
        """
        Load the photo's metadata into the rpd file
        """

        if raw_bytes is not None:
            if rpd_file.is_jpeg_type():
                rpd_file.load_metadata(
                    app1_segment=raw_bytes, et_process=self.exiftool_process
                )
            else:
                rpd_file.load_metadata(
                    raw_bytes=raw_bytes, et_process=self.exiftool_process
                )
        else:
            rpd_file.load_metadata(
                full_file_name=full_file_name,
                et_process=self.exiftool_process,
                force_exiftool=force_exiftool,
            )

    def assign_video_mdatatime(self, rpd_file: Video, full_file_name: str) -> None:
        """
        Load the video's metadata and assign the metadata time to the rpd file
        """

        if rpd_file.metadata is None:
            rpd_file.load_metadata(
                full_file_name=full_file_name, et_process=self.exiftool_process
            )
        if rpd_file.date_time() is None:
            rpd_file.mdatatime = 0.0

    def get_video_rotation(self, rpd_file: Video, full_file_name: str) -> Optional[str]:
        """
        Some videos have a rotation tag. If this video does, return it.
        """

        if rpd_file.metadata is None:
            rpd_file.load_metadata(
                full_file_name=full_file_name, et_process=self.exiftool_process
            )
        orientation = rpd_file.metadata.rotation(missing=None)
        if orientation == 180:
            return self.rotate_180
        elif orientation == 90:
            return self.rotate_90
        elif orientation == 270:
            return self.rotate_270
        return None

    def check_for_stop(self, directive: bytes, content: bytes):
        if directive == b"cmd":
            assert content == b"STOP"
            return True
        return False

    def extract_thumbnail(
        self,
        task: ExtractionTask,
        rpd_file: Union[Photo, Video],
        processing: Set[ExtractionProcessing],
        data: ThumbnailExtractorArgument,
    ) -> Tuple[Optional[QImage], Optional[str]]:
        """
        Extract the thumbnail using one of a variety of methods,
        depending on the file

        :param task: extraction task to perform
        :param rpd_file: rpd_file to work on
        :param processing: processing tasks
        :param data: some other processing arguments passed to this process
        :return: thumbnail and its orientation, if found
        """

        orientation = None

        if task == ExtractionTask.load_from_exif:
            thumbnail_details = self.get_disk_photo_thumb(
                rpd_file,
                data.full_file_name_to_work_on,
                processing,
                data.force_exiftool,
            )
            thumbnail = thumbnail_details.thumbnail
            if thumbnail is not None:
                orientation = thumbnail_details.orientation

        elif task in (
            ExtractionTask.load_file_directly,
            ExtractionTask.load_file_and_exif_directly,
            ExtractionTask.load_file_directly_metadata_from_secondary,
        ):
            thumbnail = QImage(data.full_file_name_to_work_on)

            if task == ExtractionTask.load_file_and_exif_directly:
                self.assign_photo_mdatatime(
                    rpd_file=rpd_file,
                    full_file_name=data.full_file_name_to_work_on,
                    force_exiftool=data.force_exiftool,
                )
            elif task == ExtractionTask.load_file_directly_metadata_from_secondary:
                self.assign_mdatatime(
                    rpd_file=rpd_file,
                    full_file_name=data.secondary_full_file_name,
                    force_exiftool=data.force_exiftool,
                )

            if ExtractionProcessing.orient in processing:
                orientation = self.get_photo_orientation(
                    rpd_file=rpd_file,
                    full_file_name=data.full_file_name_to_work_on,
                    force_exiftool=data.force_exiftool,
                )

        elif task in (
            ExtractionTask.load_from_bytes,
            ExtractionTask.load_from_bytes_metadata_from_temp_extract,
        ):
            try:
                assert data.thumbnail_bytes is not None
            except AssertionError:
                logging.error(
                    "Thumbnail bytes not extracted for %s (value is None)",
                    rpd_file.get_current_full_file_name(),
                )
            thumbnail = QImage.fromData(data.thumbnail_bytes)
            if (
                thumbnail.width() > self.thumbnailSizeNeeded.width()
                or thumbnail.height() > self.thumbnailSizeNeeded.height()
            ):
                processing.add(ExtractionProcessing.resize)
                processing.remove(ExtractionProcessing.strip_bars_photo)
            if data.exif_buffer and ExtractionProcessing.orient in processing:
                orientation = self.get_photo_orientation(
                    rpd_file=rpd_file,
                    raw_bytes=data.exif_buffer,
                    force_exiftool=data.force_exiftool,
                )
            if task == ExtractionTask.load_from_bytes_metadata_from_temp_extract:
                self.assign_mdatatime(
                    rpd_file=rpd_file,
                    full_file_name=data.secondary_full_file_name,
                    force_exiftool=data.force_exiftool,
                )
                orientation = rpd_file.metadata.orientation()
                os.remove(data.secondary_full_file_name)
                rpd_file.temp_cache_full_file_chunk = ""

        elif task == ExtractionTask.load_from_exif_buffer:
            thumbnail_details = self.get_from_buffer(
                rpd_file, data.exif_buffer, processing
            )
            thumbnail = thumbnail_details.thumbnail
            if thumbnail is not None:
                orientation = thumbnail_details.orientation

        elif task in (
            ExtractionTask.load_heif_directly,
            ExtractionTask.load_heif_and_exif_directly,
        ):
            assert have_heif_module
            thumbnail = load_heif(
                data.full_file_name_to_work_on, process_name=self.identity.decode()
            )

            if task == ExtractionTask.load_heif_and_exif_directly:
                self.assign_photo_mdatatime(
                    rpd_file=rpd_file,
                    full_file_name=data.full_file_name_to_work_on,
                    force_exiftool=data.force_exiftool,
                )
            if ExtractionProcessing.orient in processing:
                orientation = self.get_photo_orientation(
                    rpd_file=rpd_file,
                    full_file_name=data.full_file_name_to_work_on,
                    force_exiftool=data.force_exiftool,
                )

        else:
            assert task in (
                ExtractionTask.extract_from_file,
                ExtractionTask.extract_from_file_and_load_metadata,
            )
            if rpd_file.file_type == FileType.photo:
                self.assign_photo_mdatatime(
                    rpd_file=rpd_file,
                    full_file_name=data.full_file_name_to_work_on,
                    force_exiftool=data.force_exiftool,
                )
                thumbnail_bytes = (
                    rpd_file.metadata.get_small_thumbnail_or_first_indexed_preview()
                )
                if thumbnail_bytes:
                    thumbnail = QImage.fromData(thumbnail_bytes)
                    orientation = rpd_file.metadata.orientation()
            else:
                assert rpd_file.file_type == FileType.video

                if ExtractionTask.extract_from_file_and_load_metadata:
                    self.assign_video_mdatatime(
                        rpd_file=rpd_file, full_file_name=data.full_file_name_to_work_on
                    )
                if not have_gst:
                    thumbnail = None
                else:
                    png = get_video_frame(data.full_file_name_to_work_on, 1.0)
                    if not png:
                        thumbnail = None
                        logging.warning(
                            "Could not extract video thumbnail from %s",
                            data.rpd_file.get_display_full_name(),
                        )
                    else:
                        thumbnail = QImage.fromData(png)
                        if thumbnail.isNull():
                            thumbnail = None
                        else:
                            processing.add(ExtractionProcessing.add_film_strip)
                            orientation = self.get_video_rotation(
                                rpd_file, data.full_file_name_to_work_on
                            )
                            if orientation is not None:
                                processing.add(ExtractionProcessing.orient)
                            processing.add(ExtractionProcessing.resize)

        return thumbnail, orientation

    def process_files(self):
        """
        Loop continuously processing photo and video thumbnails
        """

        logging.debug("{} worker started".format(self.requester.identity.decode()))

        while True:
            directive, content = self.requester.recv_multipart()
            if self.check_for_stop(directive, content):
                break

            data = pickle.loads(content)  # type: ThumbnailExtractorArgument

            thumbnail_256 = png_data = None
            task = data.task
            processing = data.processing
            rpd_file = data.rpd_file

            logging.debug(
                "Working on task %s for %s",
                task.name,
                rpd_file.download_name or rpd_file.name,
            )

            self.write_fdo_thumbnail = data.write_fdo_thumbnail

            try:
                if rpd_file.fdo_thumbnail_256 is not None and data.write_fdo_thumbnail:
                    if rpd_file.thumbnail_status != ThumbnailCacheStatus.fdo_256_ready:
                        logging.error(
                            "Unexpected thumbnail cache status for %s: %s",
                            rpd_file.full_file_name,
                            rpd_file.thumbnail_status.name,
                        )
                    thumbnail = thumbnail_256 = QImage.fromData(
                        rpd_file.fdo_thumbnail_256
                    )
                    orientation_unknown = False
                else:
                    thumbnail, orientation = self.extract_thumbnail(
                        task, rpd_file, processing, data
                    )
                    if data.file_to_work_on_is_temporary:
                        os.remove(data.full_file_name_to_work_on)
                        rpd_file.temp_cache_full_file_chunk = ""

                    if thumbnail is not None:
                        if ExtractionProcessing.strip_bars_photo in processing:
                            thumbnail = crop_160x120_thumbnail(thumbnail)
                        elif ExtractionProcessing.strip_bars_video in processing:
                            thumbnail = crop_160x120_thumbnail(thumbnail, 15)
                        if ExtractionProcessing.resize in processing:
                            # Resize the thumbnail before rotating
                            if (
                                orientation == "1" or orientation is None
                            ) and thumbnail.height() > thumbnail.width():

                                # Special case: pictures from some cellphones have already
                                # been rotated
                                thumbnail = thumbnail.scaled(
                                    self.maxStandardSize,
                                    Qt.KeepAspectRatio,
                                    Qt.SmoothTransformation,
                                )
                            else:
                                if (
                                    rpd_file.should_write_fdo()
                                    and image_large_enough_fdo(thumbnail.size())
                                    and max(thumbnail.height(), thumbnail.width()) > 256
                                ):
                                    thumbnail_256 = thumbnail.scaled(
                                        QSize(256, 256),
                                        Qt.KeepAspectRatio,
                                        Qt.SmoothTransformation,
                                    )
                                    thumbnail = thumbnail_256
                                if data.send_thumb_to_main:
                                    thumbnail = thumbnail.scaled(
                                        self.thumbnailSizeNeeded,
                                        Qt.KeepAspectRatio,
                                        Qt.SmoothTransformation,
                                    )
                                else:
                                    thumbnail = None

                            if not thumbnail is None and thumbnail.isNull():
                                thumbnail = None

                    if orientation is not None:
                        if thumbnail is not None:
                            thumbnail = self.rotate_thumb(thumbnail, orientation)
                        if thumbnail_256 is not None:
                            thumbnail_256 = self.rotate_thumb(
                                thumbnail_256, orientation
                            )

                    if ExtractionProcessing.add_film_strip in processing:
                        if thumbnail is not None:
                            thumbnail = add_filmstrip(thumbnail)
                        if thumbnail_256 is not None:
                            thumbnail = add_filmstrip(thumbnail_256)

                    if thumbnail is not None:
                        buffer = qimage_to_png_buffer(thumbnail)
                        png_data = buffer.data()

                    orientation_unknown = (
                        ExtractionProcessing.orient in processing
                        and orientation is None
                    )

                    if (
                        data.send_thumb_to_main
                        and data.use_thumbnail_cache
                        and rpd_file.thumbnail_cache_status
                        == ThumbnailCacheDiskStatus.not_found
                    ):
                        self.thumbnail_cache.save_thumbnail(
                            full_file_name=rpd_file.full_file_name,
                            size=rpd_file.size,
                            mtime=rpd_file.modification_time,
                            mdatatime=rpd_file.mdatatime,
                            generation_failed=thumbnail is None,
                            orientation_unknown=orientation_unknown,
                            thumbnail=thumbnail,
                            camera_model=rpd_file.camera_model,
                        )

                if (
                    thumbnail is not None or thumbnail_256 is not None
                ) and rpd_file.should_write_fdo():
                    if self.write_fdo_thumbnail:
                        # The modification time of the file may have changed when the
                        # file was saved Ideally it shouldn't, but it does sometimes,
                        # e.g. on NTFS! So need to get the modification time from the
                        # saved file.
                        mtime = os.path.getmtime(rpd_file.download_full_file_name)

                        if thumbnail_256 is not None:
                            rpd_file.fdo_thumbnail_256_name = (
                                self.fdo_cache_large.save_thumbnail(
                                    full_file_name=rpd_file.download_full_file_name,
                                    size=rpd_file.size,
                                    modification_time=mtime,
                                    generation_failed=False,
                                    thumbnail=thumbnail_256,
                                    free_desktop_org=False,
                                )
                            )
                            thumbnail_128 = thumbnail_256.scaled(
                                QSize(128, 128),
                                Qt.KeepAspectRatio,
                                Qt.SmoothTransformation,
                            )
                        else:
                            thumbnail_128 = thumbnail.scaled(
                                QSize(128, 128),
                                Qt.KeepAspectRatio,
                                Qt.SmoothTransformation,
                            )
                        rpd_file.fdo_thumbnail_128_name = (
                            self.fdo_cache_normal.save_thumbnail(
                                full_file_name=rpd_file.download_full_file_name,
                                size=rpd_file.size,
                                modification_time=mtime,
                                generation_failed=False,
                                thumbnail=thumbnail_128,
                                free_desktop_org=False,
                            )
                        )
                    elif (
                        thumbnail_256 is not None and rpd_file.fdo_thumbnail_256 is None
                    ):
                        rpd_file.fdo_thumbnail_256 = qimage_to_png_buffer(
                            thumbnail
                        ).data()

                if thumbnail is not None:
                    if orientation_unknown:
                        rpd_file.thumbnail_status = (
                            ThumbnailCacheStatus.orientation_unknown
                        )
                    elif rpd_file.fdo_thumbnail_256 is not None:
                        rpd_file.thumbnail_status = ThumbnailCacheStatus.fdo_256_ready
                    else:
                        rpd_file.thumbnail_status = ThumbnailCacheStatus.ready

            except SystemExit as e:
                self.exiftool_process.terminate()
                sys.exit(e)
            except:
                logging.error("Exception working on file %s", rpd_file.full_file_name)
                logging.error("Task: %s", task)
                logging.error("Processing tasks: %s", processing)
                logging.exception("Traceback:")

            # Purge metadata, as it cannot be pickled
            if not data.send_thumb_to_main:
                png_data = None
            rpd_file.metadata = None
            self.sender.send_multipart(
                [
                    b"0",
                    b"data",
                    pickle.dumps(
                        GenerateThumbnailsResults(
                            rpd_file=rpd_file, thumbnail_bytes=png_data
                        ),
                        pickle.HIGHEST_PROTOCOL,
                    ),
                ]
            )
            self.requester.send_multipart([b"", b"", b"OK"])

    def do_work(self):
        if False:
            # exiv2 pumps out a LOT to stderr - use cautiously!
            context = show_errors()
            self.error_stream = sys.stderr
        else:
            # Redirect stderr, hiding error output from exiv2
            context = stdchannel_redirected(sys.stderr, os.devnull)
            self.error_stream = sys.stdout
        with context:
            # In some situations, using a context manager for exiftool can
            # result in exiftool processes not being terminated. So let's
            # handle starting and terminating it manually.
            self.exiftool_process = exiftool.ExifTool()
            self.exiftool_process.start()
            self.process_files()
            self.exit()

    def cleanup_pre_stop(self) -> None:
        logging.debug(
            "Terminating thumbnail extractor ExifTool process for %s",
            self.identity.decode(),
        )
        self.exiftool_process.terminate()
Пример #36
0
    def __init__(self, app: dict, icon_cache: MemoryCache, i18n: I18n,
                 screen_size: QSize):
        super(InfoDialog,
              self).__init__(flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint)
        self.setWindowTitle(str(app['__app__']))
        self.screen_size = screen_size
        self.i18n = i18n
        layout = QVBoxLayout()
        self.setLayout(layout)

        scroll = QScrollArea(self)
        scroll.setFrameShape(QFrame.NoFrame)
        scroll.setWidgetResizable(True)
        comps_container = QWidget()
        comps_container.setLayout(QVBoxLayout())
        scroll.setWidget(comps_container)

        # shows complete field string
        self.text_field = QPlainTextEdit()
        self.text_field.setReadOnly(True)
        comps_container.layout().addWidget(self.text_field)
        self.text_field.hide()

        self.gbox_info = QGroupBox()
        self.gbox_info.setLayout(QGridLayout())

        comps_container.layout().addWidget(self.gbox_info)

        # THERE ARE CRASHES WITH SOME RARE ICONS ( like insomnia ). IT CAN BE A QT BUG. IN THE MEANTIME, ONLY THE TYPE ICON WILL BE RENDERED
        #
        # icon_data = icon_cache.get(app['__app__'].model.icon_url)
        #
        # if icon_data and icon_data.get('icon'):
        #     self.setWindowIcon(icon_data.get('icon'))
        self.setWindowIcon(QIcon(app['__app__'].model.get_type_icon_path()))

        for idx, attr in enumerate(sorted(app.keys())):
            if attr not in IGNORED_ATTRS and app[attr]:
                i18n_key = app[
                    '__app__'].model.gem_name + '.info.' + attr.lower()

                if isinstance(app[attr], list):
                    val = ' '.join([str(e).strip() for e in app[attr] if e])
                    show_val = '\n'.join(
                        ['* ' + str(e).strip() for e in app[attr] if e])
                else:
                    val = str(app[attr]).strip()
                    show_val = val

                i18n_val = i18n.get('{}.{}'.format(i18n_key, val.lower()))

                if i18n_val:
                    val = i18n_val
                    show_val = val

                text = QLineEdit()
                text.setToolTip(show_val)
                text.setText(val)
                text.setCursorPosition(0)
                text.setStyleSheet("width: 400px")
                text.setReadOnly(True)

                label = QLabel(
                    i18n.get(i18n_key, i18n.get(attr.lower(),
                                                attr)).capitalize())
                label.setStyleSheet("font-weight: bold")

                self.gbox_info.layout().addWidget(label, idx, 0)
                self.gbox_info.layout().addWidget(text, idx, 1)
                self._gen_show_button(idx, show_val)

        layout.addWidget(scroll)

        lower_bar = QToolBar()
        bt_back = QPushButton(self.i18n['back'].capitalize())
        bt_back.setVisible(False)
        bt_back.setCursor(QCursor(Qt.PointingHandCursor))
        bt_back.clicked.connect(self.back_to_info)

        self.ref_bt_back = lower_bar.addWidget(bt_back)
        lower_bar.addWidget(new_spacer())

        bt_close = QPushButton(self.i18n['close'].capitalize())
        bt_close.setCursor(QCursor(Qt.PointingHandCursor))
        bt_close.clicked.connect(lambda: self.close())

        lower_bar.addWidget(bt_close)
        layout.addWidget(lower_bar)
        self.setMinimumWidth(self.gbox_info.sizeHint().width() * 1.2)
        self.setMaximumHeight(screen_size.height() * 0.8)
        self.adjustSize()
Пример #37
0
class TileLayer(Layer):
    ##
    # Constructor.
    ##
    def __init__(self, name, x, y, width, height):
        super().__init__(Layer.TileLayerType, name, x, y, width, height)
        self.mMaxTileSize = QSize(0, 0)
        self.mGrid = QVector()
        for i in range(width * height):
            self.mGrid.append(Cell())
        self.mOffsetMargins = QMargins()

    def __iter__(self):
        return self.mGrid.__iter__()
        
    ##
    # Returns the maximum tile size of this layer.
    ##
    def maxTileSize(self):
        return self.mMaxTileSize

    ##
    # Returns the margins that have to be taken into account while drawing
    # this tile layer. The margins depend on the maximum tile size and the
    # offset applied to the tiles.
    ##
    def drawMargins(self):
        return QMargins(self.mOffsetMargins.left(),
                        self.mOffsetMargins.top() + self.mMaxTileSize.height(),
                        self.mOffsetMargins.right() + self.mMaxTileSize.width(),
                        self.mOffsetMargins.bottom())

    ##
    # Recomputes the draw margins. Needed after the tile offset of a tileset
    # has changed for example.
    #
    # Generally you want to call Map.recomputeDrawMargins instead.
    ##
    def recomputeDrawMargins(self):
        maxTileSize = QSize(0, 0)
        offsetMargins = QMargins()
        i = 0
        while(i<self.mGrid.size()):
            cell = self.mGrid.at(i)
            tile = cell.tile
            if tile:
                size = tile.size()
                if (cell.flippedAntiDiagonally):
                    size.transpose()
                offset = tile.offset()
                maxTileSize = maxSize(size, maxTileSize)
                offsetMargins = maxMargins(QMargins(-offset.x(),
                                                     -offset.y(),
                                                     offset.x(),
                                                     offset.y()),
                                            offsetMargins)
            i += 1

        self.mMaxTileSize = maxTileSize
        self.mOffsetMargins = offsetMargins
        if (self.mMap):
            self.mMap.adjustDrawMargins(self.drawMargins())

    ##
    # Returns whether (x, y) is inside this map layer.
    ##
    def contains(self, *args):
        l = len(args)
        if l==2:
            x, y = args
            return x >= 0 and y >= 0 and x < self.mWidth and y < self.mHeight
        elif l==1:
            point = args[0]
            return self.contains(point.x(), point.y())

    ##
    # Calculates the region of cells in this tile layer for which the given
    # \a condition returns True.
    ##
    def region(self, *args):
        l = len(args)
        if l==1:
            condition = args[0]
            region = QRegion()
            for y in range(self.mHeight):
                for x in range(self.mWidth):
                    if (condition(self.cellAt(x, y))):
                        rangeStart = x
                        x += 1
                        while(x<=self.mWidth):
                            if (x == self.mWidth or not condition(self.cellAt(x, y))):
                                rangeEnd = x
                                region += QRect(rangeStart + self.mX, y + self.mY,
                                                rangeEnd - rangeStart, 1)
                                break
                            x += 1

            return region
        elif l==0:
            ##
            # Calculates the region occupied by the tiles of this layer. Similar to
            # Layer.bounds(), but leaves out the regions without tiles.
            ##
            return self.region(lambda cell:not cell.isEmpty())

    ##
    # Returns a read-only reference to the cell at the given coordinates. The
    # coordinates have to be within this layer.
    ##
    def cellAt(self, *args):
        l = len(args)
        if l==2:
            x, y = args
            return self.mGrid.at(x + y * self.mWidth)
        elif l==1:
            point = args[0]
            return self.cellAt(point.x(), point.y())

    ##
    # Sets the cell at the given coordinates.
    ##
    def setCell(self, x, y, cell):
        if (cell.tile):
            size = cell.tile.size()
            if (cell.flippedAntiDiagonally):
                size.transpose()
            offset = cell.tile.offset()
            self.mMaxTileSize = maxSize(size, self.mMaxTileSize)
            self.mOffsetMargins = maxMargins(QMargins(-offset.x(),
                                                 -offset.y(),
                                                 offset.x(),
                                                 offset.y()),
                                        self.mOffsetMargins)
            if (self.mMap):
                self.mMap.adjustDrawMargins(self.drawMargins())

        self.mGrid[x + y * self.mWidth] = cell

    ##
    # Returns a copy of the area specified by the given \a region. The
    # caller is responsible for the returned tile layer.
    ##
    def copy(self, *args):
        l = len(args)
        if l==1:
            region = args[0]
            if type(region) != QRegion:
                region = QRegion(region)
            area = region.intersected(QRect(0, 0, self.width(), self.height()))
            bounds = region.boundingRect()
            areaBounds = area.boundingRect()
            offsetX = max(0, areaBounds.x() - bounds.x())
            offsetY = max(0, areaBounds.y() - bounds.y())
            copied = TileLayer(QString(), 0, 0, bounds.width(), bounds.height())
            for rect in area.rects():
                for x in range(rect.left(), rect.right()+1):
                    for y in range(rect.top(), rect.bottom()+1):
                        copied.setCell(x - areaBounds.x() + offsetX,
                                        y - areaBounds.y() + offsetY,
                                        self.cellAt(x, y))
            return copied
        elif l==4:
            x, y, width, height = args

            return self.copy(QRegion(x, y, width, height))

    ##
    # Merges the given \a layer onto this layer at position \a pos. Parts that
    # fall outside of this layer will be lost and empty tiles in the given
    # layer will have no effect.
    ##
    def merge(self, pos, layer):
        # Determine the overlapping area
        area = QRect(pos, QSize(layer.width(), layer.height()))
        area &= QRect(0, 0, self.width(), self.height())
        for y in range(area.top(), area.bottom()+1):
            for x in range(area.left(), area.right()+1):
                cell = layer.cellAt(x - pos.x(), y - pos.y())
                if (not cell.isEmpty()):
                    self.setCell(x, y, cell)

    ##
    # Removes all cells in the specified region.
    ##
    def erase(self, area):
        emptyCell = Cell()
        for rect in area.rects():
            for x in range(rect.left(), rect.right()+1):
                for y in range(rect.top(), rect.bottom()+1):
                    self.setCell(x, y, emptyCell)

    ##
    # Sets the cells starting at the given position to the cells in the given
    # \a tileLayer. Parts that fall outside of this layer will be ignored.
    #
    # When a \a mask is given, only cells that fall within this mask are set.
    # The mask is applied in local coordinates.
    ##
    def setCells(self, x, y, layer, mask = QRegion()):
        # Determine the overlapping area
        area = QRegion(QRect(x, y, layer.width(), layer.height()))
        area &= QRect(0, 0, self.width(), self.height())
        if (not mask.isEmpty()):
            area &= mask
        for rect in area.rects():
            for _x in range(rect.left(), rect.right()+1):
                for _y in range(rect.top(), rect.bottom()+1):
                    self.setCell(_x, _y, layer.cellAt(_x - x, _y - y))

    ##
    # Flip this tile layer in the given \a direction. Direction must be
    # horizontal or vertical. This doesn't change the dimensions of the
    # tile layer.
    ##
    def flip(self, direction):
        newGrid = QVector()
        for i in range(self.mWidth * self.mHeight):
            newGrid.append(Cell())
        for y in range(self.mHeight):
            for x in range(self.mWidth):
                dest = newGrid[x + y * self.mWidth]
                if (direction == FlipDirection.FlipHorizontally):
                    source = self.cellAt(self.mWidth - x - 1, y)
                    dest = source
                    dest.flippedHorizontally = not source.flippedHorizontally
                elif (direction == FlipDirection.FlipVertically):
                    source = self.cellAt(x, self.mHeight - y - 1)
                    dest = source
                    dest.flippedVertically = not source.flippedVertically

        self.mGrid = newGrid

    ##
    # Rotate this tile layer by 90 degrees left or right. The tile positions
    # are rotated within the layer, and the tiles themselves are rotated. The
    # dimensions of the tile layer are swapped.
    ##
    def rotate(self, direction):
        rotateRightMask = [5, 4, 1, 0, 7, 6, 3, 2]
        rotateLeftMask  = [3, 2, 7, 6, 1, 0, 5, 4]
        if direction == RotateDirection.RotateRight:
            rotateMask = rotateRightMask
        else:
            rotateMask = rotateLeftMask
        newWidth = self.mHeight
        newHeight = self.mWidth
        newGrid = QVector(newWidth * newHeight)
        for y in range(self.mHeight):
            for x in range(self.mWidth):
                source = self.cellAt(x, y)
                dest = source
                mask = (dest.flippedHorizontally << 2) | (dest.flippedVertically << 1) | (dest.flippedAntiDiagonally << 0)
                mask = rotateMask[mask]
                dest.flippedHorizontally = (mask & 4) != 0
                dest.flippedVertically = (mask & 2) != 0
                dest.flippedAntiDiagonally = (mask & 1) != 0
                if (direction == RotateDirection.RotateRight):
                    newGrid[x * newWidth + (self.mHeight - y - 1)] = dest
                else:
                    newGrid[(self.mWidth - x - 1) * newWidth + y] = dest

        t = self.mMaxTileSize.width()
        self.mMaxTileSize.setWidth(self.mMaxTileSize.height())
        self.mMaxTileSize.setHeight(t)
        self.mWidth = newWidth
        self.mHeight = newHeight
        self.mGrid = newGrid

    ##
    # Computes and returns the set of tilesets used by this tile layer.
    ##
    def usedTilesets(self):
        tilesets = QSet()
        i = 0
        while(i<self.mGrid.size()):
            tile = self.mGrid.at(i).tile
            if tile:
                tilesets.insert(tile.tileset())
            i += 1
        return tilesets

    ##
    # Returns whether this tile layer has any cell for which the given
    # \a condition returns True.
    ##
    def hasCell(self, condition):
        i = 0
        for cell in self.mGrid:
            if (condition(cell)):
                return True
            i += 1
        return False

    ##
    # Returns whether this tile layer is referencing the given tileset.
    ##
    def referencesTileset(self, tileset):
        i = 0
        while(i<self.mGrid.size()):
            tile = self.mGrid.at(i).tile
            if (tile and tile.tileset() == tileset):
                return True
            i += 1
        return False

    ##
    # Removes all references to the given tileset. This sets all tiles on this
    # layer that are from the given tileset to null.
    ##
    def removeReferencesToTileset(self, tileset):
        i = 0
        while(i<self.mGrid.size()):
            tile = self.mGrid.at(i).tile
            if (tile and tile.tileset() == tileset):
                self.mGrid.replace(i, Cell())
            i += 1

    ##
    # Replaces all tiles from \a oldTileset with tiles from \a newTileset.
    ##
    def replaceReferencesToTileset(self, oldTileset, newTileset):
        i = 0
        while(i<self.mGrid.size()):
            tile = self.mGrid.at(i).tile
            if (tile and tile.tileset() == oldTileset):
                self.mGrid[i].tile = newTileset.tileAt(tile.id())
            i += 1

    ##
    # Resizes this tile layer to \a size, while shifting all tiles by
    # \a offset.
    ##
    def resize(self, size, offset):
        if (self.size() == size and offset.isNull()):
            return
        newGrid = QVector()
        for i in range(size.width() * size.height()):
            newGrid.append(Cell())
        # Copy over the preserved part
        startX = max(0, -offset.x())
        startY = max(0, -offset.y())
        endX = min(self.mWidth, size.width() - offset.x())
        endY = min(self.mHeight, size.height() - offset.y())
        for y in range(startY, endY):
            for x in range(startX, endX):
                index = x + offset.x() + (y + offset.y()) * size.width()
                newGrid[index] = self.cellAt(x, y)

        self.mGrid = newGrid
        self.setSize(size)

    ##
    # Offsets the tiles in this layer within \a bounds by \a offset,
    # and optionally wraps them.
    #
    # \sa ObjectGroup.offset()
    ##
    def offsetTiles(self, offset, bounds, wrapX, wrapY):
        newGrid = QVector()
        for i in range(self.mWidth * self.mHeight):
            newGrid.append(Cell())
        for y in range(self.mHeight):
            for x in range(self.mWidth):
                # Skip out of bounds tiles
                if (not bounds.contains(x, y)):
                    newGrid[x + y * self.mWidth] = self.cellAt(x, y)
                    continue

                # Get position to pull tile value from
                oldX = x - offset.x()
                oldY = y - offset.y()
                # Wrap x value that will be pulled from
                if (wrapX and bounds.width() > 0):
                    while oldX < bounds.left():
                        oldX += bounds.width()
                    while oldX > bounds.right():
                        oldX -= bounds.width()

                # Wrap y value that will be pulled from
                if (wrapY and bounds.height() > 0):
                    while oldY < bounds.top():
                        oldY += bounds.height()
                    while oldY > bounds.bottom():
                        oldY -= bounds.height()

                # Set the new tile
                if (self.contains(oldX, oldY) and bounds.contains(oldX, oldY)):
                    newGrid[x + y * self.mWidth] = self.cellAt(oldX, oldY)
                else:
                    newGrid[x + y * self.mWidth] = Cell()

        self.mGrid = newGrid

    def canMergeWith(self, other):
        return other.isTileLayer()

    def mergedWith(self, other):
        o = other
        unitedBounds = self.bounds().united(o.bounds())
        offset = self.position() - unitedBounds.topLeft()
        merged = self.clone()
        merged.resize(unitedBounds.size(), offset)
        merged.merge(o.position() - unitedBounds.topLeft(), o)
        return merged

    ##
    # Returns the region where this tile layer and the given tile layer
    # are different. The relative positions of the layers are taken into
    # account. The returned region is relative to this tile layer.
    ##
    def computeDiffRegion(self, other):
        ret = QRegion()
        dx = other.x() - self.mX
        dy = other.y() - self.mY
        r = QRect(0, 0, self.width(), self.height())
        r &= QRect(dx, dy, other.width(), other.height())
        for y in range(r.top(), r.bottom()+1):
            for x in range(r.left(), r.right()+1):
                if (self.cellAt(x, y) != other.cellAt(x - dx, y - dy)):
                    rangeStart = x
                    while (x <= r.right() and self.cellAt(x, y) != other.cellAt(x - dx, y - dy)):
                        x += 1

                    rangeEnd = x
                    ret += QRect(rangeStart, y, rangeEnd - rangeStart, 1)

        return ret

    ##
    # Returns True if all tiles in the layer are empty.
    ##
    def isEmpty(self):
        i = 0
        while(i<self.mGrid.size()):
            if (not self.mGrid.at(i).isEmpty()):
                return False
            i += 1
        return True

    ##
    # Returns a duplicate of this TileLayer.
    #
    # \sa Layer.clone()
    ##
    def clone(self):
        return self.initializeClone(TileLayer(self.mName, self.mX, self.mY, self.mWidth, self.mHeight))

    def begin(self):
        return self.mGrid.begin()
    
    def end(self):
        return self.mGrid.end()
        
    def initializeClone(self, clone):
        super().initializeClone(clone)
        clone.mGrid = self.mGrid
        clone.mMaxTileSize = self.mMaxTileSize
        clone.mOffsetMargins = self.mOffsetMargins
        return clone
Пример #38
0
class PDFWidget(QLabel):
    '''
    A widget showing one page of a PDF.
    If you want to show multiple pages of the same PDF,
    make sure you share the document (let the first PDFWidget
    create the document, then pass thatPDFwidget.document to any
    subsequent widgets you create) or use a ScrolledPDFWidget.
    Will try to resize to a reasonable size to fit inside the
    geometry passed in (typically the screen size, a PyQt5.QtCore.QRect);
    or specify dpi explicitly.
    '''
    def __init__(self,
                 url,
                 document=None,
                 pageno=1,
                 dpi=None,
                 geometry=None,
                 parent=None,
                 load_cb=None):
        '''
           load_cb: will be called when the document is loaded.
        '''
        super(PDFWidget, self).__init__(parent)

        self.geometry = geometry

        # Guess at initial size: will be overridden later.
        if geometry:
            self.winwidth = geometry.height() * .75
            self.winheight = geometry.height()
        else:
            self.geometry = PyQt5.QtCore.QSize(600, 800)
            self.winwidth = 600
            self.winheight = 800

        self.filename = url

        self.load_cb = load_cb

        self.network_manager = None

        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        if not document:
            self.document = None
            if url:
                self.start_load(url)
        else:
            self.document = document
        self.page = None
        self.pagesize = QSize(self.winwidth, self.winheight)

        self.dpi = dpi

        # Poppler page numbering starts from 0 but that's not what
        # most PDF users will expect, so subtract:
        if pageno > 0:
            pageno -= 1
        self.pageno = pageno

        self.render()

    def sizeHint(self):
        if not self.page:
            if not self.document:
                return QSize(self.winwidth, self.winheight)
            self.page = self.document.page(self.pageno)

        if not self.pagesize:
            self.pagesize = self.page.pageSize()

        return self.pagesize

    def render(self):
        '''Render to a pixmap at the current DPI setting.
        '''
        if not self.document:
            return

        if not self.page:
            self.page = self.document.page(self.pageno)
            self.pagesize = self.page.pageSize()

            self.document.setRenderHint(Poppler.Document.TextAntialiasing)
            # self.document.setRenderHint(Poppler.Document.TextHinting)

        if not self.dpi:
            # Probably first time here.
            # self.pagesize is sized in pixels assuming POINTS_PER_INCH;
            # adjust that so the page barely fits on in self.geometry.

            # First assume that it's portrait aspect ratio and that
            # vertical size will be the limiting factor.
            self.dpi = POINTS_PER_INCH * \
                self.geometry.height() / self.pagesize.height()

            # Was that too much: will it overflow in width?
            if self.pagesize.width() * self.dpi / POINTS_PER_INCH \
               > self.geometry.width():
                self.dpi = POINTS_PER_INCH * \
                    self.geometry.width() / self.pagesize.width()

            self.winwidth = self.pagesize.width() * self.dpi / POINTS_PER_INCH
            self.winheight = self.pagesize.height(
            ) * self.dpi / POINTS_PER_INCH

            # Most Qt5 programs seem to use setGeometry(x, y, w, h)
            # to set initial window size. resize() is the only method I've
            # found that doesn't force initial position as well as size.
            self.resize(self.winwidth, self.winheight)

        self.setWindowTitle('PDF Viewer')

        img = self.page.renderToImage(self.dpi, self.dpi)
        self.pixmap = QPixmap.fromImage(img)
        self.setPixmap(self.pixmap)

    def start_load(self, url):
        '''Create a Poppler.Document from the given URL, QUrl or filename.
           Return, then asynchronously call self.load_cb.
        '''

        # If it's not a local file, we'll need to load it.
        # http://doc.qt.io/qt-5/qnetworkaccessmanager.html
        qurl = QUrl(url)
        if not qurl.scheme():
            qurl = QUrl.fromLocalFile(url)
        if not self.network_manager:
            self.network_manager = QNetworkAccessManager()
        self.network_manager.finished.connect(self.download_finished)
        self.network_manager.get(QNetworkRequest(qurl))

    def download_finished(self, network_reply):
        qbytes = network_reply.readAll()
        self.document = Poppler.Document.loadFromData(qbytes)

        self.render()

        if self.load_cb:
            self.load_cb()
Пример #39
0
class Plugin(RevealerPlugin):

    MAX_PLAINTEXT_LEN = 189  # chars

    def __init__(self, parent, config, name):
        RevealerPlugin.__init__(self, parent, config, name)
        self.base_dir = os.path.join(config.electrum_path(), 'revealer')

        if self.config.get('calibration_h') is None:
            self.config.set_key('calibration_h', 0)
        if self.config.get('calibration_v') is None:
            self.config.set_key('calibration_v', 0)

        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')

        self.f_size = QSize(1014*2, 642*2)
        self.abstand_h = 21
        self.abstand_v = 34
        self.calibration_noise = int('10' * 128)
        self.rawnoise = False
        make_dir(self.base_dir)

        self.extension = False

    @hook
    def create_status_bar(self, parent):
        b = StatusBarButton(read_QIcon('revealer.png'), "Revealer "+_("secret backup utility"),
                            partial(self.setup_dialog, parent))
        parent.addPermanentWidget(b)

    def requires_settings(self):
        return True

    def settings_widget(self, window):
        return EnterButton(_('Printer Calibration'), partial(self.calibration_dialog, window))

    def password_dialog(self, msg=None, parent=None):
        from vialectrum.gui.qt.password_dialog import PasswordDialog
        parent = parent or self
        d = PasswordDialog(parent, msg)
        return d.run()

    def get_seed(self):
        password = None
        if self.wallet.has_keystore_encryption():
            password = self.password_dialog(parent=self.d.parent())
            if not password:
                raise UserCancelled()

        keystore = self.wallet.get_keystore()
        if not keystore or not keystore.has_seed():
            return
        self.extension = bool(keystore.get_passphrase(password))
        return keystore.get_seed(password)

    def setup_dialog(self, window):
        self.wallet = window.parent().wallet
        self.update_wallet_name(self.wallet)
        self.user_input = False

        self.d = WindowModalDialog(window, "Setup Dialog")
        self.d.setMinimumWidth(500)
        self.d.setMinimumHeight(210)
        self.d.setMaximumHeight(320)
        self.d.setContentsMargins(11,11,1,1)

        self.hbox = QHBoxLayout(self.d)
        vbox = QVBoxLayout()
        logo = QLabel()
        self.hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        self.hbox.addSpacing(16)
        vbox.addWidget(WWLabel("<b>"+_("Revealer Secret Backup Plugin")+"</b><br>"
                                    +_("To encrypt your backup, first we need to load some noise.")+"<br/>"))
        vbox.addSpacing(7)
        bcreate = QPushButton(_("Create a new Revealer"))
        bcreate.setMaximumWidth(181)
        bcreate.setDefault(True)
        vbox.addWidget(bcreate, Qt.AlignCenter)
        self.load_noise = ScanQRTextEdit()
        self.load_noise.setTabChangesFocus(True)
        self.load_noise.textChanged.connect(self.on_edit)
        self.load_noise.setMaximumHeight(33)
        self.hbox.addLayout(vbox)
        vbox.addWidget(WWLabel(_("or type an existing revealer code below and click 'next':")))
        vbox.addWidget(self.load_noise)
        vbox.addSpacing(3)
        self.next_button = QPushButton(_("Next"), self.d)
        self.next_button.setEnabled(False)
        vbox.addLayout(Buttons(self.next_button))
        self.next_button.clicked.connect(self.d.close)
        self.next_button.clicked.connect(partial(self.cypherseed_dialog, window))
        vbox.addWidget(
            QLabel("<b>" + _("Warning") + "</b>: " + _("Each revealer should be used only once.")
                   +"<br>"+_("more information at <a href=\"https://revealer.cc/faq\">https://revealer.cc/faq</a>")))

        def mk_digital():
            try:
                self.make_digital(self.d)
            except Exception:
                self.logger.exception('')
            else:
                self.cypherseed_dialog(window)

        bcreate.clicked.connect(mk_digital)
        return bool(self.d.exec_())

    def get_noise(self):
        text = self.load_noise.text()
        return ''.join(text.split()).lower()

    def on_edit(self):
        txt = self.get_noise()
        versioned_seed = self.get_versioned_seed_from_user_input(txt)
        if versioned_seed:
            self.versioned_seed = versioned_seed
        self.user_input = bool(versioned_seed)
        self.next_button.setEnabled(bool(versioned_seed))

    def make_digital(self, dialog):
        self.make_rawnoise(True)
        self.bdone(dialog)
        self.d.close()

    def get_path_to_revealer_file(self, ext: str= '') -> str:
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        filename = self.filename_prefix + version + "_" + code_id + ext
        path = os.path.join(self.base_dir, filename)
        return os.path.normcase(os.path.abspath(path))

    def get_path_to_calibration_file(self):
        path = os.path.join(self.base_dir, 'calibration.pdf')
        return os.path.normcase(os.path.abspath(path))

    def bcrypt(self, dialog):
        self.rawnoise = False
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([_("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").format(self.was, version, code_id),
                                     "<b>", self.get_path_to_revealer_file(), "</b>", "<br/>",
                                     "<br/>", "<b>", _("Always check your backups.")]),
                            rich_text=True)
        dialog.close()

    def ext_warning(self, dialog):
        dialog.show_message(''.join(["<b>",_("Warning"), ": </b>",
                                     _("your seed extension will <b>not</b> be included in the encrypted backup.")]),
                            rich_text=True)
        dialog.close()

    def bdone(self, dialog):
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([_("Digital Revealer ({}_{}) saved as PNG and PDF at:").format(version, code_id),
                                     "<br/>","<b>", self.get_path_to_revealer_file(), '</b>']),
                            rich_text=True)


    def customtxt_limits(self):
        txt = self.text.text()
        self.max_chars.setVisible(False)
        self.char_count.setText(f"({len(txt)}/{self.MAX_PLAINTEXT_LEN})")
        if len(txt)>0:
            self.ctext.setEnabled(True)
        if len(txt) > self.MAX_PLAINTEXT_LEN:
            self.text.setPlainText(txt[:self.MAX_PLAINTEXT_LEN])
            self.max_chars.setVisible(True)

    def t(self):
        self.txt = self.text.text()
        self.seed_img(is_seed=False)

    def warn_old_revealer(self):
        if self.versioned_seed.version == '0':
            link = "https://revealer.cc/revealer-warning-and-upgrade/"
            self.d.show_warning(("<b>{warning}: </b>{ver0}<br>"
                                 "{url}<br>"
                                 "{risk}")
                                .format(warning=_("Warning"),
                                        ver0=_("Revealers starting with 0 are not secure due to a vulnerability."),
                                        url=_("More info at: {}").format(f'<a href="{link}">{link}</a>'),
                                        risk=_("Proceed at your own risk.")),
                                rich_text=True)

    def cypherseed_dialog(self, window):
        self.warn_old_revealer()

        d = WindowModalDialog(window, "Encryption Dialog")
        d.setMinimumWidth(500)
        d.setMinimumHeight(210)
        d.setMaximumHeight(450)
        d.setContentsMargins(11, 11, 1, 1)
        self.c_dialog = d

        hbox = QHBoxLayout(d)
        self.vbox = QVBoxLayout()
        logo = QLabel()
        hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        hbox.addSpacing(16)
        self.vbox.addWidget(WWLabel("<b>" + _("Revealer Secret Backup Plugin") + "</b><br>"
                               + _("Ready to encrypt for revealer {}")
                                    .format(self.versioned_seed.version+'_'+self.versioned_seed.checksum)))
        self.vbox.addSpacing(11)
        hbox.addLayout(self.vbox)
        grid = QGridLayout()
        self.vbox.addLayout(grid)

        cprint = QPushButton(_("Encrypt {}'s seed").format(self.wallet_name))
        cprint.setMaximumWidth(250)
        cprint.clicked.connect(partial(self.seed_img, True))
        self.vbox.addWidget(cprint)
        self.vbox.addSpacing(1)
        self.vbox.addWidget(WWLabel("<b>"+_("OR")+"</b> "+_("type a custom alphanumerical secret below:")))
        self.text = ScanQRTextEdit()
        self.text.setTabChangesFocus(True)
        self.text.setMaximumHeight(70)
        self.text.textChanged.connect(self.customtxt_limits)
        self.vbox.addWidget(self.text)
        self.char_count = WWLabel("")
        self.char_count.setAlignment(Qt.AlignRight)
        self.vbox.addWidget(self.char_count)
        self.max_chars = WWLabel("<font color='red'>"
                                 + _("This version supports a maximum of {} characters.").format(self.MAX_PLAINTEXT_LEN)
                                 +"</font>")
        self.vbox.addWidget(self.max_chars)
        self.max_chars.setVisible(False)
        self.ctext = QPushButton(_("Encrypt custom secret"))
        self.ctext.clicked.connect(self.t)
        self.vbox.addWidget(self.ctext)
        self.ctext.setEnabled(False)
        self.vbox.addSpacing(11)
        self.vbox.addLayout(Buttons(CloseButton(d)))
        return bool(d.exec_())

    def update_wallet_name(self, name):
        self.wallet_name = str(name)

    def seed_img(self, is_seed = True):

        if is_seed:
            try:
                cseed = self.get_seed()
            except UserCancelled:
                return
            except InvalidPassword as e:
                self.d.show_error(str(e))
                return
            if not cseed:
                self.d.show_message(_("This wallet has no seed"))
                return
            txt = cseed.upper()
        else:
            txt = self.txt.upper()

        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.white)
        painter = QPainter()
        painter.begin(bitmap)
        QFontDatabase.addApplicationFont(os.path.join(os.path.dirname(__file__), 'SourceSansPro-Bold.otf') )
        if len(txt) < 102 :
            fontsize = 15
            linespace = 15
            max_letters = 17
            max_lines = 6
            max_words = 3
        else:
            fontsize = 12
            linespace = 10
            max_letters = 21
            max_lines = 9
            max_words = int(max_letters/4)

        font = QFont('Source Sans Pro', fontsize, QFont.Bold)
        font.setLetterSpacing(QFont.PercentageSpacing, 100)
        font.setPixelSize(fontsize)
        painter.setFont(font)
        seed_array = txt.split(' ')

        for n in range(max_lines):
            nwords = max_words
            temp_seed = seed_array[:nwords]
            while len(' '.join(map(str, temp_seed))) > max_letters:
               nwords = nwords - 1
               temp_seed = seed_array[:nwords]
            painter.drawText(QRect(0, linespace*n , self.SIZE[0], self.SIZE[1]), Qt.AlignHCenter, ' '.join(map(str, temp_seed)))
            del seed_array[:nwords]

        painter.end()
        img = bitmap.toImage()
        if (self.rawnoise == False):
            self.make_rawnoise()

        self.make_cypherseed(img, self.rawnoise, False, is_seed)
        return img

    def make_rawnoise(self, create_revealer=False):
        if not self.user_input:
            self.versioned_seed = self.gen_random_versioned_seed()
        assert self.versioned_seed
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)

        noise_map = self.get_noise_map(self.versioned_seed)
        for (x,y), pixel in noise_map.items():
            rawnoise.setPixel(x, y, pixel)

        self.rawnoise = rawnoise
        if create_revealer:
            self.make_revealer()

    def make_calnoise(self):
        random.seed(self.calibration_noise)
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)
        for x in range(w):
            for y in range(h):
                rawnoise.setPixel(x,y,random.randint(0, 1))
        self.calnoise = self.pixelcode_2x2(rawnoise)

    def make_revealer(self):
        revealer = self.pixelcode_2x2(self.rawnoise)
        revealer.invertPixels()
        revealer = QBitmap.fromImage(revealer)
        revealer = revealer.scaled(self.f_size, Qt.KeepAspectRatio)
        revealer = self.overlay_marks(revealer)

        self.filename_prefix = 'revealer_'
        revealer.save(self.get_path_to_revealer_file('.png'))
        self.toPdf(QImage(revealer))
        QDesktopServices.openUrl(QUrl.fromLocalFile(self.get_path_to_revealer_file('.pdf')))

    def make_cypherseed(self, img, rawnoise, calibration=False, is_seed = True):
        img = img.convertToFormat(QImage.Format_Mono)
        p = QPainter()
        p.begin(img)
        p.setCompositionMode(26) #xor
        p.drawImage(0, 0, rawnoise)
        p.end()
        cypherseed = self.pixelcode_2x2(img)
        cypherseed = QBitmap.fromImage(cypherseed)
        cypherseed = cypherseed.scaled(self.f_size, Qt.KeepAspectRatio)
        cypherseed = self.overlay_marks(cypherseed, True, calibration)

        if not is_seed:
            self.filename_prefix = 'custom_secret_'
            self.was = _('Custom secret')
        else:
            self.filename_prefix = self.wallet_name + '_seed_'
            self.was = self.wallet_name + ' ' + _('seed')
            if self.extension:
                self.ext_warning(self.c_dialog)


        if not calibration:
            self.toPdf(QImage(cypherseed))
            QDesktopServices.openUrl(QUrl.fromLocalFile(self.get_path_to_revealer_file('.pdf')))
            cypherseed.save(self.get_path_to_revealer_file('.png'))
            self.bcrypt(self.c_dialog)
        return cypherseed

    def calibration(self):
        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.black)
        self.make_calnoise()
        img = self.overlay_marks(self.calnoise.scaledToHeight(self.f_size.height()), False, True)
        self.calibration_pdf(img)
        QDesktopServices.openUrl(QUrl.fromLocalFile(self.get_path_to_calibration_file()))
        return img

    def toPdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_revealer_file('.pdf'))
        printer.setPageMargins(0,0,0,0,6)
        painter = QPainter()
        painter.begin(printer)

        delta_h = round(image.width()/self.abstand_v)
        delta_v = round(image.height()/self.abstand_h)

        size_h = 2028+((int(self.calibration_h)*2028/(2028-(delta_h*2)+int(self.calibration_h)))/2)
        size_v = 1284+((int(self.calibration_v)*1284/(1284-(delta_v*2)+int(self.calibration_v)))/2)

        image =  image.scaled(size_h, size_v)

        painter.drawImage(553,533, image)
        wpath = QPainterPath()
        wpath.addRoundedRect(QRectF(553,533, size_h, size_v), 19, 19)
        painter.setPen(QPen(Qt.black, 1))
        painter.drawPath(wpath)
        painter.end()

    def calibration_pdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_calibration_file())
        printer.setPageMargins(0,0,0,0,6)

        painter = QPainter()
        painter.begin(printer)
        painter.drawImage(553,533, image)
        font = QFont('Source Sans Pro', 10, QFont.Bold)
        painter.setFont(font)
        painter.drawText(254,277, _("Calibration sheet"))
        font = QFont('Source Sans Pro', 7, QFont.Bold)
        painter.setFont(font)
        painter.drawText(600,2077, _("Instructions:"))
        font = QFont('Source Sans Pro', 7, QFont.Normal)
        painter.setFont(font)
        painter.drawText(700, 2177, _("1. Place this paper on a flat and well iluminated surface."))
        painter.drawText(700, 2277, _("2. Align your Revealer borderlines to the dashed lines on the top and left."))
        painter.drawText(700, 2377, _("3. Press slightly the Revealer against the paper and read the numbers that best "
                                      "match on the opposite sides. "))
        painter.drawText(700, 2477, _("4. Type the numbers in the software"))
        painter.end()

    def pixelcode_2x2(self, img):
        result = QImage(img.width()*2, img.height()*2, QImage.Format_ARGB32 )
        white = qRgba(255,255,255,0)
        black = qRgba(0,0,0,255)

        for x in range(img.width()):
            for y in range(img.height()):
                c = img.pixel(QPoint(x,y))
                colors = QColor(c).getRgbF()
                if colors[0]:
                    result.setPixel(x*2+1,y*2+1, black)
                    result.setPixel(x*2,y*2+1, white)
                    result.setPixel(x*2+1,y*2, white)
                    result.setPixel(x*2, y*2, black)

                else:
                    result.setPixel(x*2+1,y*2+1, white)
                    result.setPixel(x*2,y*2+1, black)
                    result.setPixel(x*2+1,y*2, black)
                    result.setPixel(x*2, y*2, white)
        return result

    def overlay_marks(self, img, is_cseed=False, calibration_sheet=False):
        border_color = Qt.white
        base_img = QImage(self.f_size.width(),self.f_size.height(), QImage.Format_ARGB32)
        base_img.fill(border_color)
        img = QImage(img)

        painter = QPainter()
        painter.begin(base_img)

        total_distance_h = round(base_img.width() / self.abstand_v)
        dist_v = round(total_distance_h) / 2
        dist_h = round(total_distance_h) / 2

        img = img.scaledToWidth(base_img.width() - (2 * (total_distance_h)))
        painter.drawImage(total_distance_h,
                          total_distance_h,
                          img)

        #frame around image
        pen = QPen(Qt.black, 2)
        painter.setPen(pen)

        #horz
        painter.drawLine(0, total_distance_h, base_img.width(), total_distance_h)
        painter.drawLine(0, base_img.height()-(total_distance_h), base_img.width(), base_img.height()-(total_distance_h))
        #vert
        painter.drawLine(total_distance_h, 0,  total_distance_h, base_img.height())
        painter.drawLine(base_img.width()-(total_distance_h), 0,  base_img.width()-(total_distance_h), base_img.height())

        #border around img
        border_thick = 6
        Rpath = QPainterPath()
        Rpath.addRect(QRectF((total_distance_h)+(border_thick/2),
                             (total_distance_h)+(border_thick/2),
                             base_img.width()-((total_distance_h)*2)-((border_thick)-1),
                             (base_img.height()-((total_distance_h))*2)-((border_thick)-1)))
        pen = QPen(Qt.black, border_thick)
        pen.setJoinStyle (Qt.MiterJoin)

        painter.setPen(pen)
        painter.drawPath(Rpath)

        Bpath = QPainterPath()
        Bpath.addRect(QRectF((total_distance_h), (total_distance_h),
                             base_img.width()-((total_distance_h)*2), (base_img.height()-((total_distance_h))*2)))
        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawPath(Bpath)

        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawLine(0, base_img.height()/2, total_distance_h, base_img.height()/2)
        painter.drawLine(base_img.width()/2, 0, base_img.width()/2, total_distance_h)

        painter.drawLine(base_img.width()-total_distance_h, base_img.height()/2, base_img.width(), base_img.height()/2)
        painter.drawLine(base_img.width()/2, base_img.height(), base_img.width()/2, base_img.height() - total_distance_h)

        #print code
        f_size = 37
        QFontDatabase.addApplicationFont(os.path.join(os.path.dirname(__file__), 'DejaVuSansMono-Bold.ttf'))
        font = QFont("DejaVu Sans Mono", f_size-11, QFont.Bold)
        font.setPixelSize(35)
        painter.setFont(font)

        if not calibration_sheet:
            if is_cseed: #its a secret
                painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0,  dist_h, base_img.height())
                painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v))
                painter.drawLine(base_img.width()-(dist_h), 0,  base_img.width()-(dist_h), base_img.height())

                painter.drawImage(((total_distance_h))+11, ((total_distance_h))+11,
                                  QImage(icon_path('electrumb.png')).scaledToWidth(2.1*(total_distance_h), Qt.SmoothTransformation))

                painter.setPen(QPen(Qt.white, border_thick*8))
                painter.drawLine(base_img.width()-((total_distance_h))-(border_thick*8)/2-(border_thick/2)-2,
                                (base_img.height()-((total_distance_h)))-((border_thick*8)/2)-(border_thick/2)-2,
                                base_img.width()-((total_distance_h))-(border_thick*8)/2-(border_thick/2)-2 - 77,
                                (base_img.height()-((total_distance_h)))-((border_thick*8)/2)-(border_thick/2)-2)
                painter.setPen(QColor(0,0,0,255))
                painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick - 11,
                                       base_img.height()-total_distance_h - border_thick), Qt.AlignRight,
                                 self.versioned_seed.version + '_'+self.versioned_seed.checksum)
                painter.end()

            else: # revealer

                painter.setPen(QPen(border_color, 17))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0,  dist_h, base_img.height())
                painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v))
                painter.drawLine(base_img.width()-(dist_h), 0,  base_img.width()-(dist_h), base_img.height())

                painter.setPen(QPen(Qt.black, 2))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0,  dist_h, base_img.height())
                painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v))
                painter.drawLine(base_img.width()-(dist_h), 0,  base_img.width()-(dist_h), base_img.height())
                logo = QImage(icon_path('revealer_c.png')).scaledToWidth(1.3*(total_distance_h))
                painter.drawImage((total_distance_h)+ (border_thick), ((total_distance_h))+ (border_thick), logo, Qt.SmoothTransformation)

                #frame around logo
                painter.setPen(QPen(Qt.black, border_thick))
                painter.drawLine(total_distance_h+border_thick, total_distance_h+logo.height()+3*(border_thick/2),
                                 total_distance_h+logo.width()+border_thick, total_distance_h+logo.height()+3*(border_thick/2))
                painter.drawLine(logo.width()+total_distance_h+3*(border_thick/2), total_distance_h+(border_thick),
                                 total_distance_h+logo.width()+3*(border_thick/2), total_distance_h+logo.height()+(border_thick))

                #frame around code/qr
                qr_size = 179

                painter.drawLine((base_img.width()-((total_distance_h))-(border_thick/2)-2)-qr_size,
                                (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2,
                                 (base_img.width()/2+(total_distance_h/2)-border_thick-(border_thick*8)/2)-qr_size,
                                (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2)

                painter.drawLine((base_img.width()/2+(total_distance_h/2)-border_thick-(border_thick*8)/2)-qr_size,
                                (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2,
                                 base_img.width()/2 + (total_distance_h/2)-border_thick-(border_thick*8)/2-qr_size,
                                 ((base_img.height()-((total_distance_h)))-(border_thick/2)-2))

                painter.setPen(QPen(Qt.white, border_thick * 8))
                painter.drawLine(
                    base_img.width() - ((total_distance_h)) - (border_thick * 8) / 2 - (border_thick / 2) - 2,
                    (base_img.height() - ((total_distance_h))) - ((border_thick * 8) / 2) - (border_thick / 2) - 2,
                    base_img.width() / 2 + (total_distance_h / 2) - border_thick - qr_size,
                    (base_img.height() - ((total_distance_h))) - ((border_thick * 8) / 2) - (border_thick / 2) - 2)

                painter.setPen(QColor(0,0,0,255))
                painter.drawText(QRect(((base_img.width()/2) +21)-qr_size, base_img.height()-107,
                                       base_img.width()-total_distance_h - border_thick -93,
                                       base_img.height()-total_distance_h - border_thick), Qt.AlignLeft, self.versioned_seed.get_ui_string_version_plus_seed())
                painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick -3 -qr_size,
                                       base_img.height()-total_distance_h - border_thick), Qt.AlignRight, self.versioned_seed.checksum)

                # draw qr code
                qr_qt = self.paintQR(self.versioned_seed.get_ui_string_version_plus_seed()
                                     + self.versioned_seed.checksum)
                target = QRectF(base_img.width()-65-qr_size,
                                base_img.height()-65-qr_size,
                                qr_size, qr_size )
                painter.drawImage(target, qr_qt)
                painter.setPen(QPen(Qt.black, 4))
                painter.drawLine(base_img.width()-65-qr_size,
                                base_img.height()-65-qr_size,
                                 base_img.width() - 65 - qr_size,
                                (base_img.height() - ((total_distance_h))) - ((border_thick * 8)) - (border_thick / 2) - 4
                                 )
                painter.drawLine(base_img.width()-65-qr_size,
                                base_img.height()-65-qr_size,
                                 base_img.width() - 65,
                                base_img.height()-65-qr_size
                                 )
                painter.end()

        else: # calibration only
            painter.end()
            cal_img = QImage(self.f_size.width() + 100, self.f_size.height() + 100,
                              QImage.Format_ARGB32)
            cal_img.fill(Qt.white)

            cal_painter = QPainter()
            cal_painter.begin(cal_img)
            cal_painter.drawImage(0,0, base_img)

            #black lines in the middle of border top left only
            cal_painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
            cal_painter.drawLine(0, dist_v, base_img.width(), dist_v)
            cal_painter.drawLine(dist_h, 0,  dist_h, base_img.height())

            pen = QPen(Qt.black, 2, Qt.DashDotDotLine)
            cal_painter.setPen(pen)
            n=15

            cal_painter.setFont(QFont("DejaVu Sans Mono", 21, QFont.Bold))
            for x in range(-n,n):
                #lines on bottom (vertical calibration)
                cal_painter.drawLine((((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)-13,
                                 x+2+base_img.height()-(dist_v),
                                 (((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)+13,
                                 x+2+base_img.height()-(dist_v))

                num_pos = 9
                if x > 9 : num_pos = 17
                if x < 0 : num_pos = 20
                if x < -9: num_pos = 27

                cal_painter.drawText((((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)-num_pos,
                                 50+base_img.height()-(dist_v),
                                  str(x))

                #lines on the right (horizontal calibrations)

                cal_painter.drawLine(x+2+(base_img.width()-(dist_h)),
                                 ((base_img.height()/(2*n)) *(x))+ (base_img.height()/n)+(base_img.height()/2)-13,
                                 x+2+(base_img.width()-(dist_h)),
                                 ((base_img.height()/(2*n)) *(x))+ (base_img.height()/n)+(base_img.height()/2)+13)


                cal_painter.drawText(30+(base_img.width()-(dist_h)),
                                ((base_img.height()/(2*n)) *(x))+ (base_img.height()/2)+13, str(x))

            cal_painter.end()
            base_img = cal_img

        return base_img

    def paintQR(self, data):
        if not data:
            return
        qr = qrcode.QRCode()
        qr.add_data(data)
        matrix = qr.get_matrix()
        k = len(matrix)
        border_color = Qt.white
        base_img = QImage(k * 5, k * 5, QImage.Format_ARGB32)
        base_img.fill(border_color)
        qrpainter = QPainter()
        qrpainter.begin(base_img)
        boxsize = 5
        size = k * boxsize
        left = (base_img.width() - size)/2
        top = (base_img.height() - size)/2
        qrpainter.setBrush(Qt.black)
        qrpainter.setPen(Qt.black)

        for r in range(k):
            for c in range(k):
                if matrix[r][c]:
                    qrpainter.drawRect(left+c*boxsize, top+r*boxsize, boxsize - 1, boxsize - 1)
        qrpainter.end()
        return base_img

    def calibration_dialog(self, window):
        d = WindowModalDialog(window, _("Revealer - Printer calibration settings"))

        d.setMinimumSize(100, 200)

        vbox = QVBoxLayout(d)
        vbox.addWidget(QLabel(''.join(["<br/>", _("If you have an old printer, or want optimal precision"),"<br/>",
                                       _("print the calibration pdf and follow the instructions "), "<br/>","<br/>",
                                    ])))
        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')
        cprint = QPushButton(_("Open calibration pdf"))
        cprint.clicked.connect(self.calibration)
        vbox.addWidget(cprint)

        vbox.addWidget(QLabel(_('Calibration values:')))
        grid = QGridLayout()
        vbox.addLayout(grid)
        grid.addWidget(QLabel(_('Right side')), 0, 0)
        horizontal = QLineEdit()
        horizontal.setText(str(self.calibration_h))
        grid.addWidget(horizontal, 0, 1)

        grid.addWidget(QLabel(_('Bottom')), 1, 0)
        vertical = QLineEdit()
        vertical.setText(str(self.calibration_v))
        grid.addWidget(vertical, 1, 1)

        vbox.addStretch()
        vbox.addSpacing(13)
        vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

        if not d.exec_():
            return

        self.calibration_h = int(Decimal(horizontal.text()))
        self.config.set_key('calibration_h', self.calibration_h)
        self.calibration_v = int(Decimal(vertical.text()))
        self.config.set_key('calibration_v', self.calibration_v)
Пример #40
0
class SensorWorker(QThread):
    def __init__(self, Parent=None):

        QThread.__init__(self, parent)
        self.exiting = False
        self.size = QSize(0, 0)
        self.path = QPainterPath()
        angle = 2 * math.pi / 5
        self.outerRadius = 20
        self.innerRadius = 8
        self.path.moveTo(self.outerRadius, 0)
        for step in range(1, 6):
            self.path.lineTo(self.innerRadius * math.cos((step - 0.5) * angle),
                             self.innerRadius * math.sin((step - 0.5) * angle))
            self.path.lineTo(self.outerRadius * math.cos(step * angle),
                             self.outerRadius * math.sin(step * angle))
        self.path.closeSubpath()

    def __del__(self):

        self.exiting = True
        self.wait()

    def render(self, size, stars):

        self.size = size
        self.stars = stars
        self.start()

    def run(self):

        random.seed()
        n = self.stars
        width = self.size.width()
        height = self.size.height()

        while not self.exiting and n > 0:

            image = QImage(self.outerRadius * 2, self.outerRadius * 2,
                           QImage.Format_ARGB32)
            image.fill(qRgba(0, 0, 0, 0))

            x = random.randrange(0, width)
            y = random.randrange(0, height)
            angle = random.randrange(0, 360)
            red = random.randrange(0, 256)
            green = random.randrange(0, 256)
            blue = random.randrange(0, 256)
            alpha = random.randrange(0, 256)

            painter = QPainter()
            painter.begin(image)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(Qt.NoPen)
            painter.setBrush(QColor(red, green, blue, alpha))
            painter.translate(self.outerRadius, self.outerRadius)
            painter.rotate(angle)
            painter.drawPath(self.path)
            painter.end()
            self.emit(
                SIGNAL("output(QRect, QImage)"),
                QRect(x - self.outerRadius, y - self.outerRadius,
                      self.outerRadius * 2, self.outerRadius * 2), image)
            n -= 1
Пример #41
0
class Settings(object):

    l = logging.getLogger('Settings')

    def __init__(self, fileName='shadow.ini'):
        self.fileName = fileName
        self.tzIdx = 0
        self.mainWindowSize = QSize(1200, 700)
        self.mainWindowPos = QPoint(240, 200)

    def load(self):
        self.l.debug('Loading local settings from {}'.format(self.fileName))
        config = configparser.ConfigParser()
        config.read(self.fileName)

        if config.has_section('common'):
            commonSettings = config['common']
            self.tzIdx = int(commonSettings.get('tz', 0))

        # load main window position and size
        if config.has_section('mainWindow'):
            windowSettings = config['mainWindow']

            # read position
            pos = windowSettings.get(
                'pos', '{},{}'.format(self.mainWindowPos.x(),
                                      self.mainWindowPos.y())).split(',')
            self.mainWindowPos = QPoint(int(pos[0]), int(pos[1]))

            # read size
            size = windowSettings.get(
                'size',
                '{},{}'.format(self.mainWindowSize.width(),
                               self.mainWindowSize.height())).split(',')
            self.mainWindowSize = QSize(int(size[0]), int(size[1]))

    def dump(self, channel):
        self.l.debug('Saving local settings ...')
        config = configparser.ConfigParser()

        config.add_section('common')
        config.set('common', 'tz', str(self.tzIdx))

        config.add_section('mainWindow')
        config.set(
            'mainWindow', 'pos', '{},{}'.format(self.mainWindowPos.x(),
                                                self.mainWindowPos.y()))
        config.set(
            'mainWindow', 'size', '{},{}'.format(self.mainWindowSize.width(),
                                                 self.mainWindowSize.height()))

        config.write(channel)

    def save(self):
        with open(self.fileName, 'w') as configfile:
            self.dump(configfile)

    def setMainWindowPos(self, pos):
        self.mainWindowPos = pos

    def setMainWindowSize(self, size):
        self.mainWindowSize = size

    def getMainWindowPos(self):
        return self.mainWindowPos

    def getMainWindowSize(self):
        return self.mainWindowSize

    def getTzIndex(self):
        return self.tzIdx

    def setTzIdx(self, idx):
        self.tzIdx = idx
Пример #42
0
 def sizeHint(self, default_size: QSize) -> QSize:
     if not self.features:
         return default_size
     width = len(self.features) * (self.ICON_SIZE.width() + 1)
     return QSize(width, default_size.height())
Пример #43
0
class Window(QWidget):
    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)

        self.m_iconSize = QSize(64, 64)
        self.m_scene = QGraphicsScene()
        self.m_ui = Ui_Form()

        self.m_ui.setupUi(self)
        self.m_ui.easingCurvePicker.setIconSize(self.m_iconSize)
        self.m_ui.easingCurvePicker.setMinimumHeight(self.m_iconSize.height() +
                                                     50)
        self.m_ui.buttonGroup.setId(self.m_ui.lineRadio, 0)
        self.m_ui.buttonGroup.setId(self.m_ui.circleRadio, 1)

        dummy = QEasingCurve()
        self.m_ui.periodSpinBox.setValue(dummy.period())
        self.m_ui.amplitudeSpinBox.setValue(dummy.amplitude())
        self.m_ui.overshootSpinBox.setValue(dummy.overshoot())

        self.m_ui.easingCurvePicker.currentRowChanged.connect(
            self.curveChanged)
        self.m_ui.buttonGroup.buttonClicked[int].connect(self.pathChanged)
        self.m_ui.periodSpinBox.valueChanged.connect(self.periodChanged)
        self.m_ui.amplitudeSpinBox.valueChanged.connect(self.amplitudeChanged)
        self.m_ui.overshootSpinBox.valueChanged.connect(self.overshootChanged)
        self.createCurveIcons()

        pix = QPixmap(':/images/qt-logo.png')
        self.m_item = PixmapItem(pix)
        self.m_scene.addItem(self.m_item.pixmap_item)
        self.m_ui.graphicsView.setScene(self.m_scene)

        self.m_anim = Animation(self.m_item, b'pos')
        self.m_anim.setEasingCurve(QEasingCurve.OutBounce)
        self.m_ui.easingCurvePicker.setCurrentRow(int(QEasingCurve.OutBounce))

        self.startAnimation()

    def createCurveIcons(self):
        pix = QPixmap(self.m_iconSize)
        painter = QPainter()

        gradient = QLinearGradient(0, 0, 0, self.m_iconSize.height())
        gradient.setColorAt(0.0, QColor(240, 240, 240))
        gradient.setColorAt(1.0, QColor(224, 224, 224))

        brush = QBrush(gradient)

        # The original C++ code uses undocumented calls to get the names of the
        # different curve types.  We do the Python equivalant (but without
        # cheating).
        curve_types = [
            (n, c) for n, c in QEasingCurve.__dict__.items()
            if isinstance(c, QEasingCurve.Type) and c != QEasingCurve.Custom
        ]
        curve_types.sort(key=lambda ct: ct[1])

        painter.begin(pix)

        for curve_name, curve_type in curve_types:
            painter.fillRect(QRect(QPoint(0, 0), self.m_iconSize), brush)

            curve = QEasingCurve(curve_type)

            if curve_type == QEasingCurve.BezierSpline:
                curve.addCubicBezierSegment(QPointF(0.4,
                                                    0.1), QPointF(0.6, 0.9),
                                            QPointF(1.0, 1.0))
            elif curve_type == QEasingCurve.TCBSpline:
                curve.addTCBSegment(QPointF(0.0, 0.0), 0, 0, 0)
                curve.addTCBSegment(QPointF(0.3, 0.4), 0.2, 1, -0.2)
                curve.addTCBSegment(QPointF(0.7, 0.6), -0.2, 1, 0.2)
                curve.addTCBSegment(QPointF(1.0, 1.0), 0, 0, 0)

            painter.setPen(QColor(0, 0, 255, 64))
            xAxis = self.m_iconSize.height() / 1.5
            yAxis = self.m_iconSize.width() / 3.0
            painter.drawLine(0, xAxis, self.m_iconSize.width(), xAxis)
            painter.drawLine(yAxis, 0, yAxis, self.m_iconSize.height())

            curveScale = self.m_iconSize.height() / 2.0

            painter.setPen(Qt.NoPen)

            # Start point.
            painter.setBrush(Qt.red)
            start = QPoint(yAxis,
                           xAxis - curveScale * curve.valueForProgress(0))
            painter.draw_rect(start.x() - 1, start.y() - 1, 3, 3)

            # End point.
            painter.setBrush(Qt.blue)
            end = QPoint(yAxis + curveScale,
                         xAxis - curveScale * curve.valueForProgress(1))
            painter.draw_rect(end.x() - 1, end.y() - 1, 3, 3)

            curvePath = QPainterPath()
            curvePath.moveTo(QPointF(start))
            t = 0.0
            while t <= 1.0:
                to = QPointF(yAxis + curveScale * t,
                             xAxis - curveScale * curve.valueForProgress(t))
                curvePath.lineTo(to)
                t += 1.0 / curveScale

            painter.setRenderHint(QPainter.Antialiasing, True)
            painter.strokePath(curvePath, QColor(32, 32, 32))
            painter.setRenderHint(QPainter.Antialiasing, False)

            item = QListWidgetItem()
            item.setIcon(QIcon(pix))
            item.setText(curve_name)
            self.m_ui.easingCurvePicker.addItem(item)

        painter.end()

    def startAnimation(self):
        self.m_anim.setStartValue(QPointF(0, 0))
        self.m_anim.setEndValue(QPointF(100, 100))
        self.m_anim.setDuration(2000)
        self.m_anim.setLoopCount(-1)
        self.m_anim.start()

    def curveChanged(self, row):
        curveType = QEasingCurve.Type(row)
        self.m_anim.setEasingCurve(curveType)
        self.m_anim.setCurrentTime(0)

        isElastic = (curveType >= QEasingCurve.InElastic
                     and curveType <= QEasingCurve.OutInElastic)
        isBounce = (curveType >= QEasingCurve.InBounce
                    and curveType <= QEasingCurve.OutInBounce)

        self.m_ui.periodSpinBox.setEnabled(isElastic)
        self.m_ui.amplitudeSpinBox.setEnabled(isElastic or isBounce)
        self.m_ui.overshootSpinBox.setEnabled(
            curveType >= QEasingCurve.InBack
            and curveType <= QEasingCurve.OutInBack)

    def pathChanged(self, index):
        self.m_anim.setPathType(index)

    def periodChanged(self, value):
        curve = self.m_anim.easingCurve()
        curve.setPeriod(value)
        self.m_anim.setEasingCurve(curve)

    def amplitudeChanged(self, value):
        curve = self.m_anim.easingCurve()
        curve.setAmplitude(value)
        self.m_anim.setEasingCurve(curve)

    def overshootChanged(self, value):
        curve = self.m_anim.easingCurve()
        curve.setOvershoot(value)
        self.m_anim.setEasingCurve(curve)
Пример #44
0
class TileLayer(Layer):
    ##
    # Constructor.
    ##
    def __init__(self, name, x, y, width, height):
        super().__init__(Layer.TileLayerType, name, x, y, width, height)
        self.mMaxTileSize = QSize(0, 0)
        self.mGrid = QVector()
        for i in range(width * height):
            self.mGrid.append(Cell())
        self.mOffsetMargins = QMargins()

    def __iter__(self):
        return self.mGrid.__iter__()

    ##
    # Returns the maximum tile size of this layer.
    ##
    def maxTileSize(self):
        return self.mMaxTileSize

    ##
    # Returns the margins that have to be taken into account while drawing
    # this tile layer. The margins depend on the maximum tile size and the
    # offset applied to the tiles.
    ##
    def drawMargins(self):
        return QMargins(
            self.mOffsetMargins.left(),
            self.mOffsetMargins.top() + self.mMaxTileSize.height(),
            self.mOffsetMargins.right() + self.mMaxTileSize.width(),
            self.mOffsetMargins.bottom())

    ##
    # Recomputes the draw margins. Needed after the tile offset of a tileset
    # has changed for example.
    #
    # Generally you want to call Map.recomputeDrawMargins instead.
    ##
    def recomputeDrawMargins(self):
        maxTileSize = QSize(0, 0)
        offsetMargins = QMargins()
        i = 0
        while (i < self.mGrid.size()):
            cell = self.mGrid.at(i)
            tile = cell.tile
            if tile:
                size = tile.size()
                if (cell.flippedAntiDiagonally):
                    size.transpose()
                offset = tile.offset()
                maxTileSize = maxSize(size, maxTileSize)
                offsetMargins = maxMargins(
                    QMargins(-offset.x(), -offset.y(), offset.x(), offset.y()),
                    offsetMargins)
            i += 1

        self.mMaxTileSize = maxTileSize
        self.mOffsetMargins = offsetMargins
        if (self.mMap):
            self.mMap.adjustDrawMargins(self.drawMargins())

    ##
    # Returns whether (x, y) is inside this map layer.
    ##
    def contains(self, *args):
        l = len(args)
        if l == 2:
            x, y = args
            return x >= 0 and y >= 0 and x < self.mWidth and y < self.mHeight
        elif l == 1:
            point = args[0]
            return self.contains(point.x(), point.y())

    ##
    # Calculates the region of cells in this tile layer for which the given
    # \a condition returns True.
    ##
    def region(self, *args):
        l = len(args)
        if l == 1:
            condition = args[0]
            region = QRegion()
            for y in range(self.mHeight):
                for x in range(self.mWidth):
                    if (condition(self.cellAt(x, y))):
                        rangeStart = x
                        x += 1
                        while (x <= self.mWidth):
                            if (x == self.mWidth
                                    or not condition(self.cellAt(x, y))):
                                rangeEnd = x
                                region += QRect(rangeStart + self.mX,
                                                y + self.mY,
                                                rangeEnd - rangeStart, 1)
                                break
                            x += 1

            return region
        elif l == 0:
            ##
            # Calculates the region occupied by the tiles of this layer. Similar to
            # Layer.bounds(), but leaves out the regions without tiles.
            ##
            return self.region(lambda cell: not cell.isEmpty())

    ##
    # Returns a read-only reference to the cell at the given coordinates. The
    # coordinates have to be within this layer.
    ##
    def cellAt(self, *args):
        l = len(args)
        if l == 2:
            x, y = args
            return self.mGrid.at(x + y * self.mWidth)
        elif l == 1:
            point = args[0]
            return self.cellAt(point.x(), point.y())

    ##
    # Sets the cell at the given coordinates.
    ##
    def setCell(self, x, y, cell):
        if (cell.tile):
            size = cell.tile.size()
            if (cell.flippedAntiDiagonally):
                size.transpose()
            offset = cell.tile.offset()
            self.mMaxTileSize = maxSize(size, self.mMaxTileSize)
            self.mOffsetMargins = maxMargins(
                QMargins(-offset.x(), -offset.y(), offset.x(), offset.y()),
                self.mOffsetMargins)
            if (self.mMap):
                self.mMap.adjustDrawMargins(self.drawMargins())

        self.mGrid[x + y * self.mWidth] = cell

    ##
    # Returns a copy of the area specified by the given \a region. The
    # caller is responsible for the returned tile layer.
    ##
    def copy(self, *args):
        l = len(args)
        if l == 1:
            region = args[0]
            if type(region) != QRegion:
                region = QRegion(region)
            area = region.intersected(QRect(0, 0, self.width(), self.height()))
            bounds = region.boundingRect()
            areaBounds = area.boundingRect()
            offsetX = max(0, areaBounds.x() - bounds.x())
            offsetY = max(0, areaBounds.y() - bounds.y())
            copied = TileLayer(QString(), 0, 0, bounds.width(),
                               bounds.height())
            for rect in area.rects():
                for x in range(rect.left(), rect.right() + 1):
                    for y in range(rect.top(), rect.bottom() + 1):
                        copied.setCell(x - areaBounds.x() + offsetX,
                                       y - areaBounds.y() + offsetY,
                                       self.cellAt(x, y))
            return copied
        elif l == 4:
            x, y, width, height = args

            return self.copy(QRegion(x, y, width, height))

    ##
    # Merges the given \a layer onto this layer at position \a pos. Parts that
    # fall outside of this layer will be lost and empty tiles in the given
    # layer will have no effect.
    ##
    def merge(self, pos, layer):
        # Determine the overlapping area
        area = QRect(pos, QSize(layer.width(), layer.height()))
        area &= QRect(0, 0, self.width(), self.height())
        for y in range(area.top(), area.bottom() + 1):
            for x in range(area.left(), area.right() + 1):
                cell = layer.cellAt(x - pos.x(), y - pos.y())
                if (not cell.isEmpty()):
                    self.setCell(x, y, cell)

    ##
    # Removes all cells in the specified region.
    ##
    def erase(self, area):
        emptyCell = Cell()
        for rect in area.rects():
            for x in range(rect.left(), rect.right() + 1):
                for y in range(rect.top(), rect.bottom() + 1):
                    self.setCell(x, y, emptyCell)

    ##
    # Sets the cells starting at the given position to the cells in the given
    # \a tileLayer. Parts that fall outside of this layer will be ignored.
    #
    # When a \a mask is given, only cells that fall within this mask are set.
    # The mask is applied in local coordinates.
    ##
    def setCells(self, x, y, layer, mask=QRegion()):
        # Determine the overlapping area
        area = QRegion(QRect(x, y, layer.width(), layer.height()))
        area &= QRect(0, 0, self.width(), self.height())
        if (not mask.isEmpty()):
            area &= mask
        for rect in area.rects():
            for _x in range(rect.left(), rect.right() + 1):
                for _y in range(rect.top(), rect.bottom() + 1):
                    self.setCell(_x, _y, layer.cellAt(_x - x, _y - y))

    ##
    # Flip this tile layer in the given \a direction. Direction must be
    # horizontal or vertical. This doesn't change the dimensions of the
    # tile layer.
    ##
    def flip(self, direction):
        newGrid = QVector()
        for i in range(self.mWidth * self.mHeight):
            newGrid.append(Cell())
        for y in range(self.mHeight):
            for x in range(self.mWidth):
                dest = newGrid[x + y * self.mWidth]
                if (direction == FlipDirection.FlipHorizontally):
                    source = self.cellAt(self.mWidth - x - 1, y)
                    dest = source
                    dest.flippedHorizontally = not source.flippedHorizontally
                elif (direction == FlipDirection.FlipVertically):
                    source = self.cellAt(x, self.mHeight - y - 1)
                    dest = source
                    dest.flippedVertically = not source.flippedVertically

        self.mGrid = newGrid

    ##
    # Rotate this tile layer by 90 degrees left or right. The tile positions
    # are rotated within the layer, and the tiles themselves are rotated. The
    # dimensions of the tile layer are swapped.
    ##
    def rotate(self, direction):
        rotateRightMask = [5, 4, 1, 0, 7, 6, 3, 2]
        rotateLeftMask = [3, 2, 7, 6, 1, 0, 5, 4]
        if direction == RotateDirection.RotateRight:
            rotateMask = rotateRightMask
        else:
            rotateMask = rotateLeftMask
        newWidth = self.mHeight
        newHeight = self.mWidth
        newGrid = QVector(newWidth * newHeight)
        for y in range(self.mHeight):
            for x in range(self.mWidth):
                source = self.cellAt(x, y)
                dest = source
                mask = (dest.flippedHorizontally << 2) | (
                    dest.flippedVertically << 1) | (
                        dest.flippedAntiDiagonally << 0)
                mask = rotateMask[mask]
                dest.flippedHorizontally = (mask & 4) != 0
                dest.flippedVertically = (mask & 2) != 0
                dest.flippedAntiDiagonally = (mask & 1) != 0
                if (direction == RotateDirection.RotateRight):
                    newGrid[x * newWidth + (self.mHeight - y - 1)] = dest
                else:
                    newGrid[(self.mWidth - x - 1) * newWidth + y] = dest

        t = self.mMaxTileSize.width()
        self.mMaxTileSize.setWidth(self.mMaxTileSize.height())
        self.mMaxTileSize.setHeight(t)
        self.mWidth = newWidth
        self.mHeight = newHeight
        self.mGrid = newGrid

    ##
    # Computes and returns the set of tilesets used by this tile layer.
    ##
    def usedTilesets(self):
        tilesets = QSet()
        i = 0
        while (i < self.mGrid.size()):
            tile = self.mGrid.at(i).tile
            if tile:
                tilesets.insert(tile.tileset())
            i += 1
        return tilesets

    ##
    # Returns whether this tile layer has any cell for which the given
    # \a condition returns True.
    ##
    def hasCell(self, condition):
        i = 0
        for cell in self.mGrid:
            if (condition(cell)):
                return True
            i += 1
        return False

    ##
    # Returns whether this tile layer is referencing the given tileset.
    ##
    def referencesTileset(self, tileset):
        i = 0
        while (i < self.mGrid.size()):
            tile = self.mGrid.at(i).tile
            if (tile and tile.tileset() == tileset):
                return True
            i += 1
        return False

    ##
    # Removes all references to the given tileset. This sets all tiles on this
    # layer that are from the given tileset to null.
    ##
    def removeReferencesToTileset(self, tileset):
        i = 0
        while (i < self.mGrid.size()):
            tile = self.mGrid.at(i).tile
            if (tile and tile.tileset() == tileset):
                self.mGrid.replace(i, Cell())
            i += 1

    ##
    # Replaces all tiles from \a oldTileset with tiles from \a newTileset.
    ##
    def replaceReferencesToTileset(self, oldTileset, newTileset):
        i = 0
        while (i < self.mGrid.size()):
            tile = self.mGrid.at(i).tile
            if (tile and tile.tileset() == oldTileset):
                self.mGrid[i].tile = newTileset.tileAt(tile.id())
            i += 1

    ##
    # Resizes this tile layer to \a size, while shifting all tiles by
    # \a offset.
    ##
    def resize(self, size, offset):
        if (self.size() == size and offset.isNull()):
            return
        newGrid = QVector()
        for i in range(size.width() * size.height()):
            newGrid.append(Cell())
        # Copy over the preserved part
        startX = max(0, -offset.x())
        startY = max(0, -offset.y())
        endX = min(self.mWidth, size.width() - offset.x())
        endY = min(self.mHeight, size.height() - offset.y())
        for y in range(startY, endY):
            for x in range(startX, endX):
                index = x + offset.x() + (y + offset.y()) * size.width()
                newGrid[index] = self.cellAt(x, y)

        self.mGrid = newGrid
        self.setSize(size)

    ##
    # Offsets the tiles in this layer within \a bounds by \a offset,
    # and optionally wraps them.
    #
    # \sa ObjectGroup.offset()
    ##
    def offsetTiles(self, offset, bounds, wrapX, wrapY):
        newGrid = QVector()
        for i in range(self.mWidth * self.mHeight):
            newGrid.append(Cell())
        for y in range(self.mHeight):
            for x in range(self.mWidth):
                # Skip out of bounds tiles
                if (not bounds.contains(x, y)):
                    newGrid[x + y * self.mWidth] = self.cellAt(x, y)
                    continue

                # Get position to pull tile value from
                oldX = x - offset.x()
                oldY = y - offset.y()
                # Wrap x value that will be pulled from
                if (wrapX and bounds.width() > 0):
                    while oldX < bounds.left():
                        oldX += bounds.width()
                    while oldX > bounds.right():
                        oldX -= bounds.width()

                # Wrap y value that will be pulled from
                if (wrapY and bounds.height() > 0):
                    while oldY < bounds.top():
                        oldY += bounds.height()
                    while oldY > bounds.bottom():
                        oldY -= bounds.height()

                # Set the new tile
                if (self.contains(oldX, oldY) and bounds.contains(oldX, oldY)):
                    newGrid[x + y * self.mWidth] = self.cellAt(oldX, oldY)
                else:
                    newGrid[x + y * self.mWidth] = Cell()

        self.mGrid = newGrid

    def canMergeWith(self, other):
        return other.isTileLayer()

    def mergedWith(self, other):
        o = other
        unitedBounds = self.bounds().united(o.bounds())
        offset = self.position() - unitedBounds.topLeft()
        merged = self.clone()
        merged.resize(unitedBounds.size(), offset)
        merged.merge(o.position() - unitedBounds.topLeft(), o)
        return merged

    ##
    # Returns the region where this tile layer and the given tile layer
    # are different. The relative positions of the layers are taken into
    # account. The returned region is relative to this tile layer.
    ##
    def computeDiffRegion(self, other):
        ret = QRegion()
        dx = other.x() - self.mX
        dy = other.y() - self.mY
        r = QRect(0, 0, self.width(), self.height())
        r &= QRect(dx, dy, other.width(), other.height())
        for y in range(r.top(), r.bottom() + 1):
            for x in range(r.left(), r.right() + 1):
                if (self.cellAt(x, y) != other.cellAt(x - dx, y - dy)):
                    rangeStart = x
                    while (x <= r.right() and
                           self.cellAt(x, y) != other.cellAt(x - dx, y - dy)):
                        x += 1

                    rangeEnd = x
                    ret += QRect(rangeStart, y, rangeEnd - rangeStart, 1)

        return ret

    ##
    # Returns True if all tiles in the layer are empty.
    ##
    def isEmpty(self):
        i = 0
        while (i < self.mGrid.size()):
            if (not self.mGrid.at(i).isEmpty()):
                return False
            i += 1
        return True

    ##
    # Returns a duplicate of this TileLayer.
    #
    # \sa Layer.clone()
    ##
    def clone(self):
        return self.initializeClone(
            TileLayer(self.mName, self.mX, self.mY, self.mWidth, self.mHeight))

    def begin(self):
        return self.mGrid.begin()

    def end(self):
        return self.mGrid.end()

    def initializeClone(self, clone):
        super().initializeClone(clone)
        clone.mGrid = self.mGrid
        clone.mMaxTileSize = self.mMaxTileSize
        clone.mOffsetMargins = self.mOffsetMargins
        return clone
Пример #45
0
 def setMaximumSize(self, size: QSize, **kwargs):
     super().setMaximumSize(size)
     self._base_width = size.width()
     self._base_height = size.height()
Пример #46
0
    def paintEvent(self, _event: QPaintEvent):
        if not self.crop or not self.resolution:
            return

        painter = QPainter(self)

        # Keep a backup of the transform and create a new one
        transform = painter.worldTransform()

        # Set scaling transform
        transform = transform.scale(self.width() / self.resolution.width(),
                                    self.height() / self.resolution.height())

        # Compute the transform to flip the coordinate system on the x axis
        transform_flip = QTransform()
        if self.flip_x:
            transform_flip = transform_flip.translate(self.resolution.width(), 0.0)
            transform_flip = transform_flip.scale(-1.0, 1.0)

        # Small helper for tuple to QPoint
        def toqp(point):
            return QPoint(point[0], point[1])

        # Starting from here we care about AA
        painter.setRenderHint(QPainter.Antialiasing)

        # Draw all the QR code results
        for res in self.results:
            painter.setWorldTransform(transform_flip * transform, False)

            # Draw lines between all of the QR code points
            pen = QPen(self.qr_outline_pen)
            if res in self.validator_results.result_colors:
                pen.setColor(self.validator_results.result_colors[res])
            painter.setPen(pen)
            num_points = len(res.points)
            for i in range(0, num_points):
                i_n = i + 1

                line_from = toqp(res.points[i])
                line_from += self.crop.topLeft()

                line_to = toqp(res.points[i_n] if i_n < num_points else res.points[0])
                line_to += self.crop.topLeft()

                painter.drawLine(line_from, line_to)

            # Draw the QR code data
            # Note that we reset the world transform to only the scaled transform
            # because otherwise the text could be flipped. We only use transform_flip
            # to map the center point of the result.
            painter.setWorldTransform(transform, False)
            font_metrics = painter.fontMetrics()
            data_metrics = QSize(font_metrics.horizontalAdvance(res.data), font_metrics.capHeight())

            center_pos = toqp(res.center)
            center_pos += self.crop.topLeft()
            center_pos = transform_flip.map(center_pos)

            text_offset = QPoint(data_metrics.width(), data_metrics.height())
            text_offset = text_offset / 2
            text_offset.setX(-text_offset.x())
            center_pos += text_offset

            padding = self.BG_RECT_PADDING
            bg_rect_pos = center_pos - QPoint(padding, data_metrics.height() + padding)
            bg_rect_size = data_metrics + (QSize(padding, padding) * 2)
            bg_rect = QRect(bg_rect_pos, bg_rect_size)
            bg_rect_path = QPainterPath()
            radius = self.BG_RECT_CORNER_RADIUS
            bg_rect_path.addRoundedRect(QRectF(bg_rect), radius, radius, Qt.AbsoluteSize)
            painter.setPen(self.bg_rect_pen)
            painter.fillPath(bg_rect_path, self.bg_rect_fill)
            painter.drawPath(bg_rect_path)

            painter.setPen(self.text_pen)
            painter.drawText(center_pos, res.data)
Пример #47
0
class Window(QWidget):
    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)

        self.m_iconSize = QSize(64, 64)
        self.m_scene = QGraphicsScene()
        self.m_ui = Ui_Form()

        self.m_ui.setupUi(self)
        self.m_ui.easingCurvePicker.setIconSize(self.m_iconSize)
        self.m_ui.easingCurvePicker.setMinimumHeight(self.m_iconSize.height() + 50)
        self.m_ui.buttonGroup.setId(self.m_ui.lineRadio, 0)
        self.m_ui.buttonGroup.setId(self.m_ui.circleRadio, 1)

        dummy = QEasingCurve()
        self.m_ui.periodSpinBox.setValue(dummy.period())
        self.m_ui.amplitudeSpinBox.setValue(dummy.amplitude())
        self.m_ui.overshootSpinBox.setValue(dummy.overshoot())

        self.m_ui.easingCurvePicker.currentRowChanged.connect(self.curveChanged)
        self.m_ui.buttonGroup.buttonClicked[int].connect(self.pathChanged)
        self.m_ui.periodSpinBox.valueChanged.connect(self.periodChanged)
        self.m_ui.amplitudeSpinBox.valueChanged.connect(self.amplitudeChanged)
        self.m_ui.overshootSpinBox.valueChanged.connect(self.overshootChanged)
        self.createCurveIcons()

        pix = QPixmap(':/images/qt-logo.png')
        self.m_item = PixmapItem(pix)
        self.m_scene.addItem(self.m_item.pixmap_item)
        self.m_ui.graphicsView.setScene(self.m_scene)

        self.m_anim = Animation(self.m_item, b'pos')
        self.m_anim.setEasingCurve(QEasingCurve.OutBounce)
        self.m_ui.easingCurvePicker.setCurrentRow(int(QEasingCurve.OutBounce))

        self.startAnimation()

    def createCurveIcons(self):
        pix = QPixmap(self.m_iconSize)
        painter = QPainter()

        gradient = QLinearGradient(0, 0, 0, self.m_iconSize.height())
        gradient.setColorAt(0.0, QColor(240, 240, 240))
        gradient.setColorAt(1.0, QColor(224, 224, 224))

        brush = QBrush(gradient)

        # The original C++ code uses undocumented calls to get the names of the
        # different curve types.  We do the Python equivalant (but without
        # cheating).
        curve_types = [(n, c) for n, c in QEasingCurve.__dict__.items()
                if isinstance(c, QEasingCurve.Type) and c != QEasingCurve.Custom]
        curve_types.sort(key=lambda ct: ct[1])

        painter.begin(pix)

        for curve_name, curve_type in curve_types:
            painter.fillRect(QRect(QPoint(0, 0), self.m_iconSize), brush)

            curve = QEasingCurve(curve_type)

            if curve_type == QEasingCurve.BezierSpline:
                curve.addCubicBezierSegment(QPointF(0.4, 0.1),
                        QPointF(0.6, 0.9), QPointF(1.0, 1.0))
            elif curve_type == QEasingCurve.TCBSpline:
                curve.addTCBSegment(QPointF(0.0, 0.0), 0, 0, 0)
                curve.addTCBSegment(QPointF(0.3, 0.4), 0.2, 1, -0.2)
                curve.addTCBSegment(QPointF(0.7, 0.6), -0.2, 1, 0.2)
                curve.addTCBSegment(QPointF(1.0, 1.0), 0, 0, 0)

            painter.setPen(QColor(0, 0, 255, 64))
            xAxis = self.m_iconSize.height() / 1.5
            yAxis = self.m_iconSize.width() / 3.0
            painter.drawLine(0, xAxis, self.m_iconSize.width(),  xAxis)
            painter.drawLine(yAxis, 0, yAxis, self.m_iconSize.height())

            curveScale = self.m_iconSize.height() / 2.0;

            painter.setPen(Qt.NoPen)

            # Start point.
            painter.setBrush(Qt.red)
            start = QPoint(yAxis,
                    xAxis - curveScale * curve.valueForProgress(0))
            painter.drawRect(start.x() - 1, start.y() - 1, 3, 3)

            # End point.
            painter.setBrush(Qt.blue)
            end = QPoint(yAxis + curveScale,
                    xAxis - curveScale * curve.valueForProgress(1))
            painter.drawRect(end.x() - 1, end.y() - 1, 3, 3)

            curvePath = QPainterPath()
            curvePath.moveTo(QPointF(start))
            t = 0.0
            while t <= 1.0:
                to = QPointF(yAxis + curveScale * t,
                        xAxis - curveScale * curve.valueForProgress(t))
                curvePath.lineTo(to)
                t += 1.0 / curveScale

            painter.setRenderHint(QPainter.Antialiasing, True)
            painter.strokePath(curvePath, QColor(32, 32, 32))
            painter.setRenderHint(QPainter.Antialiasing, False)

            item = QListWidgetItem()
            item.setIcon(QIcon(pix))
            item.setText(curve_name)
            self.m_ui.easingCurvePicker.addItem(item)

        painter.end()

    def startAnimation(self):
        self.m_anim.setStartValue(QPointF(0, 0))
        self.m_anim.setEndValue(QPointF(100, 100))
        self.m_anim.setDuration(2000)
        self.m_anim.setLoopCount(-1)
        self.m_anim.start()

    def curveChanged(self, row):
        curveType = QEasingCurve.Type(row)
        self.m_anim.setEasingCurve(curveType)
        self.m_anim.setCurrentTime(0)

        isElastic = (curveType >= QEasingCurve.InElastic and curveType <= QEasingCurve.OutInElastic)
        isBounce = (curveType >= QEasingCurve.InBounce and curveType <= QEasingCurve.OutInBounce)

        self.m_ui.periodSpinBox.setEnabled(isElastic)
        self.m_ui.amplitudeSpinBox.setEnabled(isElastic or isBounce)
        self.m_ui.overshootSpinBox.setEnabled(curveType >= QEasingCurve.InBack and curveType <= QEasingCurve.OutInBack)

    def pathChanged(self, index):
        self.m_anim.setPathType(index)

    def periodChanged(self, value):
        curve = self.m_anim.easingCurve()
        curve.setPeriod(value)
        self.m_anim.setEasingCurve(curve)

    def amplitudeChanged(self, value):
        curve = self.m_anim.easingCurve()
        curve.setAmplitude(value)
        self.m_anim.setEasingCurve(curve)

    def overshootChanged(self, value):
        curve = self.m_anim.easingCurve()
        curve.setOvershoot(value)
        self.m_anim.setEasingCurve(curve)
Пример #48
0
 def setCanvas(self, size: QSize, color: QColor = QColor(255, 255, 255, 0)):
     self.setGeometry(0, 0, size.width(), size.height())
     self.setPixmap(QPixmap(size.width(), size.height()))
     self.pixmap().fill(color)
Пример #49
0
 def wheelEvent(self, a0: QtGui.QWheelEvent):
     if self.ctrlPressed:
         global IMAGE_SIZE
         deltaY = a0.angleDelta().y()/12
         IMAGE_SIZE = QSize(IMAGE_SIZE.width()+deltaY, IMAGE_SIZE.height()+deltaY)
         self.images_listWidget.setIconSize(IMAGE_SIZE)
Пример #50
0
class Plugin(RevealerPlugin):

    MAX_PLAINTEXT_LEN = 189  # chars

    def __init__(self, parent, config, name):
        RevealerPlugin.__init__(self, parent, config, name)
        self.base_dir = os.path.join(config.actilectrum_path(), 'revealer')

        if self.config.get('calibration_h') is None:
            self.config.set_key('calibration_h', 0)
        if self.config.get('calibration_v') is None:
            self.config.set_key('calibration_v', 0)

        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')

        self.f_size = QSize(1014 * 2, 642 * 2)
        self.abstand_h = 21
        self.abstand_v = 34
        self.calibration_noise = int('10' * 128)
        self.rawnoise = False
        make_dir(self.base_dir)

        self.extension = False

    @hook
    def create_status_bar(self, parent):
        b = StatusBarButton(read_QIcon('revealer.png'),
                            "Revealer " + _("secret backup utility"),
                            partial(self.setup_dialog, parent))
        parent.addPermanentWidget(b)

    def requires_settings(self):
        return True

    def settings_widget(self, window):
        return EnterButton(_('Printer Calibration'),
                           partial(self.calibration_dialog, window))

    def password_dialog(self, msg=None, parent=None):
        from actilectrum.gui.qt.password_dialog import PasswordDialog
        parent = parent or self
        d = PasswordDialog(parent, msg)
        return d.run()

    def get_seed(self):
        password = None
        if self.wallet.has_keystore_encryption():
            password = self.password_dialog(parent=self.d.parent())
            if not password:
                raise UserCancelled()

        keystore = self.wallet.get_keystore()
        if not keystore or not keystore.has_seed():
            return
        self.extension = bool(keystore.get_passphrase(password))
        return keystore.get_seed(password)

    def setup_dialog(self, window):
        self.wallet = window.parent().wallet
        self.update_wallet_name(self.wallet)
        self.user_input = False

        self.d = WindowModalDialog(window, "Setup Dialog")
        self.d.setMinimumWidth(500)
        self.d.setMinimumHeight(210)
        self.d.setMaximumHeight(320)
        self.d.setContentsMargins(11, 11, 1, 1)

        self.hbox = QHBoxLayout(self.d)
        vbox = QVBoxLayout()
        logo = QLabel()
        self.hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        self.hbox.addSpacing(16)
        vbox.addWidget(
            WWLabel(
                "<b>" + _("Revealer Secret Backup Plugin") + "</b><br>" +
                _("To encrypt your backup, first we need to load some noise.")
                + "<br/>"))
        vbox.addSpacing(7)
        bcreate = QPushButton(_("Create a new Revealer"))
        bcreate.setMaximumWidth(181)
        bcreate.setDefault(True)
        vbox.addWidget(bcreate, Qt.AlignCenter)
        self.load_noise = ScanQRTextEdit()
        self.load_noise.setTabChangesFocus(True)
        self.load_noise.textChanged.connect(self.on_edit)
        self.load_noise.setMaximumHeight(33)
        self.hbox.addLayout(vbox)
        vbox.addWidget(
            WWLabel(
                _("or type an existing revealer code below and click 'next':"))
        )
        vbox.addWidget(self.load_noise)
        vbox.addSpacing(3)
        self.next_button = QPushButton(_("Next"), self.d)
        self.next_button.setEnabled(False)
        vbox.addLayout(Buttons(self.next_button))
        self.next_button.clicked.connect(self.d.close)
        self.next_button.clicked.connect(
            partial(self.cypherseed_dialog, window))
        vbox.addWidget(
            QLabel("<b>" + _("Warning") + "</b>: " + _(
                "Each revealer should be used only once."
            ) + "<br>" + _(
                "more information at <a href=\"https://revealer.cc/faq\">https://revealer.cc/faq</a>"
            )))

        def mk_digital():
            try:
                self.make_digital(self.d)
            except Exception:
                self.logger.exception('')
            else:
                self.cypherseed_dialog(window)

        bcreate.clicked.connect(mk_digital)
        return bool(self.d.exec_())

    def get_noise(self):
        text = self.load_noise.text()
        return ''.join(text.split()).lower()

    def on_edit(self):
        txt = self.get_noise()
        versioned_seed = self.get_versioned_seed_from_user_input(txt)
        if versioned_seed:
            self.versioned_seed = versioned_seed
        self.user_input = bool(versioned_seed)
        self.next_button.setEnabled(bool(versioned_seed))

    def make_digital(self, dialog):
        self.make_rawnoise(True)
        self.bdone(dialog)
        self.d.close()

    def get_path_to_revealer_file(self, ext: str = '') -> str:
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        filename = self.filename_prefix + version + "_" + code_id + ext
        path = os.path.join(self.base_dir, filename)
        return os.path.normcase(os.path.abspath(path))

    def get_path_to_calibration_file(self):
        path = os.path.join(self.base_dir, 'calibration.pdf')
        return os.path.normcase(os.path.abspath(path))

    def bcrypt(self, dialog):
        self.rawnoise = False
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([
            _("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").
            format(self.was, version, code_id), "<b>",
            self.get_path_to_revealer_file(), "</b>", "<br/>", "<br/>", "<b>",
            _("Always check your backups.")
        ]),
                            rich_text=True)
        dialog.close()

    def ext_warning(self, dialog):
        dialog.show_message(''.join([
            "<b>",
            _("Warning"), ": </b>",
            _("your seed extension will <b>not</b> be included in the encrypted backup."
              )
        ]),
                            rich_text=True)
        dialog.close()

    def bdone(self, dialog):
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([
            _("Digital Revealer ({}_{}) saved as PNG and PDF at:").format(
                version, code_id), "<br/>", "<b>",
            self.get_path_to_revealer_file(), '</b>'
        ]),
                            rich_text=True)

    def customtxt_limits(self):
        txt = self.text.text()
        self.max_chars.setVisible(False)
        self.char_count.setText(f"({len(txt)}/{self.MAX_PLAINTEXT_LEN})")
        if len(txt) > 0:
            self.ctext.setEnabled(True)
        if len(txt) > self.MAX_PLAINTEXT_LEN:
            self.text.setPlainText(txt[:self.MAX_PLAINTEXT_LEN])
            self.max_chars.setVisible(True)

    def t(self):
        self.txt = self.text.text()
        self.seed_img(is_seed=False)

    def warn_old_revealer(self):
        if self.versioned_seed.version == '0':
            link = "https://revealer.cc/revealer-warning-and-upgrade/"
            self.d.show_warning((
                "<b>{warning}: </b>{ver0}<br>"
                "{url}<br>"
                "{risk}"
            ).format(
                warning=_("Warning"),
                ver0=
                _("Revealers starting with 0 are not secure due to a vulnerability."
                  ),
                url=_("More info at: {}").format(
                    f'<a href="{link}">{link}</a>'),
                risk=_("Proceed at your own risk.")),
                                rich_text=True)

    def cypherseed_dialog(self, window):
        self.warn_old_revealer()

        d = WindowModalDialog(window, "Encryption Dialog")
        d.setMinimumWidth(500)
        d.setMinimumHeight(210)
        d.setMaximumHeight(450)
        d.setContentsMargins(11, 11, 1, 1)
        self.c_dialog = d

        hbox = QHBoxLayout(d)
        self.vbox = QVBoxLayout()
        logo = QLabel()
        hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        hbox.addSpacing(16)
        self.vbox.addWidget(
            WWLabel("<b>" + _("Revealer Secret Backup Plugin") + "</b><br>" +
                    _("Ready to encrypt for revealer {}").format(
                        self.versioned_seed.version + '_' +
                        self.versioned_seed.checksum)))
        self.vbox.addSpacing(11)
        hbox.addLayout(self.vbox)
        grid = QGridLayout()
        self.vbox.addLayout(grid)

        cprint = QPushButton(_("Encrypt {}'s seed").format(self.wallet_name))
        cprint.setMaximumWidth(250)
        cprint.clicked.connect(partial(self.seed_img, True))
        self.vbox.addWidget(cprint)
        self.vbox.addSpacing(1)
        self.vbox.addWidget(
            WWLabel("<b>" + _("OR") + "</b> " +
                    _("type a custom alphanumerical secret below:")))
        self.text = ScanQRTextEdit()
        self.text.setTabChangesFocus(True)
        self.text.setMaximumHeight(70)
        self.text.textChanged.connect(self.customtxt_limits)
        self.vbox.addWidget(self.text)
        self.char_count = WWLabel("")
        self.char_count.setAlignment(Qt.AlignRight)
        self.vbox.addWidget(self.char_count)
        self.max_chars = WWLabel(
            "<font color='red'>" +
            _("This version supports a maximum of {} characters.").format(
                self.MAX_PLAINTEXT_LEN) + "</font>")
        self.vbox.addWidget(self.max_chars)
        self.max_chars.setVisible(False)
        self.ctext = QPushButton(_("Encrypt custom secret"))
        self.ctext.clicked.connect(self.t)
        self.vbox.addWidget(self.ctext)
        self.ctext.setEnabled(False)
        self.vbox.addSpacing(11)
        self.vbox.addLayout(Buttons(CloseButton(d)))
        return bool(d.exec_())

    def update_wallet_name(self, name):
        self.wallet_name = str(name)

    def seed_img(self, is_seed=True):

        if is_seed:
            try:
                cseed = self.get_seed()
            except UserCancelled:
                return
            except InvalidPassword as e:
                self.d.show_error(str(e))
                return
            if not cseed:
                self.d.show_message(_("This wallet has no seed"))
                return
            txt = cseed.upper()
        else:
            txt = self.txt.upper()

        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.white)
        painter = QPainter()
        painter.begin(bitmap)
        QFontDatabase.addApplicationFont(
            os.path.join(os.path.dirname(__file__), 'SourceSansPro-Bold.otf'))
        if len(txt) < 102:
            fontsize = 15
            linespace = 15
            max_letters = 17
            max_lines = 6
            max_words = 3
        else:
            fontsize = 12
            linespace = 10
            max_letters = 21
            max_lines = 9
            max_words = int(max_letters / 4)

        font = QFont('Source Sans Pro', fontsize, QFont.Bold)
        font.setLetterSpacing(QFont.PercentageSpacing, 100)
        font.setPixelSize(fontsize)
        painter.setFont(font)
        seed_array = txt.split(' ')

        for n in range(max_lines):
            nwords = max_words
            temp_seed = seed_array[:nwords]
            while len(' '.join(map(str, temp_seed))) > max_letters:
                nwords = nwords - 1
                temp_seed = seed_array[:nwords]
            painter.drawText(
                QRect(0, linespace * n, self.SIZE[0], self.SIZE[1]),
                Qt.AlignHCenter, ' '.join(map(str, temp_seed)))
            del seed_array[:nwords]

        painter.end()
        img = bitmap.toImage()
        if (self.rawnoise == False):
            self.make_rawnoise()

        self.make_cypherseed(img, self.rawnoise, False, is_seed)
        return img

    def make_rawnoise(self, create_revealer=False):
        if not self.user_input:
            self.versioned_seed = self.gen_random_versioned_seed()
        assert self.versioned_seed
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)

        noise_map = self.get_noise_map(self.versioned_seed)
        for (x, y), pixel in noise_map.items():
            rawnoise.setPixel(x, y, pixel)

        self.rawnoise = rawnoise
        if create_revealer:
            self.make_revealer()

    def make_calnoise(self):
        random.seed(self.calibration_noise)
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)
        for x in range(w):
            for y in range(h):
                rawnoise.setPixel(x, y, random.randint(0, 1))
        self.calnoise = self.pixelcode_2x2(rawnoise)

    def make_revealer(self):
        revealer = self.pixelcode_2x2(self.rawnoise)
        revealer.invertPixels()
        revealer = QBitmap.fromImage(revealer)
        revealer = revealer.scaled(self.f_size, Qt.KeepAspectRatio)
        revealer = self.overlay_marks(revealer)

        self.filename_prefix = 'revealer_'
        revealer.save(self.get_path_to_revealer_file('.png'))
        self.toPdf(QImage(revealer))
        QDesktopServices.openUrl(
            QUrl.fromLocalFile(self.get_path_to_revealer_file('.pdf')))

    def make_cypherseed(self, img, rawnoise, calibration=False, is_seed=True):
        img = img.convertToFormat(QImage.Format_Mono)
        p = QPainter()
        p.begin(img)
        p.setCompositionMode(26)  #xor
        p.drawImage(0, 0, rawnoise)
        p.end()
        cypherseed = self.pixelcode_2x2(img)
        cypherseed = QBitmap.fromImage(cypherseed)
        cypherseed = cypherseed.scaled(self.f_size, Qt.KeepAspectRatio)
        cypherseed = self.overlay_marks(cypherseed, True, calibration)

        if not is_seed:
            self.filename_prefix = 'custom_secret_'
            self.was = _('Custom secret')
        else:
            self.filename_prefix = self.wallet_name + '_seed_'
            self.was = self.wallet_name + ' ' + _('seed')
            if self.extension:
                self.ext_warning(self.c_dialog)

        if not calibration:
            self.toPdf(QImage(cypherseed))
            QDesktopServices.openUrl(
                QUrl.fromLocalFile(self.get_path_to_revealer_file('.pdf')))
            cypherseed.save(self.get_path_to_revealer_file('.png'))
            self.bcrypt(self.c_dialog)
        return cypherseed

    def calibration(self):
        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.black)
        self.make_calnoise()
        img = self.overlay_marks(
            self.calnoise.scaledToHeight(self.f_size.height()), False, True)
        self.calibration_pdf(img)
        QDesktopServices.openUrl(
            QUrl.fromLocalFile(self.get_path_to_calibration_file()))
        return img

    def toPdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_revealer_file('.pdf'))
        printer.setPageMargins(0, 0, 0, 0, 6)
        painter = QPainter()
        painter.begin(printer)

        delta_h = round(image.width() / self.abstand_v)
        delta_v = round(image.height() / self.abstand_h)

        size_h = 2028 + ((int(self.calibration_h) * 2028 /
                          (2028 -
                           (delta_h * 2) + int(self.calibration_h))) / 2)
        size_v = 1284 + ((int(self.calibration_v) * 1284 /
                          (1284 -
                           (delta_v * 2) + int(self.calibration_v))) / 2)

        image = image.scaled(size_h, size_v)

        painter.drawImage(553, 533, image)
        wpath = QPainterPath()
        wpath.addRoundedRect(QRectF(553, 533, size_h, size_v), 19, 19)
        painter.setPen(QPen(Qt.black, 1))
        painter.drawPath(wpath)
        painter.end()

    def calibration_pdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_calibration_file())
        printer.setPageMargins(0, 0, 0, 0, 6)

        painter = QPainter()
        painter.begin(printer)
        painter.drawImage(553, 533, image)
        font = QFont('Source Sans Pro', 10, QFont.Bold)
        painter.setFont(font)
        painter.drawText(254, 277, _("Calibration sheet"))
        font = QFont('Source Sans Pro', 7, QFont.Bold)
        painter.setFont(font)
        painter.drawText(600, 2077, _("Instructions:"))
        font = QFont('Source Sans Pro', 7, QFont.Normal)
        painter.setFont(font)
        painter.drawText(
            700, 2177,
            _("1. Place this paper on a flat and well iluminated surface."))
        painter.drawText(
            700, 2277,
            _("2. Align your Revealer borderlines to the dashed lines on the top and left."
              ))
        painter.drawText(
            700, 2377,
            _("3. Press slightly the Revealer against the paper and read the numbers that best "
              "match on the opposite sides. "))
        painter.drawText(700, 2477, _("4. Type the numbers in the software"))
        painter.end()

    def pixelcode_2x2(self, img):
        result = QImage(img.width() * 2,
                        img.height() * 2, QImage.Format_ARGB32)
        white = qRgba(255, 255, 255, 0)
        black = qRgba(0, 0, 0, 255)

        for x in range(img.width()):
            for y in range(img.height()):
                c = img.pixel(QPoint(x, y))
                colors = QColor(c).getRgbF()
                if colors[0]:
                    result.setPixel(x * 2 + 1, y * 2 + 1, black)
                    result.setPixel(x * 2, y * 2 + 1, white)
                    result.setPixel(x * 2 + 1, y * 2, white)
                    result.setPixel(x * 2, y * 2, black)

                else:
                    result.setPixel(x * 2 + 1, y * 2 + 1, white)
                    result.setPixel(x * 2, y * 2 + 1, black)
                    result.setPixel(x * 2 + 1, y * 2, black)
                    result.setPixel(x * 2, y * 2, white)
        return result

    def overlay_marks(self, img, is_cseed=False, calibration_sheet=False):
        border_color = Qt.white
        base_img = QImage(self.f_size.width(), self.f_size.height(),
                          QImage.Format_ARGB32)
        base_img.fill(border_color)
        img = QImage(img)

        painter = QPainter()
        painter.begin(base_img)

        total_distance_h = round(base_img.width() / self.abstand_v)
        dist_v = round(total_distance_h) / 2
        dist_h = round(total_distance_h) / 2

        img = img.scaledToWidth(base_img.width() - (2 * (total_distance_h)))
        painter.drawImage(total_distance_h, total_distance_h, img)

        #frame around image
        pen = QPen(Qt.black, 2)
        painter.setPen(pen)

        #horz
        painter.drawLine(0, total_distance_h, base_img.width(),
                         total_distance_h)
        painter.drawLine(0,
                         base_img.height() - (total_distance_h),
                         base_img.width(),
                         base_img.height() - (total_distance_h))
        #vert
        painter.drawLine(total_distance_h, 0, total_distance_h,
                         base_img.height())
        painter.drawLine(base_img.width() - (total_distance_h), 0,
                         base_img.width() - (total_distance_h),
                         base_img.height())

        #border around img
        border_thick = 6
        Rpath = QPainterPath()
        Rpath.addRect(
            QRectF((total_distance_h) + (border_thick / 2),
                   (total_distance_h) + (border_thick / 2),
                   base_img.width() - ((total_distance_h) * 2) -
                   ((border_thick) - 1),
                   (base_img.height() -
                    ((total_distance_h)) * 2) - ((border_thick) - 1)))
        pen = QPen(Qt.black, border_thick)
        pen.setJoinStyle(Qt.MiterJoin)

        painter.setPen(pen)
        painter.drawPath(Rpath)

        Bpath = QPainterPath()
        Bpath.addRect(
            QRectF((total_distance_h), (total_distance_h),
                   base_img.width() - ((total_distance_h) * 2),
                   (base_img.height() - ((total_distance_h)) * 2)))
        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawPath(Bpath)

        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawLine(0,
                         base_img.height() / 2, total_distance_h,
                         base_img.height() / 2)
        painter.drawLine(base_img.width() / 2, 0,
                         base_img.width() / 2, total_distance_h)

        painter.drawLine(base_img.width() - total_distance_h,
                         base_img.height() / 2, base_img.width(),
                         base_img.height() / 2)
        painter.drawLine(base_img.width() / 2, base_img.height(),
                         base_img.width() / 2,
                         base_img.height() - total_distance_h)

        #print code
        f_size = 37
        QFontDatabase.addApplicationFont(
            os.path.join(os.path.dirname(__file__), 'DejaVuSansMono-Bold.ttf'))
        font = QFont("DejaVu Sans Mono", f_size - 11, QFont.Bold)
        font.setPixelSize(35)
        painter.setFont(font)

        if not calibration_sheet:
            if is_cseed:  #its a secret
                painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0, dist_h, base_img.height())
                painter.drawLine(0,
                                 base_img.height() - dist_v, base_img.width(),
                                 base_img.height() - (dist_v))
                painter.drawLine(base_img.width() - (dist_h), 0,
                                 base_img.width() - (dist_h),
                                 base_img.height())

                painter.drawImage(
                    ((total_distance_h)) + 11, ((total_distance_h)) + 11,
                    QImage(icon_path('actilectrum.png')).scaledToWidth(
                        2.1 * (total_distance_h), Qt.SmoothTransformation))

                painter.setPen(QPen(Qt.white, border_thick * 8))
                painter.drawLine(
                    base_img.width() - ((total_distance_h)) -
                    (border_thick * 8) / 2 - (border_thick / 2) - 2,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2,
                    base_img.width() - ((total_distance_h)) -
                    (border_thick * 8) / 2 - (border_thick / 2) - 2 - 77,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2)
                painter.setPen(QColor(0, 0, 0, 255))
                painter.drawText(
                    QRect(
                        0,
                        base_img.height() - 107,
                        base_img.width() - total_distance_h - border_thick -
                        11,
                        base_img.height() - total_distance_h - border_thick),
                    Qt.AlignRight, self.versioned_seed.version + '_' +
                    self.versioned_seed.checksum)
                painter.end()

            else:  # revealer

                painter.setPen(QPen(border_color, 17))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0, dist_h, base_img.height())
                painter.drawLine(0,
                                 base_img.height() - dist_v, base_img.width(),
                                 base_img.height() - (dist_v))
                painter.drawLine(base_img.width() - (dist_h), 0,
                                 base_img.width() - (dist_h),
                                 base_img.height())

                painter.setPen(QPen(Qt.black, 2))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0, dist_h, base_img.height())
                painter.drawLine(0,
                                 base_img.height() - dist_v, base_img.width(),
                                 base_img.height() - (dist_v))
                painter.drawLine(base_img.width() - (dist_h), 0,
                                 base_img.width() - (dist_h),
                                 base_img.height())
                logo = QImage(icon_path('revealer_c.png')).scaledToWidth(
                    1.3 * (total_distance_h))
                painter.drawImage((total_distance_h) + (border_thick),
                                  ((total_distance_h)) + (border_thick), logo,
                                  Qt.SmoothTransformation)

                #frame around logo
                painter.setPen(QPen(Qt.black, border_thick))
                painter.drawLine(
                    total_distance_h + border_thick,
                    total_distance_h + logo.height() + 3 * (border_thick / 2),
                    total_distance_h + logo.width() + border_thick,
                    total_distance_h + logo.height() + 3 * (border_thick / 2))
                painter.drawLine(
                    logo.width() + total_distance_h + 3 * (border_thick / 2),
                    total_distance_h + (border_thick),
                    total_distance_h + logo.width() + 3 * (border_thick / 2),
                    total_distance_h + logo.height() + (border_thick))

                #frame around code/qr
                qr_size = 179

                painter.drawLine((base_img.width() - ((total_distance_h)) -
                                  (border_thick / 2) - 2) - qr_size,
                                 (base_img.height() - ((total_distance_h))) -
                                 ((border_thick * 8)) - (border_thick / 2) - 2,
                                 (base_img.width() / 2 +
                                  (total_distance_h / 2) - border_thick -
                                  (border_thick * 8) / 2) - qr_size,
                                 (base_img.height() - ((total_distance_h))) -
                                 ((border_thick * 8)) - (border_thick / 2) - 2)

                painter.drawLine(
                    (base_img.width() / 2 +
                     (total_distance_h / 2) - border_thick -
                     (border_thick * 8) / 2) - qr_size,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8)) - (border_thick / 2) - 2,
                    base_img.width() / 2 + (total_distance_h / 2) -
                    border_thick - (border_thick * 8) / 2 - qr_size,
                    ((base_img.height() - ((total_distance_h))) -
                     (border_thick / 2) - 2))

                painter.setPen(QPen(Qt.white, border_thick * 8))
                painter.drawLine(
                    base_img.width() - ((total_distance_h)) -
                    (border_thick * 8) / 2 - (border_thick / 2) - 2,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2,
                    base_img.width() / 2 + (total_distance_h / 2) -
                    border_thick - qr_size,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2)

                painter.setPen(QColor(0, 0, 0, 255))
                painter.drawText(
                    QRect(((base_img.width() / 2) + 21) - qr_size,
                          base_img.height() - 107,
                          base_img.width() - total_distance_h - border_thick -
                          93,
                          base_img.height() - total_distance_h - border_thick),
                    Qt.AlignLeft,
                    self.versioned_seed.get_ui_string_version_plus_seed())
                painter.drawText(
                    QRect(
                        0,
                        base_img.height() - 107,
                        base_img.width() - total_distance_h - border_thick -
                        3 - qr_size,
                        base_img.height() - total_distance_h - border_thick),
                    Qt.AlignRight, self.versioned_seed.checksum)

                # draw qr code
                qr_qt = self.paintQR(
                    self.versioned_seed.get_ui_string_version_plus_seed() +
                    self.versioned_seed.checksum)
                target = QRectF(base_img.width() - 65 - qr_size,
                                base_img.height() - 65 - qr_size, qr_size,
                                qr_size)
                painter.drawImage(target, qr_qt)
                painter.setPen(QPen(Qt.black, 4))
                painter.drawLine(base_img.width() - 65 - qr_size,
                                 base_img.height() - 65 - qr_size,
                                 base_img.width() - 65 - qr_size,
                                 (base_img.height() - ((total_distance_h))) -
                                 ((border_thick * 8)) - (border_thick / 2) - 4)
                painter.drawLine(base_img.width() - 65 - qr_size,
                                 base_img.height() - 65 - qr_size,
                                 base_img.width() - 65,
                                 base_img.height() - 65 - qr_size)
                painter.end()

        else:  # calibration only
            painter.end()
            cal_img = QImage(self.f_size.width() + 100,
                             self.f_size.height() + 100, QImage.Format_ARGB32)
            cal_img.fill(Qt.white)

            cal_painter = QPainter()
            cal_painter.begin(cal_img)
            cal_painter.drawImage(0, 0, base_img)

            #black lines in the middle of border top left only
            cal_painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
            cal_painter.drawLine(0, dist_v, base_img.width(), dist_v)
            cal_painter.drawLine(dist_h, 0, dist_h, base_img.height())

            pen = QPen(Qt.black, 2, Qt.DashDotDotLine)
            cal_painter.setPen(pen)
            n = 15

            cal_painter.setFont(QFont("DejaVu Sans Mono", 21, QFont.Bold))
            for x in range(-n, n):
                #lines on bottom (vertical calibration)
                cal_painter.drawLine((((base_img.width()) / (n * 2)) *
                                      (x)) + (base_img.width() / 2) - 13,
                                     x + 2 + base_img.height() - (dist_v),
                                     (((base_img.width()) / (n * 2)) *
                                      (x)) + (base_img.width() / 2) + 13,
                                     x + 2 + base_img.height() - (dist_v))

                num_pos = 9
                if x > 9: num_pos = 17
                if x < 0: num_pos = 20
                if x < -9: num_pos = 27

                cal_painter.drawText((((base_img.width()) / (n * 2)) *
                                      (x)) + (base_img.width() / 2) - num_pos,
                                     50 + base_img.height() - (dist_v), str(x))

                #lines on the right (horizontal calibrations)

                cal_painter.drawLine(
                    x + 2 + (base_img.width() - (dist_h)),
                    ((base_img.height() / (2 * n)) * (x)) +
                    (base_img.height() / n) + (base_img.height() / 2) - 13,
                    x + 2 + (base_img.width() - (dist_h)),
                    ((base_img.height() / (2 * n)) * (x)) +
                    (base_img.height() / n) + (base_img.height() / 2) + 13)

                cal_painter.drawText(30 + (base_img.width() - (dist_h)),
                                     ((base_img.height() / (2 * n)) *
                                      (x)) + (base_img.height() / 2) + 13,
                                     str(x))

            cal_painter.end()
            base_img = cal_img

        return base_img

    def paintQR(self, data):
        if not data:
            return
        qr = qrcode.QRCode()
        qr.add_data(data)
        matrix = qr.get_matrix()
        k = len(matrix)
        border_color = Qt.white
        base_img = QImage(k * 5, k * 5, QImage.Format_ARGB32)
        base_img.fill(border_color)
        qrpainter = QPainter()
        qrpainter.begin(base_img)
        boxsize = 5
        size = k * boxsize
        left = (base_img.width() - size) / 2
        top = (base_img.height() - size) / 2
        qrpainter.setBrush(Qt.black)
        qrpainter.setPen(Qt.black)

        for r in range(k):
            for c in range(k):
                if matrix[r][c]:
                    qrpainter.drawRect(left + c * boxsize, top + r * boxsize,
                                       boxsize - 1, boxsize - 1)
        qrpainter.end()
        return base_img

    def calibration_dialog(self, window):
        d = WindowModalDialog(window,
                              _("Revealer - Printer calibration settings"))

        d.setMinimumSize(100, 200)

        vbox = QVBoxLayout(d)
        vbox.addWidget(
            QLabel(''.join([
                "<br/>",
                _("If you have an old printer, or want optimal precision"),
                "<br/>",
                _("print the calibration pdf and follow the instructions "),
                "<br/>",
                "<br/>",
            ])))
        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')
        cprint = QPushButton(_("Open calibration pdf"))
        cprint.clicked.connect(self.calibration)
        vbox.addWidget(cprint)

        vbox.addWidget(QLabel(_('Calibration values:')))
        grid = QGridLayout()
        vbox.addLayout(grid)
        grid.addWidget(QLabel(_('Right side')), 0, 0)
        horizontal = QLineEdit()
        horizontal.setText(str(self.calibration_h))
        grid.addWidget(horizontal, 0, 1)

        grid.addWidget(QLabel(_('Bottom')), 1, 0)
        vertical = QLineEdit()
        vertical.setText(str(self.calibration_v))
        grid.addWidget(vertical, 1, 1)

        vbox.addStretch()
        vbox.addSpacing(13)
        vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

        if not d.exec_():
            return

        self.calibration_h = int(Decimal(horizontal.text()))
        self.config.set_key('calibration_h', self.calibration_h)
        self.calibration_v = int(Decimal(vertical.text()))
        self.config.set_key('calibration_v', self.calibration_v)
Пример #51
0
 def smallPic(self):
     global IMAGE_SIZE
     IMAGE_SIZE = QSize(IMAGE_SIZE.width() - 10, IMAGE_SIZE.height() - 10)
     self.images_listWidget.setIconSize(IMAGE_SIZE)
Пример #52
0
    def paintEvent(self, event, *args):
        """ Custom paint event """
        self.mutex.lock()

        # Paint custom frame image on QWidget
        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing, True)

        # Fill background black
        painter.fillRect(event.rect(), self.palette().window())

        if self.current_image:
            # DRAW FRAME
            # Calculate new frame image size, maintaining aspect ratio
            pixSize = self.current_image.size()
            pixSize.scale(event.rect().size(), Qt.KeepAspectRatio)

            # Scale image
            scaledPix = self.current_image.scaled(pixSize, Qt.KeepAspectRatio, Qt.SmoothTransformation)

            # Calculate center of QWidget and Draw image
            center = self.centeredViewport(self.width(), self.height())
            painter.drawImage(center, scaledPix)

        if self.transforming_clip:
            # Draw transform handles on top of video preview
            # Get framerate
            fps = get_app().project.get(["fps"])
            fps_float = float(fps["num"]) / float(fps["den"])

            # Determine frame # of clip
            start_of_clip = round(float(self.transforming_clip.data["start"]) * fps_float)
            position_of_clip = (float(self.transforming_clip.data["position"]) * fps_float) + 1
            playhead_position = float(get_app().window.preview_thread.current_frame)
            clip_frame_number = round(playhead_position - position_of_clip) + start_of_clip + 1

            # Get properties of clip at current frame
            raw_properties = json.loads(self.transforming_clip_object.PropertiesJSON(clip_frame_number))

            # Get size of current video player
            player_width = self.rect().width()
            player_height = self.rect().height()

            # Determine original size of clip's reader
            source_width = self.transforming_clip.data['reader']['width']
            source_height = self.transforming_clip.data['reader']['height']
            source_size = QSize(source_width, source_height)

            # Determine scale of clip
            scale = self.transforming_clip.data['scale']
            if scale == openshot.SCALE_FIT:
                source_size.scale(player_width, player_height, Qt.KeepAspectRatio)

            elif scale == openshot.SCALE_STRETCH:
                source_size.scale(player_width, player_height, Qt.IgnoreAspectRatio)

            elif scale == openshot.SCALE_CROP:
                width_size = QSize(player_width, round(player_width / (float(source_width) / float(source_height))))
                height_size = QSize(round(player_height / (float(source_height) / float(source_width))), player_height)
                if width_size.width() >= player_width and width_size.height() >= player_height:
                    source_size.scale(width_size.width(), width_size.height(), Qt.KeepAspectRatio)
                else:
                    source_size.scale(height_size.width(), height_size.height(), Qt.KeepAspectRatio)

            # Get new source width / height (after scaling mode applied)
            source_width = source_size.width()
            source_height = source_size.height()

            # Init X/Y
            x = 0.0
            y = 0.0

            # Get scaled source image size (scale_x, scale_y)
            sx = float(raw_properties.get('scale_x').get('value'))
            sy = float(raw_properties.get('scale_y').get('value'))
            scaled_source_width = source_width * sx
            scaled_source_height = source_height * sy

            # Determine gravity of clip
            gravity = self.transforming_clip.data['gravity']
            if gravity == openshot.GRAVITY_TOP_LEFT:
                x += self.centeredViewport(self.width(), self.height()).x()  # nudge right
                y += self.centeredViewport(self.width(), self.height()).y()  # nudge down
            elif gravity == openshot.GRAVITY_TOP:
                x = (player_width - scaled_source_width) / 2.0 # center
                y += self.centeredViewport(self.width(), self.height()).y()  # nudge down
            elif gravity == openshot.GRAVITY_TOP_RIGHT:
                x = player_width - scaled_source_width # right
                x -= self.centeredViewport(self.width(), self.height()).x()  # nudge left
                y += self.centeredViewport(self.width(), self.height()).y()  # nudge down
            elif gravity == openshot.GRAVITY_LEFT:
                y = (player_height - scaled_source_height) / 2.0 # center
                x += self.centeredViewport(self.width(), self.height()).x()  # nudge right
            elif gravity == openshot.GRAVITY_CENTER:
                x = (player_width - scaled_source_width) / 2.0 # center
                y = (player_height - scaled_source_height) / 2.0 # center
            elif gravity == openshot.GRAVITY_RIGHT:
                x = player_width - scaled_source_width # right
                y = (player_height - scaled_source_height) / 2.0 # center
                x -= self.centeredViewport(self.width(), self.height()).x()  # nudge left
            elif gravity == openshot.GRAVITY_BOTTOM_LEFT:
                y = (player_height - scaled_source_height) # bottom
                x += self.centeredViewport(self.width(), self.height()).x()  # nudge right
                y -= self.centeredViewport(self.width(), self.height()).y()  # nudge up
            elif gravity == openshot.GRAVITY_BOTTOM:
                x = (player_width - scaled_source_width) / 2.0 # center
                y = (player_height - scaled_source_height) # bottom
                y -= self.centeredViewport(self.width(), self.height()).y()  # nudge up
            elif gravity == openshot.GRAVITY_BOTTOM_RIGHT:
                x = player_width - scaled_source_width # right
                y = (player_height - scaled_source_height) # bottom
                x -= self.centeredViewport(self.width(), self.height()).x()  # nudge left
                y -= self.centeredViewport(self.width(), self.height()).y()  # nudge up

            # Track gravity starting coordinate
            self.gravity_point = QPointF(x, y)

            # Scale to fit in widget
            final_size = QSize(source_width, source_height)

            # Adjust x,y for location
            x_offset = raw_properties.get('location_x').get('value')
            y_offset = raw_properties.get('location_y').get('value')
            x += (scaledPix.width() * x_offset)
            y += (scaledPix.height() * y_offset)

            self.transform = QTransform()

            # Apply translate/move
            if x or y:
                self.transform.translate(x, y)

            # Apply scale
            if sx or sy:
                self.transform.scale(sx, sy)

            # Apply shear
            shear_x = raw_properties.get('shear_x').get('value')
            shear_y = raw_properties.get('shear_y').get('value')
            if shear_x or shear_y:
                self.transform.shear(shear_x, shear_y)

            # Apply rotation
            rotation = raw_properties.get('rotation').get('value')
            if rotation:
                origin_x = x - self.centeredViewport(self.width(), self.height()).x() + (scaled_source_width / 2.0)
                origin_y = y - self.centeredViewport(self.width(), self.height()).y() + (scaled_source_height / 2.0)
                self.transform.translate(origin_x, origin_y)
                self.transform.rotate(rotation)
                self.transform.translate(-origin_x, -origin_y)

            # Apply transform
            painter.setTransform(self.transform)

            # Draw transform corners and cernter origin circle
            # Corner size
            cs = 6.0
            os = 12.0

            # Calculate 4 corners coordinates
            self.topLeftHandle = QRectF(0.0, 0.0, cs/sx, cs/sy)
            self.topRightHandle = QRectF(source_width - (cs/sx), 0, cs/sx, cs/sy)
            self.bottomLeftHandle = QRectF(0.0, source_height - (cs/sy), cs/sx, cs/sy)
            self.bottomRightHandle = QRectF(source_width - (cs/sx), source_height - (cs/sy), cs/sx, cs/sy)

            # Draw 4 corners
            painter.fillRect(self.topLeftHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.topRightHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.bottomLeftHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.bottomRightHandle, QBrush(QColor("#53a0ed")))

            # Calculate 4 side coordinates
            self.topHandle = QRectF(0.0 + (source_width / 2.0) - (cs/sx/2.0), 0, cs/sx, cs/sy)
            self.bottomHandle = QRectF(0.0 + (source_width / 2.0) - (cs/sx/2.0), source_height - (cs/sy), cs/sx, cs/sy)
            self.leftHandle = QRectF(0.0, (source_height / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)
            self.rightHandle = QRectF(source_width - (cs/sx), (source_height / 2.0) - (cs/sy/2.0), cs/sx, cs/sy)

            # Draw 4 sides (centered)
            painter.fillRect(self.topHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.bottomHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.leftHandle, QBrush(QColor("#53a0ed")))
            painter.fillRect(self.rightHandle, QBrush(QColor("#53a0ed")))

            # Calculate center coordinate
            self.centerHandle = QRectF((source_width / 2.0) - (os/sx), (source_height / 2.0) - (os/sy), os/sx*2.0, os/sy*2.0)

            # Draw origin
            painter.setBrush(QColor(83, 160, 237, 122))
            painter.setPen(Qt.NoPen)
            painter.drawEllipse(self.centerHandle)

            # Draw translucent rectangle
            self.clipRect = QRectF(0, 0, final_size.width(), final_size.height())

            # Remove transform
            painter.resetTransform()

        # End painter
        painter.end()

        self.mutex.unlock()
Пример #53
0
class Settings(object):

    l = logging.getLogger('Settings')


    def __init__(self, fileName = 'shadow.ini'):
        self.fileName = fileName
        self.tzIdx = 0
        self.mainWindowSize = QSize(1200, 700)
        self.mainWindowPos = QPoint(240, 200)

    def load(self):
        self.l.debug('Loading local settings from {}'.format(self.fileName))
        config = configparser.ConfigParser()
        config.read(self.fileName)

        if config.has_section('common'):
            commonSettings = config['common']
            self.tzIdx = int(commonSettings.get('tz', 0))

        # load main window position and size
        if config.has_section('mainWindow'):
            windowSettings = config['mainWindow']

            # read position
            pos = windowSettings.get('pos', '{},{}'.format(self.mainWindowPos.x(), self.mainWindowPos.y())).split(',')
            self.mainWindowPos = QPoint(int(pos[0]), int(pos[1]))

            # read size
            size = windowSettings.get('size', '{},{}'.format(self.mainWindowSize.width(), self.mainWindowSize.height())).split(',')
            self.mainWindowSize = QSize(int(size[0]), int(size[1]))

        

    def dump(self, channel):
        self.l.debug('Saving local settings ...')
        config = configparser.ConfigParser()

        config.add_section('common')
        config.set('common', 'tz', str(self.tzIdx))

        config.add_section('mainWindow')
        config.set('mainWindow', 'pos', '{},{}'.format(self.mainWindowPos.x(), self.mainWindowPos.y()))
        config.set('mainWindow', 'size', '{},{}'.format(self.mainWindowSize.width(), self.mainWindowSize.height()))

        config.write(channel)

    
    def save(self):
        with open(self.fileName, 'w') as configfile:
            self.dump(configfile)

    def setMainWindowPos(self, pos):
        self.mainWindowPos = pos    

    def setMainWindowSize(self, size):
        self.mainWindowSize = size

    def getMainWindowPos(self):
        return self.mainWindowPos

    def getMainWindowSize(self):
        return self.mainWindowSize

    def getTzIndex(self):
        return self.tzIdx

    def setTzIdx(self, idx):
        self.tzIdx = idx
Пример #54
0
def setHomeWindowSize(size: QSize):
    setPreference(HOME_WIDTH, size.width())
    setPreference(HOME_HEIGHT, size.height())
Пример #55
0
class TextButton(DemoItem):
    BUTTON_WIDTH = 180
    BUTTON_HEIGHT = 19

    LEFT, RIGHT = range(2)

    SIDEBAR, PANEL, UP, DOWN = range(4)

    ON, OFF, HIGHLIGHT, DISABLED = range(4)

    def __init__(self, text, align=LEFT, userCode=0, parent=None, type=SIDEBAR):
        super(TextButton, self).__init__(parent)

        # Prevent a circular import.
        from menumanager import MenuManager
        self._menu_manager = MenuManager.instance()

        self.menuString = text
        self.buttonLabel = text
        self.alignment = align
        self.buttonType = type
        self.userCode = userCode
        self.scanAnim = None
        self.bgOn = None
        self.bgOff = None
        self.bgHighlight = None
        self.bgDisabled = None
        self.state = TextButton.OFF

        self.setAcceptHoverEvents(True)
        self.setCursor(Qt.PointingHandCursor)

        # Calculate the button size.
        if type in (TextButton.SIDEBAR, TextButton.PANEL):
            self.logicalSize = QSize(TextButton.BUTTON_WIDTH, TextButton.BUTTON_HEIGHT)
        else:
            self.logicalSize = QSize(int((TextButton.BUTTON_WIDTH / 2.0) - 5), int(TextButton.BUTTON_HEIGHT * 1.5))

        self._prepared = False

    def setMenuString(self, menu):
        self.menuString = menu

    def prepare(self):
        if not self._prepared:
            self.setupHoverText()
            self.setupScanItem()
            self.setupButtonBg()
            self._prepared = True

    def boundingRect(self):
        return QRectF(0, 0, self.logicalSize.width(),
                self.logicalSize.height())

    def setupHoverText(self):
        if not self.buttonLabel:
            return

        textItem = DemoTextItem(self.buttonLabel, Colors.buttonFont(),
                Colors.buttonText, -1, self)
        textItem.setZValue(self.zValue() + 2)
        textItem.setPos(16, 0)

    def setupScanItem(self):
        if Colors.useButtonBalls:
            scanItem = ScanItem(self)
            scanItem.setZValue(self.zValue() + 1)

            self.scanAnim = DemoItemAnimation(scanItem)

            x = 1.0
            y = 1.5
            stop = TextButton.BUTTON_WIDTH - scanItem.boundingRect().width() - x
            if self.alignment == TextButton.LEFT:
                self.scanAnim.setDuration(2500)
                self.scanAnim.setKeyValueAt(0.0, QPointF(x, y))
                self.scanAnim.setKeyValueAt(0.5, QPointF(x, y))
                self.scanAnim.setKeyValueAt(0.7, QPointF(stop, y))
                self.scanAnim.setKeyValueAt(1.0, QPointF(x, y))
                scanItem.setPos(QPointF(x, y))
            else:
                self.scanAnim.setKeyValueAt(0.0, QPointF(stop, y))
                self.scanAnim.setKeyValueAt(0.5, QPointF(x, y))
                self.scanAnim.setKeyValueAt(1.0, QPointF(stop, y))
                scanItem.setPos(QPointF(stop, y))

    def setState(self, state):
        self.state = state
        self.bgOn.setRecursiveVisible(state == TextButton.ON)
        self.bgOff.setRecursiveVisible(state == TextButton.OFF)
        self.bgHighlight.setRecursiveVisible(state == TextButton.HIGHLIGHT)
        self.bgDisabled.setRecursiveVisible(state == TextButton.DISABLED)
        if state == TextButton.DISABLED:
            self.setCursor(Qt.ArrowCursor)
        else:
            self.setCursor(Qt.PointingHandCursor)

    def setupButtonBg(self):
        self.bgOn = ButtonBackground(self.buttonType, True, True,
                self.logicalSize, self)
        self.bgOff = ButtonBackground(self.buttonType, False, False,
                self.logicalSize, self)
        self.bgHighlight = ButtonBackground(self.buttonType, True, False,
                self.logicalSize, self)
        self.bgDisabled = ButtonBackground(self.buttonType, True, True,
                self.logicalSize, self)
        self.setState(TextButton.OFF)

    def hoverEnterEvent(self, event):
        if not self.isEnabled() or self.state == TextButton.DISABLED:
            return

        if self.state == TextButton.OFF:
            self.setState(TextButton.HIGHLIGHT)

            if Colors.noAnimations and Colors.useButtonBalls:
                # Wait a bit in the beginning to enhance the effect.  We have
                # to do this here so that the adaption can be dynamic.
                self.scanAnim.setDuration(1000)
                self.scanAnim.setKeyValueAt(0.2, self.scanAnim.posAt(0))

            if (self._menu_manager.window.fpsMedian > 10 or Colors.noAdapt or
                    Colors.noTimerUpdate):
                if Colors.useButtonBalls:
                    self.scanAnim.play(True, True)

    def hoverLeaveEvent(self, event):
        if self.state == TextButton.DISABLED:
            return

        self.setState(TextButton.OFF)

        if Colors.noAnimations and Colors.useButtonBalls:
            self.scanAnim.stop()

    def mousePressEvent(self, event):
        if self.state == TextButton.DISABLED:
            return

        if self.state == TextButton.HIGHLIGHT or self.state == TextButton.OFF:
            self.setState(TextButton.ON)

    def mouseReleaseEvent(self, event):
        if self.state == TextButton.ON:
            self.setState(TextButton.OFF)
            if self.isEnabled() and self.boundingRect().contains(event.pos()):
                self._menu_manager.itemSelected(self.userCode, self.menuString)

    def animationStarted(self, _):
        if self.state == TextButton.DISABLED:
            return

        self.setState(TextButton.OFF)
Пример #56
0
class FeatureTableWidgetHHeader(QTableWidgetItem):
    def __init__(self, sigma, window_size, name=None):
        QTableWidgetItem.__init__(self)
        # init
        # ------------------------------------------------
        self.sigma = sigma
        self.window_size = window_size
        self.pixmapSize = QSize(61, 61)
        if not name:
            self.setNameAndBrush(self.sigma)
        else:
            self.setText(name)
    
    @property
    def brushSize(self):
        return int(3.0*self.sigma + 0.5)*2 + 1
        
    def setNameAndBrush(self, sigma, color=Qt.black):
        self.sigma = sigma
        self.setText(u"σ={:.1f}px".format(self.sigma))
        total_window = (1 + 2 * int(self.sigma * self.window_size + 0.5) )
        self.setToolTip( "sigma = {:.1f} pixels, window diameter = {:.1f}".format(self.sigma, total_window) )
        font = QFont() 
        font.setPointSize(10)
        font.setBold(True)
        self.setFont(font)
        self.setForeground(color)
                        
        pixmap = QPixmap(self.pixmapSize)
        pixmap.fill(Qt.transparent)
        painter = QPainter()
        painter.begin(pixmap)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setPen(color)
        brush = QBrush(color)
        painter.setBrush(brush)
        painter.drawEllipse(QRect(old_div(self.pixmapSize.width(),2) - old_div(self.brushSize,2), old_div(self.pixmapSize.height(),2) - old_div(self.brushSize,2), self.brushSize, self.brushSize))
        painter.end()
        self.setIcon(QIcon(pixmap))
        self.setTextAlignment(Qt.AlignVCenter)
        
    def setIconAndTextColor(self, color):
        self.setNameAndBrush(self.sigma, color)
Пример #57
0
class SquircleRenderer(QObject):  # QOpenGLFunctions
    """docstring for SquircleRenderer"""

    def __init__(self, parent=None):
        super(SquircleRenderer, self).__init__(parent)
        self.m_t = 0.0
        self.m_program = None
        self.m_viewportSize = QSize()

    def setT(self, t):
        self.m_t = t

    def setViewportSize(self, size):
        self.m_viewportSize = size

    def setWin(self, win):
        self.win = win

        ver = QOpenGLVersionProfile()
        ver.setVersion(2, 1)

        self.m_context = self.win.openglContext()
        self.gl = self.m_context.versionFunctions(ver)

    @pyqtSlot()
    def paint(self):
        if not self.m_program:
            self.gl.initializeOpenGLFunctions()

            self.m_program = QOpenGLShaderProgram(self)
            self.m_program.addShaderFromSourceCode(
                QOpenGLShader.Vertex,
                "attribute highp vec4 vertices;"
                "varying highp vec2 coords;"
                "void main() {"
                "    gl_Position = vertices;"
                "    coords = vertices.xy;"
                "}",
            )
            self.m_program.addShaderFromSourceCode(
                QOpenGLShader.Fragment,
                "uniform lowp float t;"
                "varying highp vec2 coords;"
                "void main() {"
                "    lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));"
                "    i = smoothstep(t - 0.8, t + 0.8, i);"
                "    i = floor(i * 20.) / 20.;"
                "    gl_FragColor = vec4(coords * .5 + .5, i, i);"
                "}",
            )

            self.m_program.bindAttributeLocation("vertices", 0)

            self.m_program.link()

        self.m_program.bind()

        self.m_program.enableAttributeArray(0)

        values = [(-1, -1), (1, -1), (-1, 1), (1, 1)]

        self.m_program.setAttributeArray(0, values)

        self.m_program.setUniformValue("t", self.m_t)

        # print("DATA:",self.m_viewportSize.width(), self.m_viewportSize.height(), self.m_t)#, self.gl.glViewport)

        self.gl.glViewport(0, 0, self.m_viewportSize.width(), self.m_viewportSize.height())

        self.gl.glDisable(self.gl.GL_DEPTH_TEST)

        self.gl.glClearColor(0, 0, 0, 1)

        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT)

        self.gl.glEnable(self.gl.GL_BLEND)

        self.gl.glBlendFunc(self.gl.GL_SRC_ALPHA, self.gl.GL_ONE)

        self.gl.glDrawArrays(self.gl.GL_TRIANGLE_STRIP, 0, 4)

        self.m_program.disableAttributeArray(0)

        self.m_program.release()
Пример #58
0
class IconEditorGrid(QWidget):
    """
    Class implementing the icon editor grid.
    
    @signal canRedoChanged(bool) emitted after the redo status has changed
    @signal canUndoChanged(bool) emitted after the undo status has changed
    @signal clipboardImageAvailable(bool) emitted to signal the availability
        of an image to be pasted
    @signal colorChanged(QColor) emitted after the drawing color was changed
    @signal imageChanged(bool) emitted after the image was modified
    @signal positionChanged(int, int) emitted after the cursor poition was
        changed
    @signal previewChanged(QPixmap) emitted to signal a new preview pixmap
    @signal selectionAvailable(bool) emitted to signal a change of the
        selection
    @signal sizeChanged(int, int) emitted after the size has been changed
    @signal zoomChanged(int) emitted to signal a change of the zoom value
    """
    canRedoChanged = pyqtSignal(bool)
    canUndoChanged = pyqtSignal(bool)
    clipboardImageAvailable = pyqtSignal(bool)
    colorChanged = pyqtSignal(QColor)
    imageChanged = pyqtSignal(bool)
    positionChanged = pyqtSignal(int, int)
    previewChanged = pyqtSignal(QPixmap)
    selectionAvailable = pyqtSignal(bool)
    sizeChanged = pyqtSignal(int, int)
    zoomChanged = pyqtSignal(int)
    
    Pencil = 1
    Rubber = 2
    Line = 3
    Rectangle = 4
    FilledRectangle = 5
    Circle = 6
    FilledCircle = 7
    Ellipse = 8
    FilledEllipse = 9
    Fill = 10
    ColorPicker = 11
    
    RectangleSelection = 20
    CircleSelection = 21
    
    MarkColor = QColor(255, 255, 255, 255)
    NoMarkColor = QColor(0, 0, 0, 0)
    
    ZoomMinimum = 100
    ZoomMaximum = 10000
    ZoomStep = 100
    ZoomDefault = 1200
    ZoomPercent = True
    
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        """
        super(IconEditorGrid, self).__init__(parent)
        
        self.setAttribute(Qt.WA_StaticContents)
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        
        self.__curColor = Qt.black
        self.__zoom = 12
        self.__curTool = self.Pencil
        self.__startPos = QPoint()
        self.__endPos = QPoint()
        self.__dirty = False
        self.__selecting = False
        self.__selRect = QRect()
        self.__isPasting = False
        self.__clipboardSize = QSize()
        self.__pasteRect = QRect()
        
        self.__undoStack = QUndoStack(self)
        self.__currentUndoCmd = None
        
        self.__image = QImage(32, 32, QImage.Format_ARGB32)
        self.__image.fill(qRgba(0, 0, 0, 0))
        self.__markImage = QImage(self.__image)
        self.__markImage.fill(self.NoMarkColor.rgba())
        
        self.__compositingMode = QPainter.CompositionMode_SourceOver
        self.__lastPos = (-1, -1)
        
        self.__gridEnabled = True
        self.__selectionAvailable = False
        
        self.__initCursors()
        self.__initUndoTexts()
        
        self.setMouseTracking(True)
        
        self.__undoStack.canRedoChanged.connect(self.canRedoChanged)
        self.__undoStack.canUndoChanged.connect(self.canUndoChanged)
        self.__undoStack.cleanChanged.connect(self.__cleanChanged)
        
        self.imageChanged.connect(self.__updatePreviewPixmap)
        QApplication.clipboard().dataChanged.connect(self.__checkClipboard)
        
        self.__checkClipboard()
    
    def __initCursors(self):
        """
        Private method to initialize the various cursors.
        """
        self.__normalCursor = QCursor(Qt.ArrowCursor)
        
        pix = QPixmap(":colorpicker-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__colorPickerCursor = QCursor(pix, 1, 21)
        
        pix = QPixmap(":paintbrush-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__paintCursor = QCursor(pix, 0, 19)
        
        pix = QPixmap(":fill-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__fillCursor = QCursor(pix, 3, 20)
        
        pix = QPixmap(":aim-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__aimCursor = QCursor(pix, 10, 10)
        
        pix = QPixmap(":eraser-cursor.xpm")
        mask = pix.createHeuristicMask()
        pix.setMask(mask)
        self.__rubberCursor = QCursor(pix, 1, 16)
    
    def __initUndoTexts(self):
        """
        Private method to initialize texts to be associated with undo commands
        for the various drawing tools.
        """
        self.__undoTexts = {
            self.Pencil: self.tr("Set Pixel"),
            self.Rubber: self.tr("Erase Pixel"),
            self.Line: self.tr("Draw Line"),
            self.Rectangle: self.tr("Draw Rectangle"),
            self.FilledRectangle: self.tr("Draw Filled Rectangle"),
            self.Circle: self.tr("Draw Circle"),
            self.FilledCircle: self.tr("Draw Filled Circle"),
            self.Ellipse: self.tr("Draw Ellipse"),
            self.FilledEllipse: self.tr("Draw Filled Ellipse"),
            self.Fill: self.tr("Fill Region"),
        }
    
    def isDirty(self):
        """
        Public method to check the dirty status.
        
        @return flag indicating a modified status (boolean)
        """
        return self.__dirty
    
    def setDirty(self, dirty, setCleanState=False):
        """
        Public slot to set the dirty flag.
        
        @param dirty flag indicating the new modification status (boolean)
        @param setCleanState flag indicating to set the undo stack to clean
            (boolean)
        """
        self.__dirty = dirty
        self.imageChanged.emit(dirty)
        
        if not dirty and setCleanState:
            self.__undoStack.setClean()
    
    def sizeHint(self):
        """
        Public method to report the size hint.
        
        @return size hint (QSize)
        """
        size = self.__zoom * self.__image.size()
        if self.__zoom >= 3 and self.__gridEnabled:
            size += QSize(1, 1)
        return size
    
    def setPenColor(self, newColor):
        """
        Public method to set the drawing color.
        
        @param newColor reference to the new color (QColor)
        """
        self.__curColor = QColor(newColor)
        self.colorChanged.emit(QColor(newColor))
    
    def penColor(self):
        """
        Public method to get the current drawing color.
        
        @return current drawing color (QColor)
        """
        return QColor(self.__curColor)
    
    def setCompositingMode(self, mode):
        """
        Public method to set the compositing mode.
        
        @param mode compositing mode to set (QPainter.CompositionMode)
        """
        self.__compositingMode = mode
    
    def compositingMode(self):
        """
        Public method to get the compositing mode.
        
        @return compositing mode (QPainter.CompositionMode)
        """
        return self.__compositingMode
    
    def setTool(self, tool):
        """
        Public method to set the current drawing tool.
        
        @param tool drawing tool to be used
            (IconEditorGrid.Pencil ... IconEditorGrid.CircleSelection)
        """
        self.__curTool = tool
        self.__lastPos = (-1, -1)
        
        if self.__curTool in [self.RectangleSelection, self.CircleSelection]:
            self.__selecting = True
        else:
            self.__selecting = False
        
        if self.__curTool in [self.RectangleSelection, self.CircleSelection,
                              self.Line, self.Rectangle, self.FilledRectangle,
                              self.Circle, self.FilledCircle,
                              self.Ellipse, self.FilledEllipse]:
            self.setCursor(self.__aimCursor)
        elif self.__curTool == self.Fill:
            self.setCursor(self.__fillCursor)
        elif self.__curTool == self.ColorPicker:
            self.setCursor(self.__colorPickerCursor)
        elif self.__curTool == self.Pencil:
            self.setCursor(self.__paintCursor)
        elif self.__curTool == self.Rubber:
            self.setCursor(self.__rubberCursor)
        else:
            self.setCursor(self.__normalCursor)
    
    def tool(self):
        """
        Public method to get the current drawing tool.
        
        @return current drawing tool
            (IconEditorGrid.Pencil ... IconEditorGrid.CircleSelection)
        """
        return self.__curTool
    
    def setIconImage(self, newImage, undoRedo=False, clearUndo=False):
        """
        Public method to set a new icon image.
        
        @param newImage reference to the new image (QImage)
        @keyparam undoRedo flag indicating an undo or redo operation (boolean)
        @keyparam clearUndo flag indicating to clear the undo stack (boolean)
        """
        if newImage != self.__image:
            self.__image = newImage.convertToFormat(QImage.Format_ARGB32)
            self.update()
            self.updateGeometry()
            self.resize(self.sizeHint())
            
            self.__markImage = QImage(self.__image)
            self.__markImage.fill(self.NoMarkColor.rgba())
            
            if undoRedo:
                self.setDirty(not self.__undoStack.isClean())
            else:
                self.setDirty(False)
            
            if clearUndo:
                self.__undoStack.clear()
            
            self.sizeChanged.emit(*self.iconSize())
    
    def iconImage(self):
        """
        Public method to get a copy of the icon image.
        
        @return copy of the icon image (QImage)
        """
        return QImage(self.__image)
    
    def iconSize(self):
        """
        Public method to get the size of the icon.
        
        @return width and height of the image as a tuple (integer, integer)
        """
        return self.__image.width(), self.__image.height()
    
    def setZoomFactor(self, newZoom):
        """
        Public method to set the zoom factor in percent.
        
        @param newZoom zoom factor (integer >= 100)
        """
        newZoom = max(100, newZoom)   # must not be less than 100
        if newZoom != self.__zoom:
            self.__zoom = newZoom // 100
            self.update()
            self.updateGeometry()
            self.resize(self.sizeHint())
            self.zoomChanged.emit(int(self.__zoom * 100))
    
    def zoomFactor(self):
        """
        Public method to get the current zoom factor in percent.
        
        @return zoom factor (integer)
        """
        return self.__zoom * 100
    
    def setGridEnabled(self, enable):
        """
        Public method to enable the display of grid lines.
        
        @param enable enabled status of the grid lines (boolean)
        """
        if enable != self.__gridEnabled:
            self.__gridEnabled = enable
            self.update()
    
    def isGridEnabled(self):
        """
        Public method to get the grid lines status.
        
        @return enabled status of the grid lines (boolean)
        """
        return self.__gridEnabled
    
    def paintEvent(self, evt):
        """
        Protected method called to repaint some of the widget.
        
        @param evt reference to the paint event object (QPaintEvent)
        """
        painter = QPainter(self)
        
        if self.__zoom >= 3 and self.__gridEnabled:
            painter.setPen(self.palette().windowText().color())
            i = 0
            while i <= self.__image.width():
                painter.drawLine(
                    self.__zoom * i, 0,
                    self.__zoom * i, self.__zoom * self.__image.height())
                i += 1
            j = 0
            while j <= self.__image.height():
                painter.drawLine(
                    0, self.__zoom * j,
                    self.__zoom * self.__image.width(), self.__zoom * j)
                j += 1
        
        col = QColor("#aaa")
        painter.setPen(Qt.DashLine)
        for i in range(0, self.__image.width()):
            for j in range(0, self.__image.height()):
                rect = self.__pixelRect(i, j)
                if evt.region().intersects(rect):
                    color = QColor.fromRgba(self.__image.pixel(i, j))
                    painter.fillRect(rect, QBrush(Qt.white))
                    painter.fillRect(QRect(rect.topLeft(), rect.center()), col)
                    painter.fillRect(QRect(rect.center(), rect.bottomRight()),
                                     col)
                    painter.fillRect(rect, QBrush(color))
                
                    if self.__isMarked(i, j):
                        painter.drawRect(rect.adjusted(0, 0, -1, -1))
        
        painter.end()
    
    def __pixelRect(self, i, j):
        """
        Private method to determine the rectangle for a given pixel coordinate.
        
        @param i x-coordinate of the pixel in the image (integer)
        @param j y-coordinate of the pixel in the image (integer)
        @return rectangle for the given pixel coordinates (QRect)
        """
        if self.__zoom >= 3 and self.__gridEnabled:
            return QRect(self.__zoom * i + 1, self.__zoom * j + 1,
                         self.__zoom - 1, self.__zoom - 1)
        else:
            return QRect(self.__zoom * i, self.__zoom * j,
                         self.__zoom, self.__zoom)
    
    def mousePressEvent(self, evt):
        """
        Protected method to handle mouse button press events.
        
        @param evt reference to the mouse event object (QMouseEvent)
        """
        if evt.button() == Qt.LeftButton:
            if self.__isPasting:
                self.__isPasting = False
                self.editPaste(True)
                self.__markImage.fill(self.NoMarkColor.rgba())
                self.update(self.__pasteRect)
                self.__pasteRect = QRect()
                return
            
            if self.__curTool == self.Pencil:
                cmd = IconEditCommand(self, self.__undoTexts[self.__curTool],
                                      self.__image)
                self.__setImagePixel(evt.pos(), True)
                self.setDirty(True)
                self.__undoStack.push(cmd)
                self.__currentUndoCmd = cmd
            elif self.__curTool == self.Rubber:
                cmd = IconEditCommand(self, self.__undoTexts[self.__curTool],
                                      self.__image)
                self.__setImagePixel(evt.pos(), False)
                self.setDirty(True)
                self.__undoStack.push(cmd)
                self.__currentUndoCmd = cmd
            elif self.__curTool == self.Fill:
                i, j = self.__imageCoordinates(evt.pos())
                col = QColor()
                col.setRgba(self.__image.pixel(i, j))
                cmd = IconEditCommand(self, self.__undoTexts[self.__curTool],
                                      self.__image)
                self.__drawFlood(i, j, col)
                self.setDirty(True)
                self.__undoStack.push(cmd)
                cmd.setAfterImage(self.__image)
            elif self.__curTool == self.ColorPicker:
                i, j = self.__imageCoordinates(evt.pos())
                col = QColor()
                col.setRgba(self.__image.pixel(i, j))
                self.setPenColor(col)
            else:
                self.__unMark()
                self.__startPos = evt.pos()
                self.__endPos = evt.pos()
    
    def mouseMoveEvent(self, evt):
        """
        Protected method to handle mouse move events.
        
        @param evt reference to the mouse event object (QMouseEvent)
        """
        self.positionChanged.emit(*self.__imageCoordinates(evt.pos()))
        
        if self.__isPasting and not (evt.buttons() & Qt.LeftButton):
            self.__drawPasteRect(evt.pos())
            return
        
        if evt.buttons() & Qt.LeftButton:
            if self.__curTool == self.Pencil:
                self.__setImagePixel(evt.pos(), True)
                self.setDirty(True)
            elif self.__curTool == self.Rubber:
                self.__setImagePixel(evt.pos(), False)
                self.setDirty(True)
            elif self.__curTool in [self.Fill, self.ColorPicker]:
                pass    # do nothing
            else:
                self.__drawTool(evt.pos(), True)
    
    def mouseReleaseEvent(self, evt):
        """
        Protected method to handle mouse button release events.
        
        @param evt reference to the mouse event object (QMouseEvent)
        """
        if evt.button() == Qt.LeftButton:
            if self.__curTool in [self.Pencil, self.Rubber]:
                if self.__currentUndoCmd:
                    self.__currentUndoCmd.setAfterImage(self.__image)
                    self.__currentUndoCmd = None
            
            if self.__curTool not in [self.Pencil, self.Rubber,
                                      self.Fill, self.ColorPicker,
                                      self.RectangleSelection,
                                      self.CircleSelection]:
                cmd = IconEditCommand(self, self.__undoTexts[self.__curTool],
                                      self.__image)
                if self.__drawTool(evt.pos(), False):
                    self.__undoStack.push(cmd)
                    cmd.setAfterImage(self.__image)
                    self.setDirty(True)
    
    def __setImagePixel(self, pos, opaque):
        """
        Private slot to set or erase a pixel.
        
        @param pos position of the pixel in the widget (QPoint)
        @param opaque flag indicating a set operation (boolean)
        """
        i, j = self.__imageCoordinates(pos)
        
        if self.__image.rect().contains(i, j) and (i, j) != self.__lastPos:
            if opaque:
                painter = QPainter(self.__image)
                painter.setPen(self.penColor())
                painter.setCompositionMode(self.__compositingMode)
                painter.drawPoint(i, j)
            else:
                self.__image.setPixel(i, j, qRgba(0, 0, 0, 0))
            self.__lastPos = (i, j)
        
            self.update(self.__pixelRect(i, j))
    
    def __imageCoordinates(self, pos):
        """
        Private method to convert from widget to image coordinates.
        
        @param pos widget coordinate (QPoint)
        @return tuple with the image coordinates (tuple of two integers)
        """
        i = pos.x() // self.__zoom
        j = pos.y() // self.__zoom
        return i, j
    
    def __drawPasteRect(self, pos):
        """
        Private slot to draw a rectangle for signaling a paste operation.
        
        @param pos widget position of the paste rectangle (QPoint)
        """
        self.__markImage.fill(self.NoMarkColor.rgba())
        if self.__pasteRect.isValid():
            self.__updateImageRect(
                self.__pasteRect.topLeft(),
                self.__pasteRect.bottomRight() + QPoint(1, 1))
        
        x, y = self.__imageCoordinates(pos)
        isize = self.__image.size()
        if x + self.__clipboardSize.width() <= isize.width():
            sx = self.__clipboardSize.width()
        else:
            sx = isize.width() - x
        if y + self.__clipboardSize.height() <= isize.height():
            sy = self.__clipboardSize.height()
        else:
            sy = isize.height() - y
        
        self.__pasteRect = QRect(QPoint(x, y), QSize(sx - 1, sy - 1))
        
        painter = QPainter(self.__markImage)
        painter.setPen(self.MarkColor)
        painter.drawRect(self.__pasteRect)
        painter.end()
        
        self.__updateImageRect(self.__pasteRect.topLeft(),
                               self.__pasteRect.bottomRight() + QPoint(1, 1))
    
    def __drawTool(self, pos, mark):
        """
        Private method to perform a draw operation depending of the current
        tool.
        
        @param pos widget coordinate to perform the draw operation at (QPoint)
        @param mark flag indicating a mark operation (boolean)
        @return flag indicating a successful draw (boolean)
        """
        self.__unMark()
        
        if mark:
            self.__endPos = QPoint(pos)
            drawColor = self.MarkColor
            img = self.__markImage
        else:
            drawColor = self.penColor()
            img = self.__image
        
        start = QPoint(*self.__imageCoordinates(self.__startPos))
        end = QPoint(*self.__imageCoordinates(pos))
        
        painter = QPainter(img)
        painter.setPen(drawColor)
        painter.setCompositionMode(self.__compositingMode)
        
        if self.__curTool == self.Line:
            painter.drawLine(start, end)
        
        elif self.__curTool in [self.Rectangle, self.FilledRectangle,
                                self.RectangleSelection]:
            left = min(start.x(), end.x())
            top = min(start.y(), end.y())
            right = max(start.x(), end.x())
            bottom = max(start.y(), end.y())
            if self.__curTool == self.RectangleSelection:
                painter.setBrush(QBrush(drawColor))
            if self.__curTool == self.FilledRectangle:
                for y in range(top, bottom + 1):
                    painter.drawLine(left, y, right, y)
            else:
                painter.drawRect(left, top, right - left, bottom - top)
            if self.__selecting:
                self.__selRect = QRect(
                    left, top, right - left + 1, bottom - top + 1)
                self.__selectionAvailable = True
                self.selectionAvailable.emit(True)
        
        elif self.__curTool in [self.Circle, self.FilledCircle,
                                self.CircleSelection]:
            r = max(abs(start.x() - end.x()), abs(start.y() - end.y()))
            if self.__curTool in [self.FilledCircle, self.CircleSelection]:
                painter.setBrush(QBrush(drawColor))
            painter.drawEllipse(start, r, r)
            if self.__selecting:
                self.__selRect = QRect(start.x() - r, start.y() - r,
                                       2 * r + 1, 2 * r + 1)
                self.__selectionAvailable = True
                self.selectionAvailable.emit(True)
        
        elif self.__curTool in [self.Ellipse, self.FilledEllipse]:
            r1 = abs(start.x() - end.x())
            r2 = abs(start.y() - end.y())
            if r1 == 0 or r2 == 0:
                return False
            if self.__curTool == self.FilledEllipse:
                painter.setBrush(QBrush(drawColor))
            painter.drawEllipse(start, r1, r2)
        
        painter.end()
        
        if self.__curTool in [self.Circle, self.FilledCircle,
                              self.Ellipse, self.FilledEllipse]:
            self.update()
        else:
            self.__updateRect(self.__startPos, pos)
        
        return True
    
    def __drawFlood(self, i, j, oldColor, doUpdate=True):
        """
        Private method to perform a flood fill operation.
        
        @param i x-value in image coordinates (integer)
        @param j y-value in image coordinates (integer)
        @param oldColor reference to the color at position i, j (QColor)
        @param doUpdate flag indicating an update is requested (boolean)
            (used for speed optimizations)
        """
        if not self.__image.rect().contains(i, j) or \
           self.__image.pixel(i, j) != oldColor.rgba() or \
           self.__image.pixel(i, j) == self.penColor().rgba():
            return
        
        self.__image.setPixel(i, j, self.penColor().rgba())
        
        self.__drawFlood(i, j - 1, oldColor, False)
        self.__drawFlood(i, j + 1, oldColor, False)
        self.__drawFlood(i - 1, j, oldColor, False)
        self.__drawFlood(i + 1, j, oldColor, False)
        
        if doUpdate:
            self.update()
    
    def __updateRect(self, pos1, pos2):
        """
        Private slot to update parts of the widget.
        
        @param pos1 top, left position for the update in widget coordinates
            (QPoint)
        @param pos2 bottom, right position for the update in widget
            coordinates (QPoint)
        """
        self.__updateImageRect(QPoint(*self.__imageCoordinates(pos1)),
                               QPoint(*self.__imageCoordinates(pos2)))
    
    def __updateImageRect(self, ipos1, ipos2):
        """
        Private slot to update parts of the widget.
        
        @param ipos1 top, left position for the update in image coordinates
            (QPoint)
        @param ipos2 bottom, right position for the update in image
            coordinates (QPoint)
        """
        r1 = self.__pixelRect(ipos1.x(), ipos1.y())
        r2 = self.__pixelRect(ipos2.x(), ipos2.y())
        
        left = min(r1.x(), r2.x())
        top = min(r1.y(), r2.y())
        right = max(r1.x() + r1.width(), r2.x() + r2.width())
        bottom = max(r1.y() + r1.height(), r2.y() + r2.height())
        self.update(left, top, right - left + 1, bottom - top + 1)
    
    def __unMark(self):
        """
        Private slot to remove the mark indicator.
        """
        self.__markImage.fill(self.NoMarkColor.rgba())
        if self.__curTool in [self.Circle, self.FilledCircle,
                              self.Ellipse, self.FilledEllipse,
                              self.CircleSelection]:
            self.update()
        else:
            self.__updateRect(self.__startPos, self.__endPos)
        
        if self.__selecting:
            self.__selRect = QRect()
            self.__selectionAvailable = False
            self.selectionAvailable.emit(False)
    
    def __isMarked(self, i, j):
        """
        Private method to check, if a pixel is marked.
        
        @param i x-value in image coordinates (integer)
        @param j y-value in image coordinates (integer)
        @return flag indicating a marked pixel (boolean)
        """
        return self.__markImage.pixel(i, j) == self.MarkColor.rgba()
    
    def __updatePreviewPixmap(self):
        """
        Private slot to generate and signal an updated preview pixmap.
        """
        p = QPixmap.fromImage(self.__image)
        self.previewChanged.emit(p)
    
    def previewPixmap(self):
        """
        Public method to generate a preview pixmap.
        
        @return preview pixmap (QPixmap)
        """
        p = QPixmap.fromImage(self.__image)
        return p
    
    def __checkClipboard(self):
        """
        Private slot to check, if the clipboard contains a valid image, and
        signal the result.
        """
        ok = self.__clipboardImage()[1]
        self.__clipboardImageAvailable = ok
        self.clipboardImageAvailable.emit(ok)
    
    def canPaste(self):
        """
        Public slot to check the availability of the paste operation.
        
        @return flag indicating availability of paste (boolean)
        """
        return self.__clipboardImageAvailable
    
    def __clipboardImage(self):
        """
        Private method to get an image from the clipboard.
        
        @return tuple with the image (QImage) and a flag indicating a
            valid image (boolean)
        """
        img = QApplication.clipboard().image()
        ok = not img.isNull()
        if ok:
            img = img.convertToFormat(QImage.Format_ARGB32)
        
        return img, ok
    
    def __getSelectionImage(self, cut):
        """
        Private method to get an image from the selection.
        
        @param cut flag indicating to cut the selection (boolean)
        @return image of the selection (QImage)
        """
        if cut:
            cmd = IconEditCommand(self, self.tr("Cut Selection"),
                                  self.__image)
        
        img = QImage(self.__selRect.size(), QImage.Format_ARGB32)
        img.fill(qRgba(0, 0, 0, 0))
        for i in range(0, self.__selRect.width()):
            for j in range(0, self.__selRect.height()):
                if self.__image.rect().contains(self.__selRect.x() + i,
                                                self.__selRect.y() + j):
                    if self.__isMarked(
                            self.__selRect.x() + i, self.__selRect.y() + j):
                        img.setPixel(i, j, self.__image.pixel(
                            self.__selRect.x() + i, self.__selRect.y() + j))
                        if cut:
                            self.__image.setPixel(self.__selRect.x() + i,
                                                  self.__selRect.y() + j,
                                                  qRgba(0, 0, 0, 0))
        
        if cut:
            self.__undoStack.push(cmd)
            cmd.setAfterImage(self.__image)
        
        self.__unMark()
        
        if cut:
            self.update(self.__selRect)
        
        return img
    
    def editCopy(self):
        """
        Public slot to copy the selection.
        """
        if self.__selRect.isValid():
            img = self.__getSelectionImage(False)
            QApplication.clipboard().setImage(img)
    
    def editCut(self):
        """
        Public slot to cut the selection.
        """
        if self.__selRect.isValid():
            img = self.__getSelectionImage(True)
            QApplication.clipboard().setImage(img)
    
    @pyqtSlot()
    def editPaste(self, pasting=False):
        """
        Public slot to paste an image from the clipboard.
        
        @param pasting flag indicating part two of the paste operation
            (boolean)
        """
        img, ok = self.__clipboardImage()
        if ok:
            if img.width() > self.__image.width() or \
                    img.height() > self.__image.height():
                res = E5MessageBox.yesNo(
                    self,
                    self.tr("Paste"),
                    self.tr(
                        """<p>The clipboard image is larger than the"""
                        """ current image.<br/>Paste as new image?</p>"""))
                if res:
                    self.editPasteAsNew()
                return
            elif not pasting:
                self.__isPasting = True
                self.__clipboardSize = img.size()
            else:
                cmd = IconEditCommand(self, self.tr("Paste Clipboard"),
                                      self.__image)
                self.__markImage.fill(self.NoMarkColor.rgba())
                painter = QPainter(self.__image)
                painter.setPen(self.penColor())
                painter.setCompositionMode(self.__compositingMode)
                painter.drawImage(
                    self.__pasteRect.x(), self.__pasteRect.y(), img, 0, 0,
                    self.__pasteRect.width() + 1,
                    self.__pasteRect.height() + 1)
                
                self.__undoStack.push(cmd)
                cmd.setAfterImage(self.__image)
                
                self.__updateImageRect(
                    self.__pasteRect.topLeft(),
                    self.__pasteRect.bottomRight() + QPoint(1, 1))
        else:
            E5MessageBox.warning(
                self,
                self.tr("Pasting Image"),
                self.tr("""Invalid image data in clipboard."""))
    
    def editPasteAsNew(self):
        """
        Public slot to paste the clipboard as a new image.
        """
        img, ok = self.__clipboardImage()
        if ok:
            cmd = IconEditCommand(
                self, self.tr("Paste Clipboard as New Image"),
                self.__image)
            self.setIconImage(img)
            self.setDirty(True)
            self.__undoStack.push(cmd)
            cmd.setAfterImage(self.__image)
    
    def editSelectAll(self):
        """
        Public slot to select the complete image.
        """
        self.__unMark()
        
        self.__startPos = QPoint(0, 0)
        self.__endPos = QPoint(self.rect().bottomRight())
        self.__markImage.fill(self.MarkColor.rgba())
        self.__selRect = self.__image.rect()
        self.__selectionAvailable = True
        self.selectionAvailable.emit(True)
        
        self.update()
    
    def editClear(self):
        """
        Public slot to clear the image.
        """
        self.__unMark()
        
        cmd = IconEditCommand(self, self.tr("Clear Image"), self.__image)
        self.__image.fill(qRgba(0, 0, 0, 0))
        self.update()
        self.setDirty(True)
        self.__undoStack.push(cmd)
        cmd.setAfterImage(self.__image)
    
    def editResize(self):
        """
        Public slot to resize the image.
        """
        from .IconSizeDialog import IconSizeDialog
        dlg = IconSizeDialog(self.__image.width(), self.__image.height())
        res = dlg.exec_()
        if res == QDialog.Accepted:
            newWidth, newHeight = dlg.getData()
            if newWidth != self.__image.width() or \
                    newHeight != self.__image.height():
                cmd = IconEditCommand(self, self.tr("Resize Image"),
                                      self.__image)
                img = self.__image.scaled(
                    newWidth, newHeight, Qt.IgnoreAspectRatio,
                    Qt.SmoothTransformation)
                self.setIconImage(img)
                self.setDirty(True)
                self.__undoStack.push(cmd)
                cmd.setAfterImage(self.__image)
    
    def editNew(self):
        """
        Public slot to generate a new, empty image.
        """
        from .IconSizeDialog import IconSizeDialog
        dlg = IconSizeDialog(self.__image.width(), self.__image.height())
        res = dlg.exec_()
        if res == QDialog.Accepted:
            width, height = dlg.getData()
            img = QImage(width, height, QImage.Format_ARGB32)
            img.fill(qRgba(0, 0, 0, 0))
            self.setIconImage(img)
    
    def grayScale(self):
        """
        Public slot to convert the image to gray preserving transparency.
        """
        cmd = IconEditCommand(self, self.tr("Convert to Grayscale"),
                              self.__image)
        for x in range(self.__image.width()):
            for y in range(self.__image.height()):
                col = self.__image.pixel(x, y)
                if col != qRgba(0, 0, 0, 0):
                    gray = qGray(col)
                    self.__image.setPixel(
                        x, y, qRgba(gray, gray, gray, qAlpha(col)))
        self.update()
        self.setDirty(True)
        self.__undoStack.push(cmd)
        cmd.setAfterImage(self.__image)

    def editUndo(self):
        """
        Public slot to perform an undo operation.
        """
        if self.__undoStack.canUndo():
            self.__undoStack.undo()
    
    def editRedo(self):
        """
        Public slot to perform a redo operation.
        """
        if self.__undoStack.canRedo():
            self.__undoStack.redo()
    
    def canUndo(self):
        """
        Public method to return the undo status.
        
        @return flag indicating the availability of undo (boolean)
        """
        return self.__undoStack.canUndo()
    
    def canRedo(self):
        """
        Public method to return the redo status.
        
        @return flag indicating the availability of redo (boolean)
        """
        return self.__undoStack.canRedo()
    
    def __cleanChanged(self, clean):
        """
        Private slot to handle the undo stack clean state change.
        
        @param clean flag indicating the clean state (boolean)
        """
        self.setDirty(not clean)
    
    def shutdown(self):
        """
        Public slot to perform some shutdown actions.
        """
        self.__undoStack.canRedoChanged.disconnect(self.canRedoChanged)
        self.__undoStack.canUndoChanged.disconnect(self.canUndoChanged)
        self.__undoStack.cleanChanged.disconnect(self.__cleanChanged)
    
    def isSelectionAvailable(self):
        """
        Public method to check the availability of a selection.
        
        @return flag indicating the availability of a selection (boolean)
        """
        return self.__selectionAvailable
class TextButton(DemoItem):
    BUTTON_WIDTH = 180
    BUTTON_HEIGHT = 19

    LEFT, RIGHT = range(2)

    SIDEBAR, PANEL, UP, DOWN = range(4)

    ON, OFF, HIGHLIGHT, DISABLED = range(4)

    def __init__(self,
                 text,
                 align=LEFT,
                 userCode=0,
                 parent=None,
                 type=SIDEBAR):
        super(TextButton, self).__init__(parent)

        # Prevent a circular import.
        from menumanager import MenuManager
        self._menu_manager = MenuManager.instance()

        self.menuString = text
        self.buttonLabel = text
        self.alignment = align
        self.buttonType = type
        self.userCode = userCode
        self.scanAnim = None
        self.bgOn = None
        self.bgOff = None
        self.bgHighlight = None
        self.bgDisabled = None
        self.state = TextButton.OFF

        self.setAcceptHoverEvents(True)
        self.setCursor(Qt.PointingHandCursor)

        # Calculate the button size.
        if type in (TextButton.SIDEBAR, TextButton.PANEL):
            self.logicalSize = QSize(TextButton.BUTTON_WIDTH,
                                     TextButton.BUTTON_HEIGHT)
        else:
            self.logicalSize = QSize(int((TextButton.BUTTON_WIDTH / 2.0) - 5),
                                     int(TextButton.BUTTON_HEIGHT * 1.5))

        self._prepared = False

    def setMenuString(self, menu):
        self.menuString = menu

    def prepare(self):
        if not self._prepared:
            self.setupHoverText()
            self.setupScanItem()
            self.setupButtonBg()
            self._prepared = True

    def boundingRect(self):
        return QRectF(0, 0, self.logicalSize.width(),
                      self.logicalSize.height())

    def setupHoverText(self):
        if not self.buttonLabel:
            return

        textItem = DemoTextItem(self.buttonLabel, Colors.buttonFont(),
                                Colors.buttonText, -1, self)
        textItem.setZValue(self.zValue() + 2)
        textItem.setPos(16, 0)

    def setupScanItem(self):
        if Colors.useButtonBalls:
            scanItem = ScanItem(self)
            scanItem.setZValue(self.zValue() + 1)

            self.scanAnim = DemoItemAnimation(scanItem)

            x = 1.0
            y = 1.5
            stop = TextButton.BUTTON_WIDTH - scanItem.boundingRect().width(
            ) - x
            if self.alignment == TextButton.LEFT:
                self.scanAnim.setDuration(2500)
                self.scanAnim.setKeyValueAt(0.0, QPointF(x, y))
                self.scanAnim.setKeyValueAt(0.5, QPointF(x, y))
                self.scanAnim.setKeyValueAt(0.7, QPointF(stop, y))
                self.scanAnim.setKeyValueAt(1.0, QPointF(x, y))
                scanItem.setPos(QPointF(x, y))
            else:
                self.scanAnim.setKeyValueAt(0.0, QPointF(stop, y))
                self.scanAnim.setKeyValueAt(0.5, QPointF(x, y))
                self.scanAnim.setKeyValueAt(1.0, QPointF(stop, y))
                scanItem.setPos(QPointF(stop, y))

    def setState(self, state):
        self.state = state
        self.bgOn.setRecursiveVisible(state == TextButton.ON)
        self.bgOff.setRecursiveVisible(state == TextButton.OFF)
        self.bgHighlight.setRecursiveVisible(state == TextButton.HIGHLIGHT)
        self.bgDisabled.setRecursiveVisible(state == TextButton.DISABLED)
        if state == TextButton.DISABLED:
            self.setCursor(Qt.ArrowCursor)
        else:
            self.setCursor(Qt.PointingHandCursor)

    def setupButtonBg(self):
        self.bgOn = ButtonBackground(self.buttonType, True, True,
                                     self.logicalSize, self)
        self.bgOff = ButtonBackground(self.buttonType, False, False,
                                      self.logicalSize, self)
        self.bgHighlight = ButtonBackground(self.buttonType, True, False,
                                            self.logicalSize, self)
        self.bgDisabled = ButtonBackground(self.buttonType, True, True,
                                           self.logicalSize, self)
        self.setState(TextButton.OFF)

    def hoverEnterEvent(self, event):
        if not self.isEnabled() or self.state == TextButton.DISABLED:
            return

        if self.state == TextButton.OFF:
            self.setState(TextButton.HIGHLIGHT)

            if Colors.noAnimations and Colors.useButtonBalls:
                # Wait a bit in the beginning to enhance the effect.  We have
                # to do this here so that the adaption can be dynamic.
                self.scanAnim.setDuration(1000)
                self.scanAnim.setKeyValueAt(0.2, self.scanAnim.posAt(0))

            if (self._menu_manager.window.fpsMedian > 10 or Colors.noAdapt
                    or Colors.noTimerUpdate):
                if Colors.useButtonBalls:
                    self.scanAnim.play(True, True)

    def hoverLeaveEvent(self, event):
        if self.state == TextButton.DISABLED:
            return

        self.setState(TextButton.OFF)

        if Colors.noAnimations and Colors.useButtonBalls:
            self.scanAnim.stop()

    def mousePressEvent(self, event):
        if self.state == TextButton.DISABLED:
            return

        if self.state == TextButton.HIGHLIGHT or self.state == TextButton.OFF:
            self.setState(TextButton.ON)

    def mouseReleaseEvent(self, event):
        if self.state == TextButton.ON:
            self.setState(TextButton.OFF)
            if self.isEnabled() and self.boundingRect().contains(event.pos()):
                self._menu_manager.itemSelected(self.userCode, self.menuString)

    def animationStarted(self, _):
        if self.state == TextButton.DISABLED:
            return

        self.setState(TextButton.OFF)
Пример #60
0
class nuser(QMainWindow):
    def __init__(self):
        super(nuser,self).__init__()
        loadUi('nUser.ui',self)
        self.tabWidget.tabBar().setVisible(False)
        self.handleButtons()
        style = open('themes/darkblue.css' , 'r')
        style = style.read()
        self.setStyleSheet(style)
        self.camOn=False
        self.video_size=QSize(601,341)
        self.image=None
    
    def handleButtons(self):
        self.homePage.clicked.connect(self.openHome)
        self.liveFPage.clicked.connect(self.openLFoot)
        self.oldFPage.clicked.connect(self.openOFoot)
        self.pathPage.clicked.connect(self.openTracker)
        self.logoutPage.clicked.connect(self.openLogout)
        self.darkO.clicked.connect(self.Dark_Orange_Theme)
        self.darkB.clicked.connect(self.Dark_Blue_Theme)
        self.darkG.clicked.connect(self.Dark_Gray_Theme)
        self.qdark.clicked.connect(self.QDark_Theme)
        self.showLive.clicked.connect(self.start_cam)
        self.pauseLive.clicked.connect(self.stop_cam)
        self.yes.clicked.connect(self.logoutYes)
        self.no.clicked.connect(self.openHome)
        self.showVid.clicked.connect(self.callPlayer)
        self.trackStart.clicked.connect(self.callTracker) #*
        self.showPlot.clicked.connect(self.displayOutput) #*
        self.trackVideoShow.clicked.connect(self.playTrackedVid) #*
        
    def openHome(self):
        self.stop_cam()
        self.tabWidget.setCurrentIndex(0)
    def openLFoot(self):
        self.tabWidget.setCurrentIndex(1)
    def openOFoot(self):
        self.stop_cam()
        self.show_Old_Vid()
        
    def openTracker(self):
        self.stop_cam()
        self.tabWidget.setCurrentIndex(3)
        conn = sqlite3.connect('capstoneSQLDB2.db')
        cur = conn.cursor()
        cur.execute("SELECT NAME FROM BLUEPRINTS")
        items=cur.fetchall()
        l=["BLUEPRINT"]
        self.blueprintSelect.clear() 
        for i in items:
            l.append(i[0])
        self.blueprintSelect.addItems(l)
        cur.close()

        
    def openLogout(self):
        self.stop_cam()
        self.tabWidget.setCurrentIndex(4)
    
    def show_Old_Vid(self):
        self.tabWidget.setCurrentIndex(2)
        conn = sqlite3.connect('capstoneSQLDB2.db')
        cur = conn.cursor()
        data=cur.execute('SELECT Code,Date,Time,Cam FROM VidHistory')
        if data:
            self.vidTable.setRowCount(0)
            self.vidTable.insertRow(0)
            for row , form in enumerate(data):
                for column , item in enumerate(form) :
                    self.vidTable.setItem(row , column , QTableWidgetItem(str(item)))
                    column += 1

                row_position = self.vidTable.rowCount()
                self.vidTable.insertRow(row_position)
        cur.close()

    def Dark_Blue_Theme(self):
        style = open('themes/darkblue.css' , 'r')
        style = style.read()
        self.setStyleSheet(style)

    def Dark_Gray_Theme(self):
        style = open('themes/darkgray.css' , 'r')
        style = style.read()
        self.setStyleSheet(style)

    def Dark_Orange_Theme(self):
        style = open('themes/darkorange.css' , 'r')
        style = style.read()
        self.setStyleSheet(style)

    def QDark_Theme(self):
        style = open('themes/qdark.css' , 'r')
        style = style.read()
        self.setStyleSheet(style)
    
    def start_cam(self):
        if self.camBox.currentText() !="SELECT":
            self.camOn=True
            if self.camBox.currentText() =="WEB CAM (TEMP)":
                self.capture=cv2.VideoCapture(0,cv2.CAP_DSHOW) 
            self.Vname="outputVideo\\"+str(time.time()).split(".")[0]+".mp4"
 
            i=0
            while i<22000:
                i+=1
            self.capture.set(cv2.CAP_PROP_FRAME_WIDTH,self.video_size.width())
            self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT,self.video_size.height())
            self.rec=cv2.VideoWriter(self.Vname,cv2.VideoWriter_fourcc(*'MJPG'),10, (self.video_size.width(),self.video_size.height())) 
            self.timer =QTimer(self)
            self.timer.timeout.connect(self.update_frame)
            self.timer.start(0.005)

    def update_frame(self):
        ret,frame=self.capture.read()
        if ret==True:
            self.rec.write(frame) 
        self.image=cv2.cvtColor(frame,1)
        qformat = QImage.Format_Indexed8
        if len(self.image.shape) == 3:  
            if (self.image.shape[2]) == 4:
                qformat = QImage.Format_RGBA8888
            else:
                qformat = QImage.Format_RGB888
        
        img = QImage(self.image, self.image.shape[1], self.image.shape[0],
                     self.image.strides[0], qformat)
        img = img.rgbSwapped()
        self.imgLabel.setPixmap(QPixmap.fromImage(img))
        self.imgLabel.setScaledContents(True)
        conn = sqlite3.connect('capstoneSQLDB2.db')
        cur = conn.cursor()
        loc="D:\\Projects\\CAPSTONE\\"+self.Vname
        cur.execute('SELECT Loc FROM VidHistory where loc=?',(loc,))
        temp=cur.fetchone()
        if temp is None:
            cur.execute('SELECT code FROM LCODE')
            code=cur.fetchone()
            cur.execute('UPDATE LCODE SET code = code + 1 WHERE code = ?',(code[0],))
            conn.commit()
            t=datetime.now()
            date=str(t.date())
            time=str(t.time())
            cur.execute('''INSERT INTO VidHistory (Code, Date, Time,Cam,Loc )
                        VALUES (?,?,?,?,?)''', (code[0],date,time,1,loc))
            conn.commit()
        cur.close()

    def stop_cam(self):
        if self.camOn:
            self.capture.release()
            self.rec.release()
            self.camOn=False
            self.timer.stop()
            img=cv2.imread("reset.png")
            qformat = QImage.Format_RGB888
            img = QImage(img, img.shape[1], img.shape[0],img.strides[0], qformat)
            img = img.rgbSwapped()
            self.imgLabel.setPixmap(QPixmap.fromImage(img))
            self.imgLabel.setScaledContents(True)
            self.image=None
    
    def logoutYes(self):        
        from login import Login        
        self.window2 = Login()
        self.close()
        self.window2.show()
        
    def callPlayer(self):
        conn = sqlite3.connect('capstoneSQLDB2.db')
        cur = conn.cursor()
        code=self.lineEdit.text()
        cur.execute('SELECT Loc FROM VidHistory WHERE Code = ? ', (code,))
        loc =cur.fetchone()
        if loc is not None:
            self.window3 = Player(loc[0])
            self.window3.resize(640,480)
            self.window3.show()
        cur.close()
    
    def enableItems(self):
        self.listView_3.setEnabled(True)
        self.blueprintSelect.setEnabled(True)
        self.trackVideoShow.setEnabled(True)
        self.trackIDs.setEnabled(True)
        self.idList.setEnabled(True)
        self.showPlot.setEnabled(True)
    
    def disableItems(self):
        self.listView_3.setEnabled(False)
        self.blueprintSelect.setEnabled(False)
        self.trackVideoShow.setEnabled(False)
        self.trackIDs.setEnabled(False)
        self.idList.setEnabled(False)
        self.showPlot.setEnabled(False)
        pixmap = QPixmap('D:/Projects/CAPSTONE/reset.PNG')
        self.outputImg.setPixmap(pixmap)
        
# =============================================================================
#         img=cv2.imread("reset.png")
#         qformat = QImage.Format_RGB888
#         img = QImage(img, img.shape[1], img.shape[0],img.strides[0], qformat)
#         img = img.rgbSwapped()
#         self.outputImg.setPixmap(QPixmap.fromImage(img))
#         self.outputImg.setScaledContents(True)
# =============================================================================
    
    def callTracker(self):
        cam=self.camCode.text()
        vid=self.vidCode.text()
        if len(cam)==0 or len(vid)==0:
            self.trackStatus.setText("ENTER ALL MANDATORY FIELDS")
            self.disableItems()
        else:
            conn = sqlite3.connect('capstoneSQLDB2.db')
            cur = conn.cursor()
            cur.execute('SELECT loc FROM VidHistory WHERE Code=? AND Cam=?',(vid,cam,))
            loc=cur.fetchone()
            cur.close()
            if loc is None:
                self.trackStatus.setText("ENTER VALID CODE COMBINATION")
                self.disableItems()
            else:
                self.trackStatus.setText("TRACKING IN PROGRESS")
                yoloTracker(YOLO(),loc[0],"D:/Projects/CAPSTONE/outputVid.mp4")
                self.trackStatus.setText("TRACKING SUCCESFULLY DONE")
                f=open("D:/Projects/CAPSTONE/cordinates.pkl",'rb')
                d=pickle.load(f)
                l=list(d.keys())
                l.sort()
                self.idList.setRowCount(len(l))
                for i in range(len(l)):
                    self.idList.setItem(i,0, QTableWidgetItem(str(l[i])))
                self.enableItems()
                self.playTrackedVid()
            
    
    def displayOutput(self):
        blueprint=self.blueprintSelect.currentText()
        if blueprint=='BLUEPRINT':
            self.trackStatus.setText("SELECT A BLUEPRINT")
        else:
            ids=self.trackIDs.text()
            if len(ids)==0:
                self.trackStatus.setText("ENTER PEOPLE IDS FOR TRACKING")
            else:
                f=open("D:/Projects/CAPSTONE/cordinates.pkl",'rb')
                d=pickle.load(f)
                l=list(d.keys())
                l2=[]
                c=True
                ids=ids.split(",")
                for i in ids:
                    if i.isdigit() and (int(i) in l):
                        l2.append(int(i))
                    else:
                        c=False
                        break
                if c:
                    self.trackStatus.setText("")
                    conn = sqlite3.connect('capstoneSQLDB2.db')
                    cur = conn.cursor()
                    cur.execute('SELECT loc FROM blueprints WHERE name=?',(blueprint,))
                    loc=cur.fetchone()
                    cur.close()
                    plot_trackers(loc[0],l2)
                    pixmap = QPixmap('D:/Projects/CAPSTONE/plotted.jpg')
                    pixmap=pixmap.scaled(self.outputImg.size())
                    self.outputImg.setPixmap(pixmap)
                else:
                    self.trackStatus.setText("ENTER VALID IDs!")
                

    def playTrackedVid(self):
        #loc="D:/Projects/CAPSTONE/output3_yolov3S.mp4" ##FOR TESTING ONLY
        loc="D:/Projects/CAPSTONE/outputVid.mp4"
        self.window4=Player(loc)
        self.window4.resize(1280,960)
        self.window4.show()