2024-06-16-Pyside6高级操作02
1. 数据库操作
1.1 SQL数据库
数据库 (database)是统一管理的、有组织的、可共享的大量数据的集合。数据库将数据存储在一个或多个表格中,管理这个数据库的软件称为 数据库管理系统(database management system, DBMS)。
数据库不是针对具体的应用程序,而是立足于数据本身的管理,它将所有数据保存在数据库中,进行科学的组织,并借助于数据库管理系统,通过 SQL 与各种应用程序或应用系统连接,使之能方便地使用数据库中的数据。
关系型数据库管理系统一般都支持结构化查询语言 (structured query language, SQL),SQL 是数据库的基础,通过它可以实现对关系型数据库进行查询、新增、更新、删除、求和和排序等操作。关系型数据库由数据表 (table)构成,数据表的一行数据称为字段,可以在数据库中添加和删除数据表,在数据表中可以查询、添加和删除字段。
SQL 语句主要分为以下几类:
-
数据定义语言(data definition language, DDL)。DDL 是 SQL 中定义数据结构和数据库对象的语言,主要关键字有CREATE、ALTER 和 DROP。
-
数据操纵语言(data manipulation language, DML)。DML 是 SQL 中操纵数据库中数据的语言,可对数据库中的数据进行插入、删除、更新和选择,主要关键字有 INSERT、UPDATE、DELETE 和 SELECT。
-
事务控制语言
(transaction control language, TCL)。TCL 用于管理 DML 对数据所作的修改,主要关键字有提交 COMMIT 和撤销 ROLLBACK。
- 对数据库的操作需要用 COMMIT 进行确认,事务一经提交就不能撤销,如果要在提交前撤销对数据库的操作,可以用 ROLLBACK 来 “回滚” 到相应事务开始时的状态。
-
数据控制语言(data control language, DCL)。DCL 是对数据访问权限进行控制、定义安全级别及创建用户的语言,主要关键字有 GRANT 和 REVOKE。
1.2 SQLite数据库
SQLite 数据库是一个常用的开源、跨平台、轻量化本机版数据库,可以作为嵌入式数据库使用。Python 自带的 sqlite3 可以实现对 SQLite 数据库的查询,使用前需要用 import sqlite3
语句导入 sqlite3。
sqlite3 提供了两个基本的类:数据库连接 Connection 和游标 Cursor。用 sqlite3 的 connect(database:str)
方法打开一个已经存在的 SQLite 数据库或新建一个数据库,并返回 Connection 对象,其中 databaseName是本机上已经存储的数据库文件,或者新建立的数据库文件名称,还可取 ":memory:",表示在内存中创建临时数据库。用 Connection 对象的 cursor()
方法获取 Cursor 对象,用 Cursor 对象提供的方法可以执行 SQL 指令。
数据库连接 Connection 类的常用方法如下:
cursor() -> Cursor # 创建并返回Cursor对象
commit() -> None # 提交当前的事务
interrupt() -> None # 停止还未执行的任务
rollback() -> None # 放弃对数据库的操作,返回到调用commit()时的状态
close() -> None # 关闭数据库
backup(target:Connection) -> None # 将数据备份到另一个数据库中
游标 Cursor 用于执行 SQL 语句,它的常用方法如下:
execute(sql:sqk, parameters:Sequence=()) -> None # 执行一条SQL语句,parameters是SQL中的占位符的值
executemany(sql:str, parameters:Sequence) -> None # 重复执行SQL语句,parameters是SQL中的占位符的值
executescript(sql_script:str) -> None # 执行多条SQL语句
fetchone() -> Tuple # 获取数据表中的下一行数据构成的元组或None
fetchmany(size:int=cursor.arraysize) -> List # 获取多行数据,构成的列表,参数size是要获取的行数
fetchall() -> List # 获取所有剩余的行构成的列表
close() -> None # 关闭游标
import sqlite3
connection = sqlite3.connect("test.db") # 获取连接
cursor = connection.cursor() # 获取游标
# 执行SQL语句,创建表
cursor.execute("create table if not exists user(name varchar(20), age int)")
# 向表中插入数据,新增用户信息
cursor.execute("insert into user(name,age) values('Sakura',10)")
cursor.execute("insert into user(name,age) values('Mikoto',14)")
cursor.execute("insert into user(name,age) values('Shana',15)")
# 执行查询结果
cursor.execute("select * from user")
# 获取查询结果
result = cursor.fetchone() # 使用fetchone()获取一条记录
print(result,end="\n\n")
result = cursor.fetchmany(2) # 使用fetchmany()获取多条记录
print(result,end="\n\n")
result = cursor.fetchall() # 使用fetchall()获取全部查询结果
print(result,end="\n\n")
# 修改用户信息
# 使用问号作为占位符代替具体的数值,然后使用一个元组来替代问号
# 使用占位符的方式可以避免SQL注入的风险
cursor.execute("update user set age = ? where name = ?",(12,"Sakura"))
cursor.execute("select * from user where name = ?",("Sakura",))
result = cursor.fetchall()
print(result,end="\n\n")
# 删除用户数据
cursor.execute("delete from user where name = ?",("Shana",))
cursor.execute("select * from user")
result = cursor.fetchall()
print(result,end="\n\n")
cursor.close() # 关闭游标
connection.close() # 关闭数据库
1.3 Pyside6操作数据库
PySide6 提供了对常用数据库的驱动,可以从数据库中进行查询、读取、写入、修改、删除数据等操作,同时提供了可视化的控件和数据库数据模型,可以将数据从数据库读取到数据模型中,然后用 Model/View 结构在图形界面中对数据进行操作。
PySide6 可以驱动常用的关系型数据库,在对数据库进行操作之前需要用 QSqlDatabase 类建立对数据库的连接,然后再用 QSqlQuery 类执行 SQL 命令,实现对数据库的操作。
1.3.1 数据库连接
在对数据库进行操作之前,需要先建立对数据库的连接。数据库连接用 QSqlDatabase 类。
用 QSqlDatabase 创建实例的方法如下所示:
QSqlDatabase()
QSqlDatabase(type:str)
其中 type 是数据库的驱动类型,可取的值下所示。如果要建立自定义的驱动类型,可以创建 QSqlDriver 的子类。
"QDB2" # IBM DB2数据库
"QIBASE" # Borlad InterBase数据库
"QMYSQL" # MySQL数据库
"QODBC" # 支持ODBC接口的数据库
"QPSQL" # PostgreSQL数据库
"QSQLITE" # SQLite数据库
"QOCI" # Oracle数据库
QSqlDatabase 类的常用方法如下:
# 实例方法
connectionName() -> str # 获取连接的名称
driverName() -> str # 获取驱动类型的名称
setDatabaseName(name:str) -> None # 设置连接的数据库名称
databaseName() -> str # 获取连接的数据库名称
setHostName(host:str) -> None # 设置连接的主机名称
hostName() -> str # 获取连接的主机名称
setPort(port:int) -> None # 设置连接的端口号
port() -> int # 获取连接的端口号
setUserName(username:str) -> None # 设置用户名
userName() -> str # 获取用户名
setPassword(password:str) -> None # 设置密码
password() -> str # 获取密码
setConnectOptions(options="") -> None # 设置连接选项
connectOptions() -> str # 获取连接参数
open() -> bool # 打开数据库
open(user:str, password:str) -> bool # 打开数据库
isOpen() -> bool # 获取数据库是否打开
isOpenError() -> bool # 获取打开数据库是是否出错
isValid() -> bool # 获取连接是否有效
close() -> None # 关闭连接
transaction() -> bool # 开启事务
commit() -> bool # 提交事务
rollback() -> bool # 回滚事务
lastError() -> QSqlError # 获取最后的错误信息
record(tablename:str) -> QSqlRecord # 获取含有字段名称的记录
setNumericalPrecisionPolicy(precisionPolicy:QSql.NumericalPrecisionPolicy) -> None # 设置对数据库进行查询时默认的数值精度
tables(type:QSql.TableType=QSql.Tables) -> List[str] # 根据表格类型参数,获取数据库中的表格名称
# 静态方法
drivers() -> List[str] # 获取系统支持的驱动类型
isDriverAvailable(name:str) -> bool # 获取是否支持某种类型的驱动
# 添加数据库连接
addDatabase(driver:str, connectionName:str=QLatin1StringView(QSqlDatabase.defaultConnection)) -> QSqlDatabase
# 根据连接名称获取数据库连接
database(connectionName:str=QLatin1StringView(QSqlDatabase.defaultConnection), open:bool=true) -> QSqlDatabase
removeDatabase(connectionName:str) -> None # 删除数据库连接
connectionNames() -> List[str] # 获取已经添加的连接名称
# 如果connectionNames()返回值中有指定的连接,则返回True
contains(connectionName:str=QLatin1StringView(QSqlDatabase.defaultConnection)) -> bool
用静态方法 addDatabase(type:str,connectionName:str='qt_sql_default_connection')
添加某种驱动类型的连接,其中参数 type 是驱动类型。对同一个数据库可以添加多个连接,数据库连接的识别是通过连接名称 connectionName 来区分的,而不是关联的数据库。如果不输入连接名称,则该连接作为默认连接,将使用默认的连接名称 'qt_sql_default_connection'
。
在用 open()
方法打开数据库前,需要分别用 setDatabaseName(name:str)
方法、setHostName(host:str)
方法、setPassword(password:str)
方法、setPort(port:int)
方法、setUserName(name:str)
方法、setConnectOptions(options:str='')
方法设置连接的数据库文件名、主机名、密码、端口号、用户名和连接参数,如果用 open() 方法打开数据库后再设置这些参数将不起作用。
在用 setDatabaseName(name:str)
方法打开 SQLite 数据库时,如果数据库不存在则创建新数据库,参数 name 也可取 ':memory:'
,在内存中临时创建数据库,程序运行结束后删除数据库。对于 ODBC 数据库,参数 nam e是 *.dsn
文件或连接字符串;对于 Oracle 数据库,name 参数是 TNS服务名称
。
用 setConnectOptions(options:str='')
方法设置数据库的参数,不同的驱动类型需要设置的参数也不同,各参数值之间用分号 “;” 隔开,例如 SQLite 数据库,可选参数如下:
QSQLITE_BUSY_TIMEOUT
QSQLITE_OPEN_READONLY
QSQLITE_OPEN_URI
QSQLITE_ENABLE_SHARED_CACHE
QSQLITE_ENABLE_REGEXP
QSQLITE_NO_USE_EXTENDED_RESULT_CODES
用 tables(type:QSql.TableType=QSql.Tables)
方法获取数据库中存在的数据表名称列表,其中 type 取值是 QSql.TableType 的枚举值,可取值如下:
QSql.TableType.Tables # 对用户可见的所有表
QSql.TableType.SystemTables # 数据库使用的内部表
QSql.TableType.Views # 对用户可见的所有视图
QSql.TableType.AllTables # 以上三种表和视图
用 setNumericalPrecisionPolicy(precisionPolicy:QSql.NumericalPrecisionPolicy)
方法设置对数据库进行查询时默认的数值精度,参数 precisionPolicy 可取值如下:
QSql.NumericalPrecisionPolicy.LowPrecisionInt32 # 32位整数,忽略小数部分
QSql.NumericalPrecisionPolicy.LowPrecisionInt64 # 64位整数,忽略小数部分
QSql.NumericalPrecisionPolicy.LowPrecisionDouble # 双精度值,默认值
QSql.NumericalPrecisionPolicy.HighPrecision # 保持数据的原有精度
用 lastError()
方法获取最后的出错信息 QSqlError 对象,用 QSqlError 的 type()
方法可以获取出错类型,返回值是 QSqlError.ErrorType 的枚举值或 -1(不能确定错误类型),QSqlError.ErrorType 的枚举值如下:
QSqlError.ErrorType.NoError # 0,没有错误
QSqlError.ErrorType.ConnectionError # 1,数据库连接错误
QSqlError.ErrorType.StatementError # 2,SQL命令语法错误
QSqlError.ErrorType.TransactionError # 3,事务错误
QSqlError.ErrorType.UnknownError # 4,未知错误
1.3.2 数据库查询
数据库查询 QSqlQuery 用于执行标准的 SQ L命令,例如 CREATE TABLE、SELECT、INSERT、UPDATE、DELETE 等,还可执行特定的非标准的 SQL 命令。
用 QSqlQuery 类创建实例对象的方法如下所示:
QSqlQuery(db:QSqlDatabase)
QSqlQuery(other:QSqlQuery)
QSqlQuery(query:str="", db:QSqlDatabase=Default(QSqldatabase))
QSqlQuery 类常用方法如下:
prepare(query:str) -> bool # 准备SQL语句,成功返回True
addBindValue(val:Any, type:QSql.ParamType=QSql.In) -> None # 如果prepare(query)中有占位符,则按顺序依次设置占位符的值
bindValue(placeholder:str, val:Any, type:QSql.ParamType=QSql.In) -> None # 如果prepare(query)中有占位符,则根据占位名称设置占位符的值
bindValue(pos:int, val:Any, type:QSql.ParamType=QSql.In) -> None # 如果prepare(query)中有占位符,则根据占位位置设置占位符的值
boundValue(placeholder:str) -> Any # 根据占位符名称获取绑定值
boundValue(pos:int) -> Any # 根据占位位置获取绑定值
boundValues() -> List[Any] # 获取绑定值列表
exec() -> bool # 执行prepare(query)准备的SQL语句
exec(query:str) -> bool # 执行SQL语句,成功返回True
# 批处理用prepare()方法准备的命令
execBatch(mode:QSqlQuery.BatchExecutionMode=QSqlQuery.BatchExecutionMode.ValuesAsRows) -> bool
finish() -> None # 完成查询,不再获取数据
clear() -> None # 清空结果,释放所有资源,查询处于不活跃状态
executedQuery() -> str # 返回最后正确执行的SQL语句
lastQuery() -> str # 返回当前查询使用使用的SQL语句
# 返回查询的当前内部位置,第一个记录位置是0,如果位置无效,则返回值是QSql.BeforeFirstRow(值是-1)或QSql.AfterLastRow(值是-2)
at() -> int
isSelect() -> bool # 当前SQL语句是SELECT语句时返回True
isValid() -> bool # 当前查询定位在有效记录时返回True
isActive() -> bool # 获取查询是否处于活跃状态
isNull(field:int) -> bool # 将查询处于非活跃状态,查询定位在无效记录或空字段上时返回True
isNull(name:str) -> bool # 将查询处于非活跃状态,查询定位在无效记录或空字段上时返回True
first() -> bool # 将当前查询位置定位在第一个记录
last() -> bool # 将当前查询位置定位到最后一个记录
previous() -> bool # 将当前查询位置定位到前一个记录
next() -> bool # 将当前查询位置定位到下一个记录
seek(i:int, relative:bool=false) -> bool # 将当前查询位置定位到指定记录
setForwardOnly(forward:bool) -> None # 当forward取True时,只能用next()和seek()方法来定位结果,此时,seek()参数为正值
isForwardOnly() -> bool # 获取定位模式
lastError() -> QSqlError # 返回最近出错信息
lastInsertId() -> Any # 返回最近插入行的对象ID号
nextResult() -> bool # 放弃当前查询结果并定位到下一个结果
record() -> QSqlRecord # 返回查询指向的当前记录
size() -> int # 返回查询结果的记录数,无法确定、非SELECT命令或数据库不支持该功能是返回-1、
value(i:int) -> Any # 根据字段索引,获取当前记录的字段值
value(name:int) -> Any # 根据字段名称,获取当前记录的字段值
numRowsAffected() -> int # 获取受影响的行的个数,无法确定或查询处于非活跃状态时返回-1
swap(other:QSqlQuery) -> None # 与其它查询交换数据
用 exec(query:str)
方法直接执行 SQL 命令。也可以用 prepare(query:str)
方法准备要执行的 SQL 命令;用exec()
方法或execBatch(mode=QSqlQuery.ValuesAsRows)
方法执行已经准备好的 SQL 命令,其中 mode 可取值如下:
QSqlQuery.BatchExecutionMode.ValuesAsRows # 更新多行,列表中的每个值作为一个值来更新下一行
QSqlQuery.BatchExecutionMode.ValuesAsColumns # 更新一行,列表作为一个值来使用
在用 prepare(query:str)
方法准备 SQL 命令时,SQL 命令中可以有占位符,占位符可以用问号 "?"
(ODBC 格式),也可以用冒号 ":surname"
(Oracle 格式)。占位符的真实值可以用 addBindValue(val:Any,type:QSql.ParamType=QSql.In)
方法按照 顺序 依次设置,也可用 bindValue(placeholder:str,val:Any,type:QSql.ParamType=QSql.In)
方法根据 占位符的名称 设置,还可以用 bindValue(pos:int,val:Any,type:QSql.ParamType=QSql.In)
方法根据 占位符的位置 设置,其中参数 type可取值如下:
QSql.ParamTypeFlag.In # 绑定参数输入到数据库中
QSql.ParamTypeFlag.Out # 绑定参数从数据库中接收数据
QSql.ParamTypeFlag.InOut # 既可以将数据输入到数据库中,也可以从数据库中接收数据
QSql.ParamTypeFlag.Binary # 数据是二进制,需要将“|”与以上三种参数联合使用
当返回的结果有多个记录时,需要首先定位到所需要的记录上,当 isActive()
方法和 isSelect()
方法的返回值是 True 时,可以用 first()
、last()
、previous()
和 next()
方法分别定位到 第一个记录、最后一个记录、前一个记录 和 下一个记录 上,成功则返回 True;用 seek(index:int,relative:bool=False)
方法可以 定位到指定的记录 上。如果只是想从开始到结束浏览数据,可以设置 setForwardOnly(True)
,这样可以节省大量的内存。
用 value(index:int)
方法或 value(name:str)
方法获取当前记录的字段值。也可用 record()
方法获取当前的记录对象 QSqlRecord,QSqlRecord 是指数据表(table)或视图(view)中的一行,然后用记录对象的 value(index:int)
方法或 value(name:str)
方法获取字段的值,用记录对象的 count()
方法获取字段的数量,用 indexOf(name:str)
方法获取字段的索引。
from PySide6.QtSql import QSqlDatabase, QSqlQuery
if __name__ == "__main__":
person = (("Sakura", 10), ("Mikoto", 14), ("Shana", 15))
# 1.添加数据库连接
db = QSqlDatabase.addDatabase("QSQLITE")
# 2.设置连接数据库的名称
db_name = "./test.db"
db.setDatabaseName(db_name)
# 3.创建数据库查询对象
query = QSqlQuery(db)
# 3.打开数据库
if db.open():
# 4.执行SQL语句,创建表
query.exec("create table if not exists user(name varchar(20), age int)")
# 6.开启事务
if db.transaction():
for i in person:
# 7.准备SQL语句
query.prepare("insert into user(name, age) values(?, ?)")
# 7.1.按顺序设置占位符的值
query.addBindValue(i[0])
# 7.2.按索引位置设置占位符的值
query.bindValue(1, i[1])
# 8.执行SQL语句
query.exec()
# 9.提交事务
db.commit()
# 10.查询数据
if query.exec("select * from user"):
while query.next():
print(query.value("name"), query.value("age"))
# 11.关闭数据库
db.close()
1.4 数据库Model/View结构
PySide6 提供了对数据库进行可视化操作的 Model/View 结构,通过数据库模型读入在数据库中查询到的数据,并通过视图控件(如 QTableView)显示数据库模型中的数据,通过代理控件在视图控件中对数据进行新增、更新、删除等操作,再通过数据模型把操作后的数据保存到数据库中。PySide6 提供的数据库模型有 QSqlQueryModel、QSqlTableModel 和 QSqlRelationalTableModel。
1.4.1 数据库查询模型
数据库查询模型 QSqlQueryModel 只能从数据库中读取数据,而不能修改数据,可以用视图控件。
用 QSqlQueryModel 创建数据库查询模型对象的方法如下所示。
QSqlQueryModel(paarent:QObject=None)
QSqlQueryModel 类常用方法如下:
setQuery(query:QSqlQuery) -> None # 设置数据库查询
setQuery(query:str, db:QSqkDatabase=QSqlDatabase()) -> None # 设置数据库查询
query() -> QSqlQuery # 获取数据库查询
record() -> QSqlRecord # 获取包含字段信息的空记录
record(row:int) -> QSqlRecord # 获取指定行的记录
clear() -> None # 清空查询模型中的数据
# 设置显示数据的视图控件表头某角色的值
setHeaderData(section:int, orientation:Qt.Orientation, value:Any, role:int=Qt.EditRole) -> bool
# 获取显示数据的视图控件表头某种角色的值
headerData(section:int, orientation:Qt.Orientation, role:int=Qt.DisplayRole) -> Any
rowCount(parent:QModelIndex=QModelIndex()) -> int # 获取数据表中记录(行的数量)
columnCount(parent:QModelIndex=QModelIndex()) -> int # 获取数据表中列的数量
用 setQuery(query:QSqlQuery)
方法或 setQuery(query:str,db:QSqlDatabase=Default(QSqlDatabase))
方法设置数据库查询 QSqlQuery;用 setHeaderData(section:int,orientation:Qt.Orientation,value:Any,role:int=Qt.EditRole)
方法设置显示数据的视图控件表头某角色的值,在 orientation 取 Qt.Horizontal,并且 section 取值合适时返回 True,其他情况返回 False,其中 value 是某种角色的值,section 是列索引。
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QTableView
from PySide6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
from PySide6.QtCore import Qt
class MyWidget(QWidget):
def __init__(self):
# 1.调用父类Qwidget类的__init__()方法
super().__init__()
# 2.使用数据库
self.useDataBase()
# 3.调用setupUi()方法初始化页面
self.setup_ui()
def setup_ui(self):
# 1.设置窗口对象大小
self.resize(700, 500)
# 2.设置表格控件
self.tableView = QTableView(self)
self.tableView.resize(700, 500)
# 3.设置表格控件的模型
self.tableView.setModel(self.sqlQueryModel)
def useDataBase(self):
person = (("Sakura", 10), ("Mikoto", 14), ("Shana", 15))
# 1.添加数据库连接
db = QSqlDatabase.addDatabase("QSQLITE")
# 2.设置连接数据库的名称
db_name = "./test.db"
db.setDatabaseName(db_name)
# 3.创建数据库查询对象
query = QSqlQuery(db)
# 4.创建数据库查询模型
self.sqlQueryModel = QSqlQueryModel()
# 5.打开数据库
if db.open():
# 6.执行SQL语句,创建表
self.sqlQueryModel.setQuery("create table if not exists user(name varchar(20), age int)")
# 7.开启事务
if db.transaction():
for i in person:
# 8.准备SQL语句
query.prepare("insert into user(name, age) values(?, ?)")
# 8.1.按顺序设置占位符的值
query.addBindValue(i[0])
# 8.2.按索引位置设置占位符的值
query.bindValue(1, i[1])
# 9.执行SQL语句
query.exec()
# 10.提交事务
db.commit()
# 11.设置查询
self.sqlQueryModel.setQuery("select * from user")
# 12.获取字段信息当作头部信息
header = self.sqlQueryModel.record()
for i in range(header.count()):
# 13.设置显示数据的视图控件表头某角色的值
self.sqlQueryModel.setHeaderData(i, Qt.Orientation.Horizontal, header.fieldName(i), Qt.ItemDataRole.DisplayRole)
# 14.关闭数据库
db.close()
if __name__ == "__main__":
# 1.创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2.创建一个窗口
window = MyWidget()
# 3.展示窗口
window.show()
# 4.进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
1.4.2 数据库表格模型
数据库表格模型 QSqlTableModel 借助视图控件可以对查询到的数据进行修改、插入、删除和排序等操作,同时将修改后的数据更新到数据库中。
用 QSqlTableModel 创建数据库表格模型的方法如下所示:
QSqlTableModel(parent:QObject=None, db:QSqlDatabase=Default(QSqlDataBase))
数据库表格模型 QSqlTableModel 的常用犯法按如下所示:
# 实例方法
insertRows(row:int, count:int, parent:QModelIndex=QModelIndex()) -> bool # 插入多个空行,在OnFieldChange和OnRowChange模式下每次只能插入一行
removeRow(row:int, parent:QModelIndex=QModelIndex()) -> bool # 删除指定行
removeRows(row:int, count:int, parent:QModelIndex=QModelIndex()) -> bool # 删除多行
insertColumns(column:int, count:int, parent:QModelIndex=QModelIndex()) -> bool # 插入多个空列
removeColumn(column:int, parent:QModelIndex=QModelIndex()) -> bool # 删除指定列
removeColumns(column:int, count:int, parent:QModelIndex=QModelIndex()) -> bool # 删除多列
revert() -> None # 恢复数据模型到初始状态
submit() -> bool # 提交数据模型中的所有更改
rowCount(parent:QModelIndex=QModelIndex()) -> int # 获取行的数量
columnCount(parent:QModelIndex=QModelIndex()) -> int # 获取列的数量
setData(index:QModelIndex, value:Any, role:Qt.ItemDataRole=Qt.EditRole) -> bool # 设置指定索引的数据项的角色值
data(index:QModelIndex, role:Qt.ItemDataRole=Qt.EditRole) -> Any # 获取指定索引的数据项的角色值
sort(column:int, order:Qt.SortOrder=Qt.AscendingOrder) -> None # 直接对结果进行排序
index(row:int, column:int, parent:QModelIndex=QModelIndex()) -> QModelIndex # 获取子索引
parent(child:QModelIndex)-> QModelIndex # 获取子索引的父索引
sibling(row:int, column:int, idx:QModelIndex) -> QModelIndex # 获取指定索引的兄弟索引
clearItemData(index:QModelIndex) -> bool # 根据索引清除数据项中的数据
setEditStrategy(strategy:QSqlTableModel.EditStrategy) -> None # 设置修改提交模式
database() -> QSqlDatabase # 获取关联的数据库连接
deleteRowFromTable(row:int) -> bool # 直接删除数据表中指定的行(记录)
fieldIndex(fieldName:str) -> int # 获取字段的索引,-1表示没有对应的字段
insertRecord(row:int, record:QSqlRecord) -> bool # 在指定行位置插入行
insertRowIntoTable(values:QSqlDatabase) -> bool # 直接在数据表中插入行
isDirty() -> bool # 获取模型中是否有脏数据
isDirty(index:QModelIndex) -> bool # 根据索引获取数据数据是否时脏数据
primaryValues(row:int) -> QSqlRecord # 返回指定行的含有表格字段的记录
revertRow(row:int) -> None # 复原指定行的更改
setRecord(row:int, record:QSqlRecord) -> bool # 用指定的记录填充指定的行
setTable(tableName:str) -> None # 设置数据表中的字段名称
setFilter(filter:str) -> None # 设置SELECT查询语句中WHERE子句部分
filter() -> str # 获取SELECT查询语句中WHERE子句部分
setSort(column:int, order:Qt.SortOrder) -> None # 设置SELECT查询语句中ORDER BY子句部分
orderByClause() -> str # 获取SELECT查询语句中ORDER BY子句部分
select() -> bool # 执行SELECT查询语句
selectRow(row:int) -> bool # 用数据库库中行更新模型中的数据
selectStatement() -> bool # 获取"SELECT ... WHERE ... ORDER BY..."
# 槽函数
revertAll() -> None # 复原所有未提交的更改
submitAll() -> bool # 提交所有更改
数据库表格模型 QSqlTableModel 的常用犯法按如下所示:
beforeDelete(row:int) # 在调用deleteRowFromTable(row:int)方法删除指定行之前发色和信号
beforeInsert(record:QSqlRecord) # 在调用insertRowIntoTable(values:QSqlRecord)方法插入记录之前发射信号,可以在插入之前修改记录
beforeUpdate(row:int, record:QSqlRecord) # 在调用updateRowInTable(row:int, values:QSqlRecord)方法更新指定行之前发射信号
primeInsert(row:int, record:QSqlRecord) # 在调用insertRows(row:int, column:int)方法对新插入的行进行初始化时发射信号
记录 QSqlRecord 表示数据表中的一行数据,一行数据中每个字段有不同的值,可用 QSqlTableModel 的 record(row:int)
方法获取 QSqlRecord 对象,以获取数据表中的一行数据。
用 QSqlRecord 创建记录实例对象的方法如下所示。
QSqlRecord()
QSqlRecord(other:QSqlRecord)
QSqlRecord 常用的方法如下所示。
append(field:QSqlField) -> None # 在末尾添加字段
insert(pos:int, field:QSqlField) -> None # 在自会顶的位置插入字段
remove(pos:int) -> None # 删除指定位置的字段
replace(pos:int, field:QSqlField) -> None # 替换指定位置的字段
setValue(name:str, val:Any) -> None # 根据字段名称设置字段的值
setValue(i:int, val:Any) -> None # 根据字段索引设置字段的值
value(name:str) -> Any # 根据字段名称获取字段的值
value(i:int) -> Any # 根据字段索引获取字段的值
setNull(name:str) -> None # 根据字段名称设置字段为NULL
setNull(i:int) -> None # 根据字段索引设置字段为NULL
isNull(name:str) -> bool # 根据字段名称判断字段是否为NULL
isNull(i:int) -> bool # 根据字段索引判断字段是否为NULL
clear() -> None # 删除所有的字段
isEmpty() -> bool # 获取是否含有字段
clearValues() -> None # 删除所有的字段值
contains(name:str) -> bool # 获取是否包含指定的字段
count() -> int # 获取字段的数量
field(name:str) -> QSqlField # 根据字段名称获取字段
field(i:int) -> QSqlField # 根据字段索引获取字段
fieldName(i:int) -> str # 获取字段的名称
indexOf(name:str) -> int # 获取字段的索引
keyValues(keyFields:QSqlRecord) -> QSqlRecord # 获取与给定的记录具有相同字段名称的记录
setGenerated(name:str, generated:bool) -> None # 根据索引设置字段值是否已经生成
setGenerated(i:int, generated:bool) -> None # 根据索引设置字段值是否已经生成
isGenerated(name:str) -> bool # 根据索引获取字段是否已经生成
isGenerated(i:int) -> bool # 根据名称获取字段是否已经生成
用 append(field:QSqlField)
方法可以在末尾添加字段;用 insert(pos:int,field:QSqlField)
方法插入字段;用 remove(pos:int)
方法移除字段;用 setValue(i:int,val:Any)
方法或 setValue(name:str,val:Any)
方法根据字段索引或字段名称设置字段的值;用 setGenerated(i:int,generated:bool)
方法或 setGenerated(name:str,generated:bool)
方法根据索引或名称设置字段值是否已经生成,只有已经生成的字段值才能用 QSqlTableModel 的 updateRowInTable(row:int,values:QSqlRecord)
方法更新到数据库中,默认值是 True。
字段 QSqlField 是数据表中的列,一个记录由多个字段构成。字段的属性有字段名、字段类型和字段值等。用 QSqlRecord 的 field(i:int)
方法或 field(name:str)
方法可获得 QSqlField。
用 QSqlField 创建字段的方法如下所示。
QSqlField(fieldName:str="", type:QMetaType:Default(QMetaType), tableName:str="")
QSqlField(other:QSqlField)
其中 type 用于定义字段的类型,可取 PySide 中常见的类。
QSqlField 类的常用方法如下:
setName(name:str) -> None # 设置字段的名称
name() -> str # 获取字段的名称
setValue(value:Any) -> None # 获取字段的值,只读时不能设置值
value() -> Any # 获取字段的值,只读时不能设置值
setDefaultValue(value:Any) -> None # 设置字段的默认值
defaultValue() -> Any # 获取字段的默认值
setMetaType(type:QMetaType) -> None # 设置字段的类型
metaType() -> QMetaType # 获取存储在数据库中的类型
setReadOnly(readOnly:bool) -> None # 设置是否只读,只读时不能修改字段的值
isReadOnly() -> bool # 获取是否只读
setRequired(required:bool) -> None # 设置字段的值是必须要输入还是可见的
setRequiredStatus(status:QSqlFieldRequiredStatus) -> None # 设置可选状态
setGenerated(gen:bool) -> None # 设置字段的生成状态
isGenerated() -> bool # 获取字段的生成状态
setLength(fieldLength:int) -> None # 设置字段的长度
length() -> int # 获取字段的长度
setPrecision(precision:int) -> None # 设置浮点数的精度
precision() -> int # 获取浮点数的精度
setTableName(tableName:str) -> None # 设置数据表名称
tableName() -> str # 获取数据表名称
setAutoValue(autoVal:bool) -> None # 将字段的值标记是由数据库自动生成的
isAutoValue() -> bool # 获取字段的值是否是由数据库自动生成的
isValid() -> bool # 获取字段的类型是否有效
clear() -> None # 清除字段的值并设置成None
isNull() -> bool # 获取字段的值是否为None
用 setName(name:str)
方法设置字段名称;用 setValue(value:Any)
方法设置字段的值;用 setDefaultValue(value:Any)
方法设置字段的默认值;用 setReadOnly(readOnly:bool)
方法设置是否是只读,在只读状态不能更改字段的值。用 setRequired(required:bool)
方法或 setRequiredStatus(status:QSqlField.RequiredStatus)
方法设置字段的值是必须要输入的还是可选的,其中参数 status 可取值如下:
QSqlField.RequiredStatus.Required
QSqlField.RequiredStatus.Optional
QSqlField.RequiredStatus.Unknown
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QTableView
from PySide6.QtSql import QSqlDatabase, QSqlTableModel, QSqlRecord
class MyWidget(QWidget):
def __init__(self):
# 1.调用父类Qwidget类的__init__()方法
super().__init__()
# 2.使用数据库
self.useDataBase()
# 3.调用setupUi()方法初始化页面
self.setup_ui()
def setup_ui(self):
# 1.设置窗口对象大小
self.resize(700, 500)
# 2.设置表格控件
self.tableView = QTableView(self)
self.tableView.resize(700, 500)
# 3.设置表格控件的模型
self.tableView.setModel(self.sqlTableModel)
def useDataBase(self):
persons = [{"name": "Sakura", "age": 10}, {"name": "Mikoto", "age": 14}, {"name": "Shana", "age": 10}]
# 1.添加数据库连接
db = QSqlDatabase.addDatabase("QSQLITE")
# 2.设置连接数据库的名称
db_name = "./test.db"
db.setDatabaseName(db_name)
# 3.创建数据库表格模型
self.sqlTableModel = QSqlTableModel(self, db)
# 4.打开数据库
if db.open():
# 5.执行SQL语句,创建表
self.sqlTableModel.setQuery("create table if not exists user(name varchar(20), age int)")
# 6.设置数据表中字段的名称
self.sqlTableModel.setTable("user")
# 7.开启事务
if db.transaction():
for i, person in enumerate(persons):
# 8.创建QSqlRecord对象,表示数据表中的一行数据
record = QSqlRecord(self.sqlTableModel.record())
for key, value in person.items():
# 9.根据字段名称设置字段的值
record.setValue(key, value)
# 10.在指定位置插入行
self.sqlTableModel.insertRecord(i, record)
# 11.提交事务
db.commit()
# 12.查询数据
self.sqlTableModel.select()
# 13.关闭数据库
db.close()
if __name__ == "__main__":
# 1.创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2.创建一个窗口
window = MyWidget()
# 3.展示窗口
window.show()
# 4.进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
1.4.3 关系表格模型
数据库关系表格模型 QSqlRelationalTableModel 继承自 QSqlTableModel,除具有 QSqlTableModel 的方法外,它还提供了外键功能。关系表格模型 QSqlRelationalTableModel 实现了 SQL 的 SELECT 命令中的 INNER JOIN 和 LEFT JOIN 功能。
用 QSqlRelationalTableModel 类创建关系表格模型的方法如下所示。
QSqlRelationalTableModel(parent:QObject=None, db:QSqlDatabase=Default(QSqlDatabase))
用 QSqlRelationalTableModel 的 setRelation(column:int,relation:QSqlRelation)
方法定义 QSqlRelationalTableModel 当前数据表格(如 table1)的外键和映射关系,其中参数 column 是 table1 的字段编号,用于确定 table1 中当作外键的字段 field1,relation 参数是 QSqlRelation 的实例对象,用于确定另外一个数据表格(如 table2)和对应的字段 field2。
QSqlRelation 实例对象的创建方法是 QSqlRelation(tableName:str,indexCol:str,displayCol:str)
,其中 tableName 用于确定第 2 个数据表格 table2,indexCol 用于指定 table2 的字段 field2,displayCol 是 table2 中用于显示在 table1 的 field1 位置处的字段 field3,用 field3 的值显示在 field1 位置处,field1 的值不显示。
另外用 QSqlRelationalTableModel 的 setJoinMode(joinMode:QSqlRelationalTableModel.JoinMode)
方法可设置两个数据表格的数据映射模式,即使 table1 和 table2 没有匹配的数据,也列出 table1 中的数据。参数 joinMode 可取值如下:
QSqlRelationalTableModel.InnerJoin # 内连接,值为0,只列出table1和table2中匹配的数据
QSqlRelationalTableModel.LeftJoin # 左连接,值为1
2.网络应用
2.1 网络应用
Pyside6 提供了 QUdpSocket、QTcpSocket、QTcpServer 这 3 个类,它们封装了许多功能,能够帮助我们快速实现基于 UDP 和 TCP 的应用程序。它们相较于 Python 标准库中的 socket 模块使用起来也更加方便。
2.2 UDP通信
UDP 客户端的程序如下:
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout
from PySide6.QtWidgets import QTextBrowser, QTextEdit, QPushButton
from PySide6.QtNetwork import QUdpSocket, QHostAddress
HOST = "127.0.0.1"
PORT = 9090
class UdpClient(QWidget):
def __init__(self):
# 1.调用父类Qwidget类的__init__()方法
super(UdpClient, self).__init__()
# 2.创建UDP套接字
self.udp = QUdpSocket()
# 2.调用setupUi()方法初始化页面
self.setup_ui()
def setup_ui(self):
# 1.设置窗口对象大小
self.resize(700, 500)
layout_v = QVBoxLayout(self)
# 2.创建富文本框,用来显示发送的数据
self.textBrowser = QTextBrowser()
self.textBrowser.setReadOnly(True)
data = "你已经加入聊天室!\n"
self.udp.writeDatagram(data.encode("utf-8"), QHostAddress(HOST), PORT)
layout_v.addWidget(self.textBrowser, 2)
# 3.创建文本框,用来发送信息
self.textEdit = QTextEdit()
self.textEdit.setPlaceholderText("请输入发送的内容:")
layout_v.addWidget(self.textEdit, 1)
# 4.创建按钮,用来发送或清除消息
self.button_clear = QPushButton("清除")
self.button_submit = QPushButton("发送")
layout_h = QHBoxLayout(self)
layout_h.addWidget(self.button_clear)
layout_h.addWidget(self.button_submit)
layout_v.addLayout(layout_h, 1)
# 5.定义信号与槽的连接
self.button_clear.clicked.connect(self.clear_data)
self.button_submit.clicked.connect(self.send)
# 当有新数据等待读取时发射readyRead信号
self.udp.readyRead.connect(self.receive)
def clear_data(self):
self.textEdit.clear()
def send(self):
# 1.获取发送文本框内的内容
data = self.textEdit.toPlainText()
if not data:
return
# 2.清除发送文本框内容
self.textEdit.clear()
# 3.将数据发送到服务端IP地址
self.udp.writeDatagram(data.encode("utf-8"), QHostAddress(HOST), PORT)
def receive(self):
# 1.判断是否存在待读取的数据报
while self.udp.hasPendingDatagrams():
# 2.获取数据报的大小
data_size = self.udp.pendingDatagramSize()
# 3.读取数据报
data, host, port = self.udp.readDatagram(data_size)
if data:
# 4.将数据报显示在接收文本框内
self.textBrowser.append(data.data().decode("utf-8"))
def closeEvent(self, event):
# 向服务端发送数据
data = f"你离开了聊天室!\n"
self.udp.writeDatagram(data.encode("utf-8"), QHostAddress(HOST), PORT)
event.accept()
if __name__ == "__main__":
# 1.创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2.创建一个窗口
window = UdpClient()
# 3.展示窗口
window.show()
# 4.进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
UDP 服务端的程序如下:
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtNetwork import QUdpSocket, QHostAddress
HOST = "127.0.0.1"
PORT = 9090
class UdpServer:
def __init__(self):
# 1.创建UDP套接字
self.udp = QUdpSocket()
# 2.绑定HOST和PORT
if self.udp.bind(QHostAddress(HOST), PORT):
# 3.定义信号与槽的连接
self.udp.readyRead.connect(self.receive)
# 4.定义客户端集合
self.client_set = set()
def receive(self):
# 1.判断是否存在待读取的数据报
while self.udp.hasPendingDatagrams():
# 2.获取数据报的大小
data_size = self.udp.pendingDatagramSize()
# 3.读取数据报
data, host, port = self.udp.readDatagram(data_size)
host = host.toString()
# 4.如果消息中有加入二字,则将主机和端口号添加到集合中
if data.contains("加入".encode("utf-8")):
self.client_set.add((host, port))
# 5.如果消息中有离开二字,则将主机和端口号从集合中删除
elif data.contains("离开".encode("utf-8")):
self.client_set.remove((host, port))
# 6.向每个客户端发送消息
self.send_to_clients((host, port), data)
def send_to_clients(self, current_client, data):
# 1.循环客户端集合
for target in self.client_set:
# 2.获取主机和端口号
host, port = target
# 3.发送消息
message = f"【{current_client[0]}:{current_client[1]}】:{str(data, 'utf-8')}".encode("utf-8")
if message.decode("utf-8").__contains__("离开"):
if target != current_client:
self.udp.writeDatagram(message, QHostAddress(host), port)
else:
self.udp.writeDatagram(message, QHostAddress(host), port)
def closeEvent(self, event):
self.udp.close()
event.accept()
if __name__ == "__main__":
# 1.创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2.创建UDP服务端对象
udp_server = UdpServer()
# 3.进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
2.3 TCP通信
TCP 客户端的程序如下:
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout
from PySide6.QtWidgets import QTextBrowser, QTextEdit, QPushButton
from PySide6.QtNetwork import QTcpSocket, QHostAddress
HOST = "127.0.0.1"
PORT = 9090
class UdpClient(QWidget):
def __init__(self):
# 1.调用父类Qwidget类的__init__()方法
super(UdpClient, self).__init__()
# 2.创建TCP套接字
self.tcp = QTcpSocket()
# 3.连接客户端(三次握手)
self.tcp.connectToHost(QHostAddress(HOST), PORT)
# 4.调用setupUi()方法初始化页面
self.setup_ui()
def setup_ui(self):
# 1.设置窗口对象大小
self.resize(700, 500)
layout_v = QVBoxLayout(self)
# 2.创建富文本框,用来显示发送的数据
self.textBrowser = QTextBrowser()
self.textBrowser.setReadOnly(True)
layout_v.addWidget(self.textBrowser, 2)
# 3.创建文本框,用来发送信息
self.textEdit = QTextEdit()
self.textEdit.setPlaceholderText("请输入发送的内容:")
layout_v.addWidget(self.textEdit, 1)
# 4.创建按钮,用来发送或清除消息
self.button_clear = QPushButton("清除")
self.button_submit = QPushButton("发送")
layout_h = QHBoxLayout(self)
layout_h.addWidget(self.button_clear)
layout_h.addWidget(self.button_submit)
layout_v.addLayout(layout_h, 1)
# 5.定义信号与槽的连接
self.button_clear.clicked.connect(self.clear_data)
self.button_submit.clicked.connect(self.send)
# 当有新数据等待读取时发射readyRead信号
self.tcp.readyRead.connect(self.receive)
def clear_data(self):
self.textEdit.clear()
def send(self):
# 1.获取发送文本框内的内容
data = self.textEdit.toPlainText()
if not data:
return
# 2.清除发送文本框内容
self.textEdit.clear()
# 3.将数据发送到服务端IP地址
self.tcp.write(data.encode("utf-8"))
def receive(self):
# 1.判断是否存在待读取的数据报
while self.tcp.bytesAvailable():
# 2.获取数据报的大小
data_size = self.tcp.bytesAvailable()
# 3.读取数据报
data = self.tcp.read(data_size)
if data:
# 4.将数据报显示在接收文本框内
self.textBrowser.append(data.data().decode("utf-8"))
def closeEvent(self, event):
self.tcp.close()
event.accept()
if __name__ == "__main__":
# 1.创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2.创建一个窗口
window = UdpClient()
# 3.展示窗口
window.show()
# 4.进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
TCP 服务端的程序如下:
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtNetwork import QTcpServer, QHostAddress
HOST = "127.0.0.1"
PORT = 9090
class UdpServer:
def __init__(self):
# 1.创建TCP套接字
self.tcp = QTcpServer()
# 2.监听主机和端口
if self.tcp.listen(QHostAddress(HOST), PORT):
# 3.定义信号与槽的连接
# 每当有来自客户端的新的连接请求时,QTcpServer就会发色和newConnection信号
self.tcp.newConnection.connect(self.new_connection)
# 4.定义客户端集合
self.client_set = set()
def new_connection(self):
# 1.获取一个连接到客户端的QTcpSocket对象
sock = self.tcp.nextPendingConnection()
# 2.往客户端集合中添加客户端
self.client_set.add(sock)
# 3.定义信号与槽的连接
# 当有新数据等待读取时发射readyRead信号
sock.readyRead.connect(lambda: self.receive(sock))
# 如果客户端与服务端断开连接,disconnected信号就会发射
sock.disconnected.connect(lambda: self.disconnected(sock))
# 4.获取地址和端口号
address, port = self.get_address_and_port(sock)
# 5.向每个客户端发送消息
data = f"你已加入聊天".encode("utf-8")
self.send_to_clients(sock, data)
def disconnected(self, sock):
# 1.移除客户端
self.client_set.remove(sock)
# 2.获取地址和端口号
address ,port = self.get_address_and_port(sock)
# 3.向每个客户端发送消息
data = f"你已离开聊天".encode("utf-8")
self.send_to_clients(sock, data)
def receive(self, sock):
# 1.判断是否存在待读取的数据报
while sock.bytesAvailable():
# 2.获取数据报的大小
data_size = sock.bytesAvailable()
# 3.读取数据报
data = sock.read(data_size)
# 4.向每个客户端发送消息
self.send_to_clients(sock, data)
def send_to_clients(self, current_client, data):
# 1.获取当前客户端的地址和端口
current_client_address, current_client_port = self.get_address_and_port(current_client)
# 2.循环客户端集合
for target in self.client_set:
# 3.发送消息
message = f"【{current_client_address}:{current_client_port}】:{str(data, 'utf-8')}".encode("utf-8")
if message.decode("utf-8").__contains__("离开"):
if target != current_client:
target.write(message)
else:
target.write(message)
def get_address_and_port(self, sock):
address = sock.peerAddress().toString()
port = sock.peerPort()
return address, port
def closeEvent(self, event):
self.tcp.close()
event.accept()
if __name__ == "__main__":
# 1.创建一个QApplication类的实例
app = QApplication(sys.argv)
udp_server = UdpServer()
# 4.进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
3.样式表
3.1 样式表
为了美化窗口或控件的外观,可以通过窗口或控件的调色板给窗口或控件按照角色和分组设置颜色,还可以对窗口或控件的每个部分进行更细致的控制,这涉及窗口或控件的样式表(Qt style sheets, QSS),它是从 HTML 的层叠样式表(cascading style sheets, CSS)演化而来的。样式表由固定格式的文本构成,用窗口或控件的 setStyleSheet(styleSheet:str)
方法设置样式,其中参数 styleSheet 是样式格式符。
定义样式表的一般规则是用 "样式属性:值"
的形式定义样式属性的值,多个样式的 "样式属性:值" 对之间用分号 “;” 隔开。如果是对某一类控件进行设置,需要先说明控件的类,然后后面跟一对 “{}”,把 “样式属性:值” 放到 “{}” 中。
setStyleSheet("QPushButton{font:20pt, color:rgb(255, 0, 0), background-color:rgb(100, 100, 100)}")
3.2 选择器
样式表除了类名、对象名和属性名外,一般不区分大小写。样式表由 选择器(selector)和 声明(declaration)两部分构成,选择器用于选择某种类型或多个类型的控件,声明是要设置的属性和属性的值。
选择器的使用方法如下所示:
选择器 | 说明 |
---|---|
* | 全局选择器,选择所有控件 |
类名 | 类型选择器,匹配所有的该类以及子类的实例 |
类名[属性名="属性值"] | 属性选择器,匹配该该属性名等于属性值的类的实例,属性可以是自定义的 |
#对象名称 | ID 选择器,匹配指定对象名称的实例,这里的对象名称是通过 setObjectName() 方法设置的 |
.类名 | 匹配指定类的所有实例,不包含子类 |
父控件类名 子控件类名 | 后代选择器,匹配父控件中直接或间接包含的所有子控件 |
父控件类名>子控件类名 | 子选择器,匹配父控件中直接包含的所有子控件 |
3.3 子控件
一些复合型控件,例如 QSpinBox,由 QLineEdit 、向上的箭头 和 向下的箭头构成,向上的箭头和向下的箭头可以称为子控件。对子控件的引用是在控件和子控件之间用两个连续的冒号 ::
隔开;QSpinBox 控件的向上调节按钮控件和向下调节按钮控件可以分别通过 ::up-button
和 ::down-button
获取到。
3.4 伪状态
控件会根据用户的不同操作呈现出不同的状态,这些状态也被称为 “伪状态”。一个控件有多种状态,例如 活跃(active)、激活(enabled)、失效(disabled)、鼠标悬停(hover)、选中(checked)、未选中(unchecked) 和 可编辑(editable)等,根据控件所处的状态,可以给控件设置不同的外观。
样式表的格式字符串中,控件与状态之间用冒号 :
隔开,例如 "QPushButton:active{...}"
设置激活时的外观;可以同时对多个状态进行设置,例如 "QPushButton:active:hover{...}"
设置激活或者光标悬停时的外观;可以在状态前加 !
,表示相反的状态。
3.5 盒子模型
大多数控件都是长方形的,一个长方形控件由 Content、Padding、Border 和 Margin 4 部分构成,每个部分都是矩形。Content 矩形是除掉边距、边框和填充之后的部分,默认情况下,边距、边框和填充的距离都为 0,因此这 4 个矩形是重合的。可以用样式表分别设置这四个矩形之间的距离、边框的颜色。
Content 是输入内容的区域,可以设置 Content 区域宽度和高度的最大值和最小值,属性名称分别为 max-width、max-height、min-width 和 min-height。
对于 Padding 区域,用 padding 属性可以分别设置 Padding 与 Content 在上、右、下和左方向的距离,也可用padding-top、padding-right、padding-bottom 和 padding-left 属性分别设置距离。
Border 区域可以设置的属性比较多,如下所示。
属性名称 | 类型 | 说明 |
---|---|---|
border | Border | 设置边框的简写方法,相当于指定 border-color、border-style 和 border-width |
border-top | Border | 设置控件顶部边框的简写方法,相当于指定 border-top-color、border-top-style 和 border-top-width |
border-right | Border | 设置控件右边框的简写方法,相当于指定 border-right-color、border-right-style 和 border-right-width |
border-bottom | Border | 设置控件底部边框的简写方法,相当于指定 border-bottom-color、border-bottom-style 和 border-bottom-width |
border-left | Border | 设置控件左边框的简写方法,相当于指定 border-left-color、border-left-style 和 border-left-width |
border-color | BoxColors | 边框边界的颜色,相当于指定 border-top-color、border-right-color、border-bottom-color 和 border-left-color,默认值为控件的前景色 |
border-top-color | Brush | 边框顶部边界线的颜色 |
border-right-color | Brush | 边框右边界线的颜色 |
border-bottom-color | Brush | 边框底部边界线的颜色 |
border-left-color | Brush | 边框左边界线的颜色 |
border-radius | Radius | 边框角落的圆角半径,相当于指定 border-top-left-radius、border-top-right-radius、border-bottom-right-radius 和 bordedr-bottom-left-radius,默认值为 0 |
border-top-left-radius | Radius | 边框左上角圆角的半径 |
border-top-right-radius | Radius | 边框右上角圆角的半径 |
border-bottom-right-radius | Radius | 边框右下角圆角的半径 |
bordedr-bottom-left-radius | Radius | 边框左下角圆角的半径 |
border-style | BorderStyle | 边框边界线的样式(虚线、实线、点划线等),默认为 None |
border-top-style | BorderStyle | 边框顶部边界线的样式 |
border-right-style | BorderStyle | 边框右侧边界线的样式 |
border-bottom-style | BorderStyle | 边框底部边界线的样式 |
border-left-style | BorderStyle | 边框左侧边界线的样式 |
border-width | BorderLengths | 边框的宽度,相当于指定 border-top-width、border-right-width、border-bottom-width 和 border-left-width |
border-top-width | Length | 边框顶部边界线的宽度 |
border-right-width | Length | 边框右侧边界线的宽度 |
border-bottom-width | Length | 边框底部边界线的宽度 |
border-left-width | Length | 边框左侧边界线的宽度 |
border-image | BorderImage | 填充边框的图像,该图像被分割成 9 个部分,并在必要时适当的拉伸 |
对于 Margin 区域可以设置页边距。margin 属性设置控件的边距,等效于指定 margin-top、margin-right、margin-bottom、margin-left,默认为 0,margin-top、margin-right、margin-bottom、margin-left 分别设置控件的上、右、下和左侧的边距。
3.6 定位
对于子控件,可以设置其在父控件中的位置,与此有关的属性名称如下所示。
属性名称 | 类型 | 说明 |
---|---|---|
subcontrol-origin | Origin | 子控件的矩形原点,默认为 padding |
subcontrol-position | Alignment | 子控件在 subcontrol-origin 属性指定的矩形内的对齐方式,默认取决于子控件 |
position | Relative Absolute | 使用 left、right、top、bottom 属性的偏移是相对坐标还是绝对坐标,默认为 relative |
spacing | Length | 控件的内部间距,默认值取决于当前风格 |
top、right、bottom、left | Length | 以 bottom 属性为例,若 position 属性是 relative(默认值),则将子控件向上移动;若 position 是 absolute(绝对的),则 bottom 属性是指子控件的下边缘的距离,该距离与 subcontrol-origin 属性有关,默认为 0 |
height、width | Length | 子空间的高度/宽度,默认值取决于当前样式。除非另有特别规定,否则在控件上设置此属性无效。若想要控件上有一个固定的高度,应将 min-height 和 max-heigth 的值设置为相同,宽度类似 |
max-height | Length | 控件或子控件的最大高度 |
max-width | Length | 控件或子控件的最大宽度 |
min-height | Length | 控件或子控件的最小高度,默认依赖与控件的内容和风格 |
min-width | Length | 控件或子控件的最小宽度,默认依赖与控件的内容和风格控件或子控件的最大高 |
4.多媒体操作
4.1 音频播放
QSoundEffect 可以用来播放无压缩的音频文件(典型的是 .wav 文件),通过它我们不仅能够以低延迟的方式来播放音频,还能够对音频进行更进一步的操作(比如控制音量)。该类非常适合用来播放交互音效,如弹出框的提示音、游戏音效等。
创建 QSoundEffect 类的对象方法如下:
QSoundEffect(parent:QObject=None)
QSoundEffect(audioDevice:QAudioDevice, parent:QObject=None)
QSoundEffect 类常用方法如下:
# 实例方法
setSource(url:QUrl) -> None # 设置音频源
source() -> QUrl # 获取音频源
setAudioDevice(device:QAudioDevice) -> None # 设置音频设备
audioDevice() -> QAudioDevice # 获取音频设备
setLoopCount(loopCount:int) -> None # 设置播放次数
loopCount() -> int # 获取播放次数
loopsRemaining() -> int # 获取剩余播放次数
setMuted(muted:bool) -> None # 设置是否静音
isMuted() -> bool # 获取是否静音
setVolume(volume:float) -> None # 设置音量
volume() -> float # 获取音量
isPlaying() -> bool # 获取是否正在播放
isLoaded() -> bool # 获取是否已经加载声源
status() -> QSoundEffet.Status # 获取播放状态
# 槽函数
play() -> None # 开始播放
stop() -> None # 停止播放
# 静态方法
supportedMimeTypes() -> List[str] # 获取支持的类型
用 setSource(url:QUrl)
方法设置音频源,参数 QUrl 可以是指向网络的文件,也可以是本机文件;用 source()
方法获取音频源 QUrl。
用 status()
方法获取当前的播放状态,返回值是枚举类型 QSoundEffect.Status,可取值如下:
QSoundEffect.Status.Null
QSoundEffect.Status.Loading
QSoundEffect.Status.Ready
QSoundEffect.Status.Error
QSoundEffect 类常用信号如下:
audioDeviceChanged() # 音频设备发生改变时发射信号
loadedChanged() # 加载状态发生改变时发射信号
loopCountChanged() # 循环次数发生改变时发射信号
loopsRemainingChanged() # 剩余循环次数发生改变时发射信号
mutedChanged() # 静音状态发生改变时发射信号
playingChanged() # 播放状态发生改变时发射信号
sourceChanged() # 音频源发生改变时发射信号
statusChanged() # 状态发生改变时发射信号
volumeChanged() # 音量发生改变时发射信号
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QLabel, QPushButton, QCheckBox, QSlider
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QFormLayout
from PySide6.QtWidgets import QFileDialog
from PySide6.QtCore import QUrl, Qt
from PySide6.QtMultimedia import QSoundEffect
class MyWidget(QWidget):
def __init__(self):
# 1、调用父类的__init__()方法
super().__init__()
self.setup_ui()
def setup_ui(self):
# 1、设置窗口对象大小
self.resize(700, 500)
# 2、创建音频播放对象
self.audio = QSoundEffect()
v_layout = QVBoxLayout(self)
self.label_open_file = QLabel("未选择文件")
v_layout.addWidget(self.label_open_file)
form_layout = QFormLayout()
v_layout.addLayout(form_layout)
self.label_volume = QLabel("音量:")
self.slider_volume = QSlider()
# 设置滑块的范围
self.slider_volume.setRange(0, 100)
# 设置滑块的的当前数据
self.volume = 80
self.slider_volume.setValue(self.volume)
form_layout.addRow(self.label_volume, self.slider_volume)
self.slider_volume.valueChanged.connect(self.change_volume)
h_layout = QHBoxLayout()
v_layout.addLayout(h_layout)
self.button_open_file = QPushButton("选择音频文件")
self.button_open_file.clicked.connect(self.open_file)
h_layout.addWidget(self.button_open_file)
self.button_action = QPushButton("开始播放")
h_layout.addWidget(self.button_action)
self.button_action.clicked.connect(self.action)
self.checkBox_mute = QCheckBox("是否静音")
h_layout.addWidget(self.checkBox_mute)
# 没有不确定状态
self.checkBox_mute.setTristate(False)
# 设置复选框状态
self.checkBox_mute.setCheckState(Qt.CheckState.Unchecked)
self.checkBox_mute.stateChanged.connect(self.mute)
# 当播放状态发生改变时发射信号
self.audio.playingChanged.connect(self.reset_play)
def open_file(self):
# 1、创建文件对话框
fileName, filter = QFileDialog.getOpenFileName(self, caption="选择音频文件", dir="./", filter="音频文件(*.wav)")
# 2、选择文件后更改标签的文本
self.label_open_file.setText(f"你选择了【{fileName}】音频文件")
# 3、设置音频播放对象的声源
self.audio.setSource(QUrl.fromLocalFile(fileName))
# 4、设置音频播放对象的播放次数
self.audio.setLoopCount(1)
# 5、设置音频播放对象不静音
self.audio.setMuted(False)
# 6、设置音量
self.audio.setVolume(self.volume / 100)
# 7、重置为开始播放按钮
self.button_action.setText("开始播放")
def action(self):
if self.button_action.text() == "开始播放":
# 开始播放
self.audio.play()
# 重新设置按键文本为停止播放
self.button_action.setText("停止播放")
elif self.button_action.text() == "停止播放":
# 停止播放
self.audio.stop()
# 重新设置按键文本为开始播放
self.button_action.setText("开始播放")
def mute(self, state):
if state:
# 设置音频播放对象静音
self.audio.setMuted(True)
else:
# 设置音频播放对象不静音
self.audio.setMuted(False)
def change_volume(self, value):
# 设置音量
self.audio.setVolume(value / 100)
def reset_play(self):
self.button_action.setText("开始播放")
if __name__ == "__main__":
# 1、创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2、创建一个窗口
window = MyWidget()
# 3、展示窗口
window.show()
# 4、进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
4.2 媒体播放器
播放器 QMediaPlayer 可以播放音频和视频,它可以直接播放的格式有限。要播放更多格式的音频或视频,例如 mp4 格式的视频文件,需要在本机上安装解码器。
QMediaPlayer 继承自 QObject,用 QMediaPlayer 定义播放器实例对象的方法如下所示,其中 parent 是继承自 QObject 类的实例对象。
QMediaPlayer(parent:QObject=None)
QMediaPlayer 类的常用方法:
# 实例方法
sourceDevice() -> QIODevice # 获取音频或视频源QIODevice
setActiveAudioTrack(index:int) -> None # 设置当前的声道
activeAudioTrack() -> int # 获取当前的声道
setActiveVideoTrack(index:int) -> None # 设置当前的视频轨道
activeVideoTrack() -> int # 获取当前的视频轨道
setActiveSubtitleTrack(index:int) -> None # 设置当前的子标题轨道
activeSubtitleTrack() -> int # 获取当前的子标题轨道
playbackRate() -> float # 获取播放速率
isSeekable() -> bool # 获取是否可以定位到某一播放时间
position() -> int # 获取当前的播放时间(毫秒)
setAudioOutput(output:QAudioOutput) -> None # 设置播放音频的设备
setVideoOutput(arg__1:QVideoWidget) -> None # 设置显示视频的控件
setVideoOutput(arg__1:QGraphcicVideoItem) -> None # 设置显示视频的图项
setLoops(loops:int) -> None # 设置循环播放次数
loops() -> int # 获取循环播放次数
duration() -> int # 获取音频或视频可以播放的总时长(毫秒)
playbackState() -> QMedioPlayer.playbackState # 获取播放状态
mediaStatus() -> mediaStatus # 获取播放器所处的状态
error() -> QMediaPlayer.Error # 获取出错原因
errorString() -> str # 获取出错信息
hasAudio() -> bool # 获取多媒体中是否有音频
hasVideo() -> bool # 获取多媒体中是否有视频
bufferProgress() -> float # 获取缓冲百分比,100%时才可以播放
# 槽方法
setSource(source:QUrl, str) -> None # 设置要播放的音频或视频源
setSourceDevice(device:QIODevice, sourceUrl:QUrl=QUrl()) -> None # 设置音频或视频地址QUrl
setPlaybackRate(rate:float) -> None # 设置播放速率
setPosition(position:int) -> None # 设置播放时间(毫秒)
play() -> None # 播放音频或视频
pause() -> None # 暂停播放
stop() -> None # 停止播放并将播放位置重置到开头
要播放音频或视频,首先需要给 QMediaPlayer 设置媒体源。可以用 setSource(source:QUrl)
方法或用 setSourceDevice(device:QIODevice,sourceUrl:QUrl=Default(QUrl))
方法设置媒体文件,其中 sourceUrl 是可选参数,用于获取额外的信息;用 source()
和 sourceDevice()
分别获取媒体源 QUrl 和 QIODevice。
要显示视频,需要将 QMediaPlayer 与显示视频的控件关联,可以显示视频的控件有 QVideoWidget、QGraphicsVideoItem,关联方法分别是 setVideoOutput(QVideoWidget)
和 setVideoOutput(QGraphicsVideoItem)
。要播放音频,需要用 setAudioOutput(output:QAudioOutput)
方法设置音频输出设备。
用 setPlaybackRate(rate:float)
方法设置播放速率,参数为 1.0 表示正常播放;参数 rate 可以为负值,表示回放速率。有些多媒体不支持回放。
用 setLoops(loops:int)
方法设置循环播放次数,参数 loops 可取 QMediaPlayer.Infinite(无限次)、QMediaPlayer.Once(一次)或 其它整数。
用 state()
方法获取播放状态 QMediaPlayer.State,返回值可能取值如下:
QMediaPlayer.State.StoppedState
QMediaPlayer.State.PlayingState
QMediaPlayer.State.PausedState
用 playbackState()
方法获取播放器的播放状态 QMediaPlayer.PlaybackState,返回值可能取值如下:
QMediaPlayer.PlaybackState.StoppedState
QMediaPlayer.PlaybackState.PlayingState
QMediaPlayer.PlaybackState.PausedState
用 mediaStatus()
方法获取播放器所处的状态 QMediaPlayer.MediaStatus,返回值可能取值如下:
QMediaPlayer.MediaStatus.NoMedia
QMediaPlayer.MediaStatus.LoadingMedia
QMediaPlayer.MediaStatus.LoadedMedia
QMediaPlayer.MediaStatus.StalledMedia
QMediaPlayer.MediaStatus.BufferingMedia
QMediaPlayer.MediaStatus.BufferedMedia
QMediaPlayer.MediaStatus.EndOfMedia
QMediaPlayer.MediaStatus.InvalidMedia
用 error()
方法获取播放器出错信息 QMediaPlayer.Error,返回值可能取值如下:
QMediaPlayer.Error.NoError
QMediaPlayer.Error.ResourceError
QMediaPlayer.Error.FormatError
QMediaPlayer.Error.NetworkError
QMediaPlayer.Error.AccessDeniedError
QMediaPlayer 类的常用信号:
activeTracksChanged() # 当轨道发生改变时发射信号
audioOutputChanged() # 音频输出设备发生改变时发射信号
bufferProgressChanged(progress:float) # 缓冲进度发生改变时发射信号
durationChanged(duration:int) # 播放时长发生改变时发射信号
errorChanged() # 出错信息发生改变时发射信号
errorOccurred(error:QMediaPlayer.Error, errorString:str) # 播放出错时发射信号
hasAudioChanged(available:bool) # 可播放音频的状态发生改变时发射信号
hasVideoChanged(videoAvailable:bool) # 可播放视频的状态发生改变时发射信号
loopsChanged() # 播放次数发生改变时发射信号
mediaStatusChanged(status:QMediaPlayer.MediaStatus) # 播放器所在的状态发生改变时发射信号
metaDataChanged() # 播放器元数据发生改变时发射信号
playbackRateChanged(rate:float) # 播放速度发生改变时发射信号
playbackStateChanged(newState:QMediaPlayer.PlaybackState) # 播放状态发生改变时发射信号
playingChanged(playing:bool) # 播放状态发生改变时发射信号
positionChanged(position:int) # 播放位置发生改变时发射信号
seekableChanged(seekable:bool) # 可定位播放状态发生改变时发射信号
sourceChanged(media:QUrl) # 音频或视频源发生改变时发射信号
tracksChanged() # 轨道发生改变时发射信号
videoOutputChanged() # 关联的视频播放器发生改变时发射信号
播放器 QMediaPlayer 需要关联音频输出设备和视频输出控件才能播放音频和视频。音频输出需要定义 QAudioOutput 的实例,QAudioOutput 用于连接 QMediaPlayer 与音频输出设备,视频输出需要用到视频控件 QVideoWidget 或视频图项 QGraphicsVideoItem,其中 QGraphicsVideoItem 作为图项应用于场景中。QAudioOutput 继承自 QObject,QVideoWidget 继承自 QWidget,QGraphicsVideoItem 继承自 QGraphicsObject。
用 QAudioOutput、QVideoWidget 和 QGraphicsVideoItem 创建实例对象的方法如下所示,其中 QAudioDevice 是本机上的音频输入输出设备。
QAudioOutput(parent:QObject=None)
QAudioOutput(device:QAudioDevice, parent:QObject=None)
QVideoWidget(parent:QWidget=None)
QGraphicsVideoItem(parent:QGrapicsItem=None)
QAudioOutput 类的常用方法如下:
# 实例方法
volume() -> float # 获取音量
isMuted() -> bool # 获取是否静音
device() -> QAudioDevice # 获取音频设备
# 槽方法
setVolume(volume:float) -> None # 设置音量,参数取值范围为0~1
setMuted(muted:bool) -> None # 设置静音状态
setDevice(device:QAudioDevice) -> None # 设置音频设备
QAudioOutput 类的常用信号如下:
deviceChanged() # 音频设备发生改变时发射信号
mutedChanged(muted:bool) # 静音状态发生改变时发送信号
volumeChanged(volume:float) # 音量发生改变时候发射信号
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QLabel, QPushButton, QCheckBox, QSlider
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QFormLayout
from PySide6.QtWidgets import QFileDialog
from PySide6.QtCore import QUrl, Qt
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
class MyWidget(QWidget):
def __init__(self):
# 1、调用父类的__init__()方法
super().__init__()
self.mediaPlayer = None
self.setup_ui()
def setup_ui(self):
# 1、设置窗口对象大小
self.resize(700, 500)
v_layout = QVBoxLayout(self)
self.label_open_file = QLabel("未选择文件")
v_layout.addWidget(self.label_open_file)
form_layout = QFormLayout()
v_layout.addLayout(form_layout)
self.label_volume = QLabel("音量:")
self.slider_volume = QSlider()
# 设置音量滑块的范围
self.slider_volume.setRange(0, 100)
form_layout.addRow(self.label_volume, self.slider_volume)
self.slider_volume.valueChanged.connect(self.change_volume)
h_layout = QHBoxLayout()
v_layout.addLayout(h_layout)
self.button_open_file = QPushButton("选择音频文件")
self.button_open_file.clicked.connect(self.open_file)
h_layout.addWidget(self.button_open_file)
self.button_action = QPushButton("开始播放")
h_layout.addWidget(self.button_action)
self.button_action.clicked.connect(self.action)
self.button_stop = QPushButton("停止播放")
h_layout.addWidget(self.button_stop)
self.button_stop.clicked.connect(self.stop)
self.checkBox_mute = QCheckBox("是否静音")
h_layout.addWidget(self.checkBox_mute)
# 没有不确定状态
self.checkBox_mute.setTristate(False)
self.checkBox_mute.toggled.connect(self.mute)
def open_file(self):
# 1、创建媒体播放器对象
self.mediaPlayer = QMediaPlayer()
# 2、创建音频播放对象
self.audioOutput = QAudioOutput()
# 3、设置播放音频的设备
self.mediaPlayer.setAudioOutput(self.audioOutput)
# 4、创建文件对话框
fileName, filter = QFileDialog.getOpenFileName(self, caption="选择音频文件", dir="./", filter="音频文件(*.wav *.flac)")
# 5、选择文件后更改标签的文本
self.label_open_file.setText(f"你选择了【{fileName}】音频文件")
# 6、重置为开始播放按钮
self.button_action.setText("开始播放")
# 7、设置媒体播放器对象的声源
self.mediaPlayer.setSource(QUrl.fromLocalFile(fileName))
# 8、设置媒体播放器对象的播放次数
self.mediaPlayer.setLoops(1)
# 9、设置音频播放对象是否静音
self.audioOutput.setMuted(False)
self.checkBox_mute.setCheckState(Qt.CheckState.Unchecked)
# 10、设置音量
self.slider_volume.setValue(80)
self.audioOutput.setVolume(0.8)
# 11、播放状态发生改变时发射信号
self.mediaPlayer.playbackStateChanged.connect(self.reset_play)
def action(self):
if self.mediaPlayer:
if self.button_action.text() == "开始播放":
# 开始播放
self.mediaPlayer.play()
# 重新设置按键文本为停止播放
self.button_action.setText("暂停播放")
elif self.button_action.text() == "暂停播放":
# 停止播放
self.mediaPlayer.pause()
# 重新设置按键文本为开始播放
self.button_action.setText("开始播放")
def stop(self):
if self.mediaPlayer:
# 1、媒体播放器停止播放
self.mediaPlayer.stop()
# 2、重置为开始播放按钮
self.button_action.setText("开始播放")
def mute(self, state):
if self.mediaPlayer:
if state:
# 设置音频播放对象静音
self.audioOutput.setMuted(True)
else:
# 设置音频播放对象不静音
self.audioOutput.setMuted(False)
def change_volume(self, value):
if self.mediaPlayer:
# 设置音量
self.audioOutput.setVolume(value / 100)
def reset_play(self, state):
if self.mediaPlayer:
if state == QMediaPlayer.PlaybackState.StoppedState:
self.button_action.setText("开始播放")
if __name__ == "__main__":
# 1、创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2、创建一个窗口
window = MyWidget()
# 3、展示窗口
window.show()
# 4、进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
QVideoWidget 类的常用方法如下:
# 实例方法
aspectRatioMode() -> Qt.AspectRatioMode # 获取长宽比模式
isFullScreen() -> bool # 获取是否全屏显示
# 槽方法
setAspectRatioMode(mode:Qt.AspectRatioMode) -> None # 设置长宽比模式
setFullScreen(fullScreen:bool) -> None # 设置全屏显示
QVideoWidget 类的常用方法如下:
aspectRatioModeChanged(mode:Qt.AspectRatioMode) # 长宽比模式发生改变时发射信号
fullScreenChanged(fullScreen:bool) # 全屏状态发生改变时发射信号
QGraphicsVideoItem 类的常用方法如下:
setAspectRatioMode(mode:Qt.AspectRatioMode) -> None # 设置长宽比模式
aspectRatioMode() -> Qt.AspectRatioMode # 获取长宽比模式
setOffset(offset:Union[QSize, QSizeF]) -> None # 设置偏移量
offset() -> QPointF # 获取偏移量
setSize(size:Union[QSize, QSizeF]) -> None # 设置尺寸
size() -> QSizeF # 获取尺寸
QGraphicsVideoItem 类的常用方法如下:
nativeSizeChanged(size:QSizeF) # 尺寸发生改变时发射信号
其中,用 setAspectRatioMode(mode:Qt.AspectRatioMode)
方法设置视频控件所播放视频的长宽比模式,参数 mode 可取值如下:
Qt.AspectRatioMode.IgnoreAspectRatio # 不保持比例关系
Qt.AspectRatioMode.KeepAspectRatio # 保持原比例关系
Qt.AspectRatioMode.KeepAspectRatioByExpanding # 通过扩充保持原比例关系
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QLabel, QPushButton, QCheckBox, QSlider
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QFormLayout
from PySide6.QtWidgets import QFileDialog
from PySide6.QtCore import QUrl, Qt
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
from PySide6.QtMultimediaWidgets import QVideoWidget, QGraphicsVideoItem
class MyWidget(QWidget):
def __init__(self):
# 1、调用父类的__init__()方法
super().__init__()
self.mediaPlayer = None
self.setup_ui()
def setup_ui(self):
# 1、设置窗口对象大小
self.resize(700, 500)
v_layout = QVBoxLayout(self)
self.label_open_file = QLabel("未选择文件")
v_layout.addWidget(self.label_open_file)
# 2、创建视频播放对象
self.videoWidget = QVideoWidget()
v_layout.addWidget(self.videoWidget)
form_layout = QFormLayout()
v_layout.addLayout(form_layout)
self.label_volume = QLabel("音量:")
self.slider_volume = QSlider()
# 设置音量滑块的布局
self.slider_volume.setOrientation(Qt.Orientation.Horizontal)
# 设置音量滑块的范围
self.slider_volume.setRange(0, 100)
form_layout.addRow(self.label_volume, self.slider_volume)
self.slider_volume.valueChanged.connect(self.change_volume)
h_layout = QHBoxLayout()
v_layout.addLayout(h_layout)
self.button_open_file = QPushButton("选择视频文件")
self.button_open_file.clicked.connect(self.open_file)
h_layout.addWidget(self.button_open_file)
self.button_action = QPushButton("开始播放")
h_layout.addWidget(self.button_action)
self.button_action.clicked.connect(self.action)
self.button_stop = QPushButton("停止播放")
h_layout.addWidget(self.button_stop)
self.button_stop.clicked.connect(self.stop)
self.checkBox_mute = QCheckBox("是否静音")
h_layout.addWidget(self.checkBox_mute)
# 没有不确定状态
self.checkBox_mute.setTristate(False)
self.checkBox_mute.stateChanged.connect(self.mute)
def open_file(self):
# 1、创建媒体播放器对象对象
self.mediaPlayer = QMediaPlayer()
# 2、创建音频播放对象
self.audioOutput = QAudioOutput()
# 3、设置播放音频的设备
self.mediaPlayer.setAudioOutput(self.audioOutput)
# 4、设置播放视频的设备
self.mediaPlayer.setVideoOutput(self.videoWidget)
# 5、创建文件对话框
fileName, filter = QFileDialog.getOpenFileName(self, caption="选择视频文件", dir="./", filter="音频文件(*.wav *.flac);;视频文件(*.avi)")
# 6、选择文件后更改标签的文本
self.label_open_file.setText(f"你选择了【{fileName}】视频文件")
# 7、重置为开始播放按钮
self.button_action.setText("开始播放")
# 8、设置音频播放对象的声源
self.mediaPlayer.setSource(QUrl.fromLocalFile(fileName))
# 9、设置音频播放对象的播放次数
self.mediaPlayer.setLoops(1)
# 10、设置音频播放对象不静音
self.checkBox_mute.setCheckState(Qt.CheckState.Unchecked)
self.audioOutput.setMuted(False)
# 11、设置音量
self.slider_volume.setValue(80)
self.audioOutput.setVolume(0.8)
# 12、设置视频控件所播放视频的长宽比模式
self.videoWidget.setAspectRatioMode(Qt.AspectRatioMode.KeepAspectRatioByExpanding)
# 13、播放状态发生改变时发射信号
self.mediaPlayer.playbackStateChanged.connect(self.reset_play)
def action(self):
if self.mediaPlayer:
if self.button_action.text() == "开始播放":
# 开始播放
self.mediaPlayer.play()
# 重新设置按键文本为停止播放
self.button_action.setText("暂停播放")
elif self.button_action.text() == "暂停播放":
# 停止播放
self.mediaPlayer.pause()
# 重新设置按键文本为开始播放
self.button_action.setText("开始播放")
def stop(self):
if self.mediaPlayer:
# 1、媒体播放器停止播放
self.mediaPlayer.stop()
# 2、重置为开始播放按钮
self.button_action.setText("开始播放")
def mute(self, state):
if self.mediaPlayer:
if state:
# 设置音频播放对象静音
self.audioOutput.setMuted(True)
else:
# 设置音频播放对象不静音
self.audioOutput.setMuted(False)
def change_volume(self, value):
if self.mediaPlayer:
# 设置音量
self.audioOutput.setVolume(value / 100)
def reset_play(self, state):
if self.mediaPlayer:
if state == QMediaPlayer.PlaybackState.StoppedState:
self.button_action.setText("开始播放")
if __name__ == "__main__":
# 1、创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2、创建一个窗口
window = MyWidget()
# 3、展示窗口
window.show()
# 4、进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
5.媒体录制
5.1 多媒体设备
多媒体设备是指本机中的音频输入设备(如麦克风)、音频输出设备(如音箱、头戴耳机)和视频输入设备(如摄像头)。多媒体设备通过 QMediaDevices 类提供的方法来获取,音频输入输出设备类是 QAudioDevice,视频输入设备类是 QCameraDevice。
用 QMediaDevices、QAudioDevice 和 QCameraDevice 创建设备实例的方法如下所示。
QMediaDevices(parent:QObject=None)
QAudioDevice()
QCameraDevice()
QMediaDevices 类的常用方法如下:
# 静态方法
audioInputs() -> List[QAudioDevice] # 获取音频输入设备
defaultAudioInput() -> QAudioDevice # 获取默认音频输入设备
audioOutputs() -> List[QAudioDevice] # 获取音频输出设备
defaultAudioOutput() -> QAudioDevice # 获取默认音频输出设备
videoInputs() -> List[QCameraDevice] # 获取视频输入设备
defaultVideoInput() -> QCameraDevice # 获取默认的视频输入设备
QMediaDevices 类的信号方法如下:
audioInputsChanged() # 当音频输入设备发生改变时发射信号
audioOutputsChanged() # 当音频输出设备发生改变时发射信号
videoInputsChanged() # 当视频输入设备发生改变时发射信号
QAudioDevice 类的常用方法如下:
description() -> str # 获取音频设备的信息
id() -> QQByteArray # 获取音频设备的识别号
isDefault() -> bool # 获取是否是默认的音频设备
isFormatSupported(format:QAudioFormat) -> bool # 获取音频设备是否支持某种音频格式
isNull() -> bool # 获取设备是否有效
minimumChannelCount() -> int # 获取音频设备支持的最小通道数
maximumChannelCount() -> int # 获取音频设备支持的最大通道数
minimumSampleRate() -> int # 获取音频设备支持的最小采样率
maximumSampleRate() -> int # 获取音频设备支持的最大采样率
mode() -> QAudioDevice.Mode # 获取音频设备是输入还是输出设备
preferredFormat() -> QAudioFormat # 获取音频设备的默认音频格式
supportedSampleFormats() -> List[QAudioFormat.SampleFormat] # 获取音频设备支持的采样格式
mode()
的方法获取音频设备是输入还是输出设备,返回值是 QAudioDevice.Mode 枚举值,返回的值可以是:
QAudioDevice.Mode.Null # 无效设备
QAudioDevice.Mode.Input # 输入设备
QAudioDevice.Mode.Output # 输出设备
supportedSampleFormats()
方法获取音频设备支持的采样格式,返回值是 QAudioFormat.SampleFormat 的列表,QAudioFormat.SampleFormat 枚举值如下:
QAudioFormat.SampleFormat.UInt8
QAudioFormat.SampleFormat.Int16
QAudioFormat.SampleFormat.Int32
QAudioFormat.SampleFormat.Float
QAudioFormat.SampleFormat.Unknown
QCameraDevice 类的常用方法如下:
description() -> str # 获取视频输入设备的信息
id() -> QQByteArray # 获取视频输入设备的识别号
isDefault() -> bool # 获取是否是默认的视频输入设备
isNull() -> bool # 获取视频输入设备是否有效
photoResolutions() -> List[QSize] # 获取视频输入设备的分辨率
position() -> QCameraDevice.Position # 获取视频输入的位置
videoFormats() -> List[QCameraFormat] # 获取视频输入设备支持的格式
position()
方法获取视频输入的位置,返回值是 QCameraDevice.Position 枚举值,返回值可以是:
QCameraDevice.Position.BackFace # 后置摄像头
QCameraDevice.Position.FrontFace # 前置摄像头
QCameraDevice.Position.UnspecifiedPosition # 位置不确定
5.2 音频接口与视频接口
要录制音频和视频,需要定义音频设备的接口 QAudioInput 和视频设备的接口 QCamera 后,才能调用音频设备和视频设备进行录制,QAudioInput 和 QCamera 相当于音频和视频输入通道。QAudioInput 是机器上的音频输入,例如内置麦克风或头戴麦克风,而 QCamera 是机器上的摄像头或外接相机。
利用 QAudioInput 和 QCamera 创建音频设备和视频设备接口的方法如下所示。
QAudioInput(parent:QObject=None)
QAudioInput(deviceInfo:QAudioDevice, parent:QObject=None)
QCamera(parent:QObject=None)
QCamera(position:QCameraDevice.Position, parent:QObject=None)
5.2.1 音频接口
音频接口 QAudioInput 类的常用方法如下:
# 实例方法
device() -> QAudioDeicve # 获取音频设备
isMuted() -> bool # 获取是否静音
volume() -> bool # 获取音量
# 槽函数
setDevice(device:QAudioDevice) -> None # 设置音频设备
setMuted(muted:bool) -> None # 设置是否静音
setVolume(volume:float) -> float # 设置音量
音频接口 QAudioInput 类的常用信号如下:
deviceChanged() # 当设备发生改变时发射信号
mutedChanged(muted:bool) # 当静音状态发生改变时发射信号
volumeChanged(volume:float) # 当音量发生改变时发射信号
5.2.2 视频接口
视频接口 QCamera 类的常用方法如下:
# 实例方法
setCameraDevice(cameraDevice:QCameraaDevice) -> None # 设置视频设备
cameraDevice() -> QCameraDevice # 获取视频设备
isActive() -> bool # 获取相机是否启用
isAvailable() -> bool # 获取相机是否可用
setCameraFormat(format:QCameraFormat) -> None # 设置视频格式
captureSession() -> QMediaCaptureSession # 获取与QCamera关联的媒体捕获器
supportedFeatures() -> QCamera.Features # 获取支持的特征
isExposureModeSupported(mode:QCamera.ExposureMode) -> None # 获取是否支持某种曝光
exposureTime() -> float # 获取曝光时间
manualExposureTime() -> float -> float # 获取自定义曝光时间
isoSensitivity() -> int # 获取光敏感值
exposureCompensation() -> float # 获取曝光补偿(EV值)
flashMode() -> QCamera.FlashMode # 获取快闪模式
isFlashModeSupported(mode:QCamera.FlashMode) -> bool # 获取是否支持某种快闪模式
isFlashReady() -> bool # 获取是否可以用快闪
setFocusMode(mode:QCamera.FocusMode) -> None # 设置对焦模式
isFlashModeSupported(mode:QCamera.FocusMode) -> bool # 获取是否支持某种焦点模式
setFocusDistance(d:float) -> None # 设置自定义焦距,0表示最近的点,1表示无限远
focusDistance() -> float # 获取自定义焦距
setCustomFocusPoint(point:Union[QPoint, QPointF]) -> None # 设置自定义焦点位置
customFocusPoint() -> QPointF # 获取自定义焦点
focusPoint() -> QPointF # 获取焦点
torchMode() -> QCamera.TorchMode # 获取辅助光源模式
isTorchModeSupported(mode:QCamera.TorchMode) -> bool # 获取是否支持某种辅助光源模式
whiteBalanceMode() -> QCamera.WhiteBalanceMode # 获取白平衡模式
isWhiteBalanceModeSupported(mode:QCamera.WhiteBalanceMode) -> bool # 获取是否支持某种白平衡模式
colorTemperature() -> int # 获取颜色温度
setZoomFactor(factor:float) -> None # 设置缩放系数
zoomFactor() -> float # 获取缩放系数
minimumExposureTime() -> float # 获取最小的曝光时间
maximumExposureTime() -> float # 获取最大的曝光时间
minimumIsoSensitivity() -> int # 获取最小的光敏感值
maximumIsoSensitivity() -> int # 获取最大的光敏感值
minimumZoomFactor() -> float # 获取最小的方法系数
maximumZoomFactor() -> float # 获取最大的放大系数
error() -> QCamera.Error # 获取出错类型
errorString() -> str # 获取出错信息
# 槽函数
start() -> None # 开启相机
stop() -> None # 关闭相机
setActive(active:bool) -> None # 获取相机是否启用
setExposureMode(mode:QCamera.ExposureMode) -> None # 设置曝光模式
setAutoExposureTime() -> None # 打开自动计算曝光时间
setManualExposureTime(seconds:float) -> None # 设置曝光时间
setAutoIsoSensitivity() -> None # 根据曝光值开启自动选择光敏感值
setExposureCompensation(ev:float) -> None # 设置曝光补偿(EV值)
setFlashMode(mode:QCamera.FlashMode) -> bool # 设置是否可以用快闪
setTorchMode(mode:QCamera.TorchMode) -> None # 设置辅助光源模式
setWhiteBalanceMode(mode:QCamera.WhiteBalanceMode) -> None # 设置白平衡模式
setColorTemperature(colorTemperature:int) -> None # 设置颜色温度
zoomTo(zoom:float, rate:float) -> None # 根据速率设置缩放系数
用 setCameraDevice(cameraDevice:QCameraDevice)
方法为视频接口设置视频设备;用 start()
方法或 setActive(true)
方法开启视频设备;用 stop()
方法或 setActive(false)
方法停止视频设备。
用 supportedFeatures()
方法获取相机支持的特征,返回值如下所示。
QCamera.Feature.ColorTemperature # 相机支持色温
QCamera.Feature.ExposureCompensation # 相机支持补偿曝光
QCamera.Feature.IsoSensitivity # 相机支持自定义敏感值
QCamera.Feature.ManualExposureTime # 相机支持自定义曝光时间
QCamera.Feature.CustomFocusPoint # 相机支持自定义焦点
QCamera.Feature.FocusDistance # 相机支持自定义焦距
用 setExposureMode(mode:QCamera.ExposureMode)
方法设置相机的曝光模式,参数 mode 的取值是 QCamera.ExposureMode 的枚举值,可取的值如下所示;
QCamera.ExposureMode.ExposureAuto # 自动
QCamera.ExposureMode.ExposureManual # 手动
QCamera.ExposureMode.ExposurePortrait # 人物
QCamera.ExposureMode.ExposureNight # 夜晚
QCamera.ExposureMode.ExposureSports # 运动
QCamera.ExposureMode.ExposureSnow # 雪景
QCamera.ExposureMode.ExposureBeach # 海景
QCamera.ExposureMode.ExposureAction # 动作
QCamera.ExposureMode.ExposureLandscape # 风景
QCamera.ExposureMode.ExposureNightPortrait # 夜晚
QCamera.ExposureMode.ExposureTheatre # 剧院
QCamera.ExposureMode.ExposureSunset # 傍晚
QCamera.ExposureMode.ExposureSteadyPhoto # 固定
QCamera.ExposureMode.ExposureFireworks # 火景
QCamera.ExposureMode.ExposureParty # 宴会
QCamera.ExposureMode.ExposureCandlelight # 烛光
QCamera.ExposureMode.ExposureBarcode # 条码
用 setFlashMode(mode:QCamera.FlashMode)
方法设置相机的快闪模式,参数 mode 的取值是 QCamera.FlashMode 的枚举值,可取值如下:
QCamera.FlashMode.FlashOff
QCamera.FlashMode.FlashOn
QCamera.FlashMode.FlashAuto
用 setFocusMode(mode:QCamera.FocusMode)
方法设置对焦模式,参数 mode 的取值是 QCamera.FocusMode 的枚举值,可取的值如下所示。
QCamera.FocusMode.FocusModeAuto # 连续自动对焦模式
QCamera.FocusMode.FocusModeAutoNear # 对近处物体连续自动对焦模式
QCamera.FocusMode.FocusModeAutoFar # 对远处物体连续自动对焦模式
QCamera.FocusMode.FocusModeHyperfocal # 对超过焦距范围的物体采用最大景深值
QCamera.FocusMode.FocusModeInfinity # 对无限远对焦模式
QCamera.FocusMode.FocusModeManual # 手动或固定对焦模式
用 setTorchMode(mode:QCamera.TorchMode)
方法设置辅助光源模式,在光线不强时可以设置该模式,并会覆盖快闪模式,参数 mode 可取值如下:
QCamera.TorchMode.TorchOff
QCamera.TorchMode.TorchOn
QCamera.TorchMode.TorchAuto
用 setWhiteBalanceMode(mode:QCamera.WhiteBalanceMode)
方法设置白平衡模式,白平衡是描述红、绿、蓝三基色混合生成后白色精确度的一项指标。在房间里的日光灯下拍摄的影像会显得发绿,在室内钨丝灯光下拍摄出来的景物会偏黄,而在日光阴影处拍摄到的照片则偏蓝,白平衡的作用是不管在任何光源下都能将白色物体还原为白色。参数 mode 是 QCamera.WhiteBalanceMode 的枚举值,可取的值如下所示。
QCamera.WhiteBalanceMode.WhiteBalanceAuto # 自动
QCamera.WhiteBalanceMode.WhiteBalanceManual # 手动
QCamera.WhiteBalanceMode.WhiteBalanceShade # 阴影
QCamera.WhiteBalanceMode.WhiteBalanceTungsten # 钨灯
QCamera.WhiteBalanceMode.WhiteBalanceFluorescent # 荧光灯
QCamera.WhiteBalanceMode.WhiteBalanceSunlight # 阳光
QCamera.WhiteBalanceMode.WhiteBalanceCloudy # 云
QCamera.WhiteBalanceMode.WhiteBalanceFlash # 快闪
QCamera.WhiteBalanceMode.WhiteBalanceSunset # 日落
在手动模式下,需要用 setColorTemperature(colorTemperature:int)
方法设置色温。
视频接口 QCamera 类的常用信号如下:
activeChanged(arg__1:bool) # 照相机启动或停止时发射信号
cameraDeviceChanged() # 照相设备发生改变时发射信号
cameraFormatChanged() # 格式发生改变时发射信号
focusModeChanged() # 焦点模式发生改变时发射信号
focusDistanceChanged(d:float) # 焦距发生改变时发射信号
focusPointChanged() # 聚焦点发生改变时发射信号
customFocusPointChanged() # 自定义聚焦点发生改变时发射信号
exposureModeChanged() # 曝光模式发生改变时发射信号
exposureCompensationChanged(value:float) # 曝光补偿发生改变时发射信号
exposureTimeChanged(speed:float) # 曝光时间发生改变时发射信号
manualExposureTimeChanged(speed:float) # 自动曝光时间发生改变时发射信号
flashModeChanged() # 快闪模式发生改变时发射信号
flashReady(ready:bool) # 可以快闪时发射信号
isoSensitivityChanged(value:int) # 光敏感值发生改变时发射信号
manualIsoSensitivityChanged(value:int) # 自定义光敏感值发生改变时发射信号
minimumZoomFactorChanged(value:float) # 最小缩放系数发生改变时发射信号
maximumZoomFactorChanged(value:float) # 最大缩放系数发生改变时发射信号
zoomFactorChanged(value:int) # 缩放系数发生改变时发射信号
saturationChanged()
supportedFeaturesChanged() # 支持的功能发生改变时发射信号
torchModeChanged() # 辅助光源模式发生改变时发射信号
whiteBalanceModeChanged() # 白平衡模式发生改变时发射信号
colorTemperatureChanged() # 色温发生改变时发射信号
errorChanged() # 错误状态发生改变时发射信号
errorOccurred(error:QCamera.Error, errorString:str) # 出现错误时发射信号
5.2.3 媒体捕获器
媒体捕获器 QMediaCaptureSession 是音频数据和视频数据的集散地,它接收从 QAudioInput 和 QCamera 传递过来的音频和视频,然后将音频转发给 QAudioOutput 播放音频,将视频转发给 QVideoWidegt 或 QGraphicsVideoItem 播放视频,或者将音频和视频转发给 QMdiaRecorder 录制音频和视频,转发给 QImageCapture 实现拍照功能。
用 QMediaCaptureSession 创建实例对象的方法如下所示。
QMediaCaptureSession(parent:QObject=None)
QMediaCaptureSession 类的常用方法如下:
setAudioInput(input:QAudioInput) -> None # 设置音频输入
audioInput() -> QAudioInput # 获取音频输入
setAudioOutput(output:QAudioOutput) -> None # 设置音频输出
audioOutput() -> QAudioOutput # 获取音频输出
setCamera(camera:QCamera) -> None # 设置视频接口
camera() -> QCamera # 获取视频接口
setVideoOutput(output:QObject) -> None # 设置视频输出
videoOutput() -> QObject # 获取视频输出
setVideoSink(sink:QVideoSink) -> None # 设置视频接收器
videoSink() -> QVideoSink # 获取视频接收器
setImageCapture(imageCapture:QImageCapture) -> None # 设置图像捕获器
imageCapture() -> QImageCapture # 获取图像捕获器
setRecorder(recorder:QMidiaRecorder) -> None # 设置媒体记录器
recorder() -> QMeidaRecorder # 获取媒体记录器
QMediaCaptureSession 类的常用信号如下:
audioInputChanged() # 当音频输入发生改变时发射信号
audioOutputChanged() # 当视频输出发生改变时发射信号
cameraChanged() # 当视频输入发生改变时发射信号
videoOutputChanged() # 当视频输出发生改变时发射信号
imageCaptureChanged() # 当图像捕获器发生改变时发射信号
screenCaptureChanged() # 当屏幕捕获器发生改变时发射信号
windowCaptureChanged() # 当窗口捕获器发生改变时发射信号
recorderChanged() # 当记录器发生改变时发射信号
import sys
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QPushButton
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout
from PySide6.QtMultimedia import QMediaDevices, QCamera, QMediaCaptureSession
from PySide6.QtMultimediaWidgets import QVideoWidget
class MyWidget(QWidget):
def __init__(self):
# 1、调用父类的__init__()方法
super().__init__()
self.setup_ui()
def setup_ui(self):
# 1、设置窗口对象大小
self.resize(700, 500)
layout_v = QVBoxLayout()
self.setLayout(layout_v)
# 2、显示视频的控件
self.videoWidget = QVideoWidget()
layout_v.addWidget(self.videoWidget)
# 3、打开和停止摄像头的按钮
self.button_open = QPushButton("打开摄像头")
self.button_stop = QPushButton("关闭摄像头")
layout_h = QHBoxLayout()
layout_h.addWidget(self.button_open)
layout_h.addWidget(self.button_stop)
layout_v.addLayout(layout_h)
# 3、媒体设备
self.mediaDevice = QMediaDevices(self)
# 4、获取默认的视频输入设备
self.cameraDevice = self.mediaDevice.defaultVideoInput()
# 5、根据视频输入设备定义视频接口
self.camera = QCamera(self.cameraDevice)
# 6、媒体捕获器
self.mediaCaptureSession = QMediaCaptureSession(self)
# 7、设备媒体捕获器的视频接口
self.mediaCaptureSession.setCamera(self.camera)
# 8、设置媒体捕获器的视频输出控件
self.mediaCaptureSession.setVideoOutput(self.videoWidget)
# 9、定义信号与槽的连接
self.button_open.clicked.connect(self.camera.start)
self.button_stop.clicked.connect(self.camera.stop)
if __name__ == "__main__":
# 1、创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2、创建一个窗口
window = MyWidget()
# 3、展示窗口
window.show()
# 4、进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
5.2.4 媒体格式
在进行音频和视频的录制时,需要指定音频和视频的记录格式,以及媒体文件的存储格式,这些格式通过 QMediaFormat 类来定义。
用 QMediaFormat 类创建媒体格式实例的方法如下所示。
QMediaFormat(format:QMediaFormat.FileFormat=QQMediaFormant.UnspecifiedFormat)
QMediaFormat(other:Union[QMediaFotmat, QMediaFotmant.FileFormat])
QMediaFormat 类的常用方法如下:
# 实例方法
setFileFormat(f:QMediaFormat.FileFormat) -> None # 设置文件格式
fileFormat() -> QMediaFormat.FileFormat # 获取文件格式
setAudioCodec(codec:QMediaFormat.AudioCode) -> None # 设置音频编码格式
audioCodec() -> QMediaFormat.AudioCodec # 获取音频编码格式
setVideoCodec(codec:QMediaFormat.VideoCodec) -> None # 设置视频编码格式
videoCodec() -> QMediaFormat.VideoCodec # 获取视频编码格式
supportedFileFormats(m:QMediaFormat.ConversionMode) -> List[QMediaFormat.FileFormat] # 获取支持的文件编码格式列表
supportedAudioCodecs(m:QMediaFormat.ConversionMode) -> List[QMediaFormat.AudioCode] # 获取支持的音频编码格式列表
supportedVideoCodecs(m:QMediaFormat.ConversionMode) -> List[QMediaFormat.VideoCode] # 获取支持的视频编码格式列表
isSupported(mode:QMediaFormat.ConversionMode) -> bool # 获取是否可以对某种编码格式编码或解码
# 静态方法
fileFormatName(fileFormat:QMediaFormat.FileFormat) -> str # 获取文件格式名称
fileFormatDescription(fileFormat:QMediaFormat.FileFormat) -> str # 获取文件格式信息
audioCodecName(codec:QMediaFormat.AudioCode) -> str # 获取音频格式名称
audioCodecDescription(codec:QMediaFormat.AudioCode) -> str # 获取音频格式信息
videoCodecName(codec:QMediaFormat.VideoCode) -> str # 获取视频格式名称
videoCodecDescription(codec:QMediaFormat.VideoCode) -> str # 获取视频格式信息
分别用 supportedFileFormats(QMediaFormat.ConversionMode)
方法、supportedAudioCodecs(QMediaFormat.ConversionMode)
方法和 supportedVideoCodecs(QMediaFormat.ConversionMode)
方法获取编码或解码时支持的文件格式列表、音频编码格式列表和视频编码格式列表,参数 QMediaFormat.ConversionMode 可取值如下:
QMediaFormat.ConversionMode.Encode
QMediaFormat.ConversionMode.Decode
5.2.5 图像捕获
用媒体捕获器 QMediaCaptureSession 的 setImageCapture(imageCapture:QImageCapture)
方法将 QImageCapture 与 QMediaCatureSession 关联,可以捕获图像,实现拍照功能。
用 QImageCapture 类定义图像捕获的方法如下所示。
QImageCapture(parent:QObject=None)
QImageCapture 类常用方法如下:
# 实例方法
isReadyForCapture() -> bool # 获取是否可以拍照
captureSession() -> QMediaCaptureSession # 获取关联的捕捉器
setFileFormat(format:QImageCapture.FileFormat) -> None # 设置文件格式
fileFormat() -> QImageCapture.FileFormat # 获取文件格式
setQuality(quality:QImageCapture.Quality) -> None # 设置图像质量
quality() -> QImageCapture.Quality # 获取图像质量
setResolution(size:QSize) -> None # 设置分辨率
setResolution(width:int, height:int) -> None # 设置分辨率
resolution() -> QSize # 获取分辨率
setMetaData(metaData:QMediaMetaData) -> None # 设置元数据
metaData() -> QMediaMetaData # 获取元数据
addMetaData(metaData:QMediaMetaData) -> None # 添加元数据
error() -> QImageCapture.Error # 获取出错信息
errorString() -> str # 获取出错信息
# 槽函数
capture() -> int # 进行拍照
captureToFile(location="") -> int # 拍照到文件
# 静态方法
fileFormatDescription(c:QImageCapture.FileFormat) -> str # 获取格式的信息
fileFormatName(c:QImageCapture.FileFormat) -> str # 获取格式的名称
supportedFormats() -> List[QImageCapture.FileFormat] # 获取支持的格式
当 isReadyForCapture()
的返回值是 True 时,可以进行拍照;用 capture()
方法进行拍照,返回值是拍照的识别号 ID,同时会发送 imageCaptured(id:int,preview:QImage)
信号和 imageExposed(id:int)
信号,可以获取拍摄的图像;也可以用 captureToFile(location:str='')
方法直接将拍摄的图像保存到文件中,同时发送 imageCaptured(id:int,preview:QImage)
信号、imageExposed(id:int)
信号和 imageSaved(id:int,fileName:str)
信号,如果没有给出保存的文件名和路径,则使用默认的路径和文件名,如果只给出文件名,则保存到默认路径下,完整路径可以通过 imageSaved(id:int,fileName:str)
信号的参数获取。
用 error()
方法获取拍照时的出错状态,返回值是 QImageCapture.Error 枚举值,可取值如下:
QImageCapture.NoError # 没有错误
QImageCapture.NotReadyError # 设备没准备好
QImageCapture.ResourceError # 设备没准备好或不可用
QImageCapture.OutOfSpaceError # 存储空间不够
QImageCapture.NotSupportedFeatureError # 设备不支持拍照
QImageCapture.FormatError # 格式出错
用 setFileFormat(format:QImageCapture.FileFormat)
方法设置拍照的格式,参数是 QImageCapture.FileFormat 的枚举值,可取值如下:
QImageCapture.FileFormat.JPEG
QImageCapture.FileFormat.PNG
QImageCapture.FileFormat.Tiff
QImageCapture.FileFormat.WebP
QImageCapture.FileFormat.UnspecifiedFormat
QImageCapture.FileFormat.LastFileFormat
用 setQuality(quality:QImageCapture.Quality)
方法设置图像质量,参数是 QImageCapture.Quality 的枚举值,可取值如下:
QImageCapture.Quality.VeryLowQuality
QImageCapture.Quality.LowQuality
QImageCapture.Quality.NormalQuality
QImageCapture.Quality.HighQuality
QImageCapture.Quality.VeryHighQuality
QImageCapture 类常用信号如下:
readyForCaptureChanged(ready:bool) # 准备状态发生改变时发射信号
imageCaptured(id:int, preview:QImage) # 捕捉到图象时发射信号
imageExposed(id:int) # 图像曝光时发射信号
imageSaved(id:int, fileName:str) # 保存图像时发射信号
imageAvailable(id:int, frame:QVideoFrame) # 可以获取图像时发射信号
metaDataChanged() # 元数据发生改变时发射信号
qualityChanged() # 图像质量发生改变时发射信号
errorOccurred(id:int, error:QImageCapture.Error, errorString:str) # 出现错误时发射信号
errorChanged() # 错误状态发生改变时发射信号
fileFormatChanged() # 文件格式发生改变时发射信号
其中 imageAvailable(id:int,frame:QVideoFrame)
信号的参数 QVideoFrame 是视频帧,利用 QVideoFrame 的 toImage()
方法可以得到 QImage。
import sys
import os, time
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QPushButton
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout
from PySide6.QtMultimedia import QMediaDevices, QCamera
from PySide6.QtMultimedia import QMediaCaptureSession, QImageCapture
from PySide6.QtMultimediaWidgets import QVideoWidget
class MyWidget(QWidget):
def __init__(self):
# 1、调用父类的__init__()方法
super().__init__()
self.imageCapture = None
self.setup_ui()
def setup_ui(self):
# 1、设置窗口对象大小
self.resize(700, 500)
layout_v = QVBoxLayout()
self.setLayout(layout_v)
# 2、显示视频的控件
self.videoWidget = QVideoWidget()
layout_v.addWidget(self.videoWidget)
# 3、打开和停止摄像头的按钮
self.button_camera_action = QPushButton("打开摄像头")
self.button_camera_take_photo = QPushButton("拍照")
layout_h = QHBoxLayout()
layout_h.addWidget(self.button_camera_action)
layout_h.addWidget(self.button_camera_take_photo)
layout_v.addLayout(layout_h)
# 3、媒体设备
self.mediaDevice = QMediaDevices(self)
# 4、获取默认的视频输入设备
self.cameraDevice = self.mediaDevice.defaultVideoInput()
# 5、根据视频输入设备定义视频接口
self.camera = QCamera(self.cameraDevice)
# 6、媒体捕获器
self.mediaCaptureSession = QMediaCaptureSession(self)
# 7、设备媒体捕获器的视频接口
self.mediaCaptureSession.setCamera(self.camera)
# 8、设置媒体捕获器的视频输出控件
self.mediaCaptureSession.setVideoOutput(self.videoWidget)
# 9、定义信号与槽的连接
self.button_camera_action.clicked.connect(self.camera_action)
self.button_camera_take_photo.clicked.connect(self.take_photo)
def camera_action(self):
if self.button_camera_action.text() == "打开摄像头":
# 打开摄像头
self.camera.start()
# 摄像头操作按钮文本显示为关闭摄像头
self.button_camera_action.setText("关闭摄像头")
# 创建图片捕获对象,并关联摄像头
self.imageCapture = QImageCapture()
# 设置图像图示
self.imageCapture.setFileFormat(QImageCapture.FileFormat.JPEG)
# 设置分辨率
self.imageCapture.setResolution(1920, 1080)
# 设置图片质量
self.imageCapture.setQuality(QImageCapture.Quality.HighQuality)
# 设置捕获器
self.mediaCaptureSession.setImageCapture(self.imageCapture)
elif self.button_camera_action.text() == "关闭摄像头":
# 关闭摄像头
self.camera.stop()
# 摄像头操作按钮文本显示为打开摄像头
self.button_camera_action.setText("打开摄像头")
self.imageCapture = None
def take_photo(self):
if self.imageCapture:
fileName = f"{os.getcwd()}/{time.strftime('%Y%m%d%H%M%S', time.localtime())}.jpeg"
self.imageCapture.captureToFile(fileName)
if __name__ == "__main__":
# 1、创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2、创建一个窗口
window = MyWidget()
# 3、展示窗口
window.show()
# 4、进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
5.2.6 媒体录制
QMediaRecorder 可以录制从 QMediaCaptureSession 获取的音频和视频,并对音频和视频进行编码,需要用 QMediaCaptureSession的setRecorder(recorder:QMediaRecorder)
方法设置关联的捕获器。
用 QMediaRecorder 创建实例的方法如下所示。
QMediaRecorder(parent:QObject=None)
QMediaRecorder 类的常用方法如下:
# 实例方法
isAvailable() -> bool # 获取是否可以录制
duration() -> int # 获取录制时间
recorderState() -> QMediaRecorder.recorderState # 获取录制状态
error() -> QMediaRecorder.Error # 获取出错内容
errorString() -> str # 获取错误信息
setAudioBitRate(bitRate:int) -> None # 设置音频比特率
audioBitRate() -> int # 获取音频比特率
setAudioChannelCount(channels:int) -> None # 设置音频通道数
audioChannelCount() -> int # 获取音频通道数
setAudioSampleRate(sampleRate:int) -> None # 设置音频采样率
audioSampleRate() -> int # 获取音频采样率
setEncodingMode(mode:QMediaRecorder.EncodingMode) -> None # 设置编码格式
encodingMode() -> QMeidaRecorder.encodingMode # 获取编码格式
setMediaFormat(format:Union[QMediaFormat, QMediaFormat.FileFotmat]) -> None # 设置媒体格式
setQuality(quality:QMediaRecorder.Quality) -> None # 设置录制质量
quality() -> QMediaRecorder.Quality # 获取录制质量
setVideoBitRate(bitRate:int) -> None # 设置视频比特率
videoBitRate() -> int # 获取视频比特率
setVideoFrameRate(frameRate:float) -> None # 设置视频帧速
videoFrameRate() -> float # 获取视频帧速
setVideoResolution(size:QSize) -> None # 设置视频分辨率
setVideoResolution(width:int, height:int) -> None # 设置视频分辨率
videoResolution() -> QSize # 获取视频分辨率
setMetaData(metaData:QMediaMetaData) -> None # 设置媒体元数据
metaData() -> QMediaMeta # 获取媒体元数据
addMetaData(metaData:QMediaMetaData) -> None # 添加媒体元数据
setOutputLocation(location:QUrl) -> None # 设置媒体输出位置
outputLocation() -> QUrl # 获取输出位置
actualLocation() -> QUrl # 获取实际的输出位置
# 槽方法
record() -> None # 开始录制
pause() -> None # 暂停录制
stop() -> None # 停止录制
用 recorderState()
方法获取录制状态,返回值是 QMediaRecorder.RecorderState 的枚举值,可取值如下:
QMediaRecorder.RecorderState.StoppedState
QMediaRecorder.RecorderState.RecordingState
QMediaRecorder.RecorderState.PausedState
录制过程中如果出错,可以用 error()
方法获取出错内容,返回值是 QMediaRecorder.Error 的枚举值,可取值如下:
QMediaRecorder.Error.NoError # 没有问题
QMediaRecorder.Error.ResourceError # 设备没有准备好
QMediaRecorder.Error.FormatError # 不支持该格式
QMediaRecorder.Error.OutOfSpaceError # 存储空间不足
QMediaRecorder.Error.LocationNotWritable # 输出位置不可写
用 setEncodingMode(QMediaRecorder.EncodingMode)
方法设置编码模式,参数是 QMediaRecorder.EncodingMode 的枚举值,可取值如下:
QMediaRecorder.EncodingMode.ConstantQualityEncoding # 常质量编码
QMediaRecorder.EncodingMode.ConstantBitRateEncoding # 常比特率编码
QMediaRecorder.EncodingMode.AverageBitRateEncoding # 平均比特率编码
QMediaRecorder.EncodingMode.TwoPassEncoding # 二次编码
用 setQuality(quality:QMediaRecorder.Quality)
方法设置录制质量,参数是 QMediaRecorder.Quality 的枚举值,可取如下:
QMediaRecorder.Quality.VeryLowQuality
QMediaRecorder.Quality.LowQuality
QMediaRecorder.Quality.NormalQuality
QMediaRecorder.Quality.HighQuality
QMediaRecorder.Quality.VeryHighQuality
QMediaRecorder 类的常用信号如下:
actualLocationChanged(location:QUrl) # 存储位置发生改变时发射信号
durationChanged(duration:int) # 录制时间发生改变时发射信号
errorChanged() # 错误状态发生改变时发射信号
errorOccurred(error:QMediaRecorder.Error, errorString:str) # 出现错误时发射信号
mediaFormatChanged() # 格式发生改变时发射信号
metaDataChanged() # 元数据发生改变时发射信号
recorderStateChanged(state:QMediaRecorder.RecorderState) # 录制状态发生改变时发射信号
import sys
import os, time
from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QPushButton
from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout
from PySide6.QtMultimedia import QMediaDevices, QCamera
from PySide6.QtMultimedia import QMediaCaptureSession, QAudioInput
from PySide6.QtMultimedia import QMediaRecorder, QMediaFormat
from PySide6.QtMultimediaWidgets import QVideoWidget
from PySide6.QtCore import QUrl
class MyWidget(QWidget):
def __init__(self):
# 1、调用父类的__init__()方法
super().__init__()
self.mediaRecorder = None
self.setup_ui()
def setup_ui(self):
# 1、设置窗口对象大小
self.resize(700, 500)
layout_v = QVBoxLayout()
self.setLayout(layout_v)
# 2、显示视频的控件
self.videoWidget = QVideoWidget()
layout_v.addWidget(self.videoWidget)
# 3、打开和停止摄像头的按钮
self.button_camera_action = QPushButton("打开摄像头")
self.button_camera_recorder_action = QPushButton("开始录像")
self.button_camera_recorder_stop = QPushButton("停止录像")
layout_h = QHBoxLayout()
layout_h.addWidget(self.button_camera_action)
layout_h.addWidget(self.button_camera_recorder_action)
layout_h.addWidget(self.button_camera_recorder_stop)
layout_v.addLayout(layout_h)
# 3、媒体设备
self.mediaDevice = QMediaDevices(self)
# 4、获取默认的视频输入设备
self.cameraDevice = self.mediaDevice.defaultVideoInput()
# 5、根据视频输入设备定义视频接口
self.camera = QCamera(self.cameraDevice)
# 6、媒体捕获器
self.mediaCaptureSession = QMediaCaptureSession(self)
# 7、设备媒体捕获器的视频接口
self.mediaCaptureSession.setCamera(self.camera)
# 8、设置媒体捕获器的视频输出控件
self.mediaCaptureSession.setVideoOutput(self.videoWidget)
# 9、定义信号与槽的连接
self.button_camera_action.clicked.connect(self.camera_action)
self.button_camera_recorder_action.clicked.connect(self.camera_recorder_action)
self.button_camera_recorder_stop.clicked.connect(self.camera_recorder_stop)
def camera_action(self):
if self.button_camera_action.text() == "打开摄像头":
# 打开摄像头
self.camera.start()
# 摄像头操作按钮文本显示为关闭摄像头
self.button_camera_action.setText("关闭摄像头")
# 创建媒体录制对象,并关联摄像头
self.mediaRecorder = QMediaRecorder(self.camera)
# 设置输出视频的格式
self.mediaRecorder.setMediaFormat(QMediaFormat.FileFormat.AVI)
# 设置分辨率
self.mediaRecorder.setVideoResolution(1920, 1080)
# 设置输出视频的质量
self.mediaRecorder.setQuality(QMediaRecorder.Quality.HighQuality)
# 设置媒体输出位置
fileName = f"{os.getcwd()}/{time.strftime('%Y%m%d%H%M%S', time.localtime())}.avi"
self.mediaRecorder.setOutputLocation(QUrl.fromLocalFile(fileName))
# 设置音频输出
self.audioInput = QAudioInput()
self.mediaCaptureSession.setAudioInput(self.audioInput)
# 设置捕获器
self.mediaCaptureSession.setRecorder(self.mediaRecorder)
self.mediaRecorder.errorOccurred.connect(self.error)
elif self.button_camera_action.text() == "关闭摄像头":
# 关闭摄像头
self.camera.stop()
# 摄像头操作按钮文本显示为打开摄像头
self.button_camera_action.setText("打开摄像头")
# 结束录制
if self.mediaRecorder:
self.mediaRecorder.stop()
self.button_camera_recorder_action.setText("开始录像")
self.mediaRecorder = None
def camera_recorder_action(self):
if self.mediaRecorder:
if self.button_camera_recorder_action.text() == "开始录像":
# 开始录像
self.mediaRecorder.record()
# 录制操作按钮文本显示为暂停录像
self.button_camera_recorder_action.setText("暂停录像")
elif self.button_camera_recorder_action.text() == "暂停录像":
# 暂停录像
self.mediaRecorder.pause()
# 录制操作按钮文本显示为开始录像
self.button_camera_recorder_action.setText("开始录像")
def camera_recorder_stop(self):
if self.mediaRecorder:
# 结束录像
self.mediaRecorder.stop()
# 录制操作按钮文本显示为开始录像
self.button_camera_recorder_action.setText("开始录像")
# 关闭摄像头
self.camera.stop()
# 摄像头操作按钮文本显示为打开摄像头
self.button_camera_action.setText("打开摄像头")
self.mediaRecorder = None
def error(self, error, errorString):
print(error)
print(errorString)
if __name__ == "__main__":
# 1、创建一个QApplication类的实例
app = QApplication(sys.argv)
# 2、创建一个窗口
window = MyWidget()
# 3、展示窗口
window.show()
# 4、进入程序的主循环并通过exit()函数确保主循环安全结束
sys.exit(app.exec())
6.程序打包发布
我们编写的 Python 程序需要借助 Python 解释器才能运行。因此,其他人运行 Python 程序,也需要安装 Python 解释器才行。此时,我们可以将 Python 的源代码打包成可以双击运行的程序。在 Python 中没有内置可以直接打包程序的模块,我们可以借助第三方模块 Pyinstaller 模块来实现打包功能。
我们可以在终端中使用 pip 安装 pyinstalller 模块。
pip install pyinstaller
6.1 打包成多个文件
pyinstaller -D -w --icon="图标文件.ico" 启动文件.py -n 打包文件名
其中,-D
表示打包成多个文件,打包的程序会在当前项目下的【dist】文件夹中,-w
表示窗口程序,即双击运行程序时,不会弹出命令行,--icon
表示打包成可运行程序的图标,图标文件要使用 .ico
类型的图标文件。-n
6.2 打包成单个文件
pyinstaller -F -w --icon="图标文件.ico" 启动文件.py -n 打包文件名
其中,-F
表示打包成单个文件,打包的程序会在当前项目下的【dist】文件夹中,-w
表示窗口程序,即双击运行程序时,不会弹出命令行,--icon
表示打包成可运行程序的图标,图标文件要使用 .ico
类型的图标文件。
本文来自博客园,作者:Yasuo_Hasaki,转载请注明原文链接:https://www.cnblogs.com/hasaki-yasuo/p/18827063