【学习记录】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类型
目前接触的内容较少,后续可继续补充…待补充待补充
控件属性 示例 作用
colorcolor:black;设置控件文字颜色,如黑色
border-imageborder-image: url(:function.png);设置控件的边缘图片,如function.png
fontfont:12pt “楷体”;设置字体和字号,如12个像素的楷体
borderborder:1px groove gray;设置边缘,如一个像素的灰线
border-radiusborder-radius:10px or border-radius:1px 2px 3px 4px;设置边缘圆角半径,一个值就是四个半径一样;四个值意味着左上角、右上角、右下角、左下角(顺时针顺序)如10个像素为半径;顺时针依次为1,2,3,4像素半径
paddingpadding: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 turefor i in range(0, large-number) 等等, 导致在界面关闭时还没有循环结束。

解决办法大致分3种(鄙见哈):

  1. 利用进程,如守护进程之类的方法,当某进程关闭也关闭子进程。
  2. 不利用进程,在关闭界面时,强制退出当前的程序。
  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()

致谢

欲尽善本文,因所视短浅,怎奈所书皆是瞽言蒭议。行文至此,诚向予助与余者致以谢意。

参考

  1. 白月黑羽: http://www.byhy.net
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值