上一节中,我们已经完成了程序的界面设计,并初步编写了主函数可以生成程序的界面。这一节将介绍如何编写各控件的调用函数,实现摄像头参数的调控,并通过time函数实现摄像头的实时图像显示。
在上一节中,我们通过QtDesigner生成了程序界面的ui文件,然后通过PyUIC将该ui文件转换成了py文件。在PyCharm中打开pyuic生成的py文件:“QtTest.py”,该文件的主要内容如下所示:
该文件中定义了一个界面类Ui_CamShow,在该类中定义了两个函数setupUi和retranslateUi。展开setupUi函数可以发现,该函数的内容正是我们在QtDesigner中进行的各种控件、布局的创建和属性设置。而retranslateUi函数主要实现各控件的显示设置。由于setupUi函数的末尾调用了retranslateUi函数,因此我们在主函数中只需要调用setupUi函数就可以生成程序界面。
在上一节中,为了观察程序界面的运行效果,我们简单编写了一下主函数,内容如下图所示。首先,我们从创建好的QtTest中导入界面类Ui_CamShow,然后导入程序需要的各种库函数。接下来,我们定义了一个新的类CamShow,用来实现程序的主要功能。程序的最后一段,“if name == ‘main’:”后面的内容是程序的主函数,它的内容很少,在后续工作中也不再进行修改,其功能就是控制程序的开始和结束,实例化CamShow类。我们的主要代码内容都放在CamShow类中。
目前,在CamShow类中,我们只定义了一个初始化函数“init(self,parent=None):”,在实例化CamShow类时,会自动调用它的初始化函数,因此,我们把想要让程序自动实现的程序功能都放在该函数下。目前,该函数下调用了两个函数:super函数实现了类的继承,将QMainWindow,Ui_CamShow的各种属性过继给CamShow类;setupUI函数则是从Ui_CamShow中继承过来的函数,用来实现程序界面的创建和属性设置。
super(CamShow,self).__init__(parent)
self.setupUi(self)
接下来,我们在初始化函数“init(self,parent=None):”中继续添加新的函数,来实现程序的功能。我们添加的函数如下图所示,根据我们程序的需要,一共添加六个函数,函数PrepSliders用来实现各个slider和对应的spinbox的关联,保证两个控件的值始终相等;函数PrepWidgets用来初始化各个控件,由于某些控件需要在其它控件背后的函数运行后才能工作,因此需要先将它“disable”,例如,“保存”功能,必须在我们点击“开始”按钮开始取图后才有效,因此在程序最开始的时候,需要将“保存”按钮“disable”;PrepParameters用来定义并初始化程序运行过程中会用到的变量;CallBackFunctions则是各个控件背后的功能函数的集合,它定义了我们在程序界面上进行某项操作后实际执行的代码;最后两行,则是计时器的定义和调用,它定义了一个定时器,等我们执行计时器开始的代码后,该计时器就开始计时,每次计时结束都会调用一次函数TimerOutFun,我们用计时器实现对摄像头图像的循环读取和显示。当然,这些函数都是我们自己定义的函数,我们后面的工作就是编写代码实现这些函数的功能。
def __init__(self,parent=None):
super(CamShow,self).__init__(parent)
self.setupUi(self)
self.PrepSliders()
self.PrepWidgets()
self.PrepParameters()
self.CallBackFunctions()
self.Timer = QTimer()
self.Timer.timeout.connect(self.TimerOutFun)
另外,由于我们定义了很多新的函数,这些函数会调用到很多新的库函数,我们需要把对应的库全部import进来。我们需要导入的所有库函数如下所示:
from QtTest import Ui_CamShow
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QFileDialog
from PyQt5.QtCore import QTimer,QCoreApplication
from PyQt5.QtGui import QPixmap
import cv2
import qimage2ndarray
import time
接下来,我们来逐个实现刚才定义的这些函数。
PrepSliders函数的实现代码如下所示,我们一共定义了7对slider和spinbox,我们需要逐个地将它们关联起来。函数的代码其实很容易理解,基本上就像读英语一样。以前两行为例,它们的功能就是一旦RedColorSld的值发生变化后,就调用RedColorSpB的setValue函数,将变化后的值赋给RedColorSpB;反过来,一旦RedColorSpB的值发生变化后,就调用RedColorSld的setValue函数,将变化后的值赋给RedColorSld。这样一来,RedColorSld和RedColorSpB就关联起来了,不管哪一个的值发生改变,另一个也会随之而改变。
def PrepSliders(self):
self.RedColorSld.valueChanged.connect(self.RedColorSpB.setValue)
self.RedColorSpB.valueChanged.connect(self.RedColorSld.setValue)
self.GreenColorSld.valueChanged.connect(self.GreenColorSpB.setValue)
self.GreenColorSpB.valueChanged.connect(self.GreenColorSld.setValue)
self.BlueColorSld.valueChanged.connect(self.BlueColorSpB.setValue)
self.BlueColorSpB.valueChanged.connect(self.BlueColorSld.setValue)
self.ExpTimeSld.valueChanged.connect(self.ExpTimeSpB.setValue)
self.ExpTimeSpB.valueChanged.connect(self.ExpTimeSld.setValue)
self.GainSld.valueChanged.connect(self.GainSpB.setValue)
self.GainSpB.valueChanged.connect(self.GainSld.setValue)
self.BrightSld.valueChanged.connect(self.BrightSpB.setValue)
self.BrightSpB.valueChanged.connect(self.BrightSld.setValue)
self.ContrastSld.valueChanged.connect(self.ContrastSpB.setValue)
self.ContrastSpB.valueChanged.connect(self.ContrastSld.setValue)
这里需要注意的是,我们需要保证slider和对应的spinbox的取值范围是一致的,也就是它们的最大和最小值必须相等,这一点,我们在QtDesigner设计时可以设置它们的相关属性来保证,在接下来的程序编写过程中,如果涉及到它们的最大或最小值变动时也要保证slider和spinbox同时变动。
PrepWidgets函数的内容如下所示,首先要调用一个PrepCamera函数,这个函数我们后面再编写,接下来,就是在开始取图前,将相关的控件都“disable”,设置方法就是调用“setEnabled”函数,并给它赋值为False,类似地,如果赋值为True的话就是将该控件“enable”。
def PrepWidgets(self):
self.PrepCamera()
self.StopBt.setEnabled(False)
self.RecordBt.setEnabled(False)
self.GrayImgCkB.setEnabled(False)
self.RedColorSld.setEnabled(False)
self.RedColorSpB.setEnabled(False)
self.GreenColorSld.setEnabled(False)
self.GreenColorSpB.setEnabled(False)
self.BlueColorSld.setEnabled(False)
self.BlueColorSpB.setEnabled(False)
self.ExpTimeSld.setEnabled(False)
self.ExpTimeSpB.setEnabled(False)
self.GainSld.setEnabled(False)
self.GainSpB.setEnabled(False)
self.BrightSld.setEnabled(False)
self.BrightSpB.setEnabled(False)
self.ContrastSld.setEnabled(False)
self.ContrastSpB.setEnabled(False)
PrepCamera函数的代码内容如下所示,为了防止程序出错退出,我们采用了try…except…语句,它实现的功能是先尝试运行try语句后面的代码,如果没有问题就继续往下运行,如果出错就执行except后面的代码。
def PrepCamera(self):
try:
self.camera=cv2.VideoCapture(0)
self.MsgTE.clear()
self.MsgTE.append('Oboard camera connected.')
except Exception as e:
self.MsgTE.clear()
self.MsgTE.append(str(e))
我们先来看try后面的代码,self.camera=cv2.VideoCapture(0)
,的功能就是调用OpenCV的VideoCapture函数打开摄像头,并使用变量self.camera代表该摄像头,参数0意味着打开笔记本自带的摄像头。self.MsgTE.clear()
:清空文本框MsgTE中的内容。self.MsgTE.append('Oboard camera connected.')
:在文本框MsgTE中显示’Oboard camera connected.’,其中append函数意思是在现有的内容后继续添加内容,除此之外,textedit控件还有setPlainText函数,意识是用括号中的文本覆盖原来的文本。
而except语句后面的内容则是捕获出错内容Exception,并将其内容显示在文本框MsgTE中,这样,当程序出错时,程序就不会自动退出,而且我们可以通过文本框MsgTE查看出错信息,帮助我们找到问题所在。
PrepParameters函数的代码如下所示。我们定义了变量self.RecordFlag,该值为0时不保存视频,为1才时开始保存视频。变量self.RecordPath定义了默认的文件存储路径。self.FilePathLE.setText(self.RecordPath)
将路径名显示在文本框FilePathLE中。变量self.Image_num定义了读取图片的次数,self.R、self.G、 self.B为三个颜色通道的强度增益系数。
def PrepParameters(self):
self.RecordFlag=0
self.RecordPath='D:/Python/PyQt/'
self.FilePathLE.setText(self.RecordPath)
self.Image_num=0
self.R=1
self.G=1
self.B=1
self.ExpTimeSld.setValue(self.camera.get(15))
self.SetExposure()
self.GainSld.setValue(self.camera.get(14))
self.SetGain()
self.BrightSld.setValue(self.camera.get(10))
self.SetBrightness()
self.ContrastSld.setValue(self.camera.get(11))
self.SetContrast()
self.MsgTE.clear()
后面的内容用来初始化摄像头参数控件的值,即读取摄像头的曝光、增益、亮度、对比度等参数,并将这些值显示在相应的控件上。摄像头参数读取函数为self.camera.get(),括号中的不同序号代表不同的参数,具体的对应关系可见链接cvGetCaptureProperty。
CallBackFunctions函数的代码内容如下所示,它定义了每一个功能控件的触发条件和相应的回调函数。通常来讲,button的触发条件为clicked,即鼠标单击按钮后调用相应的函数;checkbox的触发条件为stateChanged,即单击使其状态发生变化后调用相应函数;slider的触发条件可选择valueChanged、sliderRressed、sliderReleased等,可以根据需要选择触发条件。当然,各个回调函数需要我们后面再分别编写。
def CallBackFunctions(self):
self.FilePathBt.clicked.connect(self.SetFilePath)
self.ShowBt.clicked.connect(self.StartCamera)
self.StopBt.clicked.connect(self.StopCamera)
self.RecordBt.clicked.connect(self.RecordCamera)
self.ExitBt.clicked.connect(self.ExitApp)
self.GrayImgCkB.stateChanged.connect(self.SetGray)
self.ExpTimeSld.valueChanged.connect(self.SetExposure)
self.GainSld.valueChanged.connect(self.SetGain)
self.BrightSld.valueChanged.connect(self.SetBrightness)
self.ContrastSld.valueChanged.connect(self.SetContrast)
self.RedColorSld.valueChanged.connect(self.SetR)
self.GreenColorSld.valueChanged.connect(self.SetG)
self.BlueColorSld.valueChanged.connect(self.SetB)
上面代码中,我们一共调用了13个回调函数,程序功能的具体实现的主要通过这些函数来实现,下一节我们将继续介绍这些函数的具体内容。