yolo自动化项目实例解析(六)自建UI(主窗口、预览窗口)
前面我们大致把各个代码块梳理出来了,但是还是不知道从那块开始,我们这里主要先通过ui页面的元素去推理整个执行过程,我们首先需要知道ui功能里面有那些组件
qt设计师基础控件
Qt Designer 是一个图形界面设计工具,用于创建 Qt 应用程序的用户界面。它提供了丰富的控件(也称为窗口部件或widgets)来构建各种类型的用户界面
类型 | 窗口部件 | 说明 |
---|---|---|
基础控件 | QLabel | 显示静态文本或图像 |
基础控件 | QPushButton | 用户可以点击的按钮 |
基础控件 | QLineEdit | 单行文本输入框 |
基础控件 | QTextEdit | 多行文本编辑器 |
基础控件 | QCheckBox | 复选框,用户可以选择或取消选择 |
基础控件 | QRadioButton | 单选按钮,用于一组互斥的选择项 |
基础控件 | QComboBox | 下拉列表框,用户可以从列表中选择一项 |
基础控件 | QSlider | 滑块控件,用于数值的选择范围 |
基础控件 | QSpinBox/QDoubleSpinBox | 数字输入框,用户可以输入整数或浮点数 |
基础控件 | QProgressBar | 进度条,显示任务完成的进度 |
基础控件 | QToolButton | 工具按钮,常用于工具栏上,支持弹出菜单 |
基础控件 | QGraphicsView | 图形视图,用于显示复杂的图形场景 |
布局管理器 | QVBoxLayout | 垂直布局管理器,用于管理控件的垂直排列 |
布局管理器 | QHBoxLayout | 水平布局管理器,用于管理控件的水平排列 |
布局管理器 | QGridLayout | 网格布局管理器,用于将控件放置在网格单元格中 |
布局管理器 | QFormLayout | 表单布局管理器,用于创建标签和控件成对出现的布局 |
布局管理器 | QStackedLayout | 堆叠布局管理器,用于堆叠多个控件,一次只显示一个 |
容器 | QWidget | 最基础的窗口部件,可以包含其他控件 |
容器 | QFrame | 带边框的容器,可以设置不同的样式 |
容器 | QGroupBox | 带标题的框架,常用于分组控件 |
容器 | QTabWidget | 带标签页的容器,允许用户在多个页面之间切换 |
容器 | QScrollArea | 滚动区域,当内容超过显示区域时提供滚动条 |
容器 | QDockWidget | 码头窗口,通常用于显示附加信息或工具 |
对话框 | QMessageBox | 提示信息对话框,用于显示警告、错误等消息 |
对话框 | QFileDialog | 文件对话框,用于选择文件或目录 |
对话框 | QColorDialog | 颜色选择对话框,用于选取颜色 |
对话框 | QFontDialog | 字体选择对话框,用于选取字体 |
其他 | QMenuBar | 菜单栏,通常位于窗口顶部 |
其他 | QToolBar | 工具栏,通常包含一些快捷按钮 |
其他 | QStatusBar | 状态栏,显示临时的消息 |
我们打开窗口的时候会发现中间有一块显示大屏,如下
我们先看看怎么实现这个大屏显示,打开qt设计师
新建项目目录
我们先不在原本的项目上找,先根据已有的信息参考源代码进行推断
一、添加主窗口
1、主窗口、窗口大小、标题
1、打开Qt Designer。
2、选择“文件” > “新建”,创建一个新的Qt Designer文档。
3、在“选择类”对话框中,选择QMainWindow作为基础类,并命名为mainWindow。4、添加布局,layouts--Horizontal layouts--- 添加后选择类名右键--布局--水平布局,然后将我们刚才添加的Horizontal layouts删除
5、设置窗口大小(geometry关键字)为800x600像素,并设置窗口标题(windowTitle)为 "主页"
2、添加工具栏、移除菜单栏
3、组件重命名
mainWindow
centralwidget
sa_main
scrollAreaWidgetContents
sb_main
toolBar
这里我们给布局内的各个元素重命名后,我们在ui转py会转换为各个关键字方便使用
4、布局重命名
我们上面临时用了下布局样式这里可能叫_2什么的,修改为下图
5、工具栏调整到侧边
6、添加动作、ui转py
#动作文本名称如下
Save_All
Add_Mode
Add_Script
ui转换
pyuic5 -o main.py main.ui
代码
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'main.ui'
#
# Created by: PyQt5 UI code generator 5.15.11
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_mainWindow(object):
def setupUi(self, mainWindow):
mainWindow.setObjectName("mainWindow")
mainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(mainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.sa_main = QtWidgets.QScrollArea(self.centralwidget)
self.sa_main.setWidgetResizable(True)
self.sa_main.setObjectName("sa_main")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 762, 551))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.sa_main.setWidget(self.scrollAreaWidgetContents)
self.horizontalLayout.addWidget(self.sa_main)
mainWindow.setCentralWidget(self.centralwidget)
self.sb_main = QtWidgets.QStatusBar(mainWindow)
self.sb_main.setObjectName("sb_main")
mainWindow.setStatusBar(self.sb_main)
self.toolBar = QtWidgets.QToolBar(mainWindow)
self.toolBar.setObjectName("toolBar")
mainWindow.addToolBar(QtCore.Qt.LeftToolBarArea, self.toolBar)
self.actionSave_All = QtWidgets.QAction(mainWindow)
self.actionSave_All.setObjectName("actionSave_All")
self.actionAdd_Mode = QtWidgets.QAction(mainWindow)
self.actionAdd_Mode.setObjectName("actionAdd_Mode")
self.actionAdd_Script = QtWidgets.QAction(mainWindow)
self.actionAdd_Script.setObjectName("actionAdd_Script")
self.retranslateUi(mainWindow)
QtCore.QMetaObject.connectSlotsByName(mainWindow)
def retranslateUi(self, mainWindow):
_translate = QtCore.QCoreApplication.translate
mainWindow.setWindowTitle(_translate("mainWindow", "主页"))
self.toolBar.setWindowTitle(_translate("mainWindow", "toolBar"))
self.actionSave_All.setText(_translate("mainWindow", "Save_All "))
self.actionAdd_Mode.setText(_translate("mainWindow", "Add_Mode "))
self.actionAdd_Script.setText(_translate("mainWindow", "Add_Script"))
7、打开主窗口
import ctypes
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QDockWidget, QMainWindow, QApplication
import state
from ui.show import Ui_DockWidget
from ui.main import Ui_mainWindow
class MainWindow(QMainWindow, Ui_mainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self) # 设置UI布局
self.retranslateUi(self) # 重新翻译UI组件的文字
if __name__ == '__main__':
# 获取屏幕宽度和高度
screen_width = ctypes.windll.user32.GetSystemMetrics(0)
screen_height = ctypes.windll.user32.GetSystemMetrics(1)
#初始化QT应用
app = QApplication(sys.argv)
ratio = screen_width / 2560 # 分辨率比例
# 设置全局字体大小 计算字体大小
base_font_size = 13
# 基准字体大小,适合1920*1080分辨率
new_font_size = int(base_font_size * ratio)
font = QFont("Arial", new_font_size)
# 子控件的宽度
item_width = 320
item_height = 240
item_height_min = 60
window_main = MainWindow()
window_main.show() #开启主窗口
sys.exit(app.exec_()) # 监听消息不关闭
8、设置主窗口样式
class MainWindow(QMainWindow, Ui_mainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self) # 设置UI布局
self.retranslateUi(self) # 重新翻译UI组件的文字
#修改窗口样式为黑灰色,高亮
self.set_ui()
#设置主窗口样式
def set_ui(self):
# 设置主题样式为 Flatwhite
# 创建 QtitanRibbon 实例
from qt_material import apply_stylesheet
apply_stylesheet(app, theme='dark_pink.xml')
self.setStyleSheet("""
QScrollBar::handle:horizontal {
background-color: #A50F2C; /* 设置滑块颜色 */
}
QScrollBar::handle:horizontal:hover{
background-color: #FF1744; /* 设置滑块颜色 */
}
QPushButton:hover{
background-color: #DFC472; /* 设置颜色 */
}
QPlainTextEdit{padding: 0px;margin: 0px;}
QPushButton{padding: 0px;margin: 1px;}
""" + "font-family: {}; font-size: {}pt;".format(font.family(), font.pointSize()))
9、滚动可扩展窗口
class MainWindow(QMainWindow, Ui_mainWindow):
def __init__(self):
。。。
#sa_main是我们之前定义的QScrollArea 也就是滚动条类型的容器
#我们在滚动条布局sa_main内放了一个普通部件QWidget() ,self.sa_main.setWidget(self.container_widget)
#这样一来,如果普通部件内的数据超出了显示范围,就能通过滚动条查看了
self.g_box = QGridLayout()
#创建了一个普通的窗口部件(QWidget),这个部件将用于包含所有的子控件,并且可以独立于其父窗口进行尺寸调整,为了可以让g_布局超过窗口大小
self.container_widget = QWidget() # 创建一个中间部件
self.container_widget.setLayout(self.g_box) # 将网格布局设置给中间部件
self.g_box.setAlignment(Qt.AlignTop | Qt.AlignLeft) # 布局 左上对齐
self.g_box.setContentsMargins(0, 0, 0, 0) # 边距
self.g_box.setAlignment(Qt.AlignTop)
self.sa_main.setWidget(self.container_widget) # 将中间部件设置为QScrollArea的子部件
# 获取纵向滚动条控件
vertical_scrollbar = self.findChild(QScrollArea)
self.v_scrollbar = vertical_scrollbar.verticalScrollBar()
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.row = 0
self.column = -1
self.run_times = 1
self.column_step = 310
# 获取主窗口的状态栏对象
self.statusbar = self.statusBar()
# 设置状态栏文本
self.statusbar.showMessage('欢迎使用')
10、延迟布局更新
当窗口尺寸变化或其他条件满足时(例如计时器触发),重新计算并更新网格布局中的控件位置,使其适应当前窗口的大小
class MainWindow(QMainWindow, Ui_mainWindow):
def __init__(self):
。。。
# 所有数据
self.datas = []
# 创建一个计时器,用于延迟布局
self.timer = QTimer(self)
self.timer.setSingleShot(True) # 设置为单次触发
self.timer.timeout.connect(self.update_layout)
def update_layout(self):
try:
# 获取主窗口的宽度
width = self.centralWidget().width()
# 计算每行可以容纳的组件数量
num_per_row = (width) // (self.column_step * ratio) # 假设每个组件的宽度为200
if num_per_row < 1:
num_per_row = 1
# 清空当前布局
for i in reversed(range(self.g_box.count())):
self.g_box.itemAt(i).widget().setParent(None)
# 重新添加组件到网格布局
for i, data in enumerate(self.datas):
self.row = int(i // num_per_row)
self.column = int(i % num_per_row)
self.g_box.addWidget(data["f_item"], self.row, self.column)
except:
pass
11、全量代码
import ctypes
import sys
from PyQt5.QtCore import Qt, QTimer, QObject, pyqtSignal
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QDockWidget, QMainWindow, QApplication, QGridLayout, QWidget, QScrollArea
from ui.show import Ui_DockWidget
from ui.main import Ui_mainWindow
class MainWindow(QMainWindow, Ui_mainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self) # 设置UI布局
self.retranslateUi(self) # 重新翻译UI组件的文字
#修改窗口样式为黑灰色,高亮
self.set_ui()
# self.setWindowIcon(QIcon("datas/logo.png")) 设置图标
self.setWindowTitle(f" 修改主页标题")
self.resize(640, 900) # 窗口的长宽
self.move(0, 300) # 窗口的起点位置
#sa_main是我们之前定义的QScrollArea 也就是滚动条类型的容器
#我们在滚动条布局sa_main内放了一个普通部件QWidget() ,self.sa_main.setWidget(self.container_widget)
#这样一来,如果普通部件内的数据超出了显示范围,就能通过滚动条查看了
self.g_box = QGridLayout()
#创建了一个普通的窗口部件(QWidget),这个部件将用于包含所有的子控件,并且可以独立于其父窗口进行尺寸调整,为了可以让g_布局超过窗口大小
self.container_widget = QWidget() # 创建一个中间部件
self.container_widget.setLayout(self.g_box) # 将网格布局设置给中间部件
self.g_box.setAlignment(Qt.AlignTop | Qt.AlignLeft) # 布局 左上对齐
self.g_box.setContentsMargins(0, 0, 0, 0) # 边距
self.g_box.setAlignment(Qt.AlignTop)
self.sa_main.setWidget(self.container_widget) # 将中间部件设置为QScrollArea的子部件
# 获取纵向滚动条控件
vertical_scrollbar = self.findChild(QScrollArea)
self.v_scrollbar = vertical_scrollbar.verticalScrollBar()
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.row = 0
self.column = -1
self.run_times = 1
self.column_step = 310
# 获取主窗口的状态栏对象
self.statusbar = self.statusBar()
# 设置状态栏文本
self.statusbar.showMessage('欢迎使用')
# 所有数据
self.datas = []
# 创建一个计时器,用于延迟布局
self.timer = QTimer(self)
self.timer.setSingleShot(True) # 设置为单次触发
self.timer.timeout.connect(self.update_layout)
#更新布局
def update_layout(self):
try:
# 获取主窗口的宽度
width = self.centralWidget().width()
# 计算每行可以容纳的组件数量
num_per_row = (width) // (self.column_step * ratio) # 假设每个组件的宽度为200
if num_per_row < 1:
num_per_row = 1
# 清空当前布局
for i in reversed(range(self.g_box.count())):
self.g_box.itemAt(i).widget().setParent(None)
# 重新添加组件到网格布局
for i, data in enumerate(self.datas):
self.row = int(i // num_per_row)
self.column = int(i % num_per_row)
self.g_box.addWidget(data["f_item"], self.row, self.column)
except:
pass
#设置主窗口样式
def set_ui(self):
# 设置主题样式为 Flatwhite
# 创建 QtitanRibbon 实例
from qt_material import apply_stylesheet
apply_stylesheet(app, theme='dark_pink.xml')
self.setStyleSheet("""
QScrollBar::handle:horizontal {
background-color: #A50F2C; /* 设置滑块颜色 */
}
QScrollBar::handle:horizontal:hover{
background-color: #FF1744; /* 设置滑块颜色 */
}
QPushButton:hover{
background-color: #DFC472; /* 设置颜色 */
}
QPlainTextEdit{padding: 0px;margin: 0px;}
QPushButton{padding: 0px;margin: 1px;}
""" + "font-family: {}; font-size: {}pt;".format(font.family(), font.pointSize()))
if __name__ == '__main__':
# 获取屏幕宽度和高度
screen_width = ctypes.windll.user32.GetSystemMetrics(0)
screen_height = ctypes.windll.user32.GetSystemMetrics(1)
#初始化QT应用
app = QApplication(sys.argv)
ratio = screen_width / 2560 # 分辨率比例
# 设置全局字体大小 计算字体大小
base_font_size = 13
# 基准字体大小,适合1920*1080分辨率
new_font_size = int(base_font_size * ratio)
font = QFont("Arial", new_font_size)
# 子控件的宽度
item_width = 320
item_height = 240
item_height_min = 60
window_main = MainWindow()
window_main.show() #开启主窗口
sys.exit(app.exec_()) # 监听消息不关闭
二、添加UI预览窗口
QDockWidget 码头窗口,通常用于显示附加信息或工具
1、预览窗口定义
1、新建项目-窗口部件--QDockWidget (483X565)
2、添加两个label 大小480 X 270 ,设置样式表
3、添加按钮 修改按钮文本 + -
#寻路标签背景颜色为黑色
background-color: rgb(0, 0, 0);
#标签背景颜色为灰色
background-color: rgb(20, 20, 20);
这里图里搞错了,yolo那个label应该设置为background-color: rgb(20, 20, 20);
2、组件名称修改
DockWidget
dockWidgetContents
bt_jia
bt_jian
lb_xunlu
lb_yolov
3、label最大尺寸修改
寻路
yolo
4、调整各个组件前后台
jia 和jian调整为前台
xunlu和yolo调整到后台
(直接右键组件--放到前面/放到后面)
5、ui转py
pyuic5 -o show.py show.ui
py代码
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'show.ui'
#
# Created by: PyQt5 UI code generator 5.15.11
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_DockWidget(object):
def setupUi(self, DockWidget):
DockWidget.setObjectName("DockWidget")
DockWidget.resize(483, 565)
self.dockWidgetContents = QtWidgets.QWidget()
self.dockWidgetContents.setObjectName("dockWidgetContents")
self.lb_xunlu = QtWidgets.QLabel(self.dockWidgetContents)
self.lb_xunlu.setGeometry(QtCore.QRect(0, 0, 480, 270))
self.lb_xunlu.setMinimumSize(QtCore.QSize(0, 0))
self.lb_xunlu.setMaximumSize(QtCore.QSize(16777215, 16777215))
self.lb_xunlu.setStyleSheet("background-color: rgb(0, 0, 0);")
self.lb_xunlu.setText("")
self.lb_xunlu.setObjectName("lb_xunlu")
self.lb_yolov = QtWidgets.QLabel(self.dockWidgetContents)
self.lb_yolov.setGeometry(QtCore.QRect(0, 270, 480, 270))
self.lb_yolov.setMinimumSize(QtCore.QSize(480, 270))
self.lb_yolov.setMaximumSize(QtCore.QSize(1920, 1080))
self.lb_yolov.setStyleSheet("background-color: rgb(20, 20, 20);")
self.lb_yolov.setText("")
self.lb_yolov.setObjectName("lb_yolov")
self.bt_jia = QtWidgets.QPushButton(self.dockWidgetContents)
self.bt_jia.setGeometry(QtCore.QRect(30, 40, 31, 28))
self.bt_jia.setObjectName("bt_jia")
self.bt_jian = QtWidgets.QPushButton(self.dockWidgetContents)
self.bt_jian.setGeometry(QtCore.QRect(60, 40, 31, 28))
self.bt_jian.setObjectName("bt_jian")
self.lb_yolov.raise_()
self.lb_xunlu.raise_()
self.bt_jia.raise_()
self.bt_jian.raise_()
DockWidget.setWidget(self.dockWidgetContents)
self.retranslateUi(DockWidget)
QtCore.QMetaObject.connectSlotsByName(DockWidget)
def retranslateUi(self, DockWidget):
_translate = QtCore.QCoreApplication.translate
DockWidget.setWindowTitle(_translate("DockWidget", "DockWidget"))
self.bt_jia.setText(_translate("DockWidget", "+"))
self.bt_jian.setText(_translate("DockWidget", "-"))
三、主窗口添加子窗口
1、添加自定义信号
class MySignal(QObject):
# 无参数信号,可能用于触发显示某个路径或轨迹的操作
mysig_show_xunlu = pyqtSignal()
# 无参数信号,可能与YOLOv模型有关,用于触发显示模型输出或其他相关操作
mysig_show_yolov = pyqtSignal()
2、添加预览窗口显示类
class FormShow(QDockWidget, Ui_DockWidget):
def __init__(self, parent=None):
super(QDockWidget, self).__init__(parent)
self.parent = parent
self.setParent(parent)
self.setupUi(self)
self.set_ui()
# self.timer = QTimer()
self.setWindowTitle("检测预览")
self.lb_yolov.setScaledContents(True)
self.lb_yolov.setAlignment(Qt.AlignCenter)
self.lb_xunlu.setAlignment(Qt.AlignCenter)
self.move(0, 0)
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.window_height = int(270 * ratio)
self.window_width = int(480 * ratio)
self.setMinimumSize(self.window_width, self.window_height * 2)
self.bt_jia.clicked.connect(self.clicked_jia)
self.bt_jian.clicked.connect(self.clicked_jian)
def set_ui(self):
pass
def clicked_jia(self):
if self.window_height + 108 * ratio > 1080 * ratio:
return
self.window_height += int(108 * ratio)
self.window_width += int(190 * ratio)
self.lb_xunlu.setFixedHeight(self.window_height)
if self.width() >= self.lb_yolov.width() + self.lb_xunlu.width():
self.lb_yolov.move(self.lb_xunlu.width(), 0)
else:
self.lb_yolov.move(0, self.lb_xunlu.height())
self.lb_yolov.setFixedHeight(self.window_height)
self.lb_yolov.setFixedWidth(self.window_width)
def clicked_jian(self):
if self.window_height - 108 * ratio < 270 * ratio:
return
self.window_height -= int(108 * ratio)
self.window_width -= int(190 * ratio)
self.lb_xunlu.setFixedHeight(self.window_height)
if self.width() >= self.lb_yolov.width() + self.lb_xunlu.width():
self.lb_yolov.move(self.lb_xunlu.width(), 0)
else:
self.lb_yolov.move(0, self.lb_xunlu.height())
self.lb_yolov.setFixedHeight(self.window_height)
self.lb_yolov.setFixedWidth(self.window_width)
def closeEvent(self, event):
self.parent.action_isShow.setChecked(False)
def resizeEvent(self, event):
if self.width() >= self.lb_yolov.width() + self.lb_xunlu.width():
self.lb_yolov.move(self.lb_xunlu.width(), 0)
else:
self.lb_yolov.move(0, self.lb_xunlu.height())
3、主窗口 添加 预览窗口显示
def connect_set(self):
# 创建一个顶级菜单
self.menu = self.menuBar()
self.menu_view = self.menu.addMenu("视图")
# 创建一个动作
self.action_isShow = QAction("显示检测结果窗口", self)
self.action_isShow.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_F11))
self.action_isShow.triggered.connect(self.hotkey_isShow)
self.action_isShow.setCheckable(True)
self.action_isShow.setChecked(True)
self.menu_view.addAction(self.action_isShow)
self.hotkey_isShow()
#打开预测窗口
def hotkey_isShow(self):
# 尝试创建悬浮窗口实例
if not hasattr(self, "fromshow"):
self.fromshow = FormShow(self) # 创建悬浮窗口实例
print(111111)
self.addDockWidget(Qt.TopDockWidgetArea, self.fromshow) # 添加悬浮窗口到主窗口
4、添加信号槽
# 绑定信号槽
def connect_set(self):
...
#添加信号
self.sg = MySignal()
#绑定信号槽
self.sg.mysig_show_xunlu.connect(self.show_xunlu) #寻路
self.sg.mysig_show_yolov.connect(self.show_yolov) #推理
5、添加信号槽及对应函数
这里先偷个懒,直接使用本地图片显示上去,后续整晚之后在加具体功能
def show_xunlu(self, image_path="./datas/111.png"):
# 加载本地图片
pixmap = QPixmap(image_path)
# 设置图片到 lb_xunlu 控件
self.fromshow.lb_xunlu.setPixmap(pixmap)
# 设置宽度固定,高度自适应
self.fromshow.window_width = int(self.fromshow.window_height * pixmap.width() / pixmap.height())
self.fromshow.lb_xunlu.setFixedHeight(self.fromshow.window_height)
self.fromshow.lb_xunlu.setFixedWidth(self.fromshow.window_width)
self.fromshow.lb_xunlu.setScaledContents(True)
def show_yolov(self, image_path="./datas/111.png"):
print(2222)
# 加载本地图片
pixmap1 = QPixmap(image_path)
# 设置图片到 lb_yolov 控件
self.fromshow.lb_yolov.setPixmap(pixmap1)
# 根据窗口宽度调整 lb_yolov 的位置
if self.fromshow.width() >= self.fromshow.lb_yolov.width() + self.fromshow.lb_xunlu.width():
self.fromshow.lb_yolov.move(self.fromshow.lb_xunlu.width(), 0)
else:
self.fromshow.lb_yolov.move(0, self.fromshow.lb_xunlu.height())
6、发送信号
我们在init中定义一个新的定时器,每5s触发on_timer_timeout函数,然后会发送mysig_show_xunlu和mysig_show_yolov信号,信号槽接收到后,触发show_xunlu 和show_yolov 函数,去显示图片
def __init__(self):
。。。
# 绑定信号槽
self.connect_set()
# 创建定时器 用于周期显示图片
self.timer = QTimer(self)
self.timer.timeout.connect(self.on_timer_timeout)
self.timer.start(5000) # 每5秒触发一次
def on_timer_timeout(self):
# 每隔一段时间自动显示图片
#emit() 方法用于发送信号
self.sg.mysig_show_xunlu.emit()
self.sg.mysig_show_yolov.emit()
当然我们后面使用的时候不会这么草率的发送信号,而是通过循环截取地图,和屏幕中yolo推理当前屏幕图片进行显示的
7、全量代码
import ctypes
import sys
# 导入PyQt5模块
from PyQt5.QtCore import Qt, QTimer, QObject, pyqtSignal
from PyQt5.QtGui import QFont, QImage, QPixmap, QKeySequence
from PyQt5.QtWidgets import QDockWidget, QMainWindow, QApplication, QGridLayout, QWidget, QScrollArea, QAction
# 导入UI文件生成的类
from ui.show import Ui_DockWidget
from ui.main import Ui_mainWindow
class MySignal(QObject):
# 无参数信号,可能用于触发显示某个路径或轨迹的操作
mysig_show_xunlu = pyqtSignal()
# 无参数信号,可能与YOLOv模型有关,用于触发显示模型输出或其他相关操作
mysig_show_yolov = pyqtSignal()
# 定义FormShow类,继承自QDockWidget和Ui_DockWidget
class FormShow(QDockWidget, Ui_DockWidget):
def __init__(self, parent=None):
super(QDockWidget, self).__init__(parent) # 调用父类构造函数
self.parent = parent # 保存父窗口引用
self.setParent(parent) # 设置父窗口
self.setupUi(self) # 初始化UI界面
self.set_ui() # 自定义设置UI界面
# 设置窗口标题
self.setWindowTitle("检测预览")
# 设置标签控件属性
self.lb_yolov.setScaledContents(True)
self.lb_yolov.setAlignment(Qt.AlignCenter)
self.lb_xunlu.setAlignment(Qt.AlignCenter)
# 移动窗口位置
self.move(0, 0)
# 设置窗口保持在最顶层
self.setWindowFlags(Qt.WindowStaysOnTopHint)
# 计算窗口大小
self.window_height = int(270 * ratio)
self.window_width = int(480 * ratio)
# 设置窗口最小尺寸
self.setMinimumSize(self.window_width, self.window_height * 2)
# 连接按钮点击事件
self.bt_jia.clicked.connect(self.clicked_jia)
self.bt_jian.clicked.connect(self.clicked_jian)
# 自定义UI设置
def set_ui(self):
pass # 此处可添加更多UI设置
# 按钮“加”点击事件处理
def clicked_jia(self):
# 如果窗口高度加上增量后超过屏幕高度,则返回
if self.window_height + 108 * ratio > 1080 * ratio:
return
# 更新窗口大小
self.window_height += int(108 * ratio)
self.window_width += int(190 * ratio)
# 设置标签固定高度
self.lb_xunlu.setFixedHeight(self.window_height)
# 根据窗口宽度调整标签位置
if self.width() >= self.lb_yolov.width() + self.lb_xunlu.width():
self.lb_yolov.move(self.lb_xunlu.width(), 0)
else:
self.lb_yolov.move(0, self.lb_xunlu.height())
# 设置标签固定大小
self.lb_yolov.setFixedHeight(self.window_height)
self.lb_yolov.setFixedWidth(self.window_width)
# 按钮“减”点击事件处理
def clicked_jian(self):
# 如果窗口高度减去增量后小于最小高度,则返回
if self.window_height - 108 * ratio < 270 * ratio:
return
# 更新窗口大小
self.window_height -= int(108 * ratio)
self.window_width -= int(190 * ratio)
# 设置标签固定高度
self.lb_xunlu.setFixedHeight(self.window_height)
# 根据窗口宽度调整标签位置
if self.width() >= self.lb_yolov.width() + self.lb_xunlu.width():
self.lb_yolov.move(self.lb_xunlu.width(), 0)
else:
self.lb_yolov.move(0, self.lb_xunlu.height())
# 设置标签固定大小
self.lb_yolov.setFixedHeight(self.window_height)
self.lb_yolov.setFixedWidth(self.window_width)
# 重写关闭事件
def closeEvent(self, event):
# 关闭窗口时取消主窗口上的动作选中状态
self.parent.action_isShow.setChecked(False)
# 重写调整大小事件
def resizeEvent(self, event):
# 根据窗口宽度调整标签位置
if self.width() >= self.lb_yolov.width() + self.lb_xunlu.width():
self.lb_yolov.move(self.lb_xunlu.width(), 0)
else:
self.lb_yolov.move(0, self.lb_xunlu.height())
# 定义MainWindow类,继承自QMainWindow和Ui_mainWindow
class MainWindow(QMainWindow, Ui_mainWindow):
def __init__(self):
super(MainWindow, self).__init__() # 调用父类构造函数
self.setupUi(self) # 设置UI布局
self.retranslateUi(self) # 重新翻译UI组件的文字
# 修改窗口样式为黑灰色,高亮
self.set_ui()
# 设置窗口标题
self.setWindowTitle(f"修改主页标题")
# 设置窗口尺寸
self.resize(640, 900)
# 设置窗口起始位置
self.move(0, 300)
# 创建网格布局
self.g_box = QGridLayout()
# 创建一个中间部件
self.container_widget = QWidget()
# 将网格布局设置给中间部件
self.container_widget.setLayout(self.g_box)
# 设置布局左上对齐
self.g_box.setAlignment(Qt.AlignTop | Qt.AlignLeft)
# 设置布局边距
self.g_box.setContentsMargins(0, 0, 0, 0)
# 设置布局左对齐
self.g_box.setAlignment(Qt.AlignTop)
# 将中间部件设置为QScrollArea的子部件
self.sa_main.setWidget(self.container_widget)
# 获取纵向滚动条控件
vertical_scrollbar = self.findChild(QScrollArea)
self.v_scrollbar = vertical_scrollbar.verticalScrollBar()
# 设置窗口保持在最顶层
self.setWindowFlags(Qt.WindowStaysOnTopHint)
# 初始化变量
self.row = 0
self.column = -1
self.run_times = 1
self.column_step = 310
# 获取主窗口的状态栏对象
self.statusbar = self.statusBar()
# 设置状态栏文本
self.statusbar.showMessage('欢迎使用')
# 初始化数据列表
self.datas = []
# 创建一个计时器,用于延迟布局
self.timer = QTimer(self)
self.timer.setSingleShot(True) # 设置为单次触发
self.timer.timeout.connect(self.update_layout)
# 绑定信号槽
self.connect_set()
# 创建定时器 用于周期显示图片
self.timer = QTimer(self)
self.timer.timeout.connect(self.on_timer_timeout)
self.timer.start(5000) # 每5秒触发一次
def on_timer_timeout(self):
# 每隔一段时间自动显示图片
self.sg.mysig_show_xunlu.emit()
self.sg.mysig_show_yolov.emit()
# 绑定信号槽
def connect_set(self):
# 创建一个顶级菜单
self.menu = self.menuBar()
self.menu_view = self.menu.addMenu("视图")
# 创建一个动作
self.action_isShow = QAction("显示检测结果窗口", self)
self.action_isShow.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_F11))
self.action_isShow.triggered.connect(self.hotkey_isShow)
self.action_isShow.setCheckable(True)
self.action_isShow.setChecked(True)
self.menu_view.addAction(self.action_isShow)
# 打开预测窗口
self.hotkey_isShow()
self.sg = MySignal()
self.sg.mysig_show_xunlu.connect(self.show_xunlu)
self.sg.mysig_show_yolov.connect(self.show_yolov)
def show_xunlu(self, image_path="./datas/111.png"):
# 加载本地图片
pixmap = QPixmap(image_path)
# 设置图片到 lb_xunlu 控件
self.fromshow.lb_xunlu.setPixmap(pixmap)
# 设置宽度固定,高度自适应
self.fromshow.window_width = int(self.fromshow.window_height * pixmap.width() / pixmap.height())
self.fromshow.lb_xunlu.setFixedHeight(self.fromshow.window_height)
self.fromshow.lb_xunlu.setFixedWidth(self.fromshow.window_width)
self.fromshow.lb_xunlu.setScaledContents(True)
def show_yolov(self, image_path="./datas/111.png"):
print(2222)
# 加载本地图片
pixmap1 = QPixmap(image_path)
# 设置图片到 lb_yolov 控件
self.fromshow.lb_yolov.setPixmap(pixmap1)
# 根据窗口宽度调整 lb_yolov 的位置
if self.fromshow.width() >= self.fromshow.lb_yolov.width() + self.fromshow.lb_xunlu.width():
self.fromshow.lb_yolov.move(self.fromshow.lb_xunlu.width(), 0)
else:
self.fromshow.lb_yolov.move(0, self.fromshow.lb_xunlu.height())
# 打开预测窗口
def hotkey_isShow(self):
# 尝试创建悬浮窗口实例
if not hasattr(self, "fromshow"):
self.fromshow = FormShow(self) # 创建悬浮窗口实例
self.addDockWidget(Qt.TopDockWidgetArea, self.fromshow) # 添加悬浮窗口到主窗口
# 更新布局
def update_layout(self):
try:
# 获取主窗口的宽度
width = self.centralWidget().width()
# 计算每行可以容纳的组件数量
num_per_row = (width) // (self.column_step * ratio)
if num_per_row < 1:
num_per_row = 1
# 清空当前布局
for i in reversed(range(self.g_box.count())):
self.g_box.itemAt(i).widget().setParent(None)
# 重新添加组件到网格布局
for i, data in enumerate(self.datas):
self.row = int(i // num_per_row)
self.column = int(i % num_per_row)
self.g_box.addWidget(data["f_item"], self.row, self.column)
except:
pass
# 设置主窗口样式
def set_ui(self):
# 设置主题样式为 Flatwhite
from qt_material import apply_stylesheet
apply_stylesheet(app, theme='dark_pink.xml')
# 设置窗口样式表
self.setStyleSheet("""
QScrollBar::handle:horizontal {
background-color: #A50F2C; /* 设置滑块颜色 */
}
QScrollBar::handle:horizontal:hover {
background-color: #FF1744; /* 设置滑块颜色 */
}
QPushButton:hover {
background-color: #DFC472; /* 设置颜色 */
}
QPlainTextEdit {
padding: 0px;
margin: 0px;
}
QPushButton {
padding: 0px;
margin: 1px;
}
""" + "font-family: {}; font-size: {}pt;".format(font.family(), font.pointSize()))
# 主程序入口
if __name__ == '__main__':
# 获取屏幕宽度和高度
screen_width = ctypes.windll.user32.GetSystemMetrics(0)
screen_height = ctypes.windll.user32.GetSystemMetrics(1)
# 初始化QT应用
app = QApplication(sys.argv)
# 计算分辨率比例
ratio = screen_width / 2560
# 设置全局字体大小
base_font_size = 13
new_font_size = int(base_font_size * ratio)
font = QFont("Arial", new_font_size)
# 创建主窗口实例
window_main = MainWindow()
# 显示主窗口
window_main.show()
# 监听消息不关闭
sys.exit(app.exec_())