[英]get real size of QPixmap in Qlabel
PyQt5 中是否有一些簡單的方法可以獲取 QLabel 中顯示的像素圖的真實尺寸? 我正在嘗試用橡皮筋 select 圖像的一部分。 但是我找不到將橡皮筋限制為像素圖的方法。 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
您可以將圖像的寬度和高度(在從字節創建像素圖之前)存儲到全局變量中,然后使用 getter 從 class 外部訪問它。
您的問題的“簡單”答案是您可以通過移動 QPixmap 的QRect
來獲得其實際幾何形狀。 由於您使用的是中心 alignment,因此非常簡單:
pixmap_rect = self.pixmap.rect()
pixmap_rect.moveCenter(self.rect().center())
不幸的是,你不能只在你的實現中使用那個矩形,主要是因為你並沒有真正使用 QRubberBand。
兒童橡皮筋的概念,使用大小夾來調整大小,很聰明,但有很多限制。
雖然 QSizeGrips 使調整大小變得更容易,但它們的行為不能輕易“限制”:您可能最終會嘗試重新實現resize
和resizeEvent
(冒着遞歸的風險),可能會進行一些棘手且復雜的鼠標事件檢查。 此外,您永遠無法將“虛擬”橡皮筋的大小調整為小於 QSizeGrips 大小的總和,也無法調整為“負”選擇。
此外,在您的代碼中,您永遠不會調整實際的 QRubberBand 幾何形狀(但這可以在ResizableRubberBand.resizeEvent()
中完成)。
最后,即使您沒有在調整圖像大小后實現選擇調整大小,如果您這樣做了,您也會遇到很多問題(主要是因為前面提到的最小尺寸限制)。
我認為更好的解決方案是使用簡單的 QRubberBand 並直接從使用它的小部件實現其交互。 這使您可以更好地控制它,還允許完整的調整大小功能(不僅是左上角和右下角)。
我稍微修改了您的基本 class 代碼,因為您應該避免在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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.