Qt是GUI开发中的一个工具,可以根据用户需求进行程序界面的开发。Qt的开发有C++版的和python版的,不论你有哪种编程语言的基础都很好上手学习。PyQt5是Qt框架的Python语言实现,也是本文将要介绍的,并将会建立一个PyQt专栏不断更新供大家学习。
环境说明
在使用PyQt前需要确保你以安装相关安装包:
qt5-applications:5.15.2.2.3
qt5-tools :5.15.2.1.3
QtPy :2.1.0
PyQt5 :5.15.9
pyqt5-plugins :5.15.9.2.3
PyQt5-Qt5 :5.15.2
PyQt5-sip :12.11.0
pyqt5-tools :5.15.9.3.3
Qt开发大致可以有两种方法:
1.直接用代码设计界面和控件;
2.用Qt designer 设计布局和控件,再用代码实现具体方法;
在本文中将进行一个简单的示例来让大家了解和入手PyQt5。比如我们要实现一个这样的界面,可以加载图片和加载torch模型的GUI。
1.直接用代码设计布局
我们需要import以下包
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QTextBrowser, QWidget, QLabel, QPushButton, QFileDialog
其中QImage、QPixmap是和图像处理有关的类;
QApplication用于创建应用程序对象,有且仅有一个【放在main函数中】;
QWidget是窗口类,可以创建一个空白的窗口;
QTextBrowser,QLabel,QPushButton等均是控件;
我们现在需要实现一个自己的界面(窗口),需要继承QWidget,代码如下:
继承QWidget父类,并调用父类的构造函数初始化,self.initUI()是我们接下来定义的一些初始化参数和方法。
class MyWidget(QWidget):
def __init__(self):
QWidget.__init__(self)
self.initUI()
在我们的Widget中,我们需要设定以下界面大小为[640,480],给窗口起一个名字。
def initUI(self):
# 初始化标题,界面大小
self.resize(640, 480)
# 窗口名称
self.setWindowTitle('my first widget')
创建我们的第一个控件text_browser,我们需要实例化对象QTextBrowser。move函数指的是把改控件移动到哪里,resize是该控件的大小,并且可以用setText设置里面的初始化文本信息。
# 创建文本浏览器对象,self指的是在当前widget添加显示
self.text_browser = QTextBrowser(self)
self.text_browser.move(10, 10)
self.text_browser.resize(620, 30)
self.text_browser.setText("The Hello World Before!")
那么此时得到的界面如下:
然后我们插入一个控件用来显示图像,这里我用的是QLabel。下面的代码是用QLabel进行相关属性的设置,我们这里将该控件的背景设置为白色,大小为300x300,该控件的名称为"image_show"。
# 定义图片标签大小(显示图像大小)
self.label_h = 300
self.label_w = 300
self.label_show_camera = QLabel(self)
self.label_show_camera.move(10, 50)
# 设置Qlabel窗口大小
self.label_show_camera.setFixedSize(self.label_w, self.label_h)
self.label_show_camera.setText("TextLabel")
# Qlalbel的背景变为白色
self.label_show_camera.setStyleSheet("QLabel{background:white;}")
# 设置标签对象的名称
self.label_show_camera.setObjectName("image_show")
然后将图像在上述的Qlabel中进行显示。
show = Image.open("../dog.jpg").convert("RGB")
show = show.resize([self.label_w, self.label_h])
showImage = QImage(np.array(show), np.shape(show)[1], np.shape(show)[0], QImage.Format_RGB888)
# 设置图像
self.label_show_camera.setPixmap(QPixmap.fromImage(showImage))
接下来我们可以创建一个按钮,通过点击该按钮实现加载模型的功能。这里其实有个比较专业化的术语,叫信号和槽,在Qt中我们要实现类似点击并调用某功能的实现,这个过程包含:信号的发送者(按钮),发送的具体信号(点击),信号的接受者(显示的界面),信号处理(槽)(处理你定义的函数)。
我们先创建一个按钮(信号的发送者),实例化QPushButton即可,self就是本Widget窗口,和c++中的this指针类似。
# 创建按钮对象
self.load_model_button = QPushButton('load model', self)
self.load_model_button.move(350, 350)
然后我们定义一个槽函数,该槽函数是通过点击按钮后加载模型并在text_browser控件中显示加载成功。代码如下:
# 槽函数
def load_model(self):
# 打开文件对话框以选择模型文件
file_path, _ = QFileDialog.getOpenFileNames(self, 'Choose a model file', '', 'Torch Model Files (*.pth)')
# 如果用户选择了文件
if file_path:
print(file_path)
ckpt = torch.load(file_path[0], 'cuda')
result = "Model loaded successfully"
# 更新界面上的结果标签
self.text_browser.setText(result)
print("Selected model file:", file_path)
建立号槽函数以后就可以建立这个信号连接了,代码如下:
# 创建按钮对象
self.load_model_button = QPushButton('load model', self)
self.load_model_button.move(350, 350)
self.load_model_button.clicked.connect(self.load_model)
self.show()
最终界面如下:
可以看到加载模型后上面的窗口显示为加载成功。
完整代码如下:
import sys
from PIL import Image
import numpy as np
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QTextBrowser, QWidget, QLabel, QPushButton, QFileDialog
import torch
class MyWidget(QWidget):
def __init__(self):
QWidget.__init__(self)
self.initUI()
def initUI(self):
# 初始化标题,界面大小
self.resize(640, 480)
# 窗口名称
self.setWindowTitle('my first widget')
# 创建文本浏览器对象,self指的是在当前widget添加显示
self.text_browser = QTextBrowser(self)
self.text_browser.move(10, 10)
self.text_browser.resize(620, 30)
self.text_browser.setText("The Hello World Before!")
# 定义图片标签大小(显示图像大小)
self.label_h = 300
self.label_w = 300
self.label_show_camera = QLabel(self)
self.label_show_camera.move(10, 50)
# 设置Qlabel窗口大小
self.label_show_camera.setFixedSize(self.label_w, self.label_h)
self.label_show_camera.setText("TextLabel")
# Qlalbel的背景变为白色
self.label_show_camera.setStyleSheet("QLabel{background:white;}")
# 设置标签对象的名称
self.label_show_camera.setObjectName("image_show")
show = Image.open("../dog.jpg").convert("RGB")
show = show.resize([self.label_w, self.label_h])
showImage = QImage(np.array(show), np.shape(show)[1], np.shape(show)[0], QImage.Format_RGB888)
# 设置图像
self.label_show_camera.setPixmap(QPixmap.fromImage(showImage))
# 创建按钮对象
self.load_model_button = QPushButton('load model', self)
self.load_model_button.move(350, 350)
self.load_model_button.clicked.connect(self.load_model)
self.show()
# 槽函数
def load_model(self):
# 打开文件对话框以选择模型文件
file_path, _ = QFileDialog.getOpenFileNames(self, 'Choose a model file', '', 'Torch Model Files (*.pth)')
# 如果用户选择了文件
if file_path:
print(file_path)
ckpt = torch.load(file_path[0], 'cuda')
result = "Model loaded successfully"
# 更新界面上的结果标签
self.text_browser.setText(result)
print("Selected model file:", file_path)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyWidget()
sys.exit(app.exec_())
2.Qt designer界面设计
该方法需要用到designer工具,方法也很简单,只要你环境配置正确,然后在你的python环境下找的designer.exe即可,一般位置在你python环境/lib/site-packages/qt5-applictions/Qt/bin/
双击打开后界面如下:
先来说一下为什么推荐这种方法,直接用代码设计布局需要你在写的时候脑子里就要有布局的基础设计图,然后自己去设置每个控件、画面的大小和位置,这是很不方便的。而用designer可以先把大概的布局、位置等设置出来,然后在实现其他细节的时候直接调用控件使用即可。
创建一个Main Window。可以为用户提供主窗口的程序类。然后我们可以在左侧的工具栏拖入我们要的控件即可。
怎么设计就因人而异了,这里我只列出我用到的。
我这里设置了两QLabel用于显示图像和模型结构,并设置该布局为水平布局【设置布局,随着拖动窗口大小,控件也会相对应的缩放】。
然后设置两个按钮用于加载图像和加载模型。界面如下:
直接双击控件就可以输入文字。需要注意的是,我们在插入每个控件的时候,最后是给每个控件添加对象名,该对象名用于后续在代码中调用。例如我这里给两个Label设置对象名如下:
然后右侧的属性编辑器是可以设置控件属性用的:
例如将show image这个控件背景色变为白色【默认是透明的】。点击该控件,在右侧的属性编辑器->QWidget->styleSheet设置颜色即可。
同时在 属性编辑器->QLabel->scaledContents打上勾,不然显示的图像非常小,打勾后显示的图像会缩放到控件大小。
设计好以后保存该ui到你的python项目中。
然后加载在python中直接加载我们的ui文件。
# 加载UI文件
Ui_widget, _ = uic.loadUiType('myui.ui')
然后我们就可以用继续用代码实现UI界面中的各个功能了。实现方法也很简单,直接调用控件对象名作为属性即可。如下:
需要继承QMainWindow和Ui_widget。然后里面的self.load_model_button就是前面控件所设置的对象名。
import torch
from PyQt5 import uic
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QWidget, QFileDialog, QApplication, QMainWindow
from PIL import Image
import numpy as np
import sys
# 加载UI文件
Ui_widget, _ = uic.loadUiType('myui.ui')
class MyWidget(QMainWindow, Ui_widget):
def __init__(self):
super().__init__()
self.setupUi(self)
self.load_model_button.clicked.connect(self.load_model)
self.load_image_button.clicked.connect(self.load_image)
self.show_window_width = self.show_image_window.width()
self.show_window_height = self.show_image_window.height()
self.show()
最终的界面和效果如下:
相对应的代码如下:
# E:\ProgramData\Anaconda3\envs\pytorch1.7\Lib\site-packages\qt5_applications\Qt\bin
import torch
from PyQt5 import uic
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QWidget, QFileDialog, QApplication, QMainWindow
from PIL import Image
import numpy as np
import sys
# 加载UI文件
Ui_widget, _ = uic.loadUiType('myui.ui')
class MyWidget(QMainWindow, Ui_widget):
def __init__(self):
super().__init__()
self.setupUi(self)
self.load_model_button.clicked.connect(self.load_model)
self.load_image_button.clicked.connect(self.load_image)
self.show_window_width = self.show_image_window.width()
self.show_window_height = self.show_image_window.height()
self.show()
def load_image(self):
image_path, _ = QFileDialog.getOpenFileNames(self, 'Choose a image file', '', 'image files (*.jpg)')
if image_path:
img = Image.open(image_path[0]).convert("RGB")
#img = img.resize([self.show_window_width, self.show_window_height])
showImage = QImage(np.array(img), np.shape(img)[1], np.shape(img)[0], QImage.Format_RGB888)
# 设置图像
self.show_image_window.setPixmap(QPixmap.fromImage(showImage))
def load_model(self):
# 打开文件对话框以选择模型文件
file_path, _ = QFileDialog.getOpenFileNames(self, 'Choose a model file', '', 'Torch Model Files (*.pth)')
# 如果用户选择了文件
if file_path:
print(file_path)
ckpt = torch.load(file_path[0], 'cuda')
result = "Model loaded successfully"
print(result)
print("Selected model file:", file_path)
# 模型结构输出到show_model_keys中
keys = '\n'.join(ckpt.keys())
self.show_model_window.setText(keys)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyWidget()
sys.exit(app.exec_())