学习记录:QT5 界面设计的踩坑记录
前言
本博客仅为记录Pyqt5学习之用,记录在过程所遇到的问题及解决措施,目的在于后续相关的内容有资可查。所包含的大致内容: Qlabel显示视频与图片;界面关闭程序仍旧运行;常见控件的StyleSheet设置;窗口退出确认。
一、Qlabel显示视频与图片
1. 图片显示
本文是基于yolox-tiny的检测,下面介绍其中的重要语句。
image = Image.open(pic_path)
r_image, _, _, _ = yolo.detect_image(image)
frame = np.array(r_image)
showImage = QImage(frame.data, frame.shape[1], frame.shape[0], 3*frame.shape[1], QImage.Format_RGB888)
self.label_2.setPixmap(QPixmap.fromImage(showImage))
QApplication.processEvents()
self.label_2.setScaledContents(True)
1.1 显示格式
label.setPixmap(QPixmap.fromImage(showImage))
#若采用下面的语句,有时候导致图片不能显示完整或者被斜着划分为两部分。故QImage参数加上 3*frame.shape[1] 很重要。
showImage = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
1.2 label随界面缩放
label.setScaledContents(True)
1.3 界面刷新
QApplication.processEvents()
2. 视频显示
capture = cv2.VideoCapture(0) # 获取摄像头
capture = cv2.VideoCapture(video_path) # 打开video_path
while (Ture):
ref, frame = capture.read() #得到一帧图像
if ref:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) #BGR转RGB
frame = Image.fromarray(np.uint8(frame)) # 转变成Image
frame, _, _, _ = yolo.detect_image(frame)
frame = np.array(frame)
height, width, bytesPerComponent = frame.shape
bytesPerLine = bytesPerComponent * width # 格式设置
q_image = QImage(frame.data, width, height, bytesPerLine,
QImage.Format_RGB888).scaled(self.label_2.width(), self.label_2.height())
self.label_2.setPixmap(QPixmap.fromImage(q_image))
QApplication.processEvents() #刷新界面
self.label_2.setScaledContents(True)# 缩放
else: # 到达最后一帧
capture.release() # 释放capture
cv2.destroyAllWindows() #释放窗口
break
二、常见控件的StyleSheet设置
先上效果图,效果如下
1. 控件结构
此处选择了4个QFrame,使得各部分各自属于一个QFrame,可以更好的设置效果。利用不同的分布以及layoutstretch调整不同的比例。
2. 控件样式表
2.1 样式表格式
样式表格式 | 示例 | 作用 |
---|---|---|
控件类型#控件名 | QWidget#Form | 指定该对象名的控件,避免影响其他的控件 |
.控件类型 | .QRadioButton | 选择该类型的控件全体,以便批量设置同一类型的样式 |
控件类型[属性选择] | QPushButton[flat=“false”] | 选择所有flat属性值为false的 QPushButton类型 |
目前接触的内容较少,后续可继续补充… | 待补充 | 待补充 |
控件属性 | 示例 | 作用 |
---|---|---|
color | color:black; | 设置控件文字颜色,如黑色 |
border-image | border-image: url(:function.png); | 设置控件的边缘图片,如function.png |
font | font:12pt “楷体”; | 设置字体和字号,如12个像素的楷体 |
border | border:1px groove gray; | 设置边缘,如一个像素的灰线 |
border-radius | border-radius:10px or border-radius:1px 2px 3px 4px; | 设置边缘圆角半径,一个值就是四个半径一样;四个值意味着左上角、右上角、右下角、左下角(顺时针顺序)如10个像素为半径;顺时针依次为1,2,3,4像素半径 |
padding | padding:2px 4px; or padding:1px 2px 3px 4px; | 设置填充宽度或者称留白距离,也是有以top为起始的顺时针四个值; 如上下2像素,左右4像素,顺时针1 2 3 4像素 |
目前接触的内容较少,后续可继续补充… | 待补充 | 待补充 |
2.2样式表示例
当一个控件的样式被多层设置,最靠近控件设置生效,也就是说其优先级越高。
# 选择名字为Form 的QWidget设置样式
QWidget#Form {
color: rgb(67, 143, 126);
border-image: url(:/content/pic/function.png);
}
# 选择全体的QRadioButton 进行设置
.QRadioButton {
background-color: rgba(170, 200, 255, 255) ;
color:black;
font:12pt "楷体";
border-image: url();
border:1px groove gray;
border-radius:10px;
padding:2px 4px;
}
# 选择全体的QPushButton 进行设置,此处有伪状态,停留按键在按键上可实现渐变颜色
.QPushButton {
background-color: rgba(156, 214, 255, 255) ;
color:black;
font:14pt "楷体";
border-image: url();
border:1px groove gray;
border-radius:12px;
padding:2px 6px;
}
QPushButton:hover{
background-color: qlineargradient(spread:pad, x1:0, x2:1, y1:0, y2:0,
stop: 0 rgba(44,180,255,255),
stop: 0.495 rgba(80,237,255,55),
stop: 0.505 rgba(172,200,216,20),
stop: 1 rgba(23,212,255,255));
font: 15pt "楷体";
}
QPushButton:hover{
background-color: qlineargradient(spread:pad, x1:0, x2:1, y1:0, y2:0,
stop: 0 rgba(44,180,255,255),
stop: 0.495 rgba(80,237,255,55),
stop: 0.505 rgba(172,200,216,20),
stop: 1 rgba(23,212,255,255));
以上hover表示鼠标位于按键上方时的样式,其中qlineargradient表示线性渐变,x1==>x2表示水平方向的颜色变化;y1==>y2表示竖直方向的颜色变化。x1=x2,y1 != y2,则竖直方向渐变;x1!=x2,y1=y2则水平方向渐变。都不相等否则是对角线方向渐变。四个stop则是水平和竖直方向的颜色变换范围。
三、界面关闭程序仍旧运行
我们期望当界面关闭后程序也几乎同时停止,但是有时候会发现界面关闭后,程序仍在进行,以本检测为例,界面关闭后笔记本的摄像头仍没有退出,并且还在检测视频,一直占用内存,故关闭界面同时结束进程是很有必要的。
产生原因:多半是代码中具备死循环或者长时间的循环以及长时间的遍历,如 while ture, for i in range(0, large-number) 等等, 导致在界面关闭时还没有循环结束。
解决办法大致分3种(鄙见哈):
- 利用进程,如守护进程之类的方法,当某进程关闭也关闭子进程。
- 不利用进程,在关闭界面时,强制退出当前的程序。
- 利用关闭的标志位,进行判断以结束循环。
1. 守护进程
本文没有采取该方法,仅提供一个思路。
2. 强制退出
利用sys.exit(),os._exit()之类的函数强制退出。
sys.exit()(推荐)
此函数通过抛出一个SystemExit异常来尝试结束程序,Python代码可以捕获这个异常来进行一些程序退出前的清理工作,也可以不退出程序。sys.exit函数可以带一个参数(默认为0)来作为程序的退出码。
os._exit() (不推荐)
此函数直接退出python解释器,函数后的所有代码都不执行。
3. 标志位退出
如以 窗体.Hide() = Ture 和窗体.Visible =Ture 为条件,判断是否当前窗体隐藏,如窗体不可见,可利用标志位结束循环,以退出程序。当然要明确是否是循环导致的无法退出。
四、界面退出确认
退出的确认也是比较重要的,有效地避免因为误操作或者误触摸导致的关闭。与此同时相比较前面所提到的结束进程的方法中,可直接利用sys.exit() 等强制退出,简单粗暴。
对于多个窗口,也可以在此对其余界面进行简单初始化或者恢复到初始条件的设置,如清空qlabel的内容等等。
def closeEvent(self, event): # 固定函数名,切勿修改
reply = QtWidgets.QMessageBox.question(self, u'警告', u'确认退出?', QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No)
if reply == QtWidgets.QMessageBox.Yes:
event.accept() # 关闭窗口
sys.exit() # 强制退出
else:
event.ignore() # 判断为误操作,不关闭窗口
五、cv2摄像头调用与释放
1. cv2调用摄像头
opencv进行摄像头的调用,还算简单。
利用下列语句就可以调用,其中0是代表摄像头编号,只有一个的话默认为0,否则需要修改指定的摄像头编号。
cap = cv2.VideoCapture(0)
利用下列语句进一步得到一帧图像,进行显示或者后续工作,其中frame是图像信息,而正确读取,ref=Ture,类似于标志位。
ref, frame = capture.read()
但是需要注意的是cv2的颜色通道是BGR,一般需要转化为RGB。
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
点击ESC,手动退出
#等待1秒显示图像,若过程中按“Esc”(key=27)退出
c = cv.waitKey(1) & 0xff
if c == 27:
# 释放所有窗口
cv.destroyAllWindows()
break
2. cv2释放摄像头
值得一提的是,cv2释放摄像头和调用是成对出现的。一旦没释放很可能造成窗口关闭程序还在运行。利用下列语句可实现摄像头释放。
cv2.VideoCapture.release()
但是一旦前文对调用命名后,必须采取相同的名字才能得以释放,如采用上述的语句会直接抛出错误,所以注意要对应释放。
cap = cv2.VideoCapture(0)
#省略中间代码
cap.release()
致谢
欲尽善本文,因所视短浅,怎奈所书皆是瞽言蒭议。行文至此,诚向予助与余者致以谢意。
参考
- 白月黑羽: http://www.byhy.net