получить реальный размер QPixmap в Qlabel - PullRequest
0 голосов
/ 23 сентября 2019

Есть ли какой-нибудь простой способ в PyQt5 получить реальные размеры растрового изображения, отображаемого в QLabel?Я пытаюсь выделить часть изображения резинкой.Но я не могу найти способ ограничить резинку только растровым изображением.QLabel.pixmap (). Rect () возвращает размеры всего QLabel, а не только растровое изображение.Проблема возникает, когда растровое изображение масштабируется и на боковых сторонах изображения появляются полосы.

Пример изображения

Пример изображения 2

Размещенные мной сообщения говорят сами за себя.Я не хочу, чтобы резиновая полоса могла выходить за пределы изображения с белыми полосами.

class ResizableRubberBand(QWidget):

    def __init__(self, parent=None):
        super(ResizableRubberBand, self).__init__(parent)

        self.aspect_ratio = None

        self.setWindowFlags(Qt.SubWindow)
        self.layout = QHBoxLayout(self)
        self.layout.setContentsMargins(0, 0, 0, 0)

        self.grip1 = QSizeGrip(self)
        self.grip2 = QSizeGrip(self)
        self.layout.addWidget(self.grip1, 0, Qt.AlignLeft | Qt.AlignTop)
        self.layout.addWidget(self.grip2, 0, Qt.AlignRight | Qt.AlignBottom)

        self.rubberband = QRubberBand(QRubberBand.Rectangle, self)
        self.rubberband.setStyle(QStyleFactory.create("Fusion"))
        self.rubberband.move(0, 0)
        self.rubberband.show()
        self.show()


class ResizablePixmap(QLabel):

    def __init__(self, bytes_image):

        QLabel.__init__(self)
        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.setAlignment(Qt.AlignVCenter | Qt.AlignHCenter)
        self.setStyleSheet("background-color:#ffffff;")

        self.update_pixmap(bytes_image)

    def resizeEvent(self, event):

        if event:
            x = event.size().width()
            y = event.size().height()
        else:
            x = self.width()
            y = self.height()

        self.current_pixmap = self._bytes2pixmap(self.bytes_image_edit)
        self.setPixmap(self.current_pixmap.scaled(x, y, Qt.KeepAspectRatio))
        self.resize(x, y)

    def update_pixmap(self, bytes_image):

        self.bytes_image_edit = bytes_image

        self.current_pixmap = self._bytes2pixmap(bytes_image)
        self.setPixmap(self.current_pixmap)

        self.resizeEvent(None)

    @staticmethod
    def _bytes2pixmap(raw_image):

        image = QImage()
        image.loadFromData(raw_image)
        return QPixmap(image)

    @staticmethod
    def _pixmap2bytes(pixmap):

        byte_array = QByteArray()
        buffer = QBuffer(byte_array)
        buffer.open(QIODevice.WriteOnly)
        pixmap.save(buffer, 'PNG')
        return byte_array.data()

    @property
    def image_dims(self):
        return self.width(), self.height()

    def force_resize(self, qsize):
        self.resizeEvent(QResizeEvent(qsize, qsize))


class SelectablePixmap(ResizablePixmap):

    def __init__(self, bytes_image):

        super().__init__(bytes_image)

        self.currentQRubberBand = None
        self.move_rubber_band = False
        self.rubber_band_offset = None

    def cancel_selection(self):
        self.currentQRubberBand.hide()
        self.currentQRubberBand.deleteLater()
        self.currentQRubberBand = None
        self.selectionActive.emit(False)

    def mousePressEvent(self, eventQMouseEvent):

        if not self.currentQRubberBand:
            self.currentQRubberBand = ResizableRubberBand(self)
            self.selectionActive.emit(True)

        if self.currentQRubberBand.geometry().contains(eventQMouseEvent.pos()):
            self.move_rubber_band = True
            self.rubber_band_offset = (eventQMouseEvent.pos() -
                                       self.currentQRubberBand.pos())
        else:
            self.originQPoint = eventQMouseEvent.pos()
            if self.pixmap().rect().contains(self.originQPoint):
                self.currentQRubberBand.setGeometry(QRect(self.originQPoint,
                                                          QSize()))
                self.currentQRubberBand.show()

    def mouseMoveEvent(self, eventQMouseEvent):

        if self.move_rubber_band:
            pos = eventQMouseEvent.pos() - self.rubber_band_offset
            if self.pixmap().rect().contains(pos):
                self.currentQRubberBand.move(pos)
        else:
            rect = QRect(self.originQPoint, eventQMouseEvent.pos())
            self.currentQRubberBand.setGeometry(rect.normalized())

    def mouseReleaseEvent(self, eventQMouseEvent):

        if self.move_rubber_band:
            self.move_rubber_band = False

Ответы [ 2 ]

0 голосов
/ 23 сентября 2019

«Простой» ответ на ваш вопрос заключается в том, что вы можете получить реальную геометрию карты QPixmap, переместив ее QRect.Поскольку вы используете выравнивание по центру, это очень просто:

pixmap_rect = self.pixmap.rect()
pixmap_rect.moveCenter(self.rect().center())

К сожалению, вы не можете просто использовать этот прямоугольник в своей реализации, в основном потому, что вы на самом деле не используете QRubberBand.
Концепциядочерняя резиновая полоса, использующая размерные ручки для изменения размера, является умной, но имеет множество ограничений.
Хотя QSizeGrips упрощает изменение размера, их поведение не может быть легко «ограничено»: вы, вероятно, в конечном итоге попытаетесь переопределить resize и resizeEvent (риск рекурсии), возможно, с некоторой хитрой и запутанной проверкой событий мыши.Кроме того, вы никогда не сможете изменить размер этой «виртуальной» резиновой ленты до размера, меньшего суммы размеров QSizeGrips, или до «отрицательного» выбора.
Кроме того, в вашем коде вы никогда не измените размер фактического QRubberBand.геометрия (но это можно сделать в пределах ResizableRubberBand.resizeEvent()).

Наконец, даже если вы не реализовали изменение размера выделения после изменения размера изображения, у вас будет много проблем, если вы это сделаете (в основном из-за того, чтоиз вышеупомянутых ограничений минимального размера).


Я думаю, что лучшим решением будет использование простого QRubberBand и реализация его взаимодействия непосредственно из виджета, который его использует.Это позволяет лучше контролировать его, а также позволяет полностью изменять размеры (не только верхний левый и нижний правый углы).
Я немного изменил код базового класса, поскольку вам следует избегать любого изменения размера в пределах resizeEvent() (даже еслив вашем случае это ничего не дало, так как аргумент размера resize() был таким же) и сделал ненужные вызовы _bytes2pixmap.

class ResizablePixmap(QLabel):
    def __init__(self, bytes_image):
        QLabel.__init__(self)
        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.setAlignment(Qt.AlignCenter)
        self.setStyleSheet("background-color: #ffffff;")

        self.update_pixmap(bytes_image)

    def update_pixmap(self, bytes_image):
        self.bytes_image_edit = bytes_image
        self.current_pixmap = self._bytes2pixmap(bytes_image)

    def scale(self, fromResize=False):
        # use a single central method for scaling; there's no need to call it upon
        # creation and also resize() won't work anyway in a layout
        self.setPixmap(self.current_pixmap.scaled(self.width(), self.height(), 
            Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def resizeEvent(self, event):
        super(ResizablePixmap, self).resizeEvent(event)
        self.scale(True)

    @staticmethod
    def _bytes2pixmap(raw_image):
        image = QImage()
        image.loadFromData(raw_image)
        return QPixmap(image)


class SelectablePixmap(ResizablePixmap):
    selectionActive = pyqtSignal(bool)

    def __init__(self, bytes_image):
        super().__init__(bytes_image)

        # activate mouse tracking to change cursor on rubberband hover
        self.setMouseTracking(True)
        self.currentQRubberBand = None
        self.rubber_band_offset = None
        self.moveDirection = 0

    def create_selection(self, pos):
        if self.currentQRubberBand:
            self.cancel_selection()
        self.currentQRubberBand = QRubberBand(QRubberBand.Rectangle, self)
        self.currentQRubberBand.setStyle(QStyleFactory.create("Fusion"))
        self.currentQRubberBand.setGeometry(pos.x(), pos.y(), 1, 1)
        self.currentQRubberBand.show()
        self.originQPoint = pos
        self.currentQRubberBand.installEventFilter(self)

    def cancel_selection(self):
        self.currentQRubberBand.hide()
        self.currentQRubberBand.deleteLater()
        self.currentQRubberBand = None
        self.originQPoint = None
        self.selectionActive.emit(False)

    def scale(self, fromResize=False):
        if fromResize and self.currentQRubberBand:
            # keep data for rubber resizing, before scaling
            oldPixmapRect = self.pixmap().rect()
            oldOrigin = self.currentQRubberBand.pos() - self.pixmapRect.topLeft()
        super(SelectablePixmap, self).scale()

        # assuming that you always align the image in the center, get the current
        # pixmap rect and move the rectangle center to the current geometry
        self.pixmapRect = self.pixmap().rect()
        self.pixmapRect.moveCenter(self.rect().center())
        if fromResize and self.currentQRubberBand:
            # find the new size ratio based on the previous
            xRatio = self.pixmapRect.width() / oldPixmapRect.width()
            yRatio = self.pixmapRect.height() / oldPixmapRect.height()
            # create a new geometry using 0-rounding for improved accuracy
            self.currentQRubberBand.setGeometry(
                round(oldOrigin.x() * xRatio, 0) + self.pixmapRect.x(), 
                round(oldOrigin.y() * yRatio + self.pixmapRect.y(), 0), 
                round(self.currentQRubberBand.width() * xRatio, 0), 
                round(self.currentQRubberBand.height() * yRatio, 0))

    def updateMargins(self):
        # whenever the rubber rectangle geometry changes, create virtual
        # rectangles for corners and sides to ease up mouse event checking
        rect = self.currentQRubberBand.geometry()
        self.rubberTopLeft = QRect(rect.topLeft(), QSize(8, 8))
        self.rubberTopRight = QRect(rect.topRight(), QSize(-8, 8)).normalized()
        self.rubberBottomRight = QRect(rect.bottomRight(), QSize(-8, -8)).normalized()
        self.rubberBottomLeft = QRect(rect.bottomLeft(), QSize(8, -8)).normalized()
        self.rubberLeft = QRect(self.rubberTopLeft.bottomLeft(), self.rubberBottomLeft.topRight())
        self.rubberTop = QRect(self.rubberTopLeft.topRight(), self.rubberTopRight.bottomLeft())
        self.rubberRight = QRect(self.rubberTopRight.bottomLeft(), self.rubberBottomRight.topRight())
        self.rubberBottom = QRect(self.rubberBottomLeft.topRight(), self.rubberBottomRight.bottomLeft())
        self.rubberInnerRect = QRect(self.rubberTop.bottomLeft(), self.rubberBottom.topRight())

    def eventFilter(self, source, event):
        if event.type() in (QEvent.Resize, QEvent.Move):
            self.updateMargins()
        return super(SelectablePixmap, self).eventFilter(source, event)

    def mousePressEvent(self, event):
        pos = event.pos()
        if not self.currentQRubberBand or not pos in self.currentQRubberBand.geometry():
            if pos not in self.pixmapRect:
                self.originQPoint = None
                return
            self.create_selection(pos)
        elif pos in self.rubberTopLeft:
            self.originQPoint = self.currentQRubberBand.geometry().bottomRight()
        elif pos in self.rubberTopRight:
            self.originQPoint = self.currentQRubberBand.geometry().bottomLeft()
        elif pos in self.rubberBottomRight:
            self.originQPoint = self.currentQRubberBand.geometry().topLeft()
        elif pos in self.rubberBottomLeft:
            self.originQPoint = self.currentQRubberBand.geometry().topRight()
        elif pos in self.rubberTop:
            self.originQPoint = self.currentQRubberBand.geometry().bottomLeft()
            self.moveDirection = Qt.Vertical
        elif pos in self.rubberBottom:
            self.originQPoint = self.currentQRubberBand.geometry().topLeft()
            self.moveDirection = Qt.Vertical
        elif pos in self.rubberLeft:
            self.originQPoint = self.currentQRubberBand.geometry().topRight()
            self.moveDirection = Qt.Horizontal
        elif pos in self.rubberRight:
            self.originQPoint = self.currentQRubberBand.geometry().topLeft()
            self.moveDirection = Qt.Horizontal
        else:
            self.rubber_band_offset = pos - self.currentQRubberBand.pos()

    def mouseMoveEvent(self, event):
        pos = event.pos()
        if event.buttons() == Qt.NoButton and self.currentQRubberBand:
            if pos in self.rubberTopLeft or pos in self.rubberBottomRight:
                self.setCursor(Qt.SizeFDiagCursor)
            elif pos in self.rubberTopRight or pos in self.rubberBottomLeft:
                self.setCursor(Qt.SizeBDiagCursor)
            elif pos in self.rubberLeft or pos in self.rubberRight:
                self.setCursor(Qt.SizeHorCursor)
            elif pos in self.rubberTop or pos in self.rubberBottom:
                self.setCursor(Qt.SizeVerCursor)
            elif pos in self.rubberInnerRect:
                self.setCursor(Qt.SizeAllCursor)
            else:
                self.unsetCursor()
        elif event.buttons():
            if self.rubber_band_offset:
                target = pos - self.rubber_band_offset
                rect = QRect(target, self.currentQRubberBand.size())
                # limit positioning of the selection to the image rectangle
                if rect.x() < self.pixmapRect.x():
                    rect.moveLeft(self.pixmapRect.x())
                elif rect.right() > self.pixmapRect.right():
                    rect.moveRight(self.pixmapRect.right())
                if rect.y() < self.pixmapRect.y():
                    rect.moveTop(self.pixmapRect.y())
                elif rect.bottom() > self.pixmapRect.bottom():
                    rect.moveBottom(self.pixmapRect.bottom())
                self.currentQRubberBand.setGeometry(rect)
            elif self.originQPoint:
                if self.moveDirection == Qt.Vertical:
                    # keep the X fixed to the current right, so that only the
                    # vertical position is changed
                    pos.setX(self.currentQRubberBand.geometry().right())
                else:
                    # limit the X to the pixmapRect extent
                    if pos.x() < self.pixmapRect.x():
                        pos.setX(self.pixmapRect.x())
                    elif pos.x() > self.pixmapRect.right():
                        pos.setX(self.pixmapRect.right())
                if self.moveDirection == Qt.Horizontal:
                    # same as before, but for the Y position
                    pos.setY(self.currentQRubberBand.geometry().bottom())
                else:
                    # limit the Y to the pixmapRect extent
                    if pos.y() < self.pixmapRect.y():
                        pos.setY(self.pixmapRect.y())
                    elif pos.y() > self.pixmapRect.bottom():
                        pos.setY(self.pixmapRect.bottom())
                rect = QRect(self.originQPoint, pos)
                self.currentQRubberBand.setGeometry(rect.normalized())

    def mouseReleaseEvent(self, event):
        self.rubber_band_offset = None
        self.originQPoint = None
        self.moveDirection = 0
0 голосов
/ 23 сентября 2019

Вы можете сохранить ширину и высоту изображения (до создания растрового изображения из байтов) в глобальной переменной, а затем использовать метод getter для доступа к нему извне класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...