构建检测可视化界面

实现调用海康相机拍照、展示拍摄图片检测结果可视化界面。

目录

实现步骤


实现步骤:

相机调用(此处为海康相机)

功能:python调用相机获取设备、连接设备、获取实时流、点击拍照。

参考教程PyQt5打开海康工业相机

图片展示

功能:滚动框展示拍摄图片,时间从近到远排序,按钮点击图片放大展示在新QLabel中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from PyQt5.QtWidgets import QApplication, QWidget, QListWidget, QLabel, QListWidgetItem
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import pyqtSignal

# 自定义QListWidget类,实现上述功能
# 滚动框缩略图展示,点击放大功能
class ImageListWidget(QListWidget):
imageClicked = pyqtSignal(QPixmap) # 自定义信号,用于点击 QLabel 时发射信号

def __init__(self, parent=None):
super().__init__(parent)
# self.setFlow(QListWidget.LeftToRight) # 从左到右
self.setResizeMode(QListWidget.Adjust) # 自动调整大小
self.setViewMode(QListWidget.IconMode) # 以图标形式展示
# self.setIconSize(QSize(200, 200)) # 设置图标大小
# self.setSelectionMode(QListWidget.SingleSelection) # 设置单选模式
self.pixmaps = [] # 存储每个图片的pixmap

def add_image(self, pixmap, img_show_input):
# 方式1,以item图标展示
# item = QListWidgetItem()
# # item.setIcon(QIcon(pixmap)) # 以item图标展示
# self.addItem(item)

self.pixmaps.append(pixmap) # 存储当前图片的pixmap
# 方式二,以qlabel作为小部件添加到item中展示
item = QListWidgetItem()
label = QLabel() # 创建 QLabel
label.setPixmap(pixmap.scaled(img_show_input.size().width(), img_show_input.size().height()))
item.setSizeHint(img_show_input.size())
self.addItem(item)
self.setItemWidget(item, label) # 将 QLabel 设置为列表项的小部件

# label.mousePressEvent = lambda event, pixmap=pixmap: self.imageClicked.emit(pixmap) # 为 QLabel 添加点击事件处理函数
index = len(self.pixmaps) - 1
label.mousePressEvent = lambda event, index=index: self.imageClicked.emit(
self.pixmaps[index]) # 为 QLabel 添加点击事件处理函数


# 主窗口
class MainWindow(QWidget):
def __init__(self):
super().__init__()

# 创建一个滚动图片展示区域
self.image_list_widget = ImageListWidget(self)
# 方式1,设置位置和大小
self.image_list_widget.move(10, 100)
self.image_list_widget.setFixedSize(600, 600)
self.image_list_widget.setStyleSheet("background-color: white;")

# 创建用于显示放大图片的 QLabel
self.enlarged_image_label = QLabel(self)
self.enlarged_image_label.setGeometry(650, 100, 600, 600) # 方式2,设置位置和大小
self.enlarged_image_label.setStyleSheet("background-color: white;")

# 添加多张图片到滚动区域中
for i in range(10):
pixmap = QPixmap(f"tmp/{i+1}.jpg") # 从文件加载图片
self.image_list_widget.add_image(pixmap, self.enlarged_image_label)

# 连接点击信号到显示放大图片的函数
self.image_list_widget.imageClicked.connect(self.show_enlarged_image)

def show_enlarged_image(self, pixmap):
self.enlarged_image_label.setPixmap(pixmap) # 在另一个 QLabel 中显示放大的图片


if __name__ == '__main__':
app = QApplication([])
window = MainWindow()
window.resize(1200, 1200) # 设置窗口大小
window.show()
app.exec_()

图片效果呈现

功能:展示检测后画框图片,具有鼠标处放大、鼠标拖动等功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""
自定义CustomLabel类实现指定鼠标位置滚轮缩放,鼠标拖动
QLabel功能调试
"""
from PyQt5.QtGui import QImage, QPixmap, QIcon, QColor, QWheelEvent, QMouseEvent
from PyQt5.QtCore import pyqtSlot, QSize, Qt, pyqtSignal, QEvent
from PyQt5.QtWidgets import QApplication, QTextBrowser, QWidget, QLabel, QPushButton, QFileDialog, QFrame, QMessageBox, \
QListWidgetItem, QSizePolicy, QListWidget, QScrollArea
from PyQt5.Qt import QPainter, QPoint


class CustomLabel(QLabel):
def __init__(self, parent):
super().__init__(parent)
self.setMouseTracking(True)
self.img = None
self.scaled_img = None
self.start_pos = None
self.end_pos = None
self.left_click = False
self.wheel_flag = False

self.scale = 1
self.old_scale = 1
self.point = QPoint(0, 0) # 图片左上角位置
self.rb_point = QPoint(0, 0) # 图片左上角位置
self.old_rb_point = QPoint(0, 0)
self.x = -1
self.y = -1
self.new_height = -1
self.new_width = -1

def set_image(self, img):
# self.img = QPixmap(img_path)
self.img = img
width, height = self.img.width(), self.img.height()
self.img = self.img.scaled(width, height, Qt.KeepAspectRatio)
self.scaled_img = self.img

self.new_height = height
self.new_width = width
self.scale = 1

def paintEvent(self, e):
if self.scaled_img:
painter = QPainter()
painter.begin(self)
painter.scale(self.scale, self.scale)
if self.wheel_flag: # 定点缩放
self.wheel_flag = False
# 判断当前鼠标pos在不在图上
this_left_x = self.point.x() * self.old_scale # 当前图片左上角在窗口的位置
this_left_y = self.point.y() * self.old_scale

this_scale_width = self.new_width * self.old_scale
this_scale_height = self.new_height * self.old_scale

# 鼠标点在图上,以鼠标点为中心动作
gap_x = self.x - this_left_x # 鼠标位置相对左上角的偏移量
gap_y = self.y - this_left_y

if 0 < gap_x < this_scale_width and 0 < gap_y < this_scale_height:
new_left_x = int(self.x / self.scale - gap_x / self.old_scale) # 根据鼠标位置重新计算图像左上角位置
new_left_y = int(self.y / self.scale - gap_y / self.old_scale)

self.point = QPoint(new_left_x, new_left_y)
self.rb_point = QPoint(new_left_x + this_scale_width,
new_left_y + this_scale_height)

painter.drawPixmap(self.point, self.scaled_img) # 此函数中还会用scale对point进行处理
painter.end()

def wheelEvent(self, event):
angle = event.angleDelta() / 8 # 返回QPoint对象,为滚轮转过的数值,单位为1/8度
angleY = angle.y()
self.old_scale = self.scale
self.x, self.y = event.x(), event.y() # 鼠标位置
self.wheel_flag = True
# 获取当前鼠标相对于view的位置
if angleY > 0:
self.scale *= 1.05
else: # 滚轮下滚
self.scale *= 0.95
if self.scale < 1: # 最小尺寸为原图大小,比例为1
self.scale = 1
self.adjustSize()
self.update()

# 鼠标拖动
def mouseMoveEvent(self, e):
if self.left_click:
self.end_pos = e.pos() - self.start_pos # 当前位置-起始位置=差值,e.pos()当前鼠标位置,返回(x,y)
lt_new_point = self.point + self.end_pos / self.scale # 图片左上角的距离变化

rb_new_point2 = self.rb_point + self.end_pos / self.scale

# 限制拖动图片到QLabel控件中(限制了图片左上角)
if lt_new_point.x() > 0:
lt_new_point.setX(0)
if lt_new_point.y() > 0:
lt_new_point.setY(0)

# TODO 限制右下角拖进QLabel控件中(暂未实现,待优化)
# 检查右下角是否超出 QLabel 控件的边界
#if rb_new_point2.x() < self.width() - 1:
# print("右下角的最大移动量:", (self.old_rb_point.x() - self.width()) / self.scale)
# lt_new_point.setX(int(self.point.x() - (self.old_rb_point.x() - self.width()) / self.scale))
# rb_new_point2.setX(self.width())
#self.old_rb_point = rb_new_point2
#self.rb_point = rb_new_point2
#self.point = lt_new_point # 更新图片左上角位置
#self.start_pos = e.pos()
#self.repaint() # 重新画图

def mousePressEvent(self, e):
if e.button() == Qt.LeftButton:
self.left_click = True # 开始拖动
self.start_pos = e.pos() # 记录鼠标点击位置

def mouseReleaseEvent(self, e):
if e.button() == Qt.LeftButton:
self.left_click = False # 停止拖动

文字效果呈现

功能:在滚动框控件中以QLabel嵌套展示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QScrollArea

if __name__ == '__main__':
app = QApplication(sys.argv)

# 创建一个滚动区域
scroll_area = QScrollArea()

# 创建一个 QWidget 作为滚动区域的内容
container = QWidget(scroll_area)

not_other_num = 0 # 父 QLabel 的数量计数器
y_offset = 0 # 控制纵向位置的偏移量

for i in range(9): # 示例循环 5 次
# 创建父级 QLabel
parent_label = QLabel(container)
parent_label.setGeometry(0, y_offset, 200, 50) # 设置父级 QLabel 的位置和大小
parent_label.setStyleSheet("background-color: yellow;") # 设置父级 QLabel 的背景颜色

# 创建两个包含文字的 QLabel
label1 = QLabel("Label 1", parent=parent_label)
label1.setGeometry(10, 10, 80, 30) # 设置第一个 QLabel 的位置和大小
label1.setStyleSheet("background-color: red") # 设置第一个 QLabel 的背景颜色

label2 = QLabel("Label 2", parent=parent_label)
label2.setGeometry(100, 10, 80, 30) # 设置第二个 QLabel 的位置和大小
label2.setStyleSheet("background-color: blue") # 设置第二个 QLabel 的背景颜色

not_other_num += 1
y_offset += 60 # 更新纵向位置偏移量

# 设置容器的固定大小
container.setFixedSize(200, y_offset)

# 将容器设置为滚动区域的 widget
scroll_area.setWidget(container)

scroll_area.show()

sys.exit(app.exec_())

细节优化:

​ 1、所有控件随窗口自适应缩放:使用resizeEvent()函数

​ 2、相机是否连接监控:使用QTimer()创建一个定时器,每隔一段时间检查相机状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
# 界面显示相关内容
self.initUI()
# ------创建一个定时器,每隔一定时间检查一次相机状态-----
self.timer = QTimer()
self.timer.timeout.connect(self.MonitorLight)
self.timer.start(1000) # 每隔1秒检查一次相机状态

# -----监控相机是否连接-----
def MonitorLight(self):
print("连接中") # 替换为自己的实现功能

版本历史

  • 2024-04-04:输出初版文档
  • 2025-02-07:完善文档结构