持续更新中。。。。。。
===================================================================
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)