系列文章索引
PySide6开发桌面程序,PySide6入门实战(上)
PySide6开发桌面程序,PySide6入门实战(下)
一、前期准备
1、简介及安装
(1)为什么用PySide而不是PyQt
PyQt是由 Riverbank Computing 公司开发,出现的比较早;它采用GPLV3许可证和商业许可证;这表示你如果使用PyOt,则必须将你的代码进行开源;如果要闭源,则需要购买商业许可。
https://riverbankcomputing.com/software/pyqt/intro
PySide是 Ot 官方开源的Python Qt库,出现的时间要比PyOt晚的多,这也是很多人知道PyOt不知道PySide的原因;但随着版本的选代,PvSide越来越强大,目前最新的是PvSide6,建议直接学PvSide6。https://doc.qt.io/qtforpython-6/
(2)QT开发常见技术栈
C++ QWidget;
C++ QML;
Qt5、Qt6;
PyQt5、PyQt6;
PySide2、PySide6;
Python QML。
(3)安装pyside6
pip install pyside6 -i https://mirror.baidu.com/pypi/simple
2、PyCharm PySide6环境搭建
(1)基础环境
win10/win11 64位,安装python环境(可以用Anaconda),安装PyCharm。
(2)配置QT Designer、PyUIC、PyRCC
1、安装好PySide6之后,会下载这些:
2、配置PySide6_designer
:(Qt设计器,可以往窗口上拖控件,设置布局,快速构建窗口,文件后缀.ui)
Program:C:\Users\Admin\.conda\envs\pyside6\Scripts\pyside6-designer.exe
Arguments不需要 填。
Working directory:$ProjectFileDir$
3、配置PySide6_uic
(将.ui文件编译为python代码)
Program:C:\Users\Admin\.conda\envs\pyside6\Scripts\pyside6-uic.exe
Arguments:$FileName$ -o $FileNameWithoutExtension$.py
Working directory:$ProjectFileDir$
4、配置PySide6_rcc
(将资源文件.qrc编译为python代码)
Program:C:\Users\Admin\.conda\envs\pyside6\Scripts\pyside6-rcc.exe
Arguments:$FileName$ -o $FileNameWithoutExtension$.py
Working directory:$ProjectFileDir$
5、此时,右键项目,就会有这几个工具了:
运行之后,返回码是0就说明成功。
(3)使用pyside6项目
1、右键项目,选择我们安装的Designer,就可以拖动组件,创建一个ui布局了,保存之后,会保存在项目根路径。
这是一个xml文件,双击可以再次打开。
2、右键ui文件,生成python代码
3、编写MainWindowImpl.py
继承主窗口
import LoginWindow
# 定义一个子类,自定义逻辑可以写在子类
class MainWindowImpl(LoginWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# 可以写自定义逻辑
#self.lineEdit.setText("user")
4、编写主类
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
import MainWindowImpl
if __name__ == '__main__':
app = QApplication(sys.argv)
mw = QMainWindow()
impl = MainWindowImpl.MainWindowImpl(mw)
mw.show()
sys.exit(app.exec()) # 进入QT事件循环
# 也可以不用子类,直接这样写,但是自定义逻辑不太好写,每次修改ui会覆盖代码
# if __name__ == '__main__':
# app = QApplication(sys.argv)
#
# mw = QMainWindow()
# mwReal = MainWindow.Ui_MainWindow()
# mwReal.setupUi(mw)
# mw.show()
#
# sys.exit(app.exec()) # 进入QT事件循环
(4)资源文件编写与编译
1、右键,new file :Resources.qrc
同时添加上资源文件。
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file alias="logo">./Resources/test.jpg</file>
</qresource>
</RCC>
2、qrc文件右键,进行rcc编译
3、使用图片资源
import sys
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QApplication, QMainWindow
import MainWindowImpl
# 引入资源!!一定要做的
import Resources
if __name__ == '__main__':
app = QApplication(sys.argv)
# 使用资源,使用别名
app.setWindowIcon(QIcon(':logo'))
mw = QMainWindow()
impl = MainWindowImpl.MainWindowImpl(mw)
mw.show()
sys.exit(app.exec()) # 进入QT事件循环
二、QT常用控件
1、Qt窗口类
Qt包含三个窗口类:QMainWindow、QWidget、QDialog
其中,MainWindow,是包含标题栏、菜单栏更加丰富的窗口。
2、QLineEdit、QLabel、QPushButton
(1)ui布局
另存为CalcMainWindow.ui
,并使用uic生成python文件。
(2)编写子类
可以给按钮添加点击事件。
添加子类的目的是,防止每次编辑了ui,自己的代码被覆盖了。
from PySide6.QtWidgets import QMessageBox
import CalcMainWindow
class CalcMainWindowImpl(CalcMainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# pushButton的点击事件,这是那个按钮的objectName
self.pushButton.clicked.connect(self.onCalc)
# 槽函数
def onCalc(self):
# 获取第一个和第二个输入框的内容,获取的时候是字符串,需要转换
str1 = self.lineEdit1.text().strip()
str2 = self.lineEdit2.text().strip()
if len(str1) == 0 or len(str2) == 0:
QMessageBox.warning(None, 'Warning', '请输入两个加数')
return
num1 = float(str1)
num2 = float(str2)
result = num1 + num2
self.lineEdit3.setText(str(result))
(3)主类
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
import CalcMainWindowImpl
if __name__ == '__main__':
app = QApplication(sys.argv)
mw = QMainWindow()
impl = CalcMainWindowImpl.CalcMainWindowImpl(mw)
mw.show()
sys.exit(app.exec()) # 进入QT事件循环
(4)运行结果
3、QCheckBox(复选框)及其信号槽
(1)ui
ui保存命名为MainWindow
(2)编写子类
import MainWindow
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# checkBox状态改变事件的监听
self.checkBox1.stateChanged.connect(self.onCheckBox1StateChanged)
def onCheckBox1StateChanged(self, state):
# 没选中是0,选中后是2
if state == 0:
print("Checked")
if state == 2:
print("Un Checked")
4、QComboBox下拉框
(1)ui
(2)编写子类
import MainWindow
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# 设置下拉值
self.comboBox.addItem("Option 0")
self.comboBox.addItem("Option 1")
self.comboBox.addItem("Option 2")
self.comboBox.addItem("Option 3")
# 默认选项
self.comboBox.setCurrentIndex(2)
# 设置信号槽 索引改变事件监听,选项变化时会调用
self.comboBox.currentIndexChanged.connect(self.comboBoxCurrentIndexChanged)
# 用户一个选项发出时,参数是所选项的索引,还有个重载版本,str类型的,参数是文本
self.comboBox.activated.connect(self.comboBoxActivated)
def comboBoxCurrentIndexChanged(self, index):
print('comboBoxCurrentIndexChanged切换到了' + str(index))
def comboBoxActivated(self, index):
print('comboBoxActivated切换到了' + str(index))
(3)QComboBox的信号
QComboBox常用的信号包括:
activated(int)
:当用户选择一个项时发出,参数是所选项的索引。
activated(str)
:重载版本,当用户选择一个项时发出,参数是所选项的文本。
currentlndexChanged(int)
:当下拉列表的当前项改变时发出,参数是新的当前项的索引。
currentTextChanged(str)
:当下拉列表的当前项文本改变时发出,参数是新的当前项的文本。
highlighted(int)
:当用户在下拉列表中高亮一个项时发出,参数是高亮项的索引。
5、QTextEdit、QTextBrowser显示文本
(1)两者区别
QTextBrowser 继承自 QTextEdit,具有所有 QTextEdit 的功能,并添加了一些额外的功能
,特别是用于显示超链接和导航功能的能力,类似于一个简易的网页浏览器。QTextBrowser 可以显示静态的HTML文档,并支持超链接互动。这使得 QTextBrowser 非常适合用作显示只读的、格式化的文本内容,比如帮助文档。
用途:QTextEdit 更适合用于文本编辑,而 QTextBrowser 更适合用于显示格式化的只读文本
。
导航功能:QTextBrowser支持链接导航,可以处理内部的超链接事件。
只读属性:QTextBrowser 默认是只读的,而 QTextEdit 默认是可编辑的。
总的来说,如果你需要让用户编辑文本,那么 QTextEdit 是更好的选择。而如果你需要展示带有超链接的只读文本,QTextBrowser是更合适的选择。
(2)ui
(3)编写子类
import MainWindow
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# 给textEdit设置文本
self.textEdit.setText("123456")
# textEdit文本变更事件
self.textEdit.textChanged.connect(self.onTextChanged)
# 给textBrowser设置HTML内容
self.textBrowser.setHtml("""
<h1>Hello World</h1>
<p>link to <a href="https://www.python.org">Python website</a></p>
""")
self.textBrowser.setText("""
<h1>Hello World</h1>
<p>link to <a href="https://www.python.org">Python website</a></p>
""")
# 设置可以打开超链接
self.textBrowser.setOpenExternalLinks(True)
def onTextChanged(self):
# 获取里面的文本
print(self.textEdit.toPlainText())
6、QListView、QListWidget
(1)两者区别
QListWidget是一个更高级的控件,提供了接口来添加、删除和管理列表项。
不需要单独设置数据模型(Model),因为QListWidget内部就是一个简单的数据模型。对于简单的列表需求,比如只需展示文本列表项,QListWidget可能是更简单直接的选择。
QListView属于Model/View框架,是一个更灵活、更低级的控件,它需要与一个数据模型(Model)配合使用。
QListView仅仅是一个视图组件,它不存储任何数据,所有的数据都存储在它所连接的模型中。这意味着你需要创建或使用一个数据模型(如QStandardltemModel、自定义模型等),并将其设置给QListView来显示数据。由于是基于模型/视图的,QListView可以用来展示更复杂的数据结构,提供了更高的自定义性和灵活性。你可以控制数据的表示、存储和行为。
如果需求相对简单,比如只需要显示一个文本列表,并对列表项进行一些基本操作,那么QListWidget可能是更简单的选择。
但如果你需要更高的灵活性和定制性,比如显示复杂的数据结构或者需要自定义数据的展示方式,那么使用QListView配合一个合适的数据模型会是更好的选择,也就是说QListView更适合用于需要高度定制或展示复杂数据的场景。
(2)ui
(3)子类
from PySide6.QtCore import QStringListModel
from PySide6.QtWidgets import QListWidgetItem
import MainWindow
from CustomListItem import CustomListItem
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# QListWidget
firstItem = CustomListItem()
item = QListWidgetItem(self.listWidget)
item.setSizeHint(firstItem.sizeHint())
# 可以设置自定义的QWidget
self.listWidget.setItemWidget(item, firstItem)
# 添加列表项
for i in range(10):
item = QListWidgetItem(f'item0{i}')
self.listWidget.addItem(item)
# QListView
model = QStringListModel(['itemstr1', 'itemstr2', 'itemstr3', 'itemstr4'])
self.listView.setModel(model)
(4)自定义的QWidget
from PySide6.QtWidgets import QWidget, QHBoxLayout, QLabel, QVBoxLayout, QCheckBox, QPushButton, QMessageBox
# 自定义QWidget
class CustomListItem(QWidget):
def __init__(self):
super().__init__()
self.setFixedSize(100, 50)
main_hLay = QHBoxLayout()
leftLabel = QLabel()
leftLabel.setFixedSize(50, 50)
leftLabel.setText('A')
main_hLay.addWidget(leftLabel)
vLay = QVBoxLayout()
ck = QCheckBox()
ck.setText('开启')
ck.setFixedSize(50, 20)
btn = QPushButton()
btn.setFixedSize(50, 25)
btn.setText('智能润色')
vLay.addWidget(ck)
vLay.addWidget(btn)
main_hLay.addLayout(vLay)
self.setLayout(main_hLay)
btn.clicked.connect(self.onBtnRunse)
def onBtnRunse(self):
QMessageBox.information(None, '提示', '智能润色')
(5)运行效果
7、QStackedWidget
(1)ui
两个ui,其中Widget是QStackedWidget的一个页面。
QStackedWidget默认会创建出两个页面,需要我们删除。
(2)子页面
import MainWindow
import MyWidget
import Pages
# 自定义Widget
class MyWidgetImpl(MyWidget.Ui_Form):
def __init__(self, Widget):
super().__init__()
self.setupUi(Widget)
from PySide6.QtWidgets import QWidget, QHBoxLayout, QLabel, QLineEdit
class Page1(QWidget):
def __init__(self):
super().__init__()
hLay = QHBoxLayout()
label = QLabel()
label.setText('page1')
hLay.addWidget(label)
lineEdit = QLineEdit()
lineEdit.setText('这是第一个页面')
hLay.addWidget(lineEdit)
self.setLayout(hLay)
class Page2(QWidget):
def __init__(self):
super().__init__()
hLay = QHBoxLayout()
label = QLabel()
label.setText('page2')
hLay.addWidget(label)
self.setLayout(hLay)
(3)编写子类
from PySide6.QtWidgets import QWidget
import MainWindow
import MyWidgetImpl
import Pages
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# 将自定义的QWidget添加到stackedWidget
page1 = Pages.Page1()
page2 = Pages.Page2()
page3 = QWidget()
MyWidgetImpl.MyWidgetImpl(page3)
self.stackedWidget.addWidget(page1)
self.stackedWidget.addWidget(page2)
self.stackedWidget.addWidget(page3)
# ComBobox添加东西,随着下拉框可以切换页面
self.comboBox.addItems(['Page1', 'Page2', 'Page3'])
self.comboBox.currentIndexChanged.connect(self.onComboBoxIndexChanged)
def onComboBoxIndexChanged(self, index):
self.stackedWidget.setCurrentIndex(index)
(4)效果
8、QTabWidget
QTabWidget提供了一个选项卡式的界面,允许用户在不同的页面(tab)之间切换。每个页面都是一个独立的小部件,可以包含各种界面元素。
(1)ui
(2)子页面
from PySide6.QtWidgets import QWidget, QHBoxLayout, QLabel
class Tab1(QWidget):
def __init__(self):
super().__init__()
hLay = QHBoxLayout()
label = QLabel()
label.setText('tab1')
hLay.addWidget(label)
self.setLayout(hLay)
class Tab2(QWidget):
def __init__(self):
super().__init__()
hLay = QHBoxLayout()
label = QLabel()
label.setText('tab2')
hLay.addWidget(label)
self.setLayout(hLay)
class Tab3(QWidget):
def __init__(self):
super().__init__()
hLay = QHBoxLayout()
label = QLabel()
label.setText('tab3')
hLay.addWidget(label)
self.setLayout(hLay)
(3)编写子类
import MainWindow
import TabWidget
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
tab1 = TabWidget.Tab1()
tab2 = TabWidget.Tab2()
tab3 = TabWidget.Tab3()
# 添加页面,位置、Widget、名称
self.tabWidget.insertTab(0, tab1, 'tab01')
self.tabWidget.insertTab(1, tab2, 'tab02')
self.tabWidget.insertTab(2, tab3, 'tab03')
三、布局
1、概述
各种布局可以多尝试,很多复杂的设计,都是由一个个的Widget整合在一起的。
就像是html的一个个div等控件一样,设置位置。
2、弹簧控件QSpacerltem
添加弹簧控件通常指的是在布局中添加一个可伸缩的空白区域,以便控件可以按照需要展开或收缩。
这通常是用于在控件之间添加空间,或者将控件推向布局的边缘。
spacer = QSpacerltem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
layout.addSpacerltem(spacer)
'''
在上面的例子中,QSpacerltem的构造函数接收四个参数:
1.宽度(40)
2.高度(20)
3.宽度策略(OSizePolicy.Expanding)这意味着弹簧控件可以扩展,以填充布局中的任何额外空间。
4.高度策略OSizePolicy.Minimum):这意味着弹簧控件的高度保持最小值不会扩展。
通过将OSpacerltem添加到布局中,Button1和Button2会被推到窗口的两侧
'''
3、布局里常用的函数
addWidget(widget,stretch=0)
:将小部件添加到布局中。stretch参数用于指定小部件拉伸的比例。
addLayout(layout,stretch=0)
:将另一个布局嵌套到当前布局中。这允许创建复杂的布局结构。
setSpacing(spacing)
:设置布局中小部件之间的间距。
setMargin(margin)
:设置布局边缘与其容器边缘之间的距离。在最新版本的Qt中,可能需要使用setContentsMargins()
来替代此功能。
setAlignment(widget,alignment)
:设置小部件相对于其分配空间的对齐方式例如,Qt.AlignCenter、Qt.AlignLeft
等。
4、栅格布局
5、表单布局
QFormLayout设计上主要用于单列布局,每一行通常包含一个标签和一个字段(或者两个相关的窗口小部件),因此它自然支持多行但本质上是单列的布局。对于需要多行多列布局
的情况,则需要使用QGridLayout。
6、分裂器布局
分裂器布局,两个组件之间是可以拖动的
7、绝对布局
move(x, y)
对于一些位置固定的控件,需要使用绝对布局,直接根据坐标写位置。
如果有蓝湖设计图,绝对布局很轻松。
四、信号槽与事件机制
1、普通槽函数使用
from PySide6.QtWidgets import QMessageBox
import MainWindow
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# 槽函数,绑定鼠标点击按钮事件
self.pushButton.clicked.connect(self.onPushButtonClicked)
def onPushButtonClicked(self):
QMessageBox.information(None, "Hello", "Hello World")
2、鼠标事件
from PySide6.QtCore import Qt
import MainWindow
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
window.setMouseTracking(True) # 开启鼠标追踪
# 事件 ,注意,事件是在QWidget中的
window.mousePressEvent = self.mousePressEvent
window.mouseReleaseEvent = self.mouseReleaseEvent
window.mouseMoveEvent = self.mouseMoveEvent
window.wheelEvent = self.wheelEvent
window.mouseDoubleClickEvent = self.mouseDoubleClickEvent
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
print("左键")
elif event.button() == Qt.RightButton:
print("右键")
elif event.button() == Qt.MiddleButton:
print("中键")
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
print("左键释放")
elif event.button() == Qt.RightButton:
print("右键释放")
elif event.button() == Qt.MiddleButton:
print("中键释放")
def mouseDoubleClickEvent(self, event):
if event.button() == Qt.LeftButton:
print("左键双击")
elif event.button() == Qt.RightButton:
print("右键双击")
elif event.button() == Qt.MiddleButton:
print("中键双击")
def mouseMoveEvent(self, event):
print("鼠标移动")
print("x:", event.x(), "y:", event.y())
def wheelEvent(self, event):
print("鼠标滚轮")
print("滚动值:", event.angleDelta().y())
y = event.angleDelta().y()
# mac和windows的滚动方向相反 y > 0 向上滚动 y < 0 向下滚动
if y > 0:
print(f"y = {y}")
print("向上滚动")
else:
print(f"y = {y}")
print("向下滚动")
3、键盘事件
from PySide6.QtCore import Qt
import MainWindow
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
window.keyPressEvent = self.keyPressEvent
window.keyReleaseEvent = self.keyReleaseEvent
def keyPressEvent(self, event):
key = event.key()
print(f"按下了键盘键:{event.text()}")
if key == Qt.Key_Escape:
print(f"按下了键盘esc键:{event.text()}")
elif key == Qt.Key_Enter or key == Qt.Key_Return:
print(f"按下了键盘回车键:{event.text()}")
def keyReleaseEvent(self, event):
print(f"释放了键盘键:{event.text()}")
4、组合按键事件
from PySide6.QtCore import Qt
import MainWindow
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
window.mousePressEvent = self.mousePressEvent
window.mouseReleaseEvent = self.mouseReleaseEvent
window.keyPressEvent = self.keyPressEvent
window.keyReleaseEvent = self.keyReleaseEvent
def mousePressEvent(self, event):
if event.modifiers() & Qt.ControlModifier:
if event.button() == Qt.LeftButton:
print('ctrl +鼠标左键')
def mouseReleaseEvent(self, event):
print('按键已弹起')
def keyPressEvent(self, event):
key = event.key()
if event.modifiers() & Qt.ControlModifier:
if key == Qt.Key_S:
print('ctrl + S')
def keyReleaseEvent(self, event):
print('按键已弹起')
5、事件过滤
from PySide6.QtCore import Qt, QEvent
from PySide6.QtWidgets import QLineEdit, QVBoxLayout
import MainWindow
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# lineEdit添加过滤器
self.lineEdit.installEventFilter(window)
window.eventFilter = self.eventFilter
def eventFilter(self, watched, event):
if event.type() == QEvent.KeyPress:
# 空格、enter等不允许输入
if event.key() in (Qt.Key_Backspace, Qt.Key_Enter, Qt.Key_Return, Qt.Key_Delete):
return False
# 数字不允许输入
if event.text().isdigit():
return False
return True
return False
五、QMainWindow使用介绍
1、简介
QMainWindow包含菜单栏、工具栏、状态栏等。
2、菜单栏
注意,使用QT设计器,菜单栏的子菜单是无法直接输入中文的,需要粘贴进去
import MainWindow
import Resources # 资源!
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# 注意,图标貌似只能选一些内置的,想要自定义图片,需要手动设置
self.action.setIcon(QIcon(':test'))
# 菜单信号,点击事件
self.action.triggered.connect(self.actionTriggered)
def actionTriggered(self):
print('点击了')
3、工具栏
工具栏需要手动添加:
from PySide6.QtGui import QIcon
import MainWindow
import Resources
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# 注意,图标貌似只能选一些内置的,想要自定义图片,需要手动设置
self.action.setIcon(QIcon(':t1'))
# 工具栏信号,点击事件
self.action.triggered.connect(self.actionTriggered)
def actionTriggered(self):
print('点击了')
4、状态栏
import MainWindow
import Resources
class MainWindowImpl(MainWindow.Ui_MainWindow):
def __init__(self, window):
super().__init__()
self.setupUi(window)
# 状态栏,设置文字
self.statusbar.showMessage('初始状态')
5、QDockWidget
使用 addDockwidget()
将停靠窗口添加到主窗口的不同区域(例如左侧、顶部、底部或者右侧)。
QDockWidget 可以通过用户操作在停靠状态和浮动状态之间切换
#创建停靠窗口
dock = QDockWidget("Dockable",self)
self.addDockWidget(Qt.RightDockWidgetArea, dock)
注意:dockwidget只能在QMainWindow里使用,QWidget、QDialog不行