初学习使用了PySide2 ,其中遇到了许多的问题,但是在不断努力之中最终还是解决了。来张界面效果图。
其中按键比较多, 按键“打开多个文件”“打开单个文件”就是为了学习读取文件添加的没有实际功能,该按键返回文件路径。
1、Qt界面制作
包含 QLabel QPushButton 上面的其实是一个放大了的Label标签,名字为空,下面为按键。
2 、图像的读取转换
2.1 文件读取
FileDialog = QFileDialog(self.pushButton_2)
# 设置可以打开任何文件
FileDialog.setFileMode(QFileDialog.AnyFile)
# 文件过滤
Filter = "(*.jpg,*.png,*.jpeg,*.bmp,*.gif)|*.jgp;*.png;*.jpeg;*.bmp;*.gif|All files(*.*)|*.*"
image_file, _ = FileDialog.getOpenFileName(self.pushButton_2,'open file','./','Image files (*.jpg *.gif *.png *.jpeg)') # 选择目录,返回选中的路径 'Image files (*.jpg *.gif *.png *.jpeg)'
# 判断是否正确打开文件
if not image_file:
QMessageBox.warning(self.pushButton_2, "警告", "文件错误或打开文件失败!", QMessageBox.Yes)
return
print("读入文件成功")
print(image_file) # 默认打开当前路径 输出文件路径
2.2 图像转换
PySide2 中QLabel类可以显示文字和图片。利用QFileDialog.getOpenFileName()类读取文件路径,在利于cv2把视频按帧读取,本来想利于cv2播放,但是速度有点快,最后选择了QLabel加Timer播放。QLabel显示的格式必须是Qimage,但是用cv2读取的帧格式是numpy array,必须把它转换成为Qimage。
# 读取一帧
success, frame = self.cap.read()
if success:
# Mat格式图像转Qt中图像的方法
show = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
showImage = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
gui.label.setPixmap(QPixmap.fromImage(showImage))
gui.label.setScaledContents(True) # 让图片自适应 label 大小
3 、主函数代码
本来想使用动态加载UI文件,但是后来发现,如果在一个类里面使用起来还是比较方便的,如果在另一个里面调用效果不是太好,所有直接选择了加载py文件。
import sys
from PySide2.QtWidgets import QApplication, QMessageBox,QFileDialog,QMainWindow
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import Qt,QDir, QTimer
from PySide2.QtGui import QPixmap,QImage
from UI_QLabel import Ui_mainWindow
import cv2
class LoginGUI(QMainWindow, Ui_mainWindow):
def __init__(self):
super(LoginGUI, self).__init__() #调用父类
self.setupUi(self)
# super().__init__()
# # 从文件中加载UI定义
# self.ui = QUiLoader().load('UI_QLabel.ui')
# 打开文件类型,用于类的定义
self.f_type = 0
def window_init(self):
# label 风格设置
self.label.setStyleSheet('''background: rgba(177, 177, 177, 0.8);
font-family: YouYuan;
font-size: 12pt;
color: white;
''')
self.label.setAlignment(Qt.AlignCenter) # 设置字体居中现实
self.label.setText("Moonbaem 小先生") # 默认显示文字
# self.ui.label.setPixmap("login.jpg") ##输入为图片路径,比如当前文件内的logo.png图片
# self.ui.label.setFixedSize(200, 200) # 设置显示固定尺寸,可以根据图片的像素长宽来设置
# self.ui.label.setScaledContents(True) # 让图片自适应 label 大小
self.pushButton.clicked.connect(self.btnLogin_clicked)
self.pushButton_2.clicked.connect(self.OpenFileName_clicked)
self.pushButton_3.clicked.connect(self.OpenFileNames_clicked)
self.pushButton_4.clicked.connect(VdoConfig)
# 打开文件夹
def btnLogin_clicked(self):
FileDialog = QFileDialog(self.pushButton)
FileDirectory = FileDialog.getExistingDirectory(self.pushButton, "标题") # 选择目录,返回选中的路径
print(FileDirectory)
#打开单个文件
def OpenFileName_clicked(self):
FileDialog = QFileDialog(self.pushButton_2)
# 设置可以打开任何文件
FileDialog.setFileMode(QFileDialog.AnyFile)
# 文件过滤
Filter = "(*.jpg,*.png,*.jpeg,*.bmp,*.gif)|*.jgp;*.png;*.jpeg;*.bmp;*.gif|All files(*.*)|*.*"
image_file, _ = FileDialog.getOpenFileName(self.pushButton_2,'open file','./','Image files (*.jpg *.gif *.png *.jpeg)') # 选择目录,返回选中的路径 'Image files (*.jpg *.gif *.png *.jpeg)'
# 判断是否正确打开文件
if not image_file:
QMessageBox.warning(self.pushButton_2, "警告", "文件错误或打开文件失败!", QMessageBox.Yes)
return
print("读入文件成功")
print(image_file) # 'C:\\', 默认C盘打开
# 设置标签的图片
self.label.setPixmap(image_file) ##输入为图片路径,比如当前文件内的logo.png图片
#self.label.setFixedSize(600, 400) # 设置显示固定尺寸,可以根据图片的像素长宽来设置
self.label.setScaledContents(True) # 让图片自适应 label 大小
# 多选文件
def OpenFileNames_clicked(self):
FileDialog = QFileDialog(self.pushButton_3)
FileDirectory = FileDialog.getOpenFileNames(self.pushButton_3, "标题") # 选择目录,返回选中的路径
print(FileDirectory)
def open_video(self):
FileDialog = QFileDialog(gui)
# 设置可以打开任何文件
FileDialog.setFileMode(QFileDialog.AnyFile)
# 文件过滤
image_file, _ = FileDialog.getOpenFileName(self.pushButton_4, 'open file', './', )
if not image_file:
QMessageBox.warning(self.pushButton_4, "警告", "文件错误或打开文件失败!", QMessageBox.Yes)
return
print("读入文件成功")
return image_file
# 视频控制
class VdoConfig:
def __init__(self):
# 按钮使能(否)
gui.pushButton_5.setEnabled(False)
gui.pushButton_6.setEnabled(False)
gui.pushButton_7.setEnabled(False)
gui.file = gui.open_video()
if not gui.file:
return
gui.label.setText("正在读取请稍后...")
# 设置时钟
self.v_timer = QTimer() #self.
# 读取视频
self.cap = cv2.VideoCapture(gui.file)
if not self.cap:
print("打开视频失败")
return
# 获取视频FPS
self.fps = self.cap.get(cv2.CAP_PROP_FPS) # 获得码率
# 获取视频总帧数
self.total_f = self.cap.get(cv2.CAP_PROP_FRAME_COUNT)
# 获取视频当前帧所在的帧数
self.current_f = self.cap.get(cv2.CAP_PROP_POS_FRAMES)
# 设置定时器周期,单位毫秒
self.v_timer.start(int(1000 / self.fps))
print("FPS:".format(self.fps))
gui.pushButton_5.setEnabled(True)
gui.pushButton_6.setEnabled(True)
gui.pushButton_7.setEnabled(True)
gui.pushButton_5.setText("播放")
gui.pushButton_6.setText("快退")
gui.pushButton_7.setText("快进")
# 连接定时器周期溢出的槽函数,用于显示一帧视频
self.v_timer.timeout.connect(self.show_pic)
# 连接按钮和对应槽函数,lambda表达式用于传参
gui.pushButton_5.clicked.connect(self.go_pause)
gui.pushButton_6.pressed.connect(lambda: self.last_img(True))
gui.pushButton_6.clicked.connect(lambda: self.last_img(False))
gui.pushButton_7.pressed.connect(lambda: self.next_img(True))
gui.pushButton_7.clicked.connect(lambda: self.next_img(False))
print("init OK")
# 视频播放
def show_pic(self):
# 读取一帧
success, frame = self.cap.read()
if success:
# Mat格式图像转Qt中图像的方法
show = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
showImage = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
gui.label.setPixmap(QPixmap.fromImage(showImage))
gui.label.setScaledContents(True) # 让图片自适应 label 大小
# 状态栏显示信息
self.current_f = self.cap.get(cv2.CAP_PROP_POS_FRAMES)
current_t, total_t = self.calculate_time(self.current_f, self.total_f, self.fps)
gui.statusbar.showMessage("文件名:{} {}({})".format( gui.file, current_t, total_t))
def calculate_time(self, c_f, t_f, fps):
total_seconds = int(t_f/fps)
current_sec = int(c_f/fps)
c_time = "{}:{}:{}".format(int(current_sec/3600), int((current_sec % 3600)/60), int(current_sec % 60))
t_time = "{}:{}:{}".format(int(total_seconds / 3600), int((total_seconds % 3600) / 60), int(total_seconds % 60))
return c_time, t_time
def show_pic_back(self):
# 获取视频当前帧所在的帧数
self.current_f = self.cap.get(cv2.CAP_PROP_POS_FRAMES)
# 设置下一次帧为当前帧-2
self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_f-2)
# 读取一帧
success, frame = self.cap.read()
if success:
show = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
showImage = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
gui.label.setPixmap(QPixmap.fromImage(showImage))
# 状态栏显示信息
current_t, total_t = self.calculate_time(self.current_f-1, self.total_f, self.fps)
gui.statusbar.showMessage("文件名:{} {}({})".format( gui.file, current_t, total_t))
# 快退
def last_img(self, t):
gui.pushButton_5.setText("播放")
if t:
# 断开槽连接
self.v_timer.timeout.disconnect(self.show_pic)
# 连接槽连接
self.v_timer.timeout.connect(self.show_pic_back)
self.v_timer.start(int(1000/self.fps)/2)
else:
self.v_timer.timeout.disconnect(self.show_pic_back)
self.v_timer.timeout.connect(self.show_pic)
self.v_timer.start(int(1000/self.fps))
# 快进
def next_img(self, t):
gui.pushButton_5.setText("播放")
if t:
self.v_timer.start(int(1000/self.fps)/2) # 快进
else:
self.v_timer.start(int(1000/self.fps))
# 暂停播放
def go_pause(self):
if gui.pushButton_5.text() == "播放":
self.v_timer.stop()
gui.pushButton_5.setText("暂停")
elif gui.pushButton_5.text() == "暂停":
self.v_timer.start(int(1000/self.fps))
gui.pushButton_5.setText("播放")
def VdoConfig_init():
gui.f_type = VdoConfig()
if __name__=="__main__":
app = QApplication([])
gui= LoginGUI() #初始化
gui.window_init()
gui.show() #将窗口控件显示在屏幕上
sys.exit(app.exec_())
4、文件打包
使用 pyinstaller 打包exe文件
pyinstaller -F Demo_QLabel.py --noconsole --hidden-import PySide2.QtXml --icon="logo.ico"
这样就会在当前目录下产生一个名为 dist 的目录。我们的可执行程序 Demo_QLabel.exe 就在里面。
-F是打包为一个文件
–noconsole 指定不要命令行窗口,否则我们的程序运行的时候,还会多一个黑窗口。
–hidden-import PySide2.QtXml 参数是因为这个 QtXml库是动态导入,PyInstaller没法分析出来,需要我们告诉它,这个在动态加载UI文件必须要添加
–icon=“logo.ico” 添加程序的图标 ,必须要为ico文件
图标: 提供一个在线转换ico格式的地址:http://www.bitbug.net/
5、效果展示
5.1 图片查看
5.2 视频播放
视频播放下面会显示播放时长。
6 、代码获取
如有需要自行获取包含完整的代码和UI文件。下载:https://wwi.lanzous.com/ilf6Pk0wayb 密码:c3ru