【学习记录】QT5 的简单界面设计及错误总结

学习记录:QT5 的简单界面设计及错误总结

  • 前言
  • Qt简介
  • Qt Designer 简单使用
    • Qt Designer以及PyUIC、PyRcc安装
    • Qt Designer界面
    • 控件选择与设置
    • 窗口背景以及子控件属性继承
      • 1.rose
      • 2.sakura
      • 3.年甲子
      • 4.wish
    • UI转.py以及图片资源转.py
    • 子窗口切换
    • 效果图
  • 总结
    • 1.QMessageBox被主窗口的背景填充
    • 2.Qlabel播放视频帧仅有最后一帧的图片
  • 致谢
  • 参考

前言

本博客仅为记录Pyqt5学习之用,目的在于后续若需要相关的有资可查。在排版言语上恐有诸多纰漏,此新手接触,如有不对的地方,请大佬不吝赐教,必虚心改正。
本博客所包含的大致内容:QT5 Designer简单界面设计,记录在过程所遇到的问题及解决措施;基于PyUIC、PyRcc 的简单使用即UI转.py文件;常见控件的StyleSheet设置;多界面的切换。

Qt简介

Qt 是由Qt Company开发的跨平台C++图形用户界面应用程序开发框架,可以开发GUI程序。同时Qt是面向对象的框架,使用特殊的代码生成扩展以及一些宏,Qt很容易扩展,并且允许真正地组件编程。
Qt 支持的操作系统有很多,如 Windows、Linux, Android、iOS、WinPhone等等。

Qt Designer 简单使用

Qt Designer以及PyUIC、PyRcc安装

请参考这位大佬的博客:PyCharm安装PyQt5及其工具(Qt Designer、PyUIC、PyRcc)详细教程
本人也是参考的该博客进行的安装,讲的很详细。

Qt Designer界面

【学习记录】QT5 的简单界面设计及错误总结_第1张图片

控件选择与设置

从左侧拖出控件到界面即可,界面的对齐可对子部分可水平或者垂直layout。
快捷方式:Ctrl + 鼠标左键,拖动控件即可复制。
这里简单的拖入了五个按钮和一个qlabel以及文本框,按键垂直分布进行对齐,如下图

【学习记录】QT5 的简单界面设计及错误总结_第2张图片 【学习记录】QT5 的简单界面设计及错误总结_第3张图片

对控件或layout进行属性设置,可设置Margin指定到上下边缘的距离layoutStretch指定控件之间的分布,默认全是0。当然也可以对控件或者layout的size以及其他参数的设置。
【学习记录】QT5 的简单界面设计及错误总结_第4张图片

窗口背景以及子控件属性继承

选择控件后,在样式表更改样式即可实现背景,此处以Qlabel为例,选中qlabel===》右键 == =》改变样式表===》编辑资源 ===》使用资源。
【学习记录】QT5 的简单界面设计及错误总结_第5张图片
添加资源图片,点击图标后编辑资源,第一次没有图片则需要创建,创建后后会生成 .qrc 文件,导入目标的图片。
【学习记录】QT5 的简单界面设计及错误总结_第6张图片
这里显示的是导入三张图,一个窗口背景图,一个Qlabel的图片。
【学习记录】QT5 的简单界面设计及错误总结_第7张图片
在样式表中输入以下内容或者在输入资源时直接选择资源图片,会自动生成 url(…),即可实现对Qlabel显示图片。

QLabel { 
border-image: url(:/pic/img/jpg/1.jpg)
}
【学习记录】QT5 的简单界面设计及错误总结_第8张图片 【学习记录】QT5 的简单界面设计及错误总结_第9张图片

预览快捷键:Ctrl + R

【学习记录】QT5 的简单界面设计及错误总结_第10张图片
同理加入窗口的背景后,但是会出现子控件也加入了该背景,显然这不是我们想要的。
【学习记录】QT5 的简单界面设计及错误总结_第11张图片
在子控件使用如下样式表,可避免上述的问题。

QPushButton { 
    border-image: url();
    }

在对于一类子控件可批量设置,更改样式表如下:

.QPushButton { 
    border-image: url();
    }

总体的样式表如下,值得一提的是QPushButton:hover可以设置将鼠标放在按键上的效果,这里采用的是渐变效果。同时对于越靠近该控件的样式表优先生效,此处没有设置qlabel的样式,原本要被窗口背景所填充的,但是对qlabel单独进行设置后,显示的则为单独设置的背景。

QWidget { 
	color: rgb(67, 143, 126);
    border-image: url(:/img/dsds.png);
}

.QPushButton { 
    background-color: rgb(170, 255, 183) ;
    color:black;
    font:14pt "楷体";
    border-image: url();
}

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: 18pt "楷体";
}

QTextEdit { 
    border-image: url();
    background-color: rgba(67, 143, 126,255);
    font:20pt "楷体";
}

单独设置的qlabel样式表。

QLabel { 
   border-image: url(:/img/3.jpg)
}

最后的效果如下:(是有点丑,hhh)
【学习记录】QT5 的简单界面设计及错误总结_第12张图片
同理参照了上述的流程,也创建了以下4个子窗口界面,仅包含Qlabel,按钮,文本框,QMessageBox等,各个窗口的样式表也附后。

1.rose

# rose 样式表
.QPushButton { 
background-color: rgb(181, 220, 255) ;
color:black;
font:14pt "楷体";
border-image: url();
}

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(50,212,255,255));
font: 18pt "楷体";
}

【学习记录】QT5 的简单界面设计及错误总结_第13张图片

2.sakura

样式表参考rose,两个的样式表只有图片不一样。
【学习记录】QT5 的简单界面设计及错误总结_第14张图片

3.年甲子

# 甲子 样式表
QWidget#Form { 
border-image: url(:/img/60.jpg);
}

【学习记录】QT5 的简单界面设计及错误总结_第15张图片

4.wish

# wish 样式表
.QPushButton { 
background-color: rgb(170, 255, 183) ;
color:black;
font:14pt "楷体";
border-image: url();
}

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 "楷体";
}

QTextEdit { 
border-image: url();
font:12pt "楷体";
}

【学习记录】QT5 的简单界面设计及错误总结_第16张图片

UI转.py以及图片资源转.py

选择.ui文件使用工具中的外部工具Pyuic,将ui转化为.py。以年甲子的ui为例,最后生成.py文件。
【学习记录】QT5 的简单界面设计及错误总结_第17张图片
生成的py文件

# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'jia_zi.ui'
# Created by: PyQt5 UI code generator 5.9.2
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(385, 349)
        ...(省略内容)
    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "年甲子计算"))
        self.plainTextEdit.setPlaceholderText(_translate("Form", "请输入年份..."))
        self.button_2.setText(_translate("Form", "甲子介绍"))
        self.button_3.setText(_translate("Form", "使用说明"))
        self.button.setText(_translate("Form", "计算"))

import jiazi_rc

使用pyrcc将资源转化为Py文件,其中包含了图片的像素值。
【学习记录】QT5 的简单界面设计及错误总结_第18张图片
生成的py文件,内容较多,仅展示一部分。

# -*- coding: utf-8 -*-
# Resource object code
# Created by: The Resource Compiler for PyQt5 (Qt v5.9.6)
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore

qt_resource_data = b"\
\x00\x00\x9c\x66\
\xff\
\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\
\x48\x00\x00\xff\xdb\x00\x43\x00\x0c\x08\x09\x0a\x09\x07\x0c\x0a\
\x09\x0a\x0d\x0c\x0c\x0e\x11\x1d\x13\x11\x10\x10\x11\x23\x19\x1b\
\x15\x1d\x2a\x25\x2c\x2b\x29\x25\x28\x28\x2e\x34\x42\x38\x2e\x31\
(省略...

子窗口切换

主程序如下,主要包括了设置窗口logo,窗口名,按键的函数绑定,是否关闭主窗口。

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    app.setWindowIcon(QIcon('logo.png'))#图标
    main = main_Ui()#主窗口
    main.show()
    rs = rs_Ui()#四个子窗口
    sk = sk_Ui()
    jz = jz_Ui()
    wi = wi_Ui()
    main.setWindowIcon(QIcon('logo.png'))#图标和窗口标题
    sk.setWindowIcon(QIcon('sakura.jpg'))
    sk.setWindowTitle('漫天飞舞')
    rs.setWindowIcon(QIcon('rose.jpg'))
    rs.setWindowTitle('一见钟情')
    jz.setWindowIcon(QIcon('88.jpg'))
    jz.setWindowTitle('虎虎生威')
    wi.setWindowIcon(QIcon('1.jpg'))
    wi.setWindowTitle('新年祝福')
    main.Button1.clicked.connect(rs.show) #绑定函数,没有关闭主窗口
    main.Button2.clicked.connect(sk.show)
    main.Button3.clicked.connect(jz.show)
    main.Button4.clicked.connect(wi.show)
    #采用采用下列的语句可以关闭主窗口
    #main.Button1.clicked.connect(lambda: {main.close(), wi.show()})
    sys.exit(app.exec_())

其余的程序,也就是简单的功能实现,包括Qlabel显示图片、GIF、视频(MP4),词云的生成以及年甲子的计算(写的很烂,勿喷~~~~)

import sys
import time
import cv2
import jieba
import wordcloud
import numpy as np
import PIL.Image as image
from PyQt5.QtWidgets import QApplication,QMessageBox
from PyQt5 import QtWidgets
from PyQt5.QtGui import QIcon,QImage,QPixmap,QMovie
from PyQt5.QtCore import QTimer
from t import Ui_Form as maint     #导入个界面所转化的py文件
from jia_zi import Ui_Form as jz
from rose import Ui_Form as rs
from sakura import Ui_Form as sk
from wish import Ui_Form as wi

class main_Ui(QtWidgets.QFrame, maint):#主窗口初始化
    def __init__(self):
        super(main_Ui, self).__init__()
        self.setupUi(self)

class rs_Ui(QtWidgets.QFrame, rs):#rose初始化及按键函数绑定
    def __init__(self):
        super(rs_Ui, self).__init__()
        self.setupUi(self)
        self.gif_path = 'rose.gif'
        self.video_path = 'rose.mp4'
        self.pushButton.clicked.connect(self.start)
        self.pushButton_3.clicked.connect(self.restart)
        self.pushButton_2.clicked.connect(self.other)
   
    def restart(self):
        self.label.setStyleSheet('')
        self.label.setScaledContents(True)
        Movie = QMovie(self.gif_path)
        self.label.setMovie(Movie)
        Movie.start()

    def other(self):
        QMessageBox.about(self, '其他说明',  f'''其他功能 ''' )

    def start(self):
        if self.video_path is '':
            message = QMessageBox()
            message.critical(self.ui, '错误', '检测路径不能为空!\n请正确选择路径!')
        else:
            capture = cv2.VideoCapture(self.video_path)
            fps = capture.get(cv2.CAP_PROP_FPS)
            while True:
                ref, frame = capture.read()
                if ref:
                    QApplication.processEvents()  # 刷新界面,否则仅有最后一帧
                    time.sleep(0.8 / fps) #调整帧率
                    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                    gray = cv2.cvtColor(gray, cv2.COLOR_BGR2RGB)
                    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                    height, width, bytesPerComponent = frame.shape
                    bytesPerLine = bytesPerComponent * width
                    q_image = QImage(frame.data, width, height, bytesPerLine,
                                     QImage.Format_RGB888).scaled(self.label.width(), self.label.height())
                    self.label.setPixmap(QPixmap.fromImage(q_image))
                else:
                    break

class sk_Ui(QtWidgets.QFrame, sk): #sk初始化及函数绑定
    def __init__(self):
        super(sk_Ui, self).__init__()
        self.setupUi(self)
        global number
        number = 1
        self.pushButton.clicked.connect(self.start)
        self.pushButton_3.clicked.connect(self.restart)
        self.pushButton_2.clicked.connect(self.other)

    def restart(self):
        global number
        if number==5:
            number = 1
        else:
            number = number + 1
        capture = cv2.VideoCapture('sakura-' + str(number) + '.mp4')
        fps = capture.get(cv2.CAP_PROP_FPS)
        while True:
            ref, frame = capture.read()
            if ref:
                QApplication.processEvents()  # 刷新界面,否则仅有最后一帧
                time.sleep(0.1 / fps)
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                gray = cv2.cvtColor(gray, cv2.COLOR_BGR2RGB)
                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                height, width, bytesPerComponent = frame.shape
                bytesPerLine = bytesPerComponent * width
                q_image = QImage(frame.data, width, height, bytesPerLine,
                                 QImage.Format_RGB888).scaled(self.label.width(), self.label.height())
                self.label.setPixmap(QPixmap.fromImage(q_image))
            else:
                break

    def other(self):
        QMessageBox.about(self, '其他说明',  f''' 其他功能预留按钮 '''  )

    def start(self):
        capture = cv2.VideoCapture('sakura-1.mp4')
        fps = capture.get(cv2.CAP_PROP_FPS)
        while True:
            ref, frame = capture.read()
            if ref:
                QApplication.processEvents()  # 刷新界面,否则仅有最后一帧
                time.sleep(0.1 / fps)
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                gray = cv2.cvtColor(gray, cv2.COLOR_BGR2RGB)
                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                height, width, bytesPerComponent = frame.shape
                bytesPerLine = bytesPerComponent * width
                q_image = QImage(frame.data, width, height, bytesPerLine,
                                 QImage.Format_RGB888).scaled(self.label.width(), self.label.height())
                self.label.setPixmap(QPixmap.fromImage(q_image))
            else:
                break

class jz_Ui(QtWidgets.QFrame, jz): #甲子的初始化及函数绑定
    def __init__(self):
        super(jz_Ui, self).__init__()
        self.setupUi(self)
        self.button.clicked.connect(self.handle_Calc)
        self.button_2.clicked.connect(self.introduce)
        self.button_3.clicked.connect(self.usage)

    def introduce(self):
        QMessageBox.about(self, '甲子介绍', f'''六十甲子介绍 ''')

    def usage(self):
        QMessageBox.about(self, '使用说明',
                          f'''
                                仅适用于公元正整数年份!\n
                                请勿输入小数!\n
                                请勿输入汉字或特殊字符!\n
                            '''
                          )

    def handle_Calc(self):
        self.tian = {1: '甲', 2: '乙', 3: '丙', 4: '丁', 5: '戊', 6: '己',
                   7: '庚', 8: '辛', 9: '壬', 0: '癸'}
        self.di = {1: '子', 2: '丑', 3: '寅', 4: '卯', 5: '辰', 6: '巳',
                   7: '午', 8: '未', 9: '申', 10: '酉', 11: '戌', 0: '亥'}
        info = self.plainTextEdit.toPlainText()
        if not info.isdigit():
            QMessageBox.critical(self, '错误', f'''请检查输入年份!''')
            self.plainTextEdit.clear()
        else:
            year = float(info)
            years = int(year/1)
            flag  = year % 1
            if flag == 0:
                if years > 0:
                    tian_gan = (years - 3) % 10
                    di_zhi = (years - 3) % 12
                    for i in self.tian.keys():
                        if tian_gan == i:
                            for j in self.di.keys():
                                if di_zhi == j:
                                    result_tian = self.tian[i]
                                    result_di = self.di[j]
                                    QMessageBox.about(self,
                                                      '计算结果', f'''公元{int(years)}年是{result_tian}{result_di}年''')
                else:
                    QMessageBox.about(self, '温馨提示', f'''暂不支持公元前年份''',)
            else:
                QMessageBox.about(self, '温馨提示', f'''请查询整数年份''')
            self.plainTextEdit.clear()


class wi_Ui(QtWidgets.QFrame, wi): #新年祝福初始化及函数绑定
    def __init__(self):
        super(wi_Ui, self).__init__()
        self.setupUi(self)
        self.pushButton_2.clicked.connect(self.creat)
        self.pushButton_3.clicked.connect(self.make)

    def creat(self):
        text = '虎年祝福'
        self.textEdit.setPlainText(text)

    def make(self):
        t = self.textEdit.toPlainText()
        mask = np.array(image.open("hu.png"))
        ls = jieba.lcut(t)
        txt = " ".join(ls)
        w = wordcloud.WordCloud(mask=mask,font_path="msyh.ttc", background_color="red", )
        w.generate(txt)
        w.to_file("新年祝福.png")
        self.label.setStyleSheet('')
        PixMap = QPixmap("新年祝福.png")
        # PixMap = PixMap.scaled(hight, width)
        # self.ui.label.resize(hight, width)
        self.label.setPixmap(PixMap)

效果图

【学习记录】QT5 的简单界面设计及错误总结_第19张图片

总结

1.QMessageBox被主窗口的背景填充

原因:QMessageBox也是有文本框以及Qlabel组成,主窗口的背景直接继承到QMessageBox中导致。
解决办法:指定主窗口背景不影响到其他的子控件。如使用 # 指定QWidget名字为Form 的窗口背景,而不会影响子控件。

QWidget#Form {
border-image: url(:/img/60.jpg);
}

2.Qlabel播放视频帧仅有最后一帧的图片

原因:Qlabel展示视频帧,刷新速度过快在窗口无法显示导致。
解决办法:可进行延时,一般每秒25帧就ok了,同时每帧刷新界面,保证帧与帧之间的过渡。

刷新界面:QApplication.processEvents() # 刷新界面,否则仅有最后一帧

致谢

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

参考

  1. 白月黑羽: http://www.byhy.net
  2. PyCharm安装PyQt5及其工具(Qt Designer、PyUIC、PyRcc)详细教程
  3. 百度百科:https://baike.baidu.com/item/Qt/451743?fr=aladdin

你可能感兴趣的:(python,学习记录,qt5)