数学建模社区-数学中国

标题: 超全!Python图形界面框架PyQt5使用指南! [打印本页]

作者: 杨利霞    时间: 2022-9-15 12:26
标题: 超全!Python图形界面框架PyQt5使用指南!
超全!Python图形界面框架PyQt5使用指南!
: ~1 D( x8 c6 \. ]9 M
# j9 t. j2 R+ X) T; }使用Python开发图形界面的软件其实并不多,相对于GUI界面,可能Web方式的应用更受人欢迎。但对于像我一样对其他编程语言比如C#或WPF并不熟悉的人来说,未必不是一个好的工具。5 ~: }9 u3 v! [

- }9 k* _2 p& Y% r7 j- g% m常见GUI框架1 C- X5 U7 X6 k, E

- s( n4 E8 l! w' XPyQt5:Qt是一个跨平台的 C++图形用户界面库。QT一度被诺基亚拥,后出售给芬兰的软件公司Digia Oyj。PyQt5是基于Digia公司Qt5的Python接口,由一组Python模块构成。PyQt5本身拥有超过620个类和6000函数及方法。在可以运行于多个平台,包括:Unix, Windows, and Mac OS。, X5 @/ n. s  N5 A5 Z# o; e9 C

0 T* u5 M# D1 f8 c% a5 r5 {; y' O  |Pyside6:Pyside是QT公司官方提供的Python包,上一版本为Pyside2,对应的是QT5,最新版命名规则进行了调整,更改为Pyside6,对应的是QT6版本。由于官方出品的比较看好,缺点是发布比较晚,网上的资料没有PyQt5多。, L! r- }5 }( s1 h7 s

) {! b$ n/ Y1 U/ \7 TTkinter:Python内置的GUI框架,使用TCL实现,Python中内嵌了TCL解释器,使用它的时候不用安装额外的扩展包,直接import,跨平台。不足之处在于UI布局全靠代码实现,只有15种常用部件,显示效果简陋。6 @+ P$ N6 ?* B  R  b$ F

6 t5 t2 n' e' m7 Z6 X3 v  ZPySimpleGUI:PySimpleGUI 是 Tkinter 一层包装。使用 PySimpleGUI 实现自定义 GUI 所需的代码量要比使用 Tkinter 直接编写相同的 GUI 要少得多。
# Q% q* n6 L4 |5 m& n6 \0 o: z! B' G! k- u4 }6 K" u
WxPython:wxPython是Python语言对流行的wxWidgets跨平台GUI工具库的绑定。用得比较广泛,跨平台,C++编写,文档少,用户可能就需要根据编程内容对不同平台中的GUI代码做一些调整。遇到问题不好解决,代码布局控件,不直观。2 a; O+ q5 F! \! h" A9 n1 ^
. k* n0 a* S3 l0 ~3 ]
Wax:基于wxPython ,为克服wxPython的问题而制作的一个包。
: w! w4 i8 Q9 n9 M& u+ o  J# U( h
4 H' D; l, z! E9 ^$ N/ vKivy:主要针对多点触控程序,智能手机平板等,也可以在没有触屏功能的系统上,全平台支持(Windows, Linux, Mac OS X, Android and iOS.)使用Python和cython编写,中文支持差,需要自己下载中文库并且制定路径。7 S/ ?3 W; A7 k0 ]) A* F

3 U9 c! v+ L! a# J4 W7 GBeeWare:Write once. Deploy everywhere.需要与Kivy配合使用。: q! |' V1 Y+ u: x+ V

3 r" U$ @4 G& v6 M3 U/ `Toga:一个使用Python开发原生APP的GUI工具包。Toga由一个具有共享接口的基础组件库组成,以简化与平台无关的GUI开发。Toga适用于Mac OS、Windows、Linux(GTK)以及Android和iOS等移动平台。
# i5 w3 ^& E, ~! T7 q; W
) L) [! j' G/ \$ q. kEel:一个轻量的 Python 库,用于制作简单的类似于 Electron(但是比它更轻量) 的离线 HTML/JS GUI 应用程序,并具有对 Python 功能(capabilities)和库的完全访问权限。
. t  B! u% H+ ~; t4 d" H8 m; T; X& J" h6 Y2 ?
Flexx:一个纯 Python 工具包,用来创建图形化界面应用程序。其使用 Web 技术进行界面的渲染。你可以用 Flexx 来创建桌面应用,同时也可以导出一个应用到独立的 HTML 文档。因为使用纯 Python 开发,所以 Flexx 是跨平台的。只需要有 Python 和浏览器就可以运行。7 C; M' j9 I1 w% d; K0 @( M/ d
8 z; [% s# X6 V% X( R
pywebview是围绕 webview 组件的轻量型跨平台包装器(wrapper),它允许在其自己的本机 GUI 窗口中显示 HTML 内容。它使您可以在桌面应用程序中使用 Web 技术,同时尽最大可能隐藏使用浏览器构建GUI的事实。
0 b* \! S. S* C
" h; H- b5 R1 @8 |  d( H+ |  ienaml:一种能够让你用最小的努力就可以实现高质量GUI界面的的Python框架,也是一种独特的编程语言。enaml将声明性语言与基于约束的布局系统结合在一起,使用户可以轻松地定义灵活布局的UI。enaml应用程序可以在任何支持Python和Qt的平台上运行。: r; K9 @+ r$ P
% Y. A: `, L' i  C% ]* g" _
个人想法:太多学不完,先学PyQt5,原因是资料多,学有余力再学pyside6,最后看下PySimpleGUI,看能否解决一些简单问题。3 {1 B0 C/ U1 d! ^0 S! }

) w0 d, g# O8 r  `9 D: Q# kPyQt5简介
  L8 C4 p) R4 Z2 b0 r: e/ R0 _, g0 ~+ n
PyQt是Qt框架的Python语言实现,由Riverbank Computing开发,是最强大的GUI库之一。PyQt提供了一个设计良好的窗口控件集合,每一个PyQt控件都对应一个Qt控件,因此PyQt的API接口与Qt的API接口很接近,但PyQt不再使用QMake系统和Q_OBJECT宏。
1 D7 K- V+ B" a; p  ~- [. t8 {$ W0 g( |% j3 s7 Q
PyQt5提供GPL版和商业版证书,自由开发者可以使用免费的GPL许可,如果需要将PyQt用于商业应用,则必须购买商业许可。* l$ Q/ s, ], O; a% l

. C8 ]! g, i) y$ C! V( o2 d( R3 @PyQt5特性如下:2 z9 W2 L( r. ?' \/ s
8 f7 h$ C& P) k8 ^" R
基于高性能的Qt的GUI控件集。: G9 ?1 }0 L+ o( u' J7 m( E$ g- n" B
/ _4 P- P, t5 A  g1 x
能够跨平台运行在Linux、Window和Mac OS系统上。
7 r3 y0 n( i# k2 H4 _" Q* V  E: w- }3 H' }0 f. j
使用信号槽机制进行通信。7 f7 b; X) A& `: q/ s7 c9 n

  T* {" Z- v! f" H0 _( f/ o对Qt库进行完全封装。
" r5 `+ O% E# K1 P
% x( g: e7 P+ D7 {" D- @: H* ^可以使用成熟的IDE进行界面设计,并自动生成可执行的Python代码。
2 G# X# J7 J# Q! f! }
1 a0 f- Z* I& s+ K- g1 u* k8 G提供一整套种类齐全的窗口控件。
3 ^/ S5 ^/ u+ n* x4 f* F1 u
. {; D* j4 J. S' N) B5 a# [PyQt5是由一系列Python模块组成,有超过620个类,6000个函数和方法,主要模块如下:& I( N7 L/ N  |3 ]+ n

+ R5 W" q$ z5 l1 E$ q# k4 B$ WQtCore:包含了核心的非 GUI 的功能。主要和时间、文件与文件夹、各种数据、流、URLs、mime 类文件、进程与线程一起使用。+ o1 s+ U: W& g9 Z* t3 P% K* t# ^

- c0 n6 d2 Q; C+ FQtGui:包含了窗口系统、事件处理、2D 图像、基本绘画、字体和文字类。2 c" R8 m* d- s$ ?* \* c& D/ \

/ ]" b) c$ j7 r8 {9 y" e2 tQtWidgets:包含了一系列创建桌面应用的 UI 元素。1 w. G" K1 p+ U. e

5 z3 ^6 Y2 H% |- T' t# JQtMultimedia:包含了处理多媒体的内容和调用摄像头 API 的类。
- Y; t2 Z. u+ z, X$ h+ }3 k% r  _( O5 U
QtBluetooth:包含了查找和连接蓝牙的类。
- |) R7 S. P+ o
. W# s# h: B! p8 d; VQtNetwork:包含了网络编程的类,这些工具能让 TCP/IP 和 UDP 开发变得更加方便和可靠。
1 h1 ~3 R' v- E. F, p- u$ L2 ~2 C# P7 A4 }9 m+ K9 a" M/ O* }8 @
QtPositioning:包含了定位的类,可以使用卫星、WiFi 甚至文本。/ A$ s- q. H* D# \% K* v2 a
9 K4 Q+ n' `' d* c9 ?  u( P
Enginio:包含了通过客户端进入和管理 Qt Cloud 的类。
& J% q1 g! X9 f, h3 x! l* j
. U: A) b9 x7 y( q# F5 j+ f# L! wQtWebSockets:包含了 WebSocket 协议的类。& g9 M$ f/ t& Z# u. O
) X; t9 x/ R8 I# {! V0 z( x
QtWebKit:包含了一个基 WebKit2 的 web 浏览器。
/ f$ T" U* }6 Z- M' K5 m1 T8 J+ U
' I1 V1 ~* q3 t1 PQtWebKitWidgets:包含了基于 QtWidgets 的 WebKit1 的类。
0 z& U. J! ^: E1 x7 Y
. _$ _! j3 p7 m# r, tQtXml:包含了处理 xml 的类,提供了 SAX 和 DOM API 的工具。
3 ^. h6 }' J* c0 {' y( {  N
1 a+ G4 {) _* h2 ^QtSvg:提供了显示 SVG 内容的类,Scalable Vector Graphics (SVG) 是一种是一种基于可扩展标记语言 (XML),用于描述二维矢量图形的图形格式(这句话来自于维基百科)。
/ R; U0 g/ W6 D. N  |' [: ~$ Y) w! o$ a7 i5 J; y
QtSql:提供了处理数据库的工具。! M( b6 W1 P# l

- E" c" P! O6 f+ }$ _QtTest:提供了测试 PyQt5 应用的工具。  u+ w9 Q8 _/ |& E, j) z
, L9 A! k# w; Q- m3 D8 E
PyQt5的安装" L3 N( V6 j0 \
( s5 R- E3 x$ z3 I( j
由于后期要使用fbs进行打包,fbs对Python 3.7以后的版本可能存在兼容问题,所以我选择了Python 3.6.8进行了整个环境的搭建。主要内容为:Python + PyCharm + PyQt5 2 K1 Q/ m; R9 [1 l, ?6 u
# F2 T6 D: Y# J' o
安装PyQt53 ?% S! F. \. A4 L3 G  ]$ h9 x

0 S7 `0 O/ N) _# m- O0 d& f! epip install pyqt5
9 H5 H$ a  p: y  f" q" K. Y- n9 [9 r4 B
pip install pyqt5-tools
2 X" R8 l2 i: j/ I; u其中pyqt5-tools为Qt Designer拖拽式的界面设计工具。安装过程中可能会报如下错误:
3 j6 J6 {6 I, U1 t+ z, L( L7 y# Q
5 L2 @4 E- G0 J" r9 E% w  Iqt5-tools 5.15.2.1.2 has requirement click~=7.0, but you'll have click 8.0.1 which is incompatible.% F5 ~: T1 q/ Y! k
解决方案:
! ]$ h, o# Z7 q1 s& j
& v% H: n7 x, k/ cpip install click~=7.0& Z' Q9 V4 B1 I5 b
Qt Designer的配置9 c' q' W( s  y

: T6 _' D$ W7 d/ j+ z2 S$ f$ R6 Z1 ?Qt Designer 是通过拖拽的方式放置控件,并实时查看控件效果进行快速UI设计。
. \: i, X! s2 v% E, W) O3 ^
# p( Z- z! Z7 f+ r+ o- h2 ^! e1 p" n# q( p) }& U* m2 w: e
- S3 _- R' W8 j/ ]' b( u
整个画面的构成:7 }5 H& l: |1 c# E  D
/ T, ?3 K# n5 J- k  [( a
左侧的“Widget Box”就是各种可以自由拖动的组件
7 L% y, `3 ]5 y$ `0 R' P5 I2 s
: k0 t0 u# [! _0 ^- ?0 e中间的“MainWindow – untitled”窗体就是画布/ d! k; ^+ [5 ~- y; s2 w
# }9 k+ {' u- M5 k/ x8 I* _4 G/ s
右上方的”Object Inspector”可以查看当前ui的结构9 S9 |' ]2 U1 l9 k/ D
$ Y1 B4 k; b* m" J" E
右侧中部的”Property Editor”可以设置当前选中组件的属性0 |" J0 m0 I; D5 }
! f$ T/ K- i; k7 Z/ @6 s3 n5 V
右下方的”Resource Browser”可以添加各种素材,比如图片,背景等等4 |1 ^9 F9 V8 c* Q7 ]; T5 b3 [/ z( ^
$ `* R, J% Q$ q; T
最终生成.ui文件(实质上是XML格式的文件),可直接使用,也可以通过pyuic5工具转换成.py文件。
( m+ e# x8 [) h1 A6 G7 J
& H6 m" N3 K, M  fQtDisigner配置. {2 S; Y% b2 h
9 U0 P  h3 q2 L. g- e
在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:
1 `0 c; P5 m6 Z- i8 b* U4 D* v' z
- O% B6 M: I  i7 _Name: QtDisigner
6 `5 P4 M  B% ?8 j$ @( c; V7 e. p8 y* {+ X# c. g3 f
Program : D:\Program Files\Python36\Lib\site-packages\qt5_applications\Qt\bin\designer.exe # 请根据实际修改- T  X3 r1 G# u6 d7 H1 Y. p

$ s" p' v- N' E) h/ G7 i6 N4 fWorking directory: $FileDir$
0 N& `0 l4 d5 u1 GPyUIC配置% I( [% J) q7 U- |5 w0 [, h

; W8 i  m& q. z4 z9 u# |3 i4 MPyUIC主要是把Qt Designer生成的.ui文件换成.py文件。
7 e( u+ K: d* z. m: C+ K5 u! ?8 b
! n( z' i, _( v# Z! @+ s在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:
: l3 R. Y7 E+ V' {% y7 y
; v1 K3 G6 w6 H8 X+ O7 lName: PyUIC& v; p$ T' x- s' G4 k

5 z6 M6 F2 G( F% O% o* r6 K/ C! NProgram : D:\Program Files\Python36\python.exe # 当前Python目录,请根据实际修改
+ q" E2 P7 Y5 c" Z4 \& U
! g5 W, x7 A0 k# KArguments: -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py8 l; s! n9 ?  u1 f0 U

6 p5 |  {  X( n* hWorking directory: $FileDir$2 C2 b. c: ~6 @% {* S
PyRCC配置
$ ^, K( G6 [, J7 r# \$ P* ?- H
6 L) P( u' W  z: V1 OPyRCC主要是把编写的.qrc资源文件换成.py文件。在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:  Y; c/ V2 N5 y2 y/ D, ^8 I" o
8 x+ z' H- j9 c' S  g: M9 u7 [
Name: PyRCC/ R$ X5 T7 _0 s5 I

8 y; w9 C5 H4 o. c) _/ B- `" p' lProgram: D:\Program Files\Python36\pyrcc5.exe # 当前rcc工具目录,请根据实际修改# \+ k0 z8 T7 ]# ~. q; n3 G+ Y% H, t
4 a) i* \0 _5 ?0 ^
Arguments: $FileName$ -o $FileNameWithoutExtension$_rc.py
! K1 L# ?' c. ?- M2 f+ m0 T
% Z. d1 ^) w2 E' ?Working directory: $FileDir$0 f" \9 Q0 a' h
PyQt5使用示例6 q+ J, D$ S4 r. |

* b, D0 c2 p2 v, O! d创建一个空白的界面:) _5 [  e7 w  H; E. C3 F+ K
* h$ l2 R7 z0 R
import sys
; k2 m; Z, h9 X$ e3 p/ V( B) R/ l3 {* a: {! @# O
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel. D% z* j' w1 B' Z& l" `- L* j

0 v) C9 F9 K" C* mapp = QApplication(sys.argv)  D; Y% s2 G# q

8 s) C( ~' Z1 v5 Y' z* Vwin = QMainWindow(); U% r( D- U3 M' p- p! `

1 v9 y) R. ]/ ?7 w$ Nwin.setGeometry(400, 400, 400, 300)
: i( e* x' T( d3 \  x) M) u# P* R  b' z* H
win.setWindowTitle("Pyqt5 Tutorial")# b1 C0 \, Q  V! t4 G. e( @) w
( j6 ~  W" t( c3 _; |" j
win.show()
# a: [' Z; y( f# w, X% s' p* `! R& g" n$ t1 f
sys.exit(app.exec_())
3 m& @+ ]+ L* e$ k. s8 K
5 S5 \; ~: @( B* h+ u1 d2 W0 ]4 x% m( T( C. q" b: d. {
1 @$ ?4 o, }  Q
其中:
0 n' T3 I# |5 X: {6 p' n4 G- \1 K; ?. V' L! a
Qapplication():每个GUI都必须包含一个Qapplication,argv表示获取命令行参数,如果不用获取,则可以使用[]代替。2 A+ b, r  s; l* i2 L9 a' h4 H" q; C
9 y' y2 |. |2 J6 k
QMainWindow():类似一个容器(窗口)用来包含按钮、文本、输入框等widgets。arg标识可以获取命令行执行时的参数。: M( q7 ~( f$ ^7 B/ C
. f. k. w* ]/ Q; G7 E. X# X4 a
SetGeometry是用来定义 QMainWindow() 窗口的尺寸, 语法:setGeometry(x, y, width, height ),其中x,y为屏幕上的坐标点。
: C6 \, q6 V. g8 B, |0 S. k0 M) o6 a  f; X
show():用来显示窗口# k6 b$ ]3 y( b/ ?
9 Q, J  V7 e+ r' w5 B
exit(app.exec_()):设置窗口一直运行指导使用关闭按钮进行关闭
* x) n" t: E# z' h& h' y: j+ x% U5 q6 p  R8 s) j8 K0 ]/ f. f
PyQt5支持的常见Widgets有:, u! u& [! K5 U! R" H; [' o! N
3 y2 \, }* }! Y3 y6 Y$ v
9 K/ _, ?$ h+ L6 B( j

( ^# U% p. V3 H- h1 Q  K) |; w. Q从上到下,从左到右依次为:Qlabel、QcomboBox、QcheckBox、QradioButton、QpushButton、QtableWidget、QlineEdit、Qslider、QProgressBar
1 r. b" ^& v/ f  G' D) V# f7 s8 k5 m6 a: ?
对于使用Pyqt5设置文本内容,我们使用Qlabel:
5 n8 h( {( G7 n: A  z7 [' k+ B  }5 H4 j
import sys
/ }2 F2 q" c0 Q8 C3 Z1 p7 {& w" `6 e" l0 W/ O3 i2 V' E
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
# ?7 T6 G9 Y/ J& G4 P0 z) H0 @) Y- S3 H, E! n' E4 g
app = QApplication(sys.argv)* j" B4 q! Q' o, a1 X/ B3 r

$ f4 U0 r, l: ~* @+ H2 \win = QMainWindow()/ m$ V7 w. e" J: @+ W) S1 J, R
. N) d. n3 P( e2 R' m9 |9 a) H
win.setGeometry(400, 400, 400, 300)
( d6 I( ~: x) |' i& T
! v; a6 d* h' w7 rwin.setWindowTitle("Pyqt5 Tutorial")" w3 t5 r3 A2 V5 N8 @4 b4 A
7 |% w* R2 r5 m
\# Label Text
! u  Q  @8 E# m* k8 n/ u4 P1 x9 C+ z# z9 c( H0 Z, u
label = QLabel(win)
* f9 \' q; T2 c, U  f; P# M3 ^0 m7 e4 y4 F: \
label.resize(200, 100)4 F/ f1 @2 C, A2 W! F

- t' o, H+ P/ j+ {3 t5 ]label.setText("Hi this is Pyqt5")0 `8 M) l4 G) W" ?. e2 ^" i
$ O) j/ |; C3 p* K
label.move(100, 100)
$ S4 x0 o; y9 x$ f! t/ L1 \+ e- Q
) y3 T8 n4 o" X& J0 K8 F. ewin.show()9 F  o# i& Q. R: ]5 @

" m; H7 L+ m( L9 [" L: O% G& d5 W2 Msys.exit(app.exec_())/ g( z, ^' I6 R

4 g5 O+ e* j5 J0 y; f9 T- \$ A& v
! ^  ]0 Y" h8 w& |1 g/ L) @' s  D* n" \: W8 C0 [
按钮与事件:
& a/ A* ~, w' a2 |8 Q+ b2 T
4 F9 C$ @# m7 D% {import sys7 }8 V5 P- [5 Q) N4 c" O: R
2 \( Y  _: U$ Q3 z7 n/ R, T
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
  p4 U- R, V, M5 S* }
2 A2 B: Z; h0 t% Idef click():
/ @1 a( t3 W) B( Y6 Q/ y" H/ G. I0 L# y; M# ~
    print("Hy Button is clicked!")
5 I. P( @& _) M1 T
6 ?9 e7 P  Q# C  {; Eapp = QApplication(sys.argv)" E7 k/ K/ V, P, v/ P
" g% Y( @0 O( o8 r: M
win = QMainWindow()
. u8 T+ [0 p# o: z0 [& f1 q- G- }& h# z; \+ }1 J  A# c
win.setGeometry(400, 400, 400, 300): r! }: v, B% P$ q; }  w

& [; Z# j# u" u, ?! Z% `& [* Rwin.setWindowTitle("Pyqt5 Tutorial")
% b9 D9 P/ W# p! ^7 g; N7 }
! T& v1 G/ e# a7 R  J9 O% U\# Button
+ R' J% g! h! J4 x
  @2 _; q/ c- E# t  E. J- Ubutton = QPushButton(win)
/ l3 [7 e5 p& h! U8 H
* m, g7 u$ f# |button.resize(200, 100)" }' D# I4 I  ?% S* Y( }

; W( P; a) d3 A: n( \( Q, \button.setText("Hi! Click Me")* h6 `2 x( U2 Q% q# f
/ g3 \) C9 z& @" t
button.move(100, 100)
1 `% [$ P2 g  W* l" b+ v4 o$ k6 ]  R) a! J5 [- @
button.clicked.connect(click)
. P; w2 q9 T$ @' x- [1 I1 H2 F8 C. N4 j
win.show(): o2 |# b- |! j- q) r3 @( T

$ U$ E* j* C/ B% j6 D8 [sys.exit(app.exec_())6 e+ u5 c4 @3 E+ Q) z! x- Y

! S: @# o" A" d/ V; [: l& y% c, m9 }% y2 Q: h9 {9 m
" l( S2 E3 \4 \4 F& ~1 E, N
button.clicked.connect() 在按钮点击后执行特定的事件。
) ~! f' M; P/ x4 f! ?+ h. i: a
5 {7 m9 b2 s0 v  ~) F* ?; YPyQt5实战2 Y9 o. t9 ^9 _" m1 m9 K

( z3 Q! s* a; ?$ U实战项目:简易的天气查询软件) X) F4 Y0 n8 S
% f7 M: o2 ~9 X; J$ w
1、使用Qt Designer设计一个界面, W7 x5 j- Y* g' {7 B( `

: S  J) z- o! `( P# U& `+ C
/ h* j7 `5 g$ Z$ h) S9 V% o( d+ F( L( m3 w0 F
用到的控件有Button, GroupBox, Label,ComboBox,TextEdit,同时定义了两个按钮queryBtn及clearBtn,分别用来查询及清空天气数据。我们需要绑定槽函数,方法如下:/ \/ S2 N' ~  O4 t) Y

/ e2 j9 |8 a* C; t在Qt Designer右下角选择 信号/槽编辑器,点击+号新增
  U1 P) v6 M# y
' G& G4 U( w4 L2 ]# n% M( u分别选择queryBtn及clearBtn,选择信号 clicked(), 接收者 Dialog 及槽 accept(),(槽函数这里不知道如何定义,后期在代码里再进行修改)3 R/ [, w: @) N% ]# z. g) Y0 Q

" e( p) g3 z, i以上完成后保存为Weather.ui文件。8 q7 ?/ S% f5 O. g+ z  ^. A. y) [

' D; P8 w+ t$ d" ]# C, o' o$ \  ^2、转换.ui文件为.py文件
! e9 Y4 w* ^2 a6 E. j' w' D0 k* d9 S9 L% c1 P, w: J, o& A/ I
PyQt5支持直接使用.ui文件:) u, H9 S) U% x1 M

3 [, p- r& X( R: X6 B( Y, t# |: }2 _8 Ximport sys$ W+ G" G! E3 y
) m4 [% P4 u! [6 Q  e7 ]4 C9 x
from PyQt5 import QtWidgets, uic. B) n# b& R9 z- D4 }, r) e
, r- u3 ^, j; C1 G8 s8 z, b  B
app = QtWidgets.QApplication(sys.argv)
6 D' n1 g' A& _! l1 o' {
- c8 b: o- k: ?' w8 V4 p" Swindow = uic.loadUi("mainwindow.ui")5 L) L9 A3 X4 A' w

: K2 o% l6 u+ Kwindow.show()0 u  R4 `, J& X+ g5 u$ W: T' O6 N' ^3 x
8 U+ H! _. w& W  T, F! ?
app.exec()% y4 d3 d2 g" v. Q" m
但是为了更好的自定义及修改上面的槽函数,可以使用External Tools – PyUIC,即可生成Weather.py,实际运行命令如下:
. R7 p* R* i% F* m' ~' X. a6 s& |; _6 k( \
D:\Program Files\Python36\python.exe -m PyQt5.uic.pyuic Weather.ui -o Weather.py/ e  Y% [& y* l- l* Q
其中,我们需要把两个按钮绑定的槽函数:7 e2 R7 @0 l0 z
3 ?) i" `. s; _" }, Q: y' P
\# self.queryBtn.clicked.connect(Dialog.accept)9 V: `( f, ?' I7 |; g7 W
4 E4 o) s6 V5 i$ e8 Y3 L; N7 p8 k6 `
\# self.clearBtn.clicked.connect(Dialog.accept)
# O8 q, ^1 S9 [
, u' s  t+ a+ ?- P* b( }* H' @\# 修改为:
! W9 ^" X7 h" W# u. G) Z9 \5 y# O; o' z4 L6 i; O
self.queryBtn.clicked.connect(Dialog.queryWeather)  d' E9 }$ w. h& b3 B

2 l7 L3 P: |- w! R' h& Nself.clearBtn.clicked.connect(Dialog.clearText)4 E" \. r/ D' _7 w
最终的Weather.py内容如下:
. a' K2 ?/ c$ r5 P4 `# z+ q
1 S6 `% Q0 T3 l& y2 S, g\# -*- coding: utf-8 -*-
" p4 \6 \: H& U3 y/ f7 K2 Q/ n, I. d8 w5 S& t# e
\# Form implementation generated from reading ui file 'Weather.ui'; O& L1 H( V( ]0 x/ S/ y5 N
" x9 Y" D  U8 c! O; l
\#: O% L) G! E, p( H3 N+ U8 t, L1 n

1 J0 N; `3 H$ O  B" a\# Created by: PyQt5 UI code generator 5.15.4" \' V- E+ f) ]+ K1 D. f2 z

8 D: X5 @4 H! Z* g\#
/ Y+ o3 v& ~$ d/ z) \' H8 H4 A* v7 P3 Q
\# WARNING: Any manual changes made to this file will be lost when pyuic5 is
2 v3 b' b  v" v+ ^( }! D# g  g9 Y* J2 H- k6 E
\# run again.  Do not edit this file unless you know what you are doing.
, P' j) T: E1 X8 l5 ~0 L" x; z: R' ^3 B# U
from PyQt5 import QtCore, QtGui, QtWidgets
+ [# W$ G0 K9 {8 {% E
6 u7 \8 g- W3 Y8 }. G- X. Wclass Ui_Dialog(object):
9 K  {9 Z# o4 q( S! z. x/ C. b8 u" {; X; c5 p, y
    def setupUi(self, Dialog):
9 U7 M% Y( B( G1 |" |
% m( B$ _, Y; U# |        Dialog.setObjectName("Dialog")
8 w8 F& j( F. Y; `  s8 i
/ w3 g2 K7 K9 I7 Z; `6 b, E/ ]        Dialog.resize(600, 600)0 l0 `6 k! c% t& M
1 W7 Q- G# y0 m  I9 m( W
        self.groupBox = QtWidgets.QGroupBox(Dialog)
: E! Y0 v8 g0 X+ Z  E3 \3 z9 ~5 z0 b1 i
        self.groupBox.setGeometry(QtCore.QRect(30, 20, 551, 511))3 y  \1 e+ e, e9 m8 e  u) i6 W
- U+ p+ V5 ?, R! P
        self.groupBox.setObjectName("groupBox")
' o7 t2 n! y/ ?# p" k: ^! }' Y7 _) J) \. I6 x& N0 A
        self.label_2 = QtWidgets.QLabel(self.groupBox)# W- I# f* ~: n/ c
7 M6 y: ?. B, n# i
        self.label_2.setGeometry(QtCore.QRect(20, 30, 31, 16))
, ?; z! f  A, X9 l: k0 s" Q% ~: ?' T6 E4 e# |" ]
        self.label_2.setObjectName("label_2")
( z9 X0 h2 Q1 F# o% M
$ N' o2 V% f2 r        self.comboBox = QtWidgets.QComboBox(self.groupBox)
8 w, Z" J% X8 ^$ D* a
4 [5 d9 i6 O1 M8 v' t        self.comboBox.setGeometry(QtCore.QRect(70, 30, 87, 22))
( W. P- n! u* }* q/ I& X. n, b2 _4 u; H: c0 R( }! Q
        self.comboBox.setObjectName("comboBox")+ j& C  `! H- j- q
+ h( D) S. p7 s) g" P
        self.comboBox.addItem("")
1 Y" C% q7 w% G/ y* m
' E& g1 R4 q& `: Q5 Q        self.comboBox.addItem("")7 W6 j; ?' J8 |- g* X* I

8 M7 R$ ^+ B+ k  ^2 x3 y/ `        self.comboBox.addItem("")
! d/ E) P+ F" ~3 X$ e
. e* b4 T! w/ r/ B) {( k! I. z9 b4 s        self.textEdit = QtWidgets.QTextEdit(self.groupBox)- u0 n; J8 c# ]+ b8 r0 Y

! X7 s/ p9 A" {' {; C$ C% m        self.textEdit.setGeometry(QtCore.QRect(20, 70, 491, 411))
; z5 z0 _/ C4 r, L: {6 ~4 i$ F: N5 y  F5 k; T# M3 h
        self.textEdit.setObjectName("textEdit")0 }6 ~+ ?; f; S" {

; ~- }! G  N" i; v8 T8 [* |# U        self.queryBtn = QtWidgets.QPushButton(Dialog)$ T/ i7 ~# u2 ]5 p; [1 g

" v9 N' a4 j0 ^        self.queryBtn.setGeometry(QtCore.QRect(490, 560, 93, 28))2 d% w, [- w* V+ L$ F8 Z( C
" W& k4 t7 S9 I+ H
        self.queryBtn.setObjectName("queryBtn")
; M1 ]5 c2 e1 |) E9 d: j5 X! u( N; f, E1 X" K0 g+ S' y) @
        self.clearBtn = QtWidgets.QPushButton(Dialog)$ f9 ^+ \2 q7 n+ ~/ `

# T# n2 l3 F; W- V, W        self.clearBtn.setGeometry(QtCore.QRect(30, 560, 93, 28))6 d- |% ], M# O: a, @% Y5 m

4 p& p* |1 p' ^; m7 Z        self.clearBtn.setObjectName("clearBtn")* R$ k* |9 M0 x7 d7 M

. y7 D/ v3 O, b* Q- ^        self.retranslateUi(Dialog)4 `5 t1 B) G; {3 f& F+ J2 n( v

- S$ P' Y0 A+ S2 z        self.clearBtn.clicked.connect(Dialog.clearText)
( {: q* ]! {6 O$ ^) e, V% ]9 ^+ t# n& G+ w; I4 f6 Z
        self.queryBtn.clicked.connect(Dialog.queryWeather)6 s6 U- v2 \2 J1 m' o
2 C! c" D& r3 U' a; s4 F
        QtCore.QMetaObject.connectSlotsByName(Dialog)
. t5 t) Z) @; b- V0 s/ Q$ Q! u7 F( H* l1 J' H3 n& G
    def retranslateUi(self, Dialog):( B8 ?3 s8 J- j7 V* [1 k
% s; M' d6 N# z
        _translate = QtCore.QCoreApplication.translate
/ o0 w5 c# ~+ }4 p& l9 V1 c/ `- l+ X6 m
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
3 [% t: g4 e/ g0 h7 t
! }0 U2 S& z, q$ ?4 Y        self.groupBox.setTitle(_translate("Dialog", "城市天气预报"))' Z; J, q0 `$ P* f: C* n' L8 R' ^# r

' P6 Y4 z$ c3 E0 i1 O$ o        self.label_2.setText(_translate("Dialog", "城市"))7 f2 H' P9 T4 d: g6 h4 e) Y8 ]* q

+ k* g2 O% B5 o; B3 q% T        self.comboBox.setItemText(0, _translate("Dialog", "北京"))
/ K, w% x+ Y$ y  [5 h+ U+ h- u- o2 Y$ B7 [! H/ e
        self.comboBox.setItemText(1, _translate("Dialog", "苏州"))$ ?! w" O! B; _0 _9 l7 n. Z/ ?
- p! W, {5 C" |8 J' L1 d
        self.comboBox.setItemText(2, _translate("Dialog", "上海"))9 v% K5 b8 Y4 T; A4 s$ ]

8 i5 A! `9 _+ ?; [: j2 r        self.queryBtn.setText(_translate("Dialog", "查询"))# P3 s3 L. F9 V# w7 c

: B" {5 w! b% c9 q6 I* j        self.clearBtn.setText(_translate("Dialog", "清空"))1 ~7 ?4 ?4 S+ V6 n- H& E
2 [8 c9 z& n$ z2 A5 l5 s2 x  Y$ x
3、调用MainDialog
) V. g( v4 F2 p9 E
0 w0 e  F* E1 x8 u; J8 f) o( [* k在MainDialog中调用界面类Ui_Dialog,然后在其中中添加查询天气的业务逻辑代码,这样就做到了界面显示和业务逻辑的分离。新增demo.py文件, 在MainDialog类中定义了两个槽函数queryWeather()和clearText(),以便在界面文件Weather.ui中定义的两个按钮(queryBtn 和clearBtn) 触发clicked 信号与这两个槽函数进行绑定。% F4 p: w9 |3 Z' {. e- R

4 O7 `7 D$ R0 S/ _完整代码如下:
& _( J  d; k0 ]' h
$ y+ x- v: ?! o: |0 qimport sys: Y; }/ B" r! z/ C' v- D

% j8 O5 v# P- A4 m' M0 u9 qimport Weather. w- v: P# u/ x+ X
% Z0 n2 V  ?3 Z- \, ?7 l& S. W
from PyQt5.QtWidgets import QApplication, QDialog
, ~8 Y0 ~4 w/ _7 ~
0 V9 }) x& e1 N: a- V/ S' Eimport requests: Y# T3 |7 |. e! Y2 v! F, d; h

. ~! L1 {8 O) dclass MainDialog(QDialog):
7 W9 V. Z! P/ B
5 {! a- A( f; P  R% s' [1 y    def __init__(self, parent=None):" H" k( ~% c; U; j0 ?- A6 q) ~- Q

' w& c+ D& W5 a8 d7 S        super(QDialog, self).__init__(parent)' k3 {3 g0 n# R' w
, m2 x) w2 @- j( N7 E: ?2 A* m& n
        self.ui = Weather.Ui_Dialog()5 X# ^2 s# z8 A

6 w" v. P  ]6 z- i* t        self.ui.setupUi(self)
) w7 s2 `1 P0 M
/ }8 [* `0 q- B5 ^$ l, i0 J    def queryWeather(self):
$ t& I3 p0 K: Y) |8 J. V$ P0 u9 Q) ^5 M, L; D/ F; s* r  \
        cityName = self.ui.comboBox.currentText()& W# W+ J' `& q# T" m
6 v, @+ C$ m- L3 S' x1 U( ~; L0 j
        cityCode = self.getCode(cityName)
/ ~) ?9 k0 p  U0 @% H8 H; o; P+ n* M4 U  _
        r = requests.get(
5 M" T- A4 u9 V7 b$ q+ k0 z" `/ U7 f
5 s8 b0 o& m, D7 c8 C2 ?+ a            "https://restapi.amap.com/v3/weather/weatherInfo?key=f4fd5b287b6d7d51a3c60fee24e42002&city={}".format(0 b5 b6 P' e- E2 M
, B( o$ b* _1 p* f# d( O
                cityCode))
( I5 x4 n3 [4 k6 N& q8 m
: d% M; O- D3 |* A  G% O. A- [        if r.status_code == 200:
8 m) e6 @  j& s) U+ K5 r2 K" _: ^# S# P' E! X; U. B
            data = r.json()['lives'][0]
/ t+ a/ W5 d2 n! `/ B
* ]) @! |3 b& H  C            weatherMsg = '城市:{}\n天气:{}\n温度:{}\n风向:{}\n风力:{}\n湿度:{}\n发布时间:{}\n'.format(
) \9 h, @# k# p* y# V) l
. C/ I. [0 V; }& x+ K                data['city'],
+ E3 M+ L2 g4 K5 K! h- F) k% i1 T* S% ~+ [5 r; B# T
                data['weather'],
1 v% q/ e) l; b5 p4 y- B# O5 v% v5 M6 L. W( {4 V( Z
                data['temperature'],
3 @6 c* P' o, g8 n$ }0 ]
- F6 a' D3 I9 R; \4 Z6 E  X( m4 K                data['winddirection'],* J) {- o4 t' d6 ]5 N8 v  B
2 j( U' u7 r1 y  Y
                data['windpower'],
+ u% l+ Y* ]5 u$ r2 y
4 ]9 y5 _2 R, r6 b8 ^/ y                data['humidity'],
+ H4 c$ H+ L2 g1 h1 l0 l+ G" I3 u# _4 j) B
                data['reporttime'],1 Q/ Y; _/ C3 W/ x

2 r; m; i* b, w; p! l            )* S$ \/ F0 ^% F; L; E: n1 P) A/ _
$ u/ ~& _! m, r7 z2 U9 F" g
        else:; U! F: g$ @2 v; D

' b1 f$ h  `- }- i- i            weatherMsg = '天气查询失败,请稍后再试!'
/ u1 e, L, k( H0 \4 J: e2 S5 ]  T  t
        self.ui.textEdit.setText(weatherMsg)
) P  ~4 I, C/ a/ N! X; U6 q/ D! p' X
    def getCode(self, cityName):
  _' q8 f% W: I7 N: K0 ?- F: _2 k, {8 @0 B
        cityDict = {"北京": "110000",
. [3 Y; R6 x2 ]; c8 J% E
/ K6 b/ ^1 T% {+ u                    "苏州": "320500",
. C+ G' d; x1 m: E. M  K( ^0 f2 ?2 R) i. ~; O+ u7 K2 k
                    "上海": "310000"}  n3 N& c( c% [% Y# Z5 Y
6 L/ A' g- ~" [4 J7 c
        **return** cityDict.get(cityName, '101010100')0 @8 o2 M/ b2 I, `
! a2 W5 M5 i6 f/ g
    def clearText(self):
7 x0 e% {( R. j* v* y: y: K+ `; `3 x' R$ v5 l8 z2 b" |
        self.ui.textEdit.clear()
: n: W* I  L0 B5 ?4 D7 ~+ r* z4 i
if __name__ == '__main__':
( T! N! K0 ]: I  S) B. n) d8 I! d! o
6 \$ L/ N6 |3 M# ~+ s  {    myapp = QApplication(sys.argv): g2 _/ G4 W9 h# ~# C  K" W

- r0 U& y/ L9 P/ G' {+ g8 c; [9 G    myDlg = MainDialog()5 _8 ^: f# n' H7 E

  s) B4 \" p/ N  H2 O, ]9 A" g    myDlg.show()
! \0 R& h2 Z+ S, [* [' h! ^# i) ?: Y* K4 n: Q% z6 |. W$ o
    sys.exit(myapp.exec_())
  b0 L/ S6 e/ o$ T8 M2 ]2 |3 I, p' e+ h: F
运行demo.py并执行查询后的效果:8 ]; x, ?: v. V# z( j
; w7 |9 V: |2 W" @) u9 i9 C

2 i8 W- Q1 t5 c# ~0 R+ c' _0 S$ H3 O
4、将代码打包成exe文件
- h7 R( k1 S1 L3 K% B7 ~' d
+ ^9 k( N) Q# e8 N将.py文件打包成可执行的exe在Python中称为freezing,常用的工具有:PyInstaller, py2exe, cx_Freeze, bbfreze, py2app等。功能对比:' a7 j2 n/ }8 }0 f# U
6 Y" s( l7 |+ T8 W: J9 B

8 s/ I# W3 U+ l. f7 p2 ^2 F1 S# E+ h% x$ y- a
py2exe:软件更新已经不活跃,因此也就略过。
1 a9 F0 ~2 Y; C# I9 B' x: R- i3 \9 D+ F2 V! t+ c
pyinstaller:明确支持win8、win10、理论上支持win7,,支持apple Macos, linux。pyinsaller可以打包成文件夹形式内含exe入口执行文件的形式,也可以是一个单独的exe文件。
; R! Q6 e) {9 u3 u2 D- `% |" Y! \4 Q1 a8 C6 ~0 V
fbs:基于PyInstaller,使用起来更加方便
' U2 ]* k3 k0 {" X, j) C
$ F5 g) z! ]. @8 [* ?1 @, T9 W这里选择了fbs来打包。fbs的安装方法:; H- h* l( y& F7 }4 e

8 f, R) P. Q( L) o8 \5 d. Ypip install fbs
$ o3 o! l, q! p+ j使用方法,在命令行中输入:
6 l! K0 M. f1 Z
$ U3 ]% G' q4 m" g. c8 Z$ u- Pfbs startproject
& ~' C$ u+ Z* o9 \, f9 {: A6 q6 J执行完成后需要输入一些APP的名称等。完成后会生成如下目录:+ O  w" V4 o9 I+ V

# n& k/ k: v& y5 N% r1 s: {
7 a' d. A% `$ S$ T6 |. b
, m2 @$ T! m) }. D5 T5 V将刚才编写的PyQt5的代码(demo.py和Weather.py)拖到src/main/python文件夹下,删除原有的main.py,并将demo.py修改为main.py。然后打开 main.py,在文件头部添加如下代码:
2 S% M% U8 H8 z5 Y
$ J, m, [+ j3 f  Ufrom fbs_runtime.application_context.PyQt5 import ApplicationContext9 u' f! |( ?0 S8 W% _
```
6 i& `! c) P+ e! V4 ^1 i完成后执行:
6 C# }" B7 I. t8 s6 Q, u- D: g4 x```7 ^1 z$ F. z1 {7 K3 E# w4 Z
fbs freeze# A( Z" ^3 X3 U0 l
```
  @- O4 m. F- @! v5 [" w4 {即可实现打包。生成的exe可执行文件在\target\MyApp文件下。+ v% K/ q7 y: S! F- ]7 X
& Z/ D4 ?+ l: N: b; s) `
————————————————
0 z. m) [: j+ M/ M# Z版权声明:本文为CSDN博主「宋宋讲编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
. `& d& \0 p0 t; I, Q% X原文链接:https://blog.csdn.net/qiqi1220/article/details/126289667
8 ?! f# F; Y# U/ w1 O1 g
8 Y% Y' D" f) U5 y$ H) g- L  d. B7 g5 h# a

/ `& G8 ]8 n3 P: P




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5