GUI程序的开发方式太多了,这里肯定就是Python语言了,至于为什么,就不多描述了;
那么基于Python开发GUI程序的话,也是有多种框架的,常见的有TKinter、PyQt、PySide、wxPython、Kivy、PyGTK
;
针对这6个常见的框架怎么选择,这里简单的对比一下:
Tkinter
PyQt
PySide
wxPython
Kivy
PyGTK
后面三种的话,就是学习起来会比较麻烦,我们还是专注NLP,不专注GUI程序开发,所以不选择; Tkinter的话,就是太简单了,很多东西不能实现,所以不选择; 而PyQt和PySide都是基于Qt框架开发的,PyQt和PySide具有相似的API和功能,学习难度也都差不多;**另外PySide使用 LGPL 许可证,可以免费商业使用。但是PyQt使用GPL或商业许可证,商业许可证是付费的;**而且Qt打算着力培养PySide,所以PySide是更有前途的,所以选择Pyside作为GUI程序开发的框架;
PySide现在主要的就是PySide2
和PySide6
两个版本,PySide6
是基于PySide2
向后兼容的,而且PySide6相比PySide2
有更多的新特性和改进,包括对Qt 6
的支持、更好的性能和稳定性,也提供了更多的API和工具,使得开发者可以更轻松的创建高质量的GUI应用程序,所以我们选择PySide6
。
这里用的python是3.8的版本,pyside6
要求3.6以上的版本,所以这里大家要注意选择,建议新建一个python虚拟环境,包的依赖和版本管理更清晰;
直接pip
安装即可,-i
及后面都是镜像源,下载速度更快,这里用的是清华的镜像源,清华的镜像源完整而且下载速度快,所以是个不错的选择;
pip install PySide6 -i https://pypi.tuna.tsinghua.edu.cn/simple
新建一个项目,写入下面的代码,运行这个脚本即可; 每行代码的含义也都注明了;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 20:14
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
# 这一行导入了 QApplication、QWidget 和 QLabel 类,它们是 PySide6 中用于创建应用程序和窗口组件的类。
from PySide6.QtWidgets import QApplication, QWidget,QLabel
# 创建了一个 QApplication 实例,用于管理整个应用程序的事件循环和资源分配。
app = QApplication()
# 创建一个空白的 QWidget 对象,它代表着我们的窗体。
window = QWidget()
# 设置窗体的标题为 "Simple Window"。
window.setWindowTitle("Simple Window")
# 将窗体的大小固定为宽度为 400 像素、高度为 300 像素。
window.setFixedSize(400, 300)
# 创建一个 QLabel 对象,并将其作为子组件添加到窗体上。同时,设置标签的显示文本为 "Hello PySide6!"。
label = QLabel("Hello PySide6!", window)
# 创建一个 QLabel 对象,并将其作为子组件添加到窗体上。同时,设置标签的显示文本为 "Hello PySide6!"。
label.move(150, 125)
# 显示窗体
window.show()
# 启动应用程序的事件循环,等待事件的触发和处理,使窗体保持可响应状态。
app.exec()
如上,就大功告成啦!
首先,说一说上面的开发,窗体所有内容都可以用python来写,包括窗体、布局、组件等,但是如果说全部的这些布局的内容都用python来手敲,会不会太繁杂了,所以要想办法解决这个问题;
Qt Designer就随之而出;
Qt Designer 是一个可视化的界面设计工具,它允许通过拖放和设置属性的方式,轻松创建应用程序的用户界面。类似于搭积木一样,可以在设计师中选择和放置各种小部件(例如按钮、文本框、图像等),然后调整它们的位置和大小。 Qt Designer 提供了一个直观的图形用户界面,可以在其中对界面进行布局,并设置小部件的外观和行为属性。可以通过编辑器轻松调整字体、颜色、对齐方式等属性,以及连接信号和槽来处理用户交互。 设计完成后,Qt Designer 会生成一个特定格式的界面文件(通常是
.ui
文件),其中包含了界面的结构、布局和属性信息。可以使用 PySide6 中的QUiLoader
类将该界面文件加载到应用程序中,使界面在运行时动态显示和交互。 使用 Qt Designer,无需手动编写复杂的界面代码,而是可以通过直观的操作来创建界面。这,您可以更快速地实现所需的界面,并且能够更好地分离界面设计和应用程序逻辑,使开发工作更加高效、简洁和易于维护。
这里记录简单的使用,不过多讲解,后续可能会专门出一篇关于Qt Designer的博客来记录更多使用技巧和方法;
安装方式有很多,可以直接下载安装包安装,也可以使用其他办法安装;
如果安装了PySide的话,它会自动安装到PySide的包的目录下:
D:\Coding_env\Python_env\nlp\Lib\site-packages\PySide6
也就是你python安装的目录下的\Lib\site-packages\PySide6
文件夹内:
这里选择一个空白的窗体即可;
其余两个也是对话框模板,可以点击看看
这里反正就是布局、按钮、文本域等组件的添加,具体的这里不过多讲解;
这里用到的就两个组件,一个叫做Push Button,另一个是Text Browser;
你不太会的话,就直接把这些组件往画布上拖,就可以了,注意在右侧的属性编辑器中编辑每个组件的objectName即可;
不会也没关系,往下走;
点击文件菜单栏,选择保存,即可得到一个ui文件;
打开它,你会发现其实就是xml编写的内容;
到这里Qt Designer的任务就已经完成了,后面就来看看PySide怎么使用ui文件了;
如果你还是不太会使用Qt Designer的话,先手动新建一个文本文件,然后改后缀为ui,然后把下面的代码复制进去;
(先把流程走通嘛,Qt Designer后续再学呗)
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>336</width>
<height>313</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" colspan="4">
<widget class="QTextBrowser" name="textBrowser">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="bt_1">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>1</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="bt_2">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>2</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="bt_3">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>3</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="bt_add">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="bt_4">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>4</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="bt_5">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>5</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="bt_6">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>6</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="bt_minus">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="bt_7">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>7</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="bt_8">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>8</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="bt_9">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>9</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="bt_multiply">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>×</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="bt_CE">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>CE</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="bt_0">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="bt_equal">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>=</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QPushButton" name="bt_divide">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>➗</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
ui文件有两种使用方法,一种是直接引用进来,但是复杂的程序可能会有兼容性的问题,而且打包也不是很友好; 另一种是将ui转换为py文件后使用,这种方式的适配更友好; 这里我们两种方法都演示一下;
详细引入的方法见代码注释;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:03
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
import sys
from PySide6.QtCore import QFile, QIODevice
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication
app = QApplication([])
# 加载UI文件
ui_file = QFile("main.ui")
if not ui_file.open(QIODevice.ReadOnly):
print("无法打开UI文件")
sys.exit(-1)
# 创建UI加载器
loader = QUiLoader()
# 加载UI文件并实例化为窗口对象
window = loader.load(ui_file)
# 关闭UI文件
ui_file.close()
# 显示窗口
window.show()
# 运行应用程序
app.exec()
运行结果:
OK,没问题!
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:03
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
import sys
from PySide6.QtCore import QFile, QIODevice, Slot
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QPushButton
# 定义主窗口类
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 加载UI文件
loader = QUiLoader()
ui_file = QFile("main.ui")
if not ui_file.open(QIODevice.ReadOnly):
print("无法打开UI文件")
sys.exit(-1)
# 加载UI文件并实例化为窗口对象
self.window = loader.load(ui_file)
# 关闭UI文件
ui_file.close()
if __name__ == "__main__":
# 创建一个QApplication对象,它是PySide6应用程序的核心,负责处理事件和管理应用程序的生命周期。
app = QApplication([])
# 创建一个MainWindow对象,即主窗口类的实例。
main_window = MainWindow()
# 调用主窗口对象的show()方法,将主窗口显示在屏幕上。
main_window.window.show()
# 启动应用程序的事件循环,使得应用程序能够响应用户的输入和系统事件,保持运行状态。
app.exec()
为了使用信号和槽的机制,这里要定义一个主窗口类,但是定义主窗口类的作用远不只有这个作用;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:03
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
import sys
from PySide6.QtCore import QFile, QIODevice, Slot
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QPushButton
# 定义主窗口类
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 加载UI文件
loader = QUiLoader()
ui_file = QFile("main.ui")
if not ui_file.open(QIODevice.ReadOnly):
print("无法打开UI文件")
sys.exit(-1)
# 加载UI文件并实例化为窗口对象
self.window = loader.load(ui_file)
# 关闭UI文件
ui_file.close()
# 获取UI文件中的小部件对象
self.button = self.window.findChild(QPushButton, "bt_1")
# 连接信号和槽
self.button.clicked.connect(self.bt_1_click)
# 按钮点击事件处理函数
@Slot()
def bt_1_click(self):
print("1")
if __name__ == "__main__":
app = QApplication([])
main_window = MainWindow()
main_window.window.show()
app.exec()
主要的代码就这几行,其实还是很容易看懂的;
# 获取UI文件中的小部件对象
self.button = self.window.findChild(QPushButton, "bt_1")
# 连接信号和槽
self.button.clicked.connect(self.bt_1_click)
# 按钮点击事件处理函数
@Slot()
def bt_1_click(self):
print("1")
1
按钮看效果如下,点击后成果输出了;
那么说明这个流程已经打通了,接下来就是看怎么完善这个插槽函数,以及进一步复杂的逻辑了;
这里直接附上最终代码,不一步一步讲解了;
其实这个代码非常的冗余,也是因为制作ui文件的时候,对象名有问题;
这里都是简单的逻辑,复杂的还需要进一步优化,这里只是简单的先实现这个程序,也就到这个程度为止;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:03
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
import sys
from PySide6.QtCore import QFile, QIODevice, Slot
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QPushButton, QTextBrowser
# 定义主窗口类
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 加载UI文件
loader = QUiLoader()
ui_file = QFile("main.ui")
if not ui_file.open(QIODevice.ReadOnly):
print("无法打开UI文件")
sys.exit(-1)
# 加载UI文件并实例化为窗口对象
self.window = loader.load(ui_file)
# 关闭UI文件
ui_file.close()
# 获取UI文件中的小部件对象
self.textbrowser = self.window.findChild(QTextBrowser, "textBrowser")
self.bt_1 = self.window.findChild(QPushButton, "bt_1")
self.bt_2 = self.window.findChild(QPushButton, "bt_2")
self.bt_3 = self.window.findChild(QPushButton, "bt_3")
self.bt_4 = self.window.findChild(QPushButton, "bt_4")
self.bt_5 = self.window.findChild(QPushButton, "bt_5")
self.bt_6 = self.window.findChild(QPushButton, "bt_6")
self.bt_7 = self.window.findChild(QPushButton, "bt_7")
self.bt_8 = self.window.findChild(QPushButton, "bt_8")
self.bt_9 = self.window.findChild(QPushButton, "bt_9")
self.bt_0 = self.window.findChild(QPushButton, "bt_0")
self.bt_add = self.window.findChild(QPushButton, "bt_add")
self.bt_divide = self.window.findChild(QPushButton, "bt_divide")
self.bt_CE = self.window.findChild(QPushButton, "bt_CE")
self.bt_minus = self.window.findChild(QPushButton, "bt_minus")
self.bt_multiply = self.window.findChild(QPushButton, "bt_multiply")
self.bt_equal = self.window.findChild(QPushButton, "bt_equal")
# 连接信号和槽
self.bt_1.clicked.connect(self.bt_click)
self.bt_2.clicked.connect(self.bt_click)
self.bt_3.clicked.connect(self.bt_click)
self.bt_4.clicked.connect(self.bt_click)
self.bt_5.clicked.connect(self.bt_click)
self.bt_6.clicked.connect(self.bt_click)
self.bt_7.clicked.connect(self.bt_click)
self.bt_8.clicked.connect(self.bt_click)
self.bt_9.clicked.connect(self.bt_click)
self.bt_0.clicked.connect(self.bt_click)
self.bt_add.clicked.connect(self.bt_add_click)
self.bt_equal.clicked.connect(self.bt_equal_click)
self.bt_divide.clicked.connect(self.bt_divide_click)
self.bt_CE.clicked.connect(self.bt_CE_click)
self.bt_multiply.clicked.connect(self.bt_multiply_click)
self.bt_minus.clicked.connect(self.bt_minus_click)
# 加号函数
@Slot()
def bt_add_click(self):
self.textbrowser.insertPlainText('+')
# 减号函数
@Slot()
def bt_minus_click(self):
self.textbrowser.insertPlainText('-')
# 乘号函数
@Slot()
def bt_multiply_click(self):
self.textbrowser.insertPlainText('*')
# 除号函数
@Slot()
def bt_divide_click(self):
self.textbrowser.insertPlainText('/')
# 等号函数
@Slot()
def bt_equal_click(self):
result = eval(self.textbrowser.toPlainText())
self.textbrowser.insertPlainText('=')
self.textbrowser.append(str(result))
# 清空函数
@Slot()
def bt_CE_click(self):
self.textbrowser.clear()
# 点击数字按钮,获取当前按钮的值
@Slot()
def bt_click(self):
button = self.sender()
self.textbrowser.insertPlainText(button.text())
if __name__ == "__main__":
# 创建一个QApplication对象,它是PySide6应用程序的核心,负责处理事件和管理应用程序的生命周期。
app = QApplication([])
# 创建一个MainWindow对象,即主窗口类的实例。
main_window = MainWindow()
# 调用主窗口对象的show()方法,将主窗口显示在屏幕上。
main_window.window.show()
# 启动应用程序的事件循环,使得应用程序能够响应用户的输入和系统事件,保持运行状态。
app.exec()
运行效果也是完全ok的:
pyside6自带将ui文件转换为py文件的工具,在ui文件下所在目录下运行下面的命令就可以完成转换了;
main.ui
是待转换的文件;
main_ui.py
是转换后的文件;
pyside6-uic main.ui -o main_ui.py
这里也可以指定存放的目录
pyside6-uic mainwindow.ui -o ./output/mainwindow.py
可以使用-w
或--wrapper
选项来指定主类的名称:
pyside6-uic input.ui -o output.py -w Ui_MainWindow
主要的代码就几行:
from main_ui import Ui_Form
引入转换后的ui文件;
self.ui = Ui_Form()
初始化一个对象;
self.ui.setupUi(self)
使用setupUi()方法创建和设置UI界面;
self.textbrowser = self.ui.xxx
直接获取对象;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:29
# @Author : MinChess
# @File : demo.py
# @Software: PyCharm
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QTextBrowser, QPushButton
from main_ui import Ui_Form
class MyMainWindow(QWidget):
def __init__(self):
super().__init__()
# 初始化 UI
self.ui = Ui_Form()
self.ui.setupUi(self)
# 获取UI文件中的小部件对象
self.textbrowser = self.ui.textBrowser
self.bt_1 = self.ui.bt_1
self.bt_2 = self.ui.bt_2
self.bt_3 = self.ui.bt_3
self.bt_4 = self.ui.bt_4
self.bt_5 = self.ui.bt_5
self.bt_6 = self.ui.bt_6
self.bt_7 = self.ui.bt_7
self.bt_8 = self.ui.bt_8
self.bt_9 = self.ui.bt_9
self.bt_0 = self.ui.bt_0
self.bt_add = self.ui.bt_add
self.bt_divide = self.ui.bt_divide
self.bt_CE = self.ui.bt_CE
self.bt_minus = self.ui.bt_minus
self.bt_multiply = self.ui.bt_multiply
self.bt_equal = self.ui.bt_equal
# 连接信号和槽
self.bt_1.clicked.connect(self.bt_click)
self.bt_2.clicked.connect(self.bt_click)
self.bt_3.clicked.connect(self.bt_click)
self.bt_4.clicked.connect(self.bt_click)
self.bt_5.clicked.connect(self.bt_click)
self.bt_6.clicked.connect(self.bt_click)
self.bt_7.clicked.connect(self.bt_click)
self.bt_8.clicked.connect(self.bt_click)
self.bt_9.clicked.connect(self.bt_click)
self.bt_0.clicked.connect(self.bt_click)
self.bt_add.clicked.connect(self.bt_add_click)
self.bt_equal.clicked.connect(self.bt_equal_click)
self.bt_divide.clicked.connect(self.bt_divide_click)
self.bt_CE.clicked.connect(self.bt_CE_click)
self.bt_multiply.clicked.connect(self.bt_multiply_click)
self.bt_minus.clicked.connect(self.bt_minus_click)
# 加号函数
def bt_add_click(self):
self.textbrowser.insertPlainText('+')
# 减号函数
def bt_minus_click(self):
self.textbrowser.insertPlainText('-')
# 乘号函数
def bt_multiply_click(self):
self.textbrowser.insertPlainText('*')
# 除号函数
def bt_divide_click(self):
self.textbrowser.insertPlainText('/')
# 等号函数
def bt_equal_click(self):
result = eval(self.textbrowser.toPlainText())
self.textbrowser.insertPlainText('=')
self.textbrowser.append(str(result))
# 清空函数
def bt_CE_click(self):
self.textbrowser.clear()
# 点击数字按钮,获取当前按钮的值
def bt_click(self):
button = self.sender()
self.textbrowser.insertPlainText(button.text())
if __name__ == '__main__':
app = QApplication([])
mainWindow = MyMainWindow()
mainWindow.show()
app.exec()