Pyside2

首先简单说一下为什么选择pyqt5和pyside2(这两个“亲兄弟”操作基本完全一样,所以之后就不再区分了),之前有段时间尝试了学习C语言的可视化编程工具easyX,但是学习体验非常不佳,而相比之下pyside2的操作更符合正常的开发习惯,同时之后也可以迁移到C++的QT图形开发,一举多得。

  • 本质上,easyX是一个画图工具,不适合新手使用,而且不容易把图形界面和功能代码分开,最重要的一点是几乎没有人用easyX,网上的教程都是那种非常水的教程;

  • pyside2对新手非常友好的一点就是可以直接使用工程师手动搭建ui界面然后动态加载进入目标文件中,需要着重理解的点就是python的类相关以及常见控件的使用方法,QT是pyside2和pyQT的父类,所以学会了pyside2也就基本完全掌握了QT;

教程参考:Python Qt 简介 | 白月黑羽 (byhy.net)

一、概述

1.图形界面开发

Python 语言开发 跨平台 的图形界面的程序,主要有3种选择:

  • Tkinter

    基于Tk的Python库,这是Python官方采用的标准库,优点是作为Python标准库、稳定、发布程序较小,缺点是控件相对较少。

  • wxPython

    基于wxWidgets的Python库,优点是控件比较丰富,缺点是稳定性相对差点、文档少、用户少。

  • PySide2、PyQt5

    基于Qt 的Python库,优点是控件比较丰富、跨平台体验好、文档完善、用户多。

    缺点是 库比较大,发布出来的程序比较大。

PySide2、PyQt5 都是基于著名的 Qt 库。Qt库里面有非常强大的图形界面开发库,但是Qt库是C++语言开发的,PySide2、PyQt5可以让我们通过Python语言使用Qt。

2.示例程序

下面我们给出一个简单的示例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from PySide2.QtWidgets import QApplication, QMainWindow, QPushButton,  QPlainTextEdit,QMessageBox

class Stats():
def __init__(self):
self.window = QMainWindow()
self.window.resize(500, 400)
self.window.move(300, 300)
self.window.setWindowTitle('薪资统计')

self.textEdit = QPlainTextEdit(self.window)
self.textEdit.setPlaceholderText("请输入薪资表")
self.textEdit.move(10, 25)
self.textEdit.resize(300, 350)

self.button = QPushButton('统计', self.window)
self.button.move(380, 80)

self.button.clicked.connect(self.handleCalc)


def handleCalc(self):
info = self.textEdit.toPlainText()
# 薪资20000以上和 以下的人员名单
salary_above_20k = ''
salary_below_20k = ''
for line in info.splitlines():
if not line.strip():
continue
parts = line.split(' ')
# 去掉列表中的空字符串内容
parts = [p for p in parts if p]
name,salary,age = parts
if int(salary) >= 20000:
salary_above_20k += name + '\n'
else:
salary_below_20k += name + '\n'
QMessageBox.about(self.window,
'统计结果',
f'''薪资20000 以上的有:\n{salary_above_20k}
\n薪资20000 以下的有:\n{salary_below_20k}'''
)

app = QApplication([])
stats = Stats()
stats.window.show()
app.exec_()

其对应的图形化界面和功能如下:

在文本框中{姓名 薪资 年龄},点击统计按钮后就可以得出薪资高于20000和低于20000的人群

之所以要将这个程序封装在python类中是防止后续添加其他功能的时候出现太多全局变量或者不方便调用某些函数,所以在pyside2图形界面开发的过程中,【python类】是一个非常重要的概念;

def __init__(self)定义的就是整个图形化界面;

self.button.clicked.connect(self.handleCalc)将事件和处理函数绑定;

def handleCalc(self)定义的是点击’统计’按钮后触发的事件对应的处理函数;

app = QApplication([])表示实例化一个名为app的QApplication类,提供了整个图形界面程序的底层管理功能,比如:初始化、程序入口参数的处理,用户事件(对界面的点击、输入、拖拽)分发给各个对应的控件等,我们必须在任何界面控件对象创建前,先创建它;

app.window.show()表明将主窗口window(这里我们主窗口的名字就叫window)的全部控件都显示在界面上;

app.exec_()进入事件处理循环,监听触发的事件并分配给相应的对象进行处理(如果不加这个界面窗口只会一闪而过);

二、基础知识

1.QT designer

在环境变量安装的pyside2的对应目录下

1
..\My_miniconda\envs\pyqt_5\Lib\site-packages\PySide2

我们之后的设计基本上都会围绕设计师展开

当我们在设计师中设计完成后点击保存会生成ui文件

ui文件本质上就是一个XML文件,我们需要将其用在python中有多种方式,而最常使用的是动态加载的方式,与直接编码写的不同之处在于def __init__(self)函数体以及

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from PySide2.QtWidgets import QApplication, QMessageBox
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile

class Stats:

def __init__(self):
# 从ui文件夹下的stats.ui文件中加载UI定义
qfile_stats=QFile("ui/stats.ui")
qfile_stats.open(QFile.ReadOnly)
qfile_stats.close()

# 从 UI 定义中动态创建一个相应的窗口对象,此处我们将其命名为ui
# 注意:在创建窗口对象后,里面的控件对象也成为窗口对象的属性了,使用.访问属性,注意名字要与设计师中为控件取的名字一一对应!!
# 比如 self.ui.Button , self.ui.TextEdit
self.ui = QUiLoader().load(qfile_stats)
#为Button控件的clicked事件绑定(connect)事件处理函数handleCalc
self.ui.Button.clicked.connect(self.handleCalc)

def handleCalc(self):
info = self.ui.TextEdit.toPlainText()
salary_above_20k = ''
salary_below_20k = ''
for line in info.splitlines():
if not line.strip():
continue
parts = line.split(' ')
parts = [p for p in parts if p]
name,salary,age = parts
if int(salary) >= 20000:
salary_above_20k += name + '\n'
else:
salary_below_20k += name + '\n'
QMessageBox.about(self.ui,
'统计结果',
f'''薪资20000 以上的有:\n{salary_above_20k}
\n薪资20000 以下的有:\n{salary_below_20k}'''
)

app = QApplication([])
stats = Stats()
stats.ui.show()
app.exec_()

2.页面布局Layout

仅仅简单的将控件摆放在窗口上只是图形界面开发的第一步,我们常按照以下步骤来进行图形化界面设计:

  • 先不使用任何Layout,把所有控件按位置摆放在界面上(也就是直接在画布上给出整个程序的雏形);
  • 然后先从最内层开始进行控件的Layout设定(也就是按照从小到大的顺序,按照情况选择垂直布局或水平布局);
  • 逐步拓展到外层进行控件的Layout设定(将小的控件组合为一个Layout后,还可以继续将这些小Layout组合成大的Layout,此时的控件比例肯定会失调,我们将在下一步进行调整);
  • 最后调整layout中控件的大小比例,优先使用Layout的layoutStrentch属性来控制,也可以选择加入space等控件协助调整;

Layout的作用不仅仅只是将小组件组合在一起便于控制,当整体窗口都组成一个大的Layout后可以实现随着窗口的拖拽,各控件有比例的缩放;

注意:与PPT中的组合不同,pyside2中的控件组合为一个Layout后,使用鼠标拖拽的方法只能改变Layout的大小,其内部的空间大小以及间隔等无法简单地使用鼠标拖拽完成,需要进入到Layout的设置界面

通过修改其属性值调整其控件的大小比例等

三、进阶知识

1.窗口切换

当我们有诸如登录的等需求,涉及多个页面的切换问题时,我们在原窗口绑定一个事件处理函数:实例化另外一个窗口,显示新窗口,关闭老窗口;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from PySide2 import QtWidgets
import sys

class Window2(QtWidgets.QMainWindow):

def __init__(self):
super().__init__()
self.setWindowTitle('窗口2')

centralWidget = QtWidgets.QWidget()
self.setCentralWidget(centralWidget)

button = QtWidgets.QPushButton('你好,我是新窗口的按钮')

grid = QtWidgets.QGridLayout(centralWidget)
grid.addWidget(button)


class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('窗口1')

centralWidget = QtWidgets.QWidget()
self.setCentralWidget(centralWidget)

button = QtWidgets.QPushButton('点击此处打开新窗口')
button.clicked.connect(self.open_new_window)

grid = QtWidgets.QGridLayout(centralWidget)
grid.addWidget(button)

def open_new_window(self):
# 实例化另外一个窗口
self.window2 = Window2()
# 显示新窗口
self.window2.show()
# 关闭自己
self.close()

if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

运行结果如下

注意:如果经常要在两个窗口来回跳转,可以使用hide()方法隐藏窗口,而不是closes()方法关闭窗口。这样还有一个好处:被隐藏的窗口再次显示时,原来的操作内容还保存着,不会消失。

2.弹出对话框

有的时候,我们需要弹出一个模式对话框输入一些数据,然后回到 原窗口。

所谓模式对话框,就是弹出此对话框后, 原窗口就处于不可操作的状态,只有当模式对话框关闭才能继续。

参考代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from PySide2 import QtWidgets
import sys

class MyDialog(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle('模式对话框')

self.resize(500, 400)
self.textEdit = QtWidgets.QPlainTextEdit(self)
self.textEdit.setPlaceholderText("请输入薪资表")
self.textEdit.move(10, 25)
self.textEdit.resize(300, 350)

self.button = QtWidgets.QPushButton('统计', self)
self.button.move(380, 80)

class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('主窗口')

centralWidget = QtWidgets.QWidget()
self.setCentralWidget(centralWidget)

button = QtWidgets.QPushButton('打开模式对话框')
button.clicked.connect(self.open_new_window)

grid = QtWidgets.QGridLayout(centralWidget)
grid.addWidget(button)

def open_new_window(self):
# 实例化一个对话框类
self.dlg = MyDialog()
# 显示对话框,代码阻塞在这里,
# 等待对话框关闭后,才能继续往后执行
self.dlg.exec_()

if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

运行结果如下,关闭模式对话框后才能操作主窗口(这与上面所说的切换不太一样,两个窗口会同时存在但只能操作弹出的窗口)

3.发布程序

我们知道python与C/C++不同,无法编译后直接得到可执行文件,所以需要借助特殊的方法生成可执行文件及其依赖

此处使用pyinstaller制作可执行文件(还有很多其他方法可以选择)

首先在程序对应的环境下安装pyinstallerpip install pyinstaller,接着输入如下固定格式的命令即可生成可执行文件夹

1
pyinstaller httpclient.py --noconsole --hidden-import PySide2.QtXml

注意:

  • noconsole 指定不要命令行窗口,否则我们的程序运行的时候,还会多一个黑窗口。 但是我建议大家可以先去掉这个参数,等确定运行成功后,再加上参数重新制作exe。因为这个黑窗口可以显示出程序的报错,这样我们容易找到问题的线索;

  • hidden-import PySide2.QtXml 参数是因为这个 QtXml库是动态导入,PyInstaller没法分析出来,需要我们告诉它;

  • 生成的文件夹中我们需要手动添加项目以来的ui文件(pyinstaller不会自动帮我们打包ui文件),同时一些使用的png文件也需要手动添加到该可执行文件夹中;

程序在打包之前可以添加主窗口图标和应用程序图标使其看起来更加专业

1
2
3
4
5
from PySide2.QtGui import  QIcon
#添加主窗口图标
app = QApplication([])
# 加载 icon
app.setWindowIcon(QIcon('logo.png'))
1
2
#生成可执行文件时指定.ico格式的应用程序图标
pyinstaller httpclient.py --noconsole --hidden-import PySide2.QtXml --icon="logo.ico"

四、附录

1.常用控件

注意:下面介绍的方法涉及代码和设计师,有些代码我们大概知道意思就行,比如设置文本框默认提示文字,实际我们在开发过程中只需要在设计师中的右下角搜索到相应属性并手动修改值即可,有些代码比如给按钮设置图标就只能在代码中实现,另外一些代码比如信号以及获取文本框内容这种就需要我们理解并会使用(在设计师中可以直接增添表格的行、列,但用户实际使用时需要增加表格的行、列就需要我们结合代码实现)

(1)按钮

QPushButton 就是常见的按钮

信号

当按钮被点击就会发出 clicked 信号,可以这样指定处理该信号的函数

1
button.clicked.connect(handleCalc)
改变文本

代码中可以使用 setText 方法来改变按钮文本,比如

1
button.setText(text)
禁用、启用

所有控件(继承自QWidget类)都支持 禁用和启用方法,禁用后,该控件不再处理用户操作

  • 禁用
1
button.setEnabled(False)
  • 启用
1
button.setEnabled(True)
设置图标

可以通过如下方法给按钮设置图标

1
2
3
4
5
6
7
8
from PySide2.QtCore import Qt,QSize
from PySide2.QtGui import QIcon

# 设置图标
button.setIcon(QIcon('logo.png'))

# 设置图标大小
button.setIconSize(QSize(30, 30))

(2)单行文本框

QLineEdit 是只能单行编辑的文本框

信号1

文本被修改。当文本框中的内容被键盘编辑,被点击就会发出 textChanged 信号,可以这样指定处理该信号的函数,Qt在调用这个信号处理函数时,传入的参数就是文本框目前的内容字符串。

1
edit.textChanged.connect(handleTextChange)
信号2

按下回车键。当用户在文本框中任何时候按下回车键,就会发出 returnPressed 信号。有时我们需要处理这种情况,比如登录界面,用户输完密码直接按回车键就进行登录处理,比再用鼠标点击登录按钮快捷的多。可以指定处理 returnPressed 信号,如下所示

1
passwordEdit.returnPressed.connect(onLogin)
获取文本

通过 text 方法获取编辑框内的文本内容,比如

1
text = edit.text()
设置提示

通过 setPlaceholderText 方法可以设置提示文本内容,比如

1
edit.setPlaceholderText('请在这里输入URL')
设置文本

通过 setText 方法设置编辑框内的文本内容为参数里面的文本字符串,比如,原来的所有内容会被清除

1
edit.setText('你好,Tintoki')
清除所有文本

clear 方法可以清除编辑框内所有的文本内容,比如

1
edit.clear()
拷贝文本到剪贴板

copy 方法可以拷贝当前选中文本到剪贴板,比如

1
edit.copy()
粘贴剪贴板的文本

paste 方法可以把剪贴板内容,拷贝到编辑框当前光标所在处,比如

1
edit.paste()

(3)多行文本框

QPlainTextEdit 是可以多行编辑的纯文本编辑框文本浏览框内置了一个 QTextDocument 类型的对象 ,用于存放文档

信号1

文本被修改。当文本框中的内容被键盘编辑,被点击就会发出 textChanged 信号,可以这样指定处理该信号的函数

1
edit.textChanged.connect(handleTextChange)

注意: Qt在调用这个信号处理函数时,不会传入文本框目前的内容字符串,作为参数。这个行为和单行文本框不同。

信号2

光标位置改变当文本框中的光标位置变动,就会发出 cursorPositionChanged 信号,可以这样指定处理该信号的函数

1
edit.cursorPositionChanged.connect(handleChanged)
获取文本

通过 toPlainText 方法获取编辑框内的文本内容,比如

1
text = edit.toPlainText()
获取选中文本
1
2
3
# 获取 QTextCursor 对象
textCursor = edit.textCursor()
selection = textCursor.selectedText()
设置提示

通过 setPlaceholderText 方法可以设置提示文本内容,比如

1
edit.setPlaceholderText('请在这里输入薪资表')
设置文本

通过 setPlainText 方法设置编辑框内的文本内容为参数里面的文本字符串,比如,原来的所有内容会被清除

1
edit.setPlainText('''你好,Tintoki hello byhy''')
在末尾添加文本

通过 appendPlainText 方法在编辑框末尾添加文本内容,比如

1
edit.appendPlainText('你好,Tintoki')

注意:这种方法会在添加文本后 自动换行

在光标处插入文本

通过 insertPlainText 方法在光标末尾添加文本内容,比如

1
edit.insertPlainText('你好,白月黑羽')

注意:这种方法 不会 在添加文本后自动换行

清除所有文本

clear 方法可以清除编辑框内所有的文本内容,比如

1
edit.clear()
拷贝文本到剪贴板

copy 方法可以拷贝当前选中文本到剪贴板,比如

1
edit.copy()
粘贴剪贴板文本

paste 方法可以把剪贴板内容,拷贝到编辑框当前光标所在处,比如

1
edit.paste()
设置最大行数

有时候,代码会不断往文本框添加内容,为了防止占用过多资源,可以设置文本框最大行数,像这样设置最大为 1000行

1
edit.document().setMaximumBlockCount(1000)

(4)文本浏览框

QTextBrowser只能查看文本 的控件,通常用来显示一些操作日志信息、或者不需要用户编辑的大段文本内容。
该控件 获取文本、设置文本、清除文本、剪贴板复制粘贴等操作和上面介绍的多行纯文本框是一样的,下面介绍其独特之处。

在末尾添加文本

通过 append 方法在编辑框末尾添加文本内容,比如

1
textBrowser.append('你好,Tintoki')

有时,浏览框里面的内容长度超出了可见范围,我们在末尾添加了内容,往往希望控件自动翻滚到当前添加的这行,

可以通过 ensureCursorVisible 方法来实现

1
2
textBrowser.append('你好,Tintoki')
textBrowser.ensureCursorVisible()

注意:这种方法会在添加文本后 自动换行

在光标处插入文本

通过 insertPlainText 方法在光标末尾添加文本内容,比如

1
edit.insertPlainText('你好,Tintoki')

注意:这种方法 不会 在添加文本后自动换行

(5)标签

QLabel 就是常见的标签,可以用来显示文字(包括纯文本和富文本)、图片 甚至动画

改变文本

代码中可以使用 setText 方法来改变标签文本内容,比如

1
button.setText(text)
显示图片

QLabel可以用来显示图片,有时一个图片可以让界面好看很多,如下图所示

可以在 Qt Designer上 属性编辑器 QLabel 栏 的 pixmap 属性设置中选择图片文件指定显示的图片

(6)组合选择框

QComboBox 是组合选择框,如下图所示

信号

选项改变。如果用户操作修改了QComboBox中的选项就会发出 currentIndexChanged 信号,可以这样指定处理该信号的函数

1
cbox.currentIndexChanged.connect(handleSelectionChange)
添加一个选项

代码中可以使用 addItem 方法来添加一个选项到 末尾 ,参数就是选项文本

1
cbox.addItem('A')
添加多个选项

代码中可以使用 addItems 方法来添加多个选项到 末尾,参数是包含了多个选项文本的列表

1
cbox.addItems(['byhy','A','B'])
清空选项

代码中可以使用 clear 方法来清空选项,也就是删除选择框内所有的选项

1
cbox.clear()
获取当前选项文本

代码中可以使用 currentText 方法来获取当前 选中的选项 的文本,比如

1
method = cbox.currentText()

(7)列表

QListWidget 是列表控件,如下图所示

在设计师中使用如下方法创建列表

添加一个选项

代码中可以使用 addItem 方法来添加一个选项到 末尾 ,参数就是选项文本

1
listWidget.addItem('byhy')
添加多个选项

代码中可以使用 addItems 方法来添加多个选项到 末尾,参数是包含了多个选项文本的列表

1
listWidget.addItems(['byhy','白月黑羽','python教程'])
删除一个选项

代码中可以使用 takeItem 方法来删除1个选项,参数是该选项所在行

1
listWidget.takeItem(1)

就会删除第二行选项

清空选项

代码中可以使用 clear 方法来清空选项,也就是删除选择框内所有的选项

1
listWidget.clear()
获取当前选项文本

currentItem 方法可以得到列表当前选中项对象(QListWidgetItem) ,再调用这个对象的 text 方法,就可以获取文本内容,比如

1
listWidget.currentItem().text()

可以使用 item 方法获取指定某行的 QListWidgetItem,比如,

1
listWidget.item(0).text()

就获取了 第1行 的列表项的文本。

(8)表格

QTableWidget 是表格控件,如下图所示

表格控件单元格里面可以显示文字,也可以显示富文本、图片等内容。表格控件的每个单元格里面要显示内容,都必须创建一个 QTableWidgetItem 类型的对象

在设计师中使用如下方式创建表格

创建列和标题栏

我们可以通过 Qt designer 为一个表格创建列和对应的标题栏,只需要双击 Qt designer 设计的窗体中的表格控件, 就会出现这样的对话框。

标签栏中,点击左下角的加号,就可以为 添加一个列,并且设置标题栏名称。

插入一行、删除一行

insertRow 方法可以在指定位置插入一行,比如

1
table.insertRow(0)

就插入一行到第 1 行这个位置, 表格原来第1行(包括原来的第1行)以后的内容,全部往下移动一行。

1
table.insertRow(2)

就插入一行到第 3 行这个位置, 表格原来第3行(包括原来的第3行)以后的内容,全部往下移动一行。

removeRow 方法可以删除指定位置的一行,比如

1
table.removeRow(0)

就删除第 1 行, 表格原来第1行以后的内容,全部往上移动一行。

1
table.removeRow(2)

就删除第 3 行, 表格原来第3行以后的内容,全部往上移动一行。

设置单元格内容、对齐、属性

qt表格的单元格内的内容对象 是一个单元格对象 QTableWidgetItem 实例

如果单元格 没有被设置过 内容,可以这样

1
2
from PySide2.QtWidgets import QTableWidgetItem
table.setItem(row, 0, QTableWidgetItem('Tintoki'))

如果单元格 已经被设置过 文本内容,item 方法可以获取指定位置的 QTableWidgetItem ,再调用这个对象的 setText 方法,就可以设置单元格文本内容,比如

1
table.item(0,0).setText('Tintoki')

就设置了 第1行,第1列 的单元格里面的文本。

1
table.item(2,4).setText('Tintoki')

就设置了 第3行,第5列 的单元格里面的文本。

如果希望某个单元格为 只读,不允许修改,可以使用QTableWidgetItem对象的 setFlags 方法,像这样

1
2
3
4
5
6
from PySide2.QtWidgets import QTableWidgetItem
from PySide2.QtCore import Qt

item = QTableWidgetItem('白月黑羽')
item.setFlags(Qt.ItemIsEnabled) # 参数名字段不允许修改
table.setItem(row, 0, item)

如果想文本内容 居中对齐,每个当对应的QTableWidgetItem 调用 setTextAlignment,如下

1
2
3
4
5
6
7
8
from PySide2.QtWidgets import QTableWidgetItem
from PySide2.QtCore import Qt

item = QTableWidgetItem()
item.setText('白月黑羽')
# 文本居中
item.setTextAlignment(Qt.AlignCenter)
table.setItem(row, 0, item)

如果想设置 表格框 和 单元格边线 的颜色,可以使用样式如下

1
2
3
4
QTableWidget {
border:1px solid green;
gridline-color: rgb(71, 191, 255);
}
获取单元格文本的内容

item 方法可以指定位置的单元格对象(QTableWidgetItem) ,再调用这个对象的 text 方法,就可以获取文本内容,比如

1
table.item(0,0).text()

就获取了 第1行,第1列 的单元格里面的文本。

1
table.item(2,4).text()

就获取了 第3行,第5列 的单元格里面的文本。

获取所有行数、列数

代码中可以使用 rowCount 方法来获取表格所有的 行数 ,比如

1
rowcount = table.rowCount()

可以使用 columnCount 方法来获取表格所有的 列数 ,比如

1
rowcount = table.columnCount()
获取当前选中是第几行

代码中可以使用 currentRow 方法来获取当前选中是第几行,比如

1
currentrow = table.currentRow()

注意:行数是从0开始的, 第一行的行数是 0

设置表格行数、列数

代码中可以使用 setRowCount 方法来设置表格 行数 ,比如

1
table.setRowCount(10)

代码中可以使用 setColumnCount 方法来设置表格 列数 ,比如

1
table.setColumnCount(10)
清除/删除所有内容

clearContents 方法可以清除表格所有的内容,比如

1
table.clearContents()

清除后,仍然会留下表格栏,如果连表格栏都要删除,可以使用 setRowCount(0),像这样

1
table.setRowCount(0)
设定列宽、宽度自动缩放

Qt Designer 上目前没法拖拽设定 每个列的宽度,只能在代码中指定,如下所示

1
2
3
4
5
# 设定第1列的宽度为 180像素
table.setColumnWidth(0, 180)

# 设定第2列的宽度为 100像素
table.setColumnWidth(1, 100)

如想让 表格控件宽度 随着父窗口的缩放自动缩放,可以在属性编辑器中勾选 HorizontalHeaderStretchLastSection或者使用下面代码

1
table.horizontalHeader().setStretchLastSection(True)
信号:单元格内容改动

当用户修改了一个单元格的内容,会发出 cellChanged 信号,并且携带参数指明该单元格的行号和列号,我们的代码可以对该信号进行相应的处理,示例代码如下

1
2
3
4
5
6
7
8
9
def __init__(self):
# 指定单元格改动信号处理函数
self.ui.table.cellChanged.connect(self.cfgItemChanged)


def cfgItemChanged(self,row,column):
# 获取更改内容
cfgName = self.ui.table.item(row, 0).text() # 首列为配置名称
cfgValue = self.ui.table.item(row, column).text()
边界线样式

表格控件的边界线颜色可以通过QSS 属性 gridline-color 指定,如下所示

1
2
3
QTableWidget {
gridline-color: green;
}

就会指定表格边界线颜色为 绿色。

(9)单选按钮和按钮组

QRadioButton 是单选按钮,如下图所示

注意:

同一个父窗口 里面的多个单选按钮,只能选中一项。如果有多组单选按钮,每组都应该有不同的父控件,或者不同的Layout。

通常建议:多组单选按钮,放到不同的按钮组 QButtonGroup中,按钮组就是父控件

信号:选中状态改变

如果用户操作点击了按钮组 QButtonGroup 中的一个按钮, QButtonGroup 就会发出 buttonClicked 信号,可以这样指定处理该信号的函数

1
buttongroup.buttonClicked.connect(handleButtonClicked)

然后,在处理函数中调用QButtonGroup对象的 checkedButton() 函数,返回值就是被选中的按钮对象。

再调用这个返回的按钮对象的 text() 方法得到界面文本,就可以知道是哪个选项被选中了。

(10)勾选按钮和按钮组

QCheckBox 是勾选按钮,如下图所示

注意:

通常建议:多组勾选按钮,放到不同的按钮组QButtonGroup 中,按钮组就是父控件。

可以在 Qt设计师中设置 QButtonGroup 的 exclusive 属性,来控制是否只能单选一个选项。

信号:选中状态改变

如果用户操作点击了按钮组 QButtonGroup 中的一个按钮, QButtonGroup 就会发出 buttonClicked 信号,可以这样指定处理该信号的函数

1
buttongroup.buttonClicked.connect(handleButtonClicked)

QButtonGroup 设置为 单选 情况下:

在处理函数中调用QButtonGroup对象的 checkedButton() 函数,返回值就是被选中的按钮对象。

再调用这个返回的按钮对象的 text() 方法得到界面文本,就可以知道是哪个选项被选中了。

QButtonGroup 设置为 多选 情况下:

要得知哪些按钮被选中, 可以对所有该组中的按钮调用 isChecked方法来判断。

(11)Tab页控件

可以通过tab页控件把界面分为好几个页面,如下所示

通过Qt designer 只需要拖拽控件到各个页面即可,要修改tab页的标题,可以先点击该tab页,然后在下图所示处修改

tab页中布局Layout

如果我们要在tab页上布局, 你可能会在对象查看器总直接右键点击该tab,可以你会发现右键菜单里面没有布局项。

  1. 首先需要你在tab页上添加一个控件
  2. 然后点击 在对象查看器 右键点击上层 TabWidget ,这时,你就会发现有布局菜单了

(12)进度条

QProgressBar 是进度条,如下图所示

进度条也是一个常用的控件,当程序需要做一件比较耗费时间的任务(比如统计数据,下载文件等)时,可以用来向用户指示操作的进度。

而且有了进度显示,用户就知道应用程序仍在运行,并没有出问题。

QProgressBar进度条把每个进度称之为一个step(步骤)。

我们可以通过它的 setRange 方法设定步骤个数,比如

1
progressBar.setRange(0,5)

就设定了,进度分为5步。

然后,通过 setValue 方法,指定当前完成到了哪一步,比如

1
progressBar.setValue(3)

就表示完成了 3/5, 也就是 60%, 进度条就会显示60%的进度。

可以使用reset()将进度条倒退到开头。

有时候我们的任务没法知道完成了多少,比如下载一个未知大小的文件。

这时,可以把range 范围都设置为0,这样,进度条会显示忙碌指示符,而不是显示进度百分比。

(13)数字输入框

QSpinBox 是数字输入框,可以输入或使用上下箭头选择数字,如下图所示

获取数字

通过 value 方法获取编辑框内的文本内容,比如

1
number = box.value()

注意:返回的是整数对象,不是字符串

设置数字

通过 setValue 方法可以设置提示文本内容,比如

1
box.setValue(100)

(14)选择文件框

QFileDialog 类可以用来选择文件或者目录,如下图所示

选择目录

通过 getExistingDirectory 静态方法 选择目录。

该方法,第一个参数是父窗口对象,第二个参数是选择框显示的标题。比如

1
2
3
from PySide2.QtWidgets import QFileDialog

filePath = QFileDialog.getExistingDirectory(self.ui, "选择存储路径")

返回值即为选择的路径字符串。如果用户点击了选择框的取消选择按钮则返回空字符串。

选择单个文件

如果你想弹出文件选择框,选择一个已经存在的文件,可以使用QFileDialog静态方法 getOpenFileName,比如

1
2
3
4
5
6
7
8
from PySide2.QtWidgets import QFileDialog

filePath, _ = QFileDialog.getOpenFileName(
self.ui, # 父窗口对象
"选择你要上传的图片", # 标题
r"d:\\data", # 起始目录
"图片类型 (*.png *.jpg *.bmp)" # 选择类型过滤项,过滤内容在括号中
)

该方法返回值是一个元组,第一个元素是选择的文件路径,第二个元素是文件类型,如果你只想获取文件路径即可,可以采用上面的代码写法。

如果用户点击了选择框的取消选择按钮,返回空字符串。

如果你想弹出文件选择框,选择路径和文件名,来保存一个文件,可以使用 QFileDialog 静态方法 getSaveFileName ,比如

1
2
3
4
5
6
7
8
from PySide2.QtWidgets import QFileDialog

filePath, _ = QFileDialog.getSaveFileName(
self.ui, # 父窗口对象
"保存文件", # 标题
r"d:\\data", # 起始目录
"json类型 (*.json)" # 选择类型过滤项,过滤内容在括号中
)
选择多个文件

如果要选择多个文件,使用 getOpenFileNames 静态方法

1
2
3
4
5
6
7
8
from PySide2.QtWidgets import QFileDialog

filePaths, _ = QFileDialog.getOpenFileNames(
self.ui, # 父窗口对象
"选择你要上传的图片", # 标题
r"d:\\data", # 起始目录
"图片类型 (*.png *.jpg *.bmp)" # 选择类型过滤项,过滤内容在括号中
)

上例中filePaths对应的返回值是一个列表,里面包含了选择的文件。

如果用户点击了选择框的取消选择按钮,返回空列表。

(15)提示框

QMessageBox 类可以用来弹出各种提示框

该类可以通过一系列静态方法,显示如下弹出框

  • 错误报告

使用 critical 方法

1
2
3
4
QMessageBox.critical(
self.ui,
'错误',
'请选择爬取数据存储路径!')
  • 警告

使用 warning 方法

1
2
3
4
QMessageBox.warning(
self.ui,
'阅读太快',
'阅读客户协议必须超过1分钟')
  • 信息提示

使用 information 方法

1
2
3
4
QMessageBox.information(
self.ui,
'操作成功',
'请继续下一步操作')

也可以使用 about 方法

1
2
3
4
QMessageBox.about(
self.ui,
'操作成功',
'请继续下一步操作')
  • 确认继续

使用 question 方法

1
2
3
4
5
6
7
8
9
choice = QMessageBox.question(
self.ui,
'确认',
'确定要删除本文件吗?')

if choice == QMessageBox.Yes:
print('你选择了yes')
if choice == QMessageBox.No:
print('你选择了no')

(16)输入对话框

QInputDialog 输入对话框 只让用户输入一行数据信息,比如姓名、年龄等。

可以方便的用来获取简单的信息。

其他更多控件请参考:常用控件4 | 白月黑羽 (byhy.net)


Pyside2
https://gintoki-jpg.github.io/2022/06/27/工具_pyside2/
作者
杨再俨
发布于
2022年6月27日
许可协议