PyQt5开发问题及技巧合集

持续更新中。。。。。。

===================================================================

1. 点击按钮后改变按钮文字

问题描述:点击按钮后,按钮文本由‘开始任务’变为‘任务中。。。’。只写setText(’变为‘任务中。。。’),按钮文字没有改变。

解决办法:按钮性质改变后,需要self.pushButton.repaint()

2. tableWidget实现实时添加新行

1)实时添加新行

current_row_count = self.tableWidget.rowCount() # 获取当前行数

for ...:

        cur_row_count += 1                            

        self.tableWidget.setRowCount(cur_row_count) # 设置行数                                    

        temContent = QTableWidgetItem(("%s") %(res['data'][1]))

        self.tableWidget.setItem(cur_row_count-1, 0, itemContent) # 新行添加内容,注意-1

2)设置tableWidget列宽

注意:当设置列宽为自动适应内容时,在多次查询显示结果可能会有卡顿

# 设置固定列宽为80
table_obj.horizontalHeader().setDefaultSectionSize(80)

# 用户可调整,默认值为setDefaultSectionSized的值
table_obj.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
# 用户不可调整,默认值为setDefaultSectionSized的值
table_obj.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed)
# 用户不可调整,自动平分适应可用区域
table_obj.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# 用户不可调整,自动适应内容的宽度
table_obj.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
# 用户可调整,默认值为setDefaultSectionSized的值
table_obj.horizontalHeader().setSectionResizeMode(QHeaderView.Custom)

 3. PyQt5之QPainter绘图

1)导入包:

from PyQt5.QtGui import QPainter, QColor, QPen, QBrush
from PyQt5.QtCore import QT

2)在label组件中绘图

# 在label中绘图,就是创建一个QPixmap画布,然后放入到label中,在label的pixmap上绘图
canvas = QtGui.QPixmap(950, 300)
canvas.fill(QtCore.Qt.white)
self.label.setPixmap(canvas)
qp = QtGui.QPainter(self.label_2.pixmap())

3)旋转文字

# 旋转前要先保存qp,以便旋转后恢复坐标系,否则之后的坐标系乱了
qp.save() # 保存,以便旋转后恢复坐标系
qp.rotate(-90) # 顺时针旋转
qp.drawText(-110, 5 + (m + l), 50, 30, 1, 'Hello World') # 参数:x,y,width,height,flag,text,旋转-90度后x,y坐标轴数据要交换。这里的flag不清楚是什么作用
qp.restore() # 保存后,要重新加载,否则有警告

4)QPen画直线

pen = QtGui.QPen()
pen.setWidth(8)
pen.setColor(QtGui.QColor(200,0,0)) # 改变画笔颜色
qp.setPen(pen)
qp.setFont(QtGui.QFont("Monaco", 12)) # 设置字体大小
qp.drawLine(10, 115, 10, 155) # 参数:x1,y1,x2,y2

5)QPen画矩形,QBrush画刷填充

brush = QtGui.QBrush(QtCore.Qt.DiagCrossPattern)

pen=QtGui.QPen(QtCore.Qt.black,3,QtCore.Qt.SolidLine)
qp.setBrush(brush)  
qp.setPen(pen)
# 画管线\\
for i in range(len(self.mileage) - 1):
    m = int(self.mileage[i] / 2)
    l = int(self.mileage[i+1] / 2) - m # 管长
    if i < len(self.mileage) - 2:
                # 画粗管线
        qp.drawRect(10 + m, 115, l, 40)
    else:
        # 画细管线
         qp.drawRect(10 + m, 120, l, 30)

4. PyQt5之多线程

pyqt5多线程,解决执行耗时任务时界面假死。Qt有两种多线程的方法:

第一种:是继承QThread的run函数

第二种:是把一个继承于QObject的类用moveToThread函数转移到一个Thread里。

QTimer相当于一个定时器,每当定时器时间溢出后,会执行相关的函数。这个时候程序会从主线程界面跳到QTimer函数中,如果QTimer函数中有延时或者处理时间较长,就会出现界面失去响应,造成界面卡顿的现象。

1)创建QObject方式

据此文章报道,继承QThread实现多线程,是一种错误的做法。QtCore.QThread是一个管理线程的类,当我们使用其构造函数的时候,便新建了一个线程。这里要强调,QThread是一个线程管理器,不要把业务逻辑放在这个类里面,Qt的作者已经多次批评继承QThread类来实现业务逻辑的做法。正确的姿势应该是:将业务逻辑写在一个继承了QtCore.QObject的子类里面,然后新建一个实例。然后调用继承了父类的方法moveToThread方法,把该对象放进线程里面。

第一步:创建一个QObject子类

该类包含要在子线程中运行的代码,以及在子线程运行过程中需要发回主线程的信号。

import sys
from PyQt5.Qt import (QApplication, QWidget, QPushButton, QMutex)
from PyQt5.QtCore import QObject, pyqtSignal, QThread, pyqtSlot


class FooObject(QtCore.QObject):
    # signal 要在__init__方法之前定义
    foo_signal = QtCore.pyqtSignal(int)
    stop_signal = QtCore.pyqtSignal()
 
    def __init__(self):
        super(FooObject, self).__init__()
 
    @pyqtSlot()
    def run(self):
        counter = 0
        for _ in range(10):
            for i in range(5):  
                self.foo_signal.emit(counter)  # 发射信号,参数将被槽函数接收
                counter += 1
            QtCore.QThread.sleep(1)
        self.stop_signal.emit()  # 这种方式的多线程任务一定要写个发送线程结束的信号

'''
注意:
1-业务逻辑结束后发送信号,以便结束线程
'''

第二步:实例化QObject子类,并转移到子线程中。

def click(self):
    self.thread = QThread()
    self.foo_obj = FooObject() # 实例化Object类
    self.foo_obj.moveToThread(self.thread) # 将实例移动到线程种
    self.foo_obj.foo_signal.connect(print)  # Object信号连接的槽函数
    self.foo_obj.stopsignal.connect(self.stop_thread) # Objec运行结束信号连接的槽函数
    self.thread.started.connect(self.foo_obj.run) # QThread的started连接实例的运行函数
    self.thread.start()

'''
注意:
1-实例中的业务逻辑处理完后要发送个结束信号,来结束线程。线程不会自己结束,不发送结束信号,下次启动任务会报错未结束的线程被销毁,导致闪退。

2-实例移动到线程中,并不会重写线程的run,所以要线程的started连接到实例的业务函数,然后再thread.start(),否则线程不起作用
'''

第三步:结束线程

def stop_thread(self):
    print('stop thread')
    # self.thread.quit()
    self.thread.terminate() # 强制结束线程

注意:

1- 创建实例时,在业务逻辑结束后要发送结束信号

2- 开启线程,要将线程的started连接到Object实例的逻辑函数上

3- 结束线程,业务逻辑处理完后结束线程

4- 本方法不能连续点击按钮,否则会报线程未结束就被销毁的错误,导致闪退。所以在启动线程前要禁用按钮。业务逻辑结束后取消禁用。或者在业务逻辑函数中加线程锁。

# 方式一:添加线程锁

qmut = QMutex() # 创建线程锁

def run(self):
    qmut.lock() # 加锁
    values = [1, 2, 3, 4, 5]
    for i in values:
        print(i)
        time.sleep(0.5)  # 休眠
    qmut.unlock() # 解锁


# 方式二:禁用按钮
def click(self):
    self.btn.setEnabled(False)
    。。。
    self.thread.start()

def set_btn(self):
    # 一般写在线程结束函数中
    self.thread.quit()
    self.btn_2.setEnabled(True)

2)使用pyqt5的QThread

继承QThread,并重写QThread中的run方法。用此种方式写的多线程可以多次点击同一按钮,在线程没结束时点击,会再启动一个新线程,所以并不会报错闪退。

QThread方式,代码省略。。。

注意的是:

1-耗时任务不能写在窗口类中;

2-操作窗口组件,要有窗口类操作。线程发出信号给窗口类中的方法,由窗口类中的方法来操纵,不能让线程来直接操作。

5. PyQt5启动界面 

 PyQt的启动画面通过QSplashScreen类来快捷制作,支持透明图片

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import time
import sys
#重写QSplashScreen类
class MySplashScreen(QSplashScreen):
    # 鼠标点击事件
    def mousePressEvent(self, event):
        pass

# 主界面
class MyWindow(QMainWindow):
    # 初始化MenuDemo子类
    def __init__(self, parent=None):
        super(MyWindow, self).__init__(parent)
        self.setWindowTitle("Demo")
        # 宽×高
        self.resize(600, 600)
        # 最小窗口尺寸
        self.setMinimumSize(600,500)
        self.btn = QPushButton('关闭窗口')
        self.btn.clicked.connect(self.fun_Exit)
        self.setCentralWidget(self.btn)

    def load_data(self, sp):
        for i in range(1, 11):  # 模拟主程序加载过程
            time.sleep(1)  # 加载数据
            sp.showMessage("加载... {0}%".format(i * 10), QtCore.Qt.AlignHCenter | QtCore.Qt.AlignBottom, QtCore.Qt.black)
            QtWidgets.qApp.processEvents()  # 允许主进程处理事件

    # 退出菜单响应
    def fun_Exit(self):
        response_quit = QApplication.instance()
        response_quit.quit()

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    #设置启动界面
    splash = MySplashScreen()
    #初始图片
    splash.setPixmap(QPixmap('D:\image_process\image_process\Lena2.jpg'))  # 设置背景图片
    #初始文本
    splash.showMessage("加载... 0%", QtCore.Qt.AlignHCenter | QtCore.Qt.AlignBottom, QtCore.Qt.black)
    # 设置字体
    splash.setFont(QFont('微软雅黑', 10))
    # 显示启动界面
    splash.show()
    # # 显示静态启动信息
    # splash.showMessage('正在加载……')

    app.processEvents()  # 处理主进程事件
    #主窗口
    window = MyWindow()
    window.load_data(splash)  # 动态显示加载数据进度
    window.show()

    # 关闭启动画面
    splash.close() 

    app.exec_()

 为避免开始时,鼠标点击启动界面图片闪退问题,重写了QSplashScreen类的mousePressEvent事件

参考连接:PyQt5中QSplashScreen实现软件启动界面-CSDN博客

6. QSS样式

1). 基础语法

QSS同CSS语法规则类似,形式如下:
selector{attribute:value};
其中:
selector选择器:通常情况下为控件类名(如QPushButton);
attribute属性:待设置的样式表属性(如background-color);
value值:属性赋值(如rgb(40, 85, 20);)

引用图片:URL写法

QComboBox::drop-arrow{
image: url(./static/images/dropdown.png)
}

QCheckBox 让勾选框在右边,设置layoutDirection: RIGHTTOLEFT 

2). 选择器

同时选择多个控件:QPushButton#btn_1, #btn_2 {attr:val} 

选择器举例解释
通用选择器* 所有 Qt 的 widget,即不声明选择器时,属性作用于所有组建
类型选择器QPushButton作用于QPushButton及其子类的实例。
属性选择器QPushButton[flat=“false”]作用于非平面(flat=“false”)的QPushButton实例。
类选择器.QPushButton作用于QPushButton的实例,但不匹配其子类的实例。(加了个’.’)
ID选择器 QPushButton#okButton作用于对象名称为okButton的所有QPushButton实例
后代选择器QDialog QPushButton匹配作为QDialog的子体(子级、孙级等)的所有QPushButton实例。
子选择器QDialog > QPushButton匹配作为QDialog的直接子级的所有QPushButton实例。
子控制QComboBox::drop-down要设置复杂 widget 的样式,需要访问 widget 的子控件,如QComboBox的下拉按钮或QSpinBox的上下箭头。

3). 伪状态

Pseudo-State    Description
:active    此状态在widget驻留在活动窗口中时设置。
adjoins-item    此状态在QTreeView的::branch与项相邻时设置。
:alternate    当QAbstractItemView::ternatingRowColors()设置为true时,将为绘制QAbstractItemView的行时的每隔一行设置此状态。
:bottom    该项目位于底部。例如,标签位于底部的QTabBar。
:checked    该项目已选中。例如,QAbstractButton的选中状态。
:closable    这些项目可以关闭。例如,QDockWidget打开了QDockWidget::DockWidgetClosable功能。
:default    该项目为默认值。例如,QMenu中的默认QPushButton或默认操作。
:disabled    该项目已禁用。
:editable    QComboBox是可编辑的。
:edit-focus    该项具有编辑焦点(请参见QStyle::State_HasEditFocus)。此状态仅适用于Qt扩展应用程序。
:enabled    该项目已启用。
:exclusive    该项目是独占项目组的一部分。例如,独占QActionGroup中的菜单项。
:first    该项目是(列表中的)第一个项目。例如,QTabBar中的第一个选项卡。
:flat    这件物品是平的。例如,平面QPushButton。
原文链接:https://blog.csdn.net/qq_29912325/article/details/106873913

4). 加载QSS

 在最外层的 QWidget 上单击右键,然后点击 Change styleSheet...,在弹出的窗口里添加上面的 QSS,然后点击 Apply 按钮,可以看到 QSS 生效了。

如果在 控件 上单击右键,然后点击 Change styleSheet...,添加 QSS,那么 QSS 的作用域为 该控件 和它的子 widget:

如果把 QSS 写死在代码里,修改 QSS 的时候就需要修改程序的源码,就不得不重新编译,打包发布程序,效率低,不灵活,在实际项目里推荐把 QSS 放在文件里,然后读取 QSS 文件内容到程序,调用 setStyleSheet(qss) 加载 QSS,就像下面的代码片段所示:

class QSSLoader:
    def __init__(self):
        pass

    @staticmethod
    def read_qss_file(qss_file_name):
        with open(qss_file_name, 'r',  encoding='UTF-8') as file:
            return file.read()

style_file = './style.qss'
style_sheet = QSSLoader.read_qss_file(style_file)
window.setStyleSheet(style_sheet)

5). QSS样式模板

使用QSS美化PyQt界面,分享6套超赞皮肤 - 知乎 (zhihu.com)

 1. Qt-Material

# pip install qt-material

# 使用例子
import sys
# from PySide6 import QtWidgets
# from PySide2 import QtWidgets
from PyQt5 import QtWidgets
from qt_material import apply_stylesheet

# create the application and the main window
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()

# setup stylesheet
apply_stylesheet(app, theme='dark_teal.xml')

# run
window.show()
app.exec_()

2. QDarkStyleSheet

# pip install qdarkstyle

# PyQt5 使用例子
import sys
import qdarkstyle
from PyQt5 import QtWidgets

# create the application and the main window
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()

# setup stylesheet
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
# or in new API
app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyqt5'))

# run
window.show()
app.exec_()

7. PyQt5 QTabWidget样式设置

/*设置TabWidget中tab_1的样式*/
#tab_1.QWidget{
	background-color: rgb(108, 117, 125);
} 
/*设置TabWidget中tab_2的样式*/
#tab_2.QWidget{
	background-color: rgb(108, 117, 125);
}
/*设置TabWidget中tab_3的样式*/
#tab_3.QWidget{
	background-color: rgb(108, 117, 125);
}
 
/*设置TabWidget中QTabBar的样式*/
QTabBar::tab{
    background-color: #0B0E11;
	font-family:Consolas;    /*设置tab中的文本的字体*/
	font-size:10pt;
	color:#ced4da;    /*设置tab中的文本的颜色*/
	border-top-left-radius: 5px;    /*设置tab的边框的圆角(左上圆角)*/
	border-top-right-radius: 5px;    /*设置tab的边框的圆角(右上圆角)*/
	min-width: 8px;
	padding: 5px;
}
 
/*设置TabWidget中QTabBar的tab被选中时的样式*/
QTabBar::tab:selected{
    background-color: rgb(108, 117, 125);
}
 
/*设置TabWidget中鼠标悬浮在QTabBar的tab上,但未选中该Tab的样式*/
QTabBar::tab:hover:!selected {
    background-color: rgb(108, 117, 105);
}
 
/*设置TabWidget的边框的样式*/
QTabWidget::pane {
    border: 2px solid rgb(108, 117, 125);
}
 
/*当打开多个tab,右侧出现,点击后,可以向前向后的按钮的样式*/
QTabBar QToolButton {
    border: none;
	color: rgb(255, 206, 6);
    background-color: #0b0e11;
}
 
QTabBar QToolButton:hover {
	background-color: #161a1e; 
}

8. QlineEdit回车事件

editingFinished:按回车键时会触发两次槽函数。,一种就是有焦点时被触发(比如回车),另一种就是失去焦点时被触发(比如将焦点移除或者弹窗)。

returnPressed:回车触发函数

textChanged:文本改变触发。当以编程方式更改文本时,例如,通过调用setText() 发出此信号。

textEdited:文本被编辑时,就会发出这个信号。当以编程方式更改文本时,例如,通过调用setText() 不会发出此信号。

9. 自适应窗口大小

自适应的基础是布局,通过布局嵌套布局,实现自适应窗口大小。

pyqt5控件自适应窗口知识点汇总(超详细讲解,持续更新中…)_pyqt5控件随窗口变化-CSDN博客

10. 背景图片的设置

# 用border-image来设置背景图片,可以使图片自适应控件大小

# 用label设置背景,不能随窗口变化而变动,故不用label设置背景

# 给QMainWindow设置背景,要指定#MainWindow或QMainWindow,否则所有控件都加了背景。设置时在qtdesigner不显示背景,但运行时显示

方式一:QPixmap()

from PyQt5.QtGui import QPixmap

# QLabel设置图片
pix = QPixmap()
self.ui.label.setPixmap(pix)
self.ui.label.setScaledContents(True)   #自适应QLabel大小

方式二:setStyleSheet()

self.ui.pushButton.setStyleSheet(f"border-image: url(pyqt5_ui/image/data_imgs/{img}); border-radius:20px")

 

11. QTimer 定时器 秒表设计

    def __init__(self):
        super().__init__()
        self.ui = uic.loadUi("pyqt5_ui/智能交通灯.ui")

        self.timer = QTimer() # 创建定时器
        self.timer.timeout.connect(self.func) # 定时器超时触发关联函数

    def startTimer(self):
        # 开启定时器,单位毫秒。每间隔指定时间触发定时器关联函数
        self.timer.start(1000)
    
    def stopTimer(self, timer):
        self.timer.stop() # 关闭定时器
        self.count = 0
        self.flag = False
    
    def func(self):
        if self.flag:
            # 处理逻辑
            self.count += 1
            text = str(self.count)
            self.ui.label_s.setText(text)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值