打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
新手写给新手的PyQt 5开发心得(上)

0. 废话

不是CS专业,从来没做过界面,入职就被分配了写一个上位机的工作。幸亏读master的时候被操练得盲目自信不怕困难,说干就干。边学边写用Matlab写了个很烂的上位机,一旦启动阻塞电脑其他所有动作…… 但是也算能用了,空闲了几天,这时候有了新需求,那么就趁机学一下Qt吧。开始的时候,决定用PyQt 5 + Python 3,因为感觉比C++开发速度快。新需求完成后,就决定继续把旧的功能移植过来了,最后……又做了一个很烂的上位机。“很烂”主要是性能问题,后面再细说。

请多指教。

1. 开发环境

1.1 Python 3

笔者也不知道Python 2用来开发会怎样,选Python 3一是因为看到Python 2将停止维护还是什么的,二是用Vim的时候不少插件要Python 3支持。笔者是在Windows 7上开发,安装Python 3之后,就可以用pip安装需要的包了,比如说最主要的PyQt 5。中途从Python 3.5换到3.6,因为更新了Vim,结果是并无影响,至少是在这个项目涉及到的范围内没有影响。

1.2 PyQt 5

不需要安装Qt 5。一开始以为使用PyQt 5要先安装Qt 5,后来在另外一台电脑上搭环境的时候尝试了一下,发现并不需要。开始用的时候是PyQt/Qt 5.9,写这篇文章的时候是5.10了。PyQt 5相对于4,很多类都被移动了,目前来说网上的PyQt 5的教程比4的少很多,虽然可以参考着来开发,但是import的时候就要小心了,要去PyQt 5的网站或Qt官网查看一下。

1.3 Vim

既然提到了,就稍微说一下,并不是要引战。笔者是学ROS的时候突然想起有这么个编辑器,怀着一颗好奇心就入坑了,花了一个星期入门——“入门”的意思是知道:wq等等。笔者脑子不好使,.h和.c(pp)分开看不行,界面和逻辑分开看也不行,也尝试过学习Emacs之类,无奈再也没有这么多时间了,所以一直用Vim。另外通过配置vimrc也可以直接在Vim中通过快捷键运行Python代码。

Vim用了快3年,配置上基本满足当前需求了,不能说是高端配置。先挖个坑,后面补一篇指南,比如语法检查插件ALE我就搞了很久。

2. 开始

2.1 入门资料

还是先上点入门资料吧。

(1) First Programs in PyQt 5 - 建议先看这个。

(2) 《Matplotlib for Python Developers》的Chapter No. 6 - 第6章讲的是怎么把matplotlib的坐标嵌入到PyQt中去,但例子用的都是PyQt 4,需要读者自行移植。其他matplotlib的内容,笔者都是通过其官方文档及Stack Overflow学习的。

2.2 main.py

也许主文件为其他命名也可以,但是笔者的主文件就是一锅粥,以main概括就是了;里面是各个面板的实例化,以及最重要的——整个程序的实例化。这里要用到的是:

from PyQt5.QtWidgets import QApplication

PyQt 4的界面有很多种风格可选,但是PyQt 5中删减了。可通过setStyle函数来设置:

  1. if __name__ == '__main__':
  2. app = QApplication(sys.argv)
  3. app.setStyle('fusion')


2.3 界面

2.3.1 窗口:QMainWindow, QWidgets

在看过的教程里,似乎主窗口都要用到QMainWindow:

  1. from PyQt5.QtWidgets import QMainWindow
  2. class MainWindow(QMainWindow):
  3. def __init__(self):
  4. super().__init__()
  5. # Your code afterwards

用的时候并没有深究为什么一定要用QMainWindow,后来其他部件全都是用QWidget。写到这里,笔者去查了一下,有兴趣的可以看一下——链接一,链接二,链接三。(“待审核”了,删了,不好意思。)

QWidget用法和以上代码差不多,就改个类名。主要用来做了一些从主窗口分离出去的小面板,例如嵌入了matplotlib的面板。

2.3.2 窗口属性

笔者写的软件只定义了窗口初始大小和位置,以及图标:

  1. from PyQt5.QtGui import QIcon
  2. from PyQt5.QtWidgets import QDesktopWidget
  3. from PyQt5.QtWidgets import QWidget
  4. class Panel(QWidget):
  5. def __init__(self):
  6. super().__init__()
  7. # Your code afterwards.
  8. def init_panel(self):
  9. screen = QDesktopWidget().availableGeometry()
  10. width = screen.width() * 0.5 # Change the scaler (0.5 here) at your will.
  11. height = screen.height() * 0.5 # Same as above.
  12. x = screen.width() * 0.01 # Same as above.
  13. y = screen.height() * 0.03 # Same as above.
  14. self.setGeometry(x, y, width, height)
  15. self.setWindowTitle('Panel')
  16. self.setWindowIcon(QIcon('icon.png'))

2.3.3 创建组件

先把用到的组件列一下:

  1. from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
  2. from matplotlib.figure import Figure
  3. from PyQt5.QtWidgets import QSizePolicy
  4. from PyQt5.QtWidgets import QCheckBox, \
  5. QComboBox, \
  6. QDial, \
  7. QGroupBox, \
  8. QLabel, \
  9. QLineEdit, \
  10. QProgressBar, \
  11. QPushBotton
  12. from PyQt5.QtWidgets import QStyleFactory

(1) 1至3行是用来嵌入matplotlib的,QSizePolicy的用法,笔者也是对着教程依样画葫芦:

  1. class Canvas(FigureCanvasQTAgg):
  2. def __init__(self, parent):
  3. self.figure = Figure()
  4. FigureCanvasQTAgg.__init__(self, self.figure)
  5. self.axes = self.figure.add_subplot(1, 1, 1)
  6. self.setParent(parent)
  7. FigureCanvasQTAgg.setSizePolicy(self,
  8. QSizePolicy.Expanding,
  9. QSizePolicy.Expanding)
  10. FigureCanvasQTAgg.updateGeometry(self)

(2) 14行的QStyleFactory是与8行的QDial配合用的,笔者用QDial简单封装了一个罗盘,但是QDial的指针样式在PyQt 5里默认是一个圆点,需要用QStyleFactory强制显示为针状(只列出部分代码,其他属性请自行设置):

  1. class Compass(QDial):
  2. def __init__(self, parent):
  3. super().__init__()
  4. self.setStyle(QStyleFactory.create('windows'))

(3) 第6行QCheckBox:很直接,用于勾选某些选项

(4) 第7行QComboBox:用于创建下拉菜单

  1. from PyQt5.QtWidgets import QComboBox
  2. from PyQt5.QtWidgets import QWidget
  3. class Panel(QWidget):
  4. def __init__(self):
  5. super().__init__()
  6. def create_widget(self):
  7. self.menu = QComboBox(self)
  8. def init_widget(self):
  9. self.menu.addItem('1', 1)
  10. self.menu.addItem('2', 2)
  11. self.menu.addItem('3', 3)

addItem的第一个参数为菜单显示的字符串,第二个参数为item里对应的数据,可通过itemData来获取,例如,在上述例子的前提下,以下语句为True:

  1. self.menu.itemData(0) == 1
  2. self.menu.itemData(1) == 2
  3. self.menu.itemData(2) == 3

(5) 第9行QGroupBox:就是在窗口里的一个小区域,视觉上将一些关系紧密组件放在一起,可以为这个group设置一个名字。QGroupBox在面板的QGridLayout上用addWidget从(0,0)开始摆放后,组内要另外定义QGridLayout,从(0,0)开始摆放组件。其他细节请自行参考官方文档。

(6) 第10、11行配合使用:QLabel即创建标签,通常用来标记QLineEdit里显示的数据是什么东西。QLineEdit中,用setText来设置要显示的数据,同时可用setReadOnly防止数据被误改;如果是作为输入框,用getText然后转换数据类型。其他细节请自行参考官方文档。

(7) 第12行QProgressBar:用来当作电池显示条。随便鼓捣自己想要的功能,其中还用到一个网站(uiGradient)提供的渐变色配色方案。

(8) 第13行QPushButton:创建按钮,可用setCheckable(True)来使得按钮可以保持按下(Checked)的状态。

2.3.4 组件布局

笔者只用了QGridLayout,因为感觉这个布局便于安排组件。虽然冥冥中感觉QVBoxLayout和QHBoxLayout能够让界面在resize的过程中使组件的位置保持得更好(如使用addStrecth等,若有误恳请读者赐教),但由于并不是产品级的软件,所以什么方便快捷就用什么了。QGridLayout中的setRowStrecth和setColumnStretch只是用来对齐不同QBoxGroup中的组件,但效果也并不完美。

原理上,setRowStrecth和setColumnStretch可以用来调整不同组件所占空间的大小,但是笔者直接用addWidget来调整了:

  1. from PyQt5.QtWidgets import QGridLayout
  2. from PyQt5.QtWidgets import QLineEdit
  3. from PyQt5.QtWidgets import QWidget
  4. class Panel(QWidget):
  5. def __init__(self):
  6. super().__init__()
  7. self.layout = QGridLayout(self)
  8. # Your code afterwards
  9. def create_widgets(self):
  10. self.fat_box = QLineEdit(self)
  11. self.thin_box = QLineEdit(self)
  12. def add_widgets(self):
  13. self.layout.addWidget(self.fat_box, 0, 0, 3, 3)
  14. self.layout.addWidget(self.thin_box, 3, 0, 1, 1)
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
PyQt5快速入门(三)PyQt5基本窗口组件
【第二节】PyQt5基本功能
PyQt5案例汇总(完整版)
Python PyQt5整理介绍
深入解析PyQt 6:为什么它比PyQt 5更强大?
信号与槽函数的自动绑定(三)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服