震惊!!一男子用尽了各种方式都搜不到这个资源,于是他竟然将手伸向了......!?pyqt pyside 随窗口自适应、可缩放、拖动QLabel
需求场景
在小才玩yolo目标检测时,总是使用PyQt或PySide来展示识别结果。一开始小才使用自带的QLabel来显示图像,但是发现显示的比较呆,原QLabel显示有以下特点:
1. 窗口大小改变时,QLabel图片显示不会跟随窗口变化。
2. 图片不能够缩放,拖动。
于是小才就去搜索学习,许久后,总结加拓展自己实现了一个 随窗口自适应、可缩放、拖动、画框图片的自定义QLabel类(嘲笑请偷偷)。
实现效果:
实现
不废话直接上代码
import cv2
from PySide6.QtCore import QPoint, Qt, QRect
from PySide6.QtCore import Signal
from PySide6.QtGui import QPainter, QPixmap, QImage, QMouseEvent, QKeyEvent, QPen, QColor
from PySide6.QtWidgets import QLabel
from check_except import check_except
class ImageLabel(QLabel):
clicked_signal = Signal()
# clicked_signal = pyqtSignal()# pyqt5
# 当画框完成后向外输出信号 index: 0原图 1 裁剪后的图片 [2-5裁剪范围]2:x1 3:y1 4:width 5:height
bbox_down_signal = Signal(list)
def __init__(self, parent, scale_flag=True, move_flag=True, is_original=False, draw_bbox_flag=False):
super(ImageLabel, self).__init__(parent)
'''
@param parent: 父控件
@param scale_flag: 是否可以缩放
@param move_flag: 是否可以移动
@param is_original: 是否为原图
@param draw_bbox_flag: 是否开启画框
'''
self.scaled_img = None # QPixmap
self.cv_image = None # draw_bbox_flag=True 时,为cv2原图
self.point = None # 图片位置
self.start_pos = None # 鼠标按下时的位置
self.end_pos = None # 鼠标松开时的位置
self.right_press = False # 右键正在按压
self.left_press = False # 左键正在按压
self.is_original = is_original # 是否为原图显示
self.x_scale = 1
self.y_scale = 1
# 画框起始点
self.bbox_start_point = None
# 画框结束点
self.bbox_end_point = None
# 是否可以缩放 scale_flag True 可以缩放
self.scale_flag = scale_flag
# 是否可以移动
self.move_flag = move_flag
# 是否开启画框
self.draw_bbox_flag = draw_bbox_flag
@check_except()
def clear_image(self):
self.set_q_pixmap(QPixmap(None))
@check_except()
def to_q_image(self, image):
label_size = (self.height(), self.width())
height, width = image.shape[:2]
if not self.is_original:
n_height = label_size[0]
n_width = int((width / height) * label_size[0])
if n_width > label_size[1]:
n_width = label_size[1]
n_height = int((height / width) * label_size[1])
image = cv2.resize(image, (n_width, n_height))
else:
n_height = height
n_width = width
channels = len(image.shape)
if channels == 2:
bytes_per_line = n_width
q_image = QImage(image.data, n_width, n_height, bytes_per_line, QImage.Format_Grayscale8)
else:
bytes_per_line = 3 * n_width
q_image = QImage(image.data, n_width, n_height, bytes_per_line, QImage.Format_RGB888).rgbSwapped()
return q_image
@check_except()
def set_cv_image(self, img):
self.set_q_image(self.to_q_image(img))
@check_except()
def set_q_image(self, img):
self.set_q_pixmap(QPixmap.fromImage(img))
@check_except()
def set_q_pixmap(self, q_pixmap):
self.scaled_img = q_pixmap
if self.point is None:
self._reset_scale()
self._reset_point()
self.update()
def _reset_scale(self):
if self.scaled_img:
self.x_scale = self.width() / self.scaled_img.width()
b = self.scaled_img.height() / self.scaled_img.width()
n_height = self.width() * b
if n_height > self.height():
self.y_scale = self.height() / self.scaled_img.height()
self.x_scale = self.y_scale
else:
self.y_scale = self.x_scale
self.reset_bbox()
def _reset_point(self):
if self.scaled_img:
width, height = self.scaled_img.width(), self.scaled_img.height()
p1 = 0
p2 = 0
if self.width() > width * self.x_scale:
p1 = int((self.width() - width * self.x_scale) / 2)
if self.height() > height * self.y_scale:
p2 = int((self.height() - height * self.x_scale) / 2)
self.point = QPoint(int(p1 / self.x_scale) if self.x_scale != 0 else 0,
int(p2 / self.y_scale) if self.y_scale != 0 else 0)
# 重置框
self.reset_bbox()
def reset_bbox(self):
self.bbox_start_point = None
self.bbox_end_point = None
@check_except()
def paintEvent(self, e):
if self.scaled_img:
painter = QPainter()
painter.begin(self)
painter.scale(self.x_scale, self.y_scale)
painter.drawPixmap(self.point, self.scaled_img) # 此函数中还会用scale对point进行处理
if self.draw_bbox_flag and self.bbox_start_point is not None and self.bbox_end_point is not None:
lw = max(round(sum(self.cv_image.shape) / 2 * 0.003)
标签:scale,img,pyside,缩放,......,height,width,image,self
From: https://blog.csdn.net/hz1hz/article/details/142124134