button可以说是Qt中与用户交互最基本的控件,最简单的就是单击:
#初始化一个button控件
self.button_inputData = QtWidgets.QPushButton(self.widget)
#设置按钮上文本字体
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(False)
font.setWeight(50)
self.button_inputData.setFont(font)
#设置控件名
self.button_inputData.setObjectName("button_inputData")
#设置按钮上显示的文本
self.button_inputData.setText('这是button显示的内容')
#设置信号-槽的连接,当用户点击该按钮后调用槽函数,参数为函数名
self.button_inputData.clicked.connect(self.button_inputData_clicked_Event)
QToolButton类的按钮是专门为设计工具栏按钮提供的类,可以设置成单击第一次时按钮按下,触发clicked信号,并保持按下状态,直到用户再次点击此按钮,触发clicked信号,并弹起按钮,在程序运行期间任何时候都可检测按钮的“按下/弹起”状态。
设置setCheckable(True)和setAutoExclusive(True)后按钮可保持按下状态,且状态可查询:
self.button.setCheckable(True)
self.button.setAutoExclusive(True)
查询按钮是按下还是弹起的方法是:
self.button_openCamera.isChecked()
按钮按下返回值是True,按钮弹起返回值是False。
应用代码如下:
#QToolButton的初始化设置
self.button_openCamera = QtWidgets.QToolButton(win_recogFaceInVideo)
self.button_openCamera.setGeometry(QtCore.QRect(695, 15, 114, 32))
font = QtGui.QFont()
font.setPointSize(13)
self.button_openCamera.setFont(font)
self.button_openCamera.setCheckable(True)
self.button_openCamera.setAutoExclusive(True)
self.button_openCamera.setPopupMode(QtWidgets.QToolButton.InstantPopup)
self.button_openCamera.setObjectName("button_openCamera")
#设置setCheckable(True)和setAutoExclusive(True)后按钮可保持按下状态,且状态可查询
self.button_openCamera.setCheckable(True)
self.button_openCamera.setAutoExclusive(True)
#设置单击信号的槽函数
self.button_openCamera.clicked.connect(self.recog_in_camera)
#clicked槽函数中可以获取按钮“按下/弹起”的状态
def recog_in_camera(self):
#如果按钮是按下状态,则执行下面的操作
if self.button_openCamera.isChecked():
#更改此按钮button_openCamera的文本信息
self.button_openCamera.setText('关闭摄像头')
#设置另一个按钮button_openVideoFile不可操作
self.button_openVideoFile.setEnabled(False)
else:
#若按钮是弹起状态,则更改此按钮的文本信息
self.button_openCamera.setText('打开摄像头')
label显示文字是空间最基本的用法了,如下:
#初始化一个label控件,参数是父类,也就是承载这个控件的窗口win_recogFaceInVideo
self.label = QtWidgets.QLabel(win_recogFaceInVideo)
#设置控件大小和位置:宽20,高65,左上角坐标(781,406)
self.label.setGeometry(QtCore.QRect(20, 65, 781, 406))
#设置文本字体信息
font = QtGui.QFont()
font.setPointSize(30)
font.setBold(False)
font.setWeight(50)
font.setKerning(True)
self.label.setFont(font)
self.label.setTextFormat(QtCore.Qt.RichText)
#缩放对齐:不缩放、居中对齐
self.label.setScaledContents(False)
self.label.setAlignment(QtCore.Qt.AlignCenter)
#控件名
self.label.setObjectName("label")
#文本内容
self.label.setText('这是label显示的内容')
先给出个示例,实现在label控件中按原比例居中显示图片文件。
代码如下:
def show_img_in_lable_center( label, fname):
#label表示要用来显示图片的那个标签~
#fname表示事先获取到的要打开的图片文件名(含路径),可用QtWidgets.QFileDialog.getOpenFileName()获取哈~
pix_map = QPixmap(fname)
img_w = pix_map.width()
img_h = pix_map.height()
lab_w = label.width()
lab_h = label.height()
if (img_w > lab_w) | (img_h > lab_h):
#若图片宽高大于label宽高,则在label居中显示按图片原始比例缩放后的图片
w_rate = float(img_w) / float(lab_w)
h_rate = float(img_h) / float(lab_h)
if w_rate >= h_rate:
w = lab_w
h = int(img_h / w_rate)
else:
w = int(img_w / h_rate)
h = lab_h
else:
#若图片宽高都小于label宽高,则按图片原始大小显示
w = img_w
h = img_h
label.setPixmap(pix_map.scaled(w, h))
label显示视频的问题实际就是label定时显示图片,只需要引入一个定时和加入截取视频画面的过程。
网上找到的C++的代码:
VideoCapture capture;//声明视频读入类
capture.open(0);//从摄像头读入视频 0表示从摄像头读入
if (!capture.isOpened())//先判断是否打开摄像头
{
cout<<"can not open";
cin.get();
return -1;
}
namedWindow(name);
while (1) {
Mat cap;//定义一个Mat变量,用于存储每一帧的图像
capture>>cap;//读取当前帧
if (!cap.empty())//判断当前帧是否捕捉成功 **这步很重要
imshow(name, cap);//若当前帧捕捉成功,显示
else
cout<<"can not ";
waitKey(30);//延时30毫秒
}
return 0;
自己写的定时处理视频帧然后显示在label中的代码比较复杂,思路是一样的,详情参考: win_recogFaceInVideo.py。
需要注意:cv2处理图像数据的格式是bgr格式,因此必须注意格式转换。
主要代码如下:
# -*- coding: utf-8 -*-
# 识别视频中人脸
import face_recognition
import cv2
from ui_win_recogFaceInVideo import Ui_win_recogFaceInVideo
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtCore import QTimer
import sys
import myLib, face_recog
from face_recog import *
class Win_RecogFaceInVideo(QWidget, Ui_win_recogFaceInVideo):
def __init__(self, parent=None ):
super(Win_RecogFaceInVideo, self).__init__(parent)
self.setupUi(self)
...
#建立信号槽连接,点击button_openCamera按钮,启动摄像头,画面显示在label中
self.button_openCamera.clicked.connect(self.recog_in_camera)
#建立信号槽连接,点击button_openVideoFile按钮,读取视频文件显示在label中
self.button_openVideoFile.clicked.connect(self.open_video_file)
#以上都可以不关心===================================================
#省略无关代码若干===================================================
video_capture = []
def open_video_file(self):
...
fname, _ = myLib.loadFile(self,'video')#加载视频文件名,不重要
...
###==============这里才是重点==============###
#初始化读取视频帧的Capture
self.video_capture = cv2.VideoCapture(fname)
#启动定时器,设置好超时的槽函数,超时时间10ms
self.timer_camera = QTimer()
self.timer_camera.start(10)
self.timer_camera.timeout.connect(self.play1pic) # 连接槽函数
def recog_in_camera(self):
if self.button_openCamera.isChecked():#判断是否需要关闭摄像头
#初始化抓取摄像头画面的Capture
self.video_capture = cv2.VideoCapture(0)
#启动定时器,设置好超时的槽函数,超时时间10ms
self.timer_camera = QTimer()
self.timer_camera.start(10) # 1000ms == 1s
self.timer_camera.timeout.connect(self.play1pic) # 连接槽函数
else:
self.close_camera()
#关闭摄像头的方法
def close_camera(self):
#释放摄像头抓取器
self.video_capture.release()
#停止定时器
self.timer_camera.stop()
#这是重点->>在label中显示一帧画面的方法play1pic()
def play1pic(self):
#抓取一帧画面:bgr格式的
ret, frame = self.video_capture.read()
if ret:#如果抓取成功,才继续画面处理和显示的操作
#face_recog.recog_in()是根据一帧bgr数据进行人脸识别,并处理画面后,将数据转换成QImage格式后返回的过程,详细在下面
Qimg = face_recog.recog_in(frame, update_features=False, frame_type='bgr', scale_rate=4.0)
#将处理过后返回的QImage数据居中的显示在label中,
#注意QPixmap.fromImage()方法将QImage数据又转成了QPixmap的格式
#label中只能显示QPixmap格式的图像哈~
myLib.show_img_in_lable_center(self.label,QPixmap.fromImage(Qimg))
else:#如果抓取失败,说明视频文件读完了,或者摄像头故障?,停止抓取
#释放抓取器
self.video_capture.release()
#停止定时器
self.timer_camera.stop()
#补充两个跟label中显示视频有关的两个函数定义,部分操作与此话题有关,无关部分删去了:
def recog_in( frame ,frame_type = 'rgb' ):
#预处理,准备好rgb和bgr格式的帧数据
if frame_type == "rgb":
rgb_frame= frame
bgr_frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) #cv2处理图像需要bgr格式
else:
rgb_frame = cv2.cvtColor(frame2, cv2.COLOR_BGR2RGB)
bgr_frame = frame
###此处省略图像处理代码若干=====emm..很多行
#总之最终要将cv2抓到的数据或者处理过的数据显示出来,这些数据都是bgr格式的
#所以要转成rgb格式的,cv2.cvtColor转出来的数据是np格式的RGB数据
rgb_npImg = cv2.cvtColor(bgr_frame, cv2.COLOR_BGR2RGB)
#然后通过QImage把np数据转成QImage格式的
#参数分别是:np数据,图像宽,图像高,图像存储宽(单位byte,R/G/B各一字节,所以是宽乘三),最后一个是数据格式
Qimg = QImage(rgb_npImg[:], rgb_npImg.shape[1], rgb_npImg.shape[0], rgb_npImg.shape[1] * 3, QImage.Format_RGB888)
#返回转出来的QImage数据
return Qimg
#在指定标签中居中显示指定图像文件
def show_img_in_lable_center( label, pix_map):
#以下是计算要居中显示宽高比跟label不一样的画面时,显示数据的实际宽高应该是多少
#不重要
img_w = pix_map.width()
img_h = pix_map.height()
lab_w = label.width()
lab_h = label.height()
if (img_w > lab_w) | (img_h > lab_h):
w_rate = float(img_w) / float(lab_w)
h_rate = float(img_h) / float(lab_h)
if w_rate >= h_rate:
w = lab_w
h = int(img_h / w_rate)
else:
w = int(img_w / h_rate)
h = lab_h
else:
w = img_w
h = img_h
#下面才是重点,用label.setPixmap()显示出来
#在label中显示缩放后pix_map数据的画面
label.setPixmap(pix_map.scaled(w, h))
QtWidgets提供有类似“打开文件对话”框的类:QFileDialog,用来供用户选择文件或文件夹,获取文件/文件夹路径。
单个文件打开 QtWidgets.QFileDialog.getOpenFileName()
多个文件打开 QtWidgets.QFileDialog.getOpenFileNames()
文件夹选取 QtWidgets.QFileDialog.getExistingDirectory()
文件保存 QtWidgets.QFileDialog.getSaveFileName()
重点介绍QFileDialog.getOpenFileName()吧,后面几个的参数类似:
为了说明QFileDialog.getOpenFileName函数的用法,先把C++版的QFileDialog::getOpenFileName()的声明放这,便于理解参数类型,使用的时候自行对应到python的格式就好啦:
QString QFileDialog::getOpenFileName (
QWidget * parent = 0,
const QString & caption = QString(),
const QString & dir = QString(),
const QString & filter = QString(),
QString * selectedFilter = 0,
Options options = 0 )
参数
第一个参数parent
:用于指定父组件。注意,很多Qt组件的构造函数都会有这么一个parent参数,并提供一个默认值0;这里要把调用资源管理器的Qt类传进去哈~如果是在类定义里调用的就传个self
进去就行。
第二个参数caption
:是对话框的标题;也就是将会显示在对话框上的标题,自行定义,string就行~
第三个参数dir
:是对话框显示时默认打开的目录:.
代表程序运行目录,/
代表当前盘符的根目录(Windows,Linux下/就是根目录了),也可以是平台相关的,比如"C:\“等;例如我想打开程序运行目录下的Data文件夹作为默认打开路径,这里应该写成”./Data/",若想有一个默认选中的文件,则在目录后添加文件名即可:"./Data/teaser.graph"
第四个参数filter
:是对话框的后缀名过滤器;比如我们使用Image Files(*.jpg *.png)
就让它只能显示后缀名是jpg或者png的文件。如果需要使用多个过滤器,使用;;
分割,比如JPEG Files(*.jpg);;PNG Files(*.png)
;
第五个参数selectedFilter
,是默认选择的过滤器;
第六个参数options
,是对话框的一些参数设定,比如只显示文件夹等等,它的取值是enum QFileDialog::Option
,每个选项可以使用|
运算组合起来。
如果我要想选择多个文件就直接使用Qt提供的getOpenFileNames()
函数咯,其返回值是一个QStringList。可以理解成一个只能存放QString的List。
实际使用中第5、6个参数我没用到,给个python中运用的实例:
from PyQt5 import QtCore, QtGui, QtWidgets
from ui_win_inputData import Ui_win_inputData
#ui_win_inputData是用QtDesigner画的一个窗口界面文件生成的python文件,界面里有个名字叫button_choosePic的按钮
class Win_InputData(QWidget, Ui_win_inputData):
def __init__(self, parent=None):
super(Win_InputData, self).__init__(parent)
self.setupUi(self)
self.button_choosePic.clicked.connect(self.choosePic)
def choosePic(self):
fname, _ = QtWidgets.QFileDialog.getOpenFileName(self ,#第一个参数,父类
'选择图片文件', #第二个参数,对话框的标题
'/Users/tanyashi/Pictures', #对话框显示时默认打开的目录
'Image files(*.jpg *.gif *.jpeg *.png)') #对话框的后缀名过滤器
#fname就是用户选择好的文件的文件名(含路径),后面就根据需求自行打开这个文件咯~
#==============================================
#再看看另外几个类似的方法使用:
#从程序运行的路径开始,让用户选择一个文件夹路径
directory1 = QFileDialog.getExistingDirectory(self,"选取文件夹","./")
print(directory1)
#----------------------------------------------
#从程序运行的路径开始,让用户选择一个所有类型或.txt格式的文件
fileName1, filetype = QFileDialog.getOpenFileName(self,"选取文件","./","All Files (*);;Text Files (*.txt)") #设置文件扩展名过滤,注意用双分号间隔
print(fileName1,filetype)
#----------------------------------------------
#从"./"开始,允许用户选择多个文件
files, ok1 = QFileDialog.getOpenFileNames(self,"多文件选择","./","All Files (*);;Text Files (*.txt)")
print(files,ok1)
#files是一个list,元素是文件名(含路径)的字符串
#----------------------------------------------
#从"./"开始,让用户选择一个用来保存文件的文件名(含路径)
fileName2, ok2 = QFileDialog.getSaveFileName(self,"文件保存","./","All Files (*);;Text Files (*.txt)")
本例给出了三种窗口打开的模板。其中主界面打开主界面的方式比较实用,可理解后作为通用模板。
本例来自pyqt5 主界面打开新主界面的实现模板,感谢分享!
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
################################################
#######创建主窗口
################################################
class FirstMainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle('主界面')
###### 创建界面 ######
self.centralwidget = QWidget()
self.setCentralWidget(self.centralwidget)
self.Layout = QVBoxLayout(self.centralwidget)
# 设置顶部三个按钮
self.topwidget = QWidget()
self.Layout.addWidget(self.topwidget)
self.buttonLayout = QHBoxLayout(self.topwidget)
self.pushButton1 = QPushButton()
self.pushButton1.setText("打开主界面")
self.buttonLayout.addWidget(self.pushButton1)
self.pushButton2 = QPushButton()
self.pushButton2.setText("打开对话框")
self.buttonLayout.addWidget(self.pushButton2)
self.pushButton3 = QPushButton()
self.pushButton3.setText("打开提示框")
self.buttonLayout.addWidget(self.pushButton3)
# 设置中间文本
self.label = QLabel()
self.label.setText("第一个主界面")
self.label.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self.label.setAlignment(Qt.AlignCenter)
self.label.setFont(QFont("Roman times", 50, QFont.Bold))
self.Layout.addWidget(self.label)
# 设置状态栏
self.statusBar().showMessage("当前用户:一心狮")
# 窗口最大化
self.showMaximized()
###### 三个按钮事件 ######
self.pushButton1.clicked.connect(self.on_pushButton1_clicked)
self.pushButton2.clicked.connect(self.on_pushButton2_clicked)
self.pushButton3.clicked.connect(self.on_pushButton3_clicked)
# 按钮一:打开主界面
windowList = []
def on_pushButton1_clicked(self):
the_window =SecondWindow()
self.windowList.append(the_window) ##注:没有这句,是不打开另一个主界面的!
self.close()
the_window.show()
# 按钮二:打开对话框
def on_pushButton2_clicked(self):
the_dialog = TestdemoDialog()
if the_dialog.exec_() == QDialog.Accepted:
pass
# 按钮三:打开提示框
def on_pushButton3_clicked(self):
QMessageBox.information(self, "提示", "这是information框!")
#QMessageBox.question(self, "提示", "这是question框!")
#QMessageBox.warning(self, "提示", "这是warning框!")
#QMessageBox.about(self, "提示", "这是about框!")
################################################
#######第二个主界面
################################################
class SecondWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle('第二主界面')
# 设置中间文本
self.label = QLabel()
self.label.setText("第二个主界面")
self.label.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self.label.setAlignment(Qt.AlignCenter)
self.label.setFont(QFont("Roman times", 50, QFont.Bold))
self.setCentralWidget(self.label)
# 设置状态栏
self.statusBar().showMessage("当前用户:一心狮")
# 窗口最大化
self.showMaximized()
###### 重写关闭事件,回到第一界面
windowList = []
def closeEvent(self, event):
the_window = FirstMainWindow()
self.windowList.append(the_window) ##注:没有这句,是不打开另一个主界面的!
the_window.show()
event.accept()
################################################
#######对话框
################################################
class TestdemoDialog(QDialog):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle('对话框')
### 设置对话框类型
self.setWindowFlags(Qt.Tool)
################################################
#######程序入口
################################################
if __name__ == "__main__":
app = QApplication(sys.argv)
the_mainwindow = FirstMainWindow()
the_mainwindow.show()
sys.exit(app.exec_())