数学建模社区-数学中国

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

作者: 杨利霞    时间: 2022-9-15 12:26
标题: 超全!Python图形界面框架PyQt5使用指南!
超全!Python图形界面框架PyQt5使用指南!' Q6 D. V9 G: @* l7 W; `- b
  F# n! w( R) d) w
使用Python开发图形界面的软件其实并不多,相对于GUI界面,可能Web方式的应用更受人欢迎。但对于像我一样对其他编程语言比如C#或WPF并不熟悉的人来说,未必不是一个好的工具。) b7 @% M. f: P! n* w- a. p1 F
& p+ C' w  |' x  d$ k
常见GUI框架
6 M) h( x+ a: v; `
2 u& h0 Z. `9 v" G! v2 rPyQt5:Qt是一个跨平台的 C++图形用户界面库。QT一度被诺基亚拥,后出售给芬兰的软件公司Digia Oyj。PyQt5是基于Digia公司Qt5的Python接口,由一组Python模块构成。PyQt5本身拥有超过620个类和6000函数及方法。在可以运行于多个平台,包括:Unix, Windows, and Mac OS。
: \0 ]- {0 M4 [
7 f; c9 N) g; K3 s& _0 ]Pyside6:Pyside是QT公司官方提供的Python包,上一版本为Pyside2,对应的是QT5,最新版命名规则进行了调整,更改为Pyside6,对应的是QT6版本。由于官方出品的比较看好,缺点是发布比较晚,网上的资料没有PyQt5多。
% L( B( _5 |) V! A
# u% \, F4 a' j0 W; ^  dTkinter:Python内置的GUI框架,使用TCL实现,Python中内嵌了TCL解释器,使用它的时候不用安装额外的扩展包,直接import,跨平台。不足之处在于UI布局全靠代码实现,只有15种常用部件,显示效果简陋。
# `" b$ G1 M/ p; p4 [2 ~& e" e1 F- Q0 o+ K2 n1 u% b
PySimpleGUI:PySimpleGUI 是 Tkinter 一层包装。使用 PySimpleGUI 实现自定义 GUI 所需的代码量要比使用 Tkinter 直接编写相同的 GUI 要少得多。
: s3 C9 x" w2 C1 J$ @9 T) @7 q. l" ], ~2 C3 ~# Y2 g* S
WxPython:wxPython是Python语言对流行的wxWidgets跨平台GUI工具库的绑定。用得比较广泛,跨平台,C++编写,文档少,用户可能就需要根据编程内容对不同平台中的GUI代码做一些调整。遇到问题不好解决,代码布局控件,不直观。
8 Q9 w5 K0 |6 v# q. l9 m  J) f! n0 `. A  A+ B) x4 P
Wax:基于wxPython ,为克服wxPython的问题而制作的一个包。
$ L6 {% g7 A) k1 C, N2 y7 V4 D5 G
3 q! K$ l+ H' \: D; }& fKivy:主要针对多点触控程序,智能手机平板等,也可以在没有触屏功能的系统上,全平台支持(Windows, Linux, Mac OS X, Android and iOS.)使用Python和cython编写,中文支持差,需要自己下载中文库并且制定路径。6 S) ~* v( ], @4 ]7 }

, n/ S' a) Q; }3 H7 M& p, R6 p' jBeeWare:Write once. Deploy everywhere.需要与Kivy配合使用。
" u/ t9 w8 r" h8 c7 f) t- K$ a' v  `( l; L+ D1 b5 E" b
Toga:一个使用Python开发原生APP的GUI工具包。Toga由一个具有共享接口的基础组件库组成,以简化与平台无关的GUI开发。Toga适用于Mac OS、Windows、Linux(GTK)以及Android和iOS等移动平台。
5 m" i! J. J, x. X" G
6 P- p, m  M  k+ yEel:一个轻量的 Python 库,用于制作简单的类似于 Electron(但是比它更轻量) 的离线 HTML/JS GUI 应用程序,并具有对 Python 功能(capabilities)和库的完全访问权限。
* g2 J! h# d3 w. d$ j, C( p7 r
* X& R  G, m* S' ]5 y- C: t+ B6 mFlexx:一个纯 Python 工具包,用来创建图形化界面应用程序。其使用 Web 技术进行界面的渲染。你可以用 Flexx 来创建桌面应用,同时也可以导出一个应用到独立的 HTML 文档。因为使用纯 Python 开发,所以 Flexx 是跨平台的。只需要有 Python 和浏览器就可以运行。; ~& y8 e8 C& n9 K+ Z

5 y  O6 F' R5 M$ A2 Ppywebview是围绕 webview 组件的轻量型跨平台包装器(wrapper),它允许在其自己的本机 GUI 窗口中显示 HTML 内容。它使您可以在桌面应用程序中使用 Web 技术,同时尽最大可能隐藏使用浏览器构建GUI的事实。6 {4 ~: K% M$ ~) H" n# X
, E! A5 y2 ~* y- }% ]
enaml:一种能够让你用最小的努力就可以实现高质量GUI界面的的Python框架,也是一种独特的编程语言。enaml将声明性语言与基于约束的布局系统结合在一起,使用户可以轻松地定义灵活布局的UI。enaml应用程序可以在任何支持Python和Qt的平台上运行。
' B$ `2 P3 [6 R
' v# r$ D+ c6 q6 V' `个人想法:太多学不完,先学PyQt5,原因是资料多,学有余力再学pyside6,最后看下PySimpleGUI,看能否解决一些简单问题。
, [6 i8 P* N( p8 d8 K8 y: L7 g2 E1 j" u' a3 D; g- Y
PyQt5简介- W! y. L2 G( N7 R

3 N) _$ a  }- n5 a% X( t. ]PyQt是Qt框架的Python语言实现,由Riverbank Computing开发,是最强大的GUI库之一。PyQt提供了一个设计良好的窗口控件集合,每一个PyQt控件都对应一个Qt控件,因此PyQt的API接口与Qt的API接口很接近,但PyQt不再使用QMake系统和Q_OBJECT宏。/ j7 k. [! @& s; A" O4 h9 y1 D

7 k. X5 f* @. }' Q" K* I6 R! FPyQt5提供GPL版和商业版证书,自由开发者可以使用免费的GPL许可,如果需要将PyQt用于商业应用,则必须购买商业许可。
+ Z2 T0 Y0 Q' x. D8 D
' t) r9 ^% D* W: ]9 sPyQt5特性如下:6 l. m6 R; m  ~; s7 B) L1 D* F
8 L4 Z* [% |' n# `; ^2 H! [3 E5 s
基于高性能的Qt的GUI控件集。9 n4 R" L+ e7 I4 j4 l) F

7 o! |1 J" S4 Y' D5 a6 z能够跨平台运行在Linux、Window和Mac OS系统上。
+ D7 t( O0 s9 _1 }. b3 }/ I/ u$ n
5 {* T- f; [4 a5 [  ^使用信号槽机制进行通信。
" ]3 C" G; Q( I4 v
0 Q( m0 l5 _" b1 {0 H. a对Qt库进行完全封装。% m( q- b7 A7 \, q) z  v
1 K. X( f% a6 d
可以使用成熟的IDE进行界面设计,并自动生成可执行的Python代码。
) _9 D$ w: I7 C! t; `4 s0 W# |5 S" |9 E5 A! y7 G. a
提供一整套种类齐全的窗口控件。
5 q- H# V: k4 D" T3 `
  G) t7 G( `9 R4 k" ~) g  EPyQt5是由一系列Python模块组成,有超过620个类,6000个函数和方法,主要模块如下:
! C  c2 X% h% K: g. V8 @& o
% K/ o4 k$ \7 f3 Q6 _' c5 kQtCore:包含了核心的非 GUI 的功能。主要和时间、文件与文件夹、各种数据、流、URLs、mime 类文件、进程与线程一起使用。9 E1 a# S" W1 R
+ G9 h% K9 W" {! h3 r" `: e
QtGui:包含了窗口系统、事件处理、2D 图像、基本绘画、字体和文字类。( y0 R6 c2 Y2 T6 ^

  R+ w$ Q( J; N; \7 yQtWidgets:包含了一系列创建桌面应用的 UI 元素。
1 A$ D: D, z' `1 R4 ~7 p) v
. b* ^% n: E* B- }* R8 S* R8 jQtMultimedia:包含了处理多媒体的内容和调用摄像头 API 的类。
" D$ E, c6 m- X. ~
5 P+ b8 |$ Q6 g5 t/ eQtBluetooth:包含了查找和连接蓝牙的类。
2 W; b  }! A$ ]. `# j$ Z' ?3 V4 j$ i; R: H& X) H% ]
QtNetwork:包含了网络编程的类,这些工具能让 TCP/IP 和 UDP 开发变得更加方便和可靠。
# A0 ^" ~3 V# M- o( Q! U4 O/ c
2 F8 T. R0 }2 }0 [# x5 }4 |+ LQtPositioning:包含了定位的类,可以使用卫星、WiFi 甚至文本。0 h! b" r2 X6 g; Y

0 b+ E8 l8 W  ~  UEnginio:包含了通过客户端进入和管理 Qt Cloud 的类。
+ u; V& E" c2 u3 S5 I6 \) f, J7 u0 v8 L2 `, x3 R* Y, H; W
QtWebSockets:包含了 WebSocket 协议的类。
6 J) {; ]7 G3 k8 ^
9 b# \3 h. c- `$ _; pQtWebKit:包含了一个基 WebKit2 的 web 浏览器。* S# W' m  d% f0 I

& m0 o) Z2 y% ?- p- y, cQtWebKitWidgets:包含了基于 QtWidgets 的 WebKit1 的类。3 f5 P4 V/ Z  T% I: B9 c  s! F3 _
, X( l/ {) x0 Q
QtXml:包含了处理 xml 的类,提供了 SAX 和 DOM API 的工具。
$ c( N2 A. v% p/ s+ U9 ]% W, m
; g6 U& M0 U) n$ x, l8 lQtSvg:提供了显示 SVG 内容的类,Scalable Vector Graphics (SVG) 是一种是一种基于可扩展标记语言 (XML),用于描述二维矢量图形的图形格式(这句话来自于维基百科)。
/ l; P0 k: H) `, I6 N! T9 E' f, O0 W0 @# {$ j+ V
QtSql:提供了处理数据库的工具。# x) x/ P- ]& T' [4 A1 C/ p

. _3 L& h3 x5 j$ lQtTest:提供了测试 PyQt5 应用的工具。# K, b; a8 W8 n

7 T# V  x7 b) R! s0 n7 Z3 t3 FPyQt5的安装: j2 F: c6 D6 q: l. M  {. C
+ N  @& B( w  i: _' O( B- n; t3 ]5 b
由于后期要使用fbs进行打包,fbs对Python 3.7以后的版本可能存在兼容问题,所以我选择了Python 3.6.8进行了整个环境的搭建。主要内容为:Python + PyCharm + PyQt5 & C1 g! e& Q' z

0 T' @' ]/ n5 L6 F6 w2 _. Z安装PyQt5
) E) _% `6 T( b* _" \9 l" O# n9 ~: `- |+ t& E- {
pip install pyqt5
) X  R9 a5 [3 C( H, u& \' u: k( F1 O8 j! H5 J2 u
pip install pyqt5-tools4 l+ c& t4 l6 A5 b
其中pyqt5-tools为Qt Designer拖拽式的界面设计工具。安装过程中可能会报如下错误:8 R4 {: S6 @0 N5 P
0 C3 q, I, M# ~; F8 O% ^' q
qt5-tools 5.15.2.1.2 has requirement click~=7.0, but you'll have click 8.0.1 which is incompatible.
0 C- X$ v; T% N, {* T4 G4 S! `5 |解决方案:- a( C6 s) c, z) d" X

/ |4 r2 x. ^; g* n3 Q( fpip install click~=7.0
# J) ?2 F' p& S6 }" J: @Qt Designer的配置
6 L  Q$ }4 d+ |7 `+ F  N  a' Z
0 {+ `# O  S, k+ i% j6 kQt Designer 是通过拖拽的方式放置控件,并实时查看控件效果进行快速UI设计。( V% W: \% G6 m3 n
  v# B% w( D6 y6 v. A2 H' a* W: ?

* J& p* q+ c8 s. S2 t5 z4 v3 j: w; z9 z
整个画面的构成:/ u, Q( ]2 `3 [2 f7 G. s9 g( m

) r6 U! ^% u% {4 V7 |" [$ S! z, Z左侧的“Widget Box”就是各种可以自由拖动的组件
5 {1 E; Y# A6 D" z; S8 F% O9 u8 l5 u9 K
中间的“MainWindow – untitled”窗体就是画布1 }# z( v, L# @

1 i4 p3 D# x* J% _0 a右上方的”Object Inspector”可以查看当前ui的结构
1 K* v/ N" M! h) X2 e+ \  M5 t6 i3 n1 E: ]1 M% Y; e& u# u
右侧中部的”Property Editor”可以设置当前选中组件的属性
. ^: x+ p! [/ I/ m1 _& {- \- Q% A4 j- L2 ]
右下方的”Resource Browser”可以添加各种素材,比如图片,背景等等2 j% l! L, B( ?2 @4 w7 u: {

) s% Y# {8 a, x& [2 w最终生成.ui文件(实质上是XML格式的文件),可直接使用,也可以通过pyuic5工具转换成.py文件。
; E7 d1 }3 O, y+ x5 D8 M$ ?( Y* A. j
QtDisigner配置
) e; ]$ x9 b. r( x
0 _* G$ a: r! H: V7 B, }. @在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:
" V7 j8 x( l2 V- Y
+ }6 {* q0 c2 fName: QtDisigner
9 [$ x, T: e6 R4 w7 C" B+ X
3 t/ V. I5 c4 `. ?% P: z' b8 L1 LProgram : D:\Program Files\Python36\Lib\site-packages\qt5_applications\Qt\bin\designer.exe # 请根据实际修改* M$ E+ {; z9 R  e$ e1 Y
( N8 ~# P% P8 `( P( A
Working directory: $FileDir$. y5 F" h' c. i; U: _" A+ |
PyUIC配置, W; b* }% V+ ~' i. D6 z

& o; s$ {! O9 x* F8 J) GPyUIC主要是把Qt Designer生成的.ui文件换成.py文件。; q! W' H  p' \9 c2 Y

) v+ `! z, y  U! U$ O; e, [在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:
6 r+ p* v! _7 Y
/ f$ ]0 O  `* |" ?. v3 i3 GName: PyUIC
! V4 }4 `: w1 R9 C5 Q
4 |3 }5 a+ y9 ?+ b6 D0 ^Program : D:\Program Files\Python36\python.exe # 当前Python目录,请根据实际修改) }( `  a: O1 Y, o5 t

0 M1 b8 t+ b, ?/ P. sArguments: -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py$ t9 U. |& Q" H

) y2 m2 V: d* c2 U( c- q, yWorking directory: $FileDir$( n' ^2 I/ L: E0 v+ @
PyRCC配置" n+ h" B% Y3 Z
" q6 D6 J3 K- m! y/ V% I' y
PyRCC主要是把编写的.qrc资源文件换成.py文件。在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:
7 _4 M5 f+ ?3 \, J& U; O7 U& W0 P( P9 M7 W+ h5 A7 \, L
Name: PyRCC
' W0 Z# `4 ?1 T1 V/ [: Z+ A+ G
3 [# G& N: |+ cProgram: D:\Program Files\Python36\pyrcc5.exe # 当前rcc工具目录,请根据实际修改
! y& g  ]9 S0 v' Q9 u
+ f3 C+ m/ {) Q5 C$ iArguments: $FileName$ -o $FileNameWithoutExtension$_rc.py' I. Q; [4 M" `/ T
, Z+ v! C* E$ }
Working directory: $FileDir$
3 c1 j  Y/ ^4 t0 D7 DPyQt5使用示例" |: x: u/ P6 d% b- v3 `* T2 K
! M- h) ~5 P6 Y; z
创建一个空白的界面:
2 @- Y$ @1 S. D1 `* t: F% I0 i9 L$ N! r( t# e& D& @8 A* A- E
import sys" }7 ?/ Y+ O: f% x1 j& ]4 ^" ?, s
) W$ k9 M, [. z# p4 ]! P" e
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel' w# f' P  |3 z

; B$ [( y# V/ h  |7 ~app = QApplication(sys.argv)) N; R' X3 T; x; l! f$ a% E
& x) I) W# c% X7 J0 i/ F7 E
win = QMainWindow(): {' J6 w9 c& c! T" w; a8 t  g

, U7 ~6 E. t2 |win.setGeometry(400, 400, 400, 300)6 K3 {3 w4 k" Q% z( i6 p

! Q. P' \7 Y- i; H, I; s8 V" cwin.setWindowTitle("Pyqt5 Tutorial")
) }' l. i# v# Z' {  \! T3 N  B
7 L2 ?: p5 ^* F2 Bwin.show()1 H. C9 r+ H" J- |, N2 y% T
# d' x+ n, i) Z3 M3 k! H) X* K  y
sys.exit(app.exec_())+ y/ _( s; x2 _, {6 z
5 E5 w2 `+ I- {2 t3 I
' Y( t+ @9 n9 _) P, c* D' j

" y4 W* J* ^/ Z1 t4 P# V其中:* r, e2 W: |5 J4 c7 f; _
: b" T+ W' ~! \$ f2 ^% ~# R
Qapplication():每个GUI都必须包含一个Qapplication,argv表示获取命令行参数,如果不用获取,则可以使用[]代替。
/ Z% w! f5 l/ X7 T' G" ?( \8 `% B( |4 b! ~. a+ u$ v' n4 T
QMainWindow():类似一个容器(窗口)用来包含按钮、文本、输入框等widgets。arg标识可以获取命令行执行时的参数。
/ k% K, p8 U' t( N+ k1 w  w
9 i; l; ?5 }+ @5 m  Q7 L, \SetGeometry是用来定义 QMainWindow() 窗口的尺寸, 语法:setGeometry(x, y, width, height ),其中x,y为屏幕上的坐标点。8 i; p7 o- e: j, |: [+ A; {

& W1 {9 l, Z0 f1 Xshow():用来显示窗口3 M$ q- S2 J$ }7 X% C+ g

  s8 y* {3 d6 _exit(app.exec_()):设置窗口一直运行指导使用关闭按钮进行关闭  ?# K* j- p+ h
, N8 u0 L7 {7 R4 X: o  G
PyQt5支持的常见Widgets有:+ e4 s( M) N. _5 c4 [% @: P5 w

5 z# s3 A. C# d7 Z: Q9 F! N$ t' ~* a* A8 O

- F2 P  c" D7 H从上到下,从左到右依次为:Qlabel、QcomboBox、QcheckBox、QradioButton、QpushButton、QtableWidget、QlineEdit、Qslider、QProgressBar1 z$ p2 m' w% U. Z4 c8 T. l
# C: x8 E; O/ {, {5 {
对于使用Pyqt5设置文本内容,我们使用Qlabel:
: @5 w+ q0 {7 H- p6 e0 U% P( B. B. ]; ?& p* w4 V3 a- ^
import sys5 ^% ?) j5 i2 n7 B4 n6 N9 u% K; r

& ~* V0 j! v! k7 j( wfrom PyQt5.QtWidgets import QApplication, QMainWindow, QLabel) k' M$ x0 X9 T; T
- S  h% l& u6 j0 g
app = QApplication(sys.argv)
5 m7 `8 p2 B% z, Z
+ _* @# A3 S# {win = QMainWindow()4 `* W+ v$ G4 e. e0 I- A

& U' C2 S/ \. C5 Qwin.setGeometry(400, 400, 400, 300)
6 o" E% t' J1 _
4 X# R: C6 h; f6 U# wwin.setWindowTitle("Pyqt5 Tutorial")
2 W' Z, S) |/ x- I% A$ w& L; G7 P( I; D# Z6 _
\# Label Text
& @2 T7 Z9 Q! E% B( y8 I  O* ?" Q' s9 P* D2 F& F7 r; S% b
label = QLabel(win)* o7 z2 E( C8 _" I; E$ U
% P9 w* p( X8 _8 k3 b' N) x
label.resize(200, 100)  s6 C# G( a' N- I& r  w3 ]& T

1 I) r2 Y+ K) _6 X5 ?+ h* Alabel.setText("Hi this is Pyqt5")5 ^; B5 _( @; B  ]1 P  R
; {* @5 c, w; Y! a( `$ u& p/ c% |/ }
label.move(100, 100)* p& }( w1 ?8 {& ?0 T3 j* D) Y5 [
8 _8 r" P0 [& z, J8 T3 m* t: ]
win.show()
" _/ ~1 g9 |- ]+ E+ K: C
' m6 T4 `0 r  L  _# w' V3 {sys.exit(app.exec_()), n4 a/ `6 j3 L2 v, Q# g/ b
$ O) h" A0 E0 g

- C( [+ I4 f; L; S* \, }0 @
& O$ Y: D3 ^: `2 P按钮与事件:  A' E4 D: |! r5 M! A; V

/ d7 S; d; a2 f/ f) M$ I! z/ qimport sys
) R$ f- U& J- ]7 \; z# A1 F) |; Z% p' n5 z( O* `. o' U
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton: f+ |3 }" }4 ?4 g

5 z2 H* b% \( y( [! h. q5 I( c  Idef click():$ j& A3 A7 ?- @3 W- m0 k

! z# I+ v, G" `( k; l% D    print("Hy Button is clicked!")
2 C* ^, [7 O" N% X0 x
0 m3 j* C# D4 x; [app = QApplication(sys.argv); A+ I6 t9 a. J8 n' x! s0 z

* Y5 x  s  I$ @win = QMainWindow()
# q8 J$ n( A' z; V9 y2 r# E- Z' g
: @1 X1 K6 N0 a- hwin.setGeometry(400, 400, 400, 300)+ W5 b0 c+ a& [9 i
' T: F' h/ |$ P- g0 O7 y# R+ z( [
win.setWindowTitle("Pyqt5 Tutorial")
: p+ ^5 p6 Z, z
0 ?( C: p- `, J+ r0 O\# Button  }( k# X) h( B5 V' N

$ @  t6 g/ x& `5 Mbutton = QPushButton(win)
, j) f, I; H1 ?5 o0 E5 i/ N1 ~) w$ A7 f& B: S8 H
button.resize(200, 100)) i7 K7 Y# s' g0 b" X- W
5 e/ ?+ ~  _( `0 G
button.setText("Hi! Click Me")+ o( `/ C4 h, h4 `! b. i, L' h

" Y8 Y: h0 ], y: ]button.move(100, 100)- s9 n8 G) @9 V

2 i, A6 \2 o5 [8 Wbutton.clicked.connect(click)5 p6 u1 x) E# a$ z
( M  q: a* @. G$ s! p/ ^* p
win.show()2 t0 j8 O; Z  A7 x7 L% g6 Q' j/ L! {

/ l" l5 z- M0 \4 vsys.exit(app.exec_())) [2 A* u: L" z- w) ?- C( G' U! X

! |: Z. h7 p' _1 g, y6 o  R$ I. W
2 D! c8 r+ d& X# n4 G  N
+ e& G1 U4 }7 U2 bbutton.clicked.connect() 在按钮点击后执行特定的事件。
; Z$ I; |/ ~. A- J" R) b# J6 O. @9 W  `% X+ y7 I( a
PyQt5实战
, I  T) C. k/ K" X9 X0 h6 d# k$ M/ W1 U3 P1 L  _
实战项目:简易的天气查询软件; f( e) O; f/ X/ D2 @. B" [6 n
' O% E: d) W: N% N2 f8 ~
1、使用Qt Designer设计一个界面
7 b) j( R9 U8 m2 F" T
8 Y# `" K1 S$ U
3 A+ |$ T' h- A5 q5 V: i3 ~! o" f( H6 d. ~; W
用到的控件有Button, GroupBox, Label,ComboBox,TextEdit,同时定义了两个按钮queryBtn及clearBtn,分别用来查询及清空天气数据。我们需要绑定槽函数,方法如下:
' ^! K# b1 s& l* M% s0 a1 {+ ~. @' h, y; b0 @2 |; h% C: n
在Qt Designer右下角选择 信号/槽编辑器,点击+号新增3 D+ c( S0 h" T) i# v; i: g

& {2 E4 X& e9 ~  R- A分别选择queryBtn及clearBtn,选择信号 clicked(), 接收者 Dialog 及槽 accept(),(槽函数这里不知道如何定义,后期在代码里再进行修改)
, p/ N4 A" v# d& B1 c' p. s
% s3 E7 z5 x6 J; f* M+ F8 Y以上完成后保存为Weather.ui文件。
- g/ ]' z2 I. v4 b. b$ f5 g, u$ s
8 Y9 }. t$ i% O: Y5 L2、转换.ui文件为.py文件9 ~3 \0 n5 A/ W+ o6 j

4 Z" f# C5 p# G' k7 z$ f/ _PyQt5支持直接使用.ui文件:
2 Q1 ]2 R# ]. {0 }! j1 ?# A( y+ D% W4 C- U6 V0 O( [
import sys' ]' x) S" ~/ X( o5 h4 Q

  [" d+ K5 C& p5 Jfrom PyQt5 import QtWidgets, uic
# Q$ m# [( {3 o: C& L5 P
+ X5 J( O: y" L. t' O/ a3 Xapp = QtWidgets.QApplication(sys.argv)3 Y  Z" ]  U4 c$ F
) C% [, V7 C5 f7 L( ]4 H  \
window = uic.loadUi("mainwindow.ui")  U) G9 L5 v( V8 T9 R7 C

# K: h6 M) C4 Q7 @; w' k# Kwindow.show()
$ m5 C. J# p0 f
6 q9 k1 Q% m# m, Wapp.exec()( f) _% H5 F, _5 M& e. G
但是为了更好的自定义及修改上面的槽函数,可以使用External Tools – PyUIC,即可生成Weather.py,实际运行命令如下:
0 W$ I  w. I7 |- R# I1 F' J. t1 E( V) k; W0 K+ r
D:\Program Files\Python36\python.exe -m PyQt5.uic.pyuic Weather.ui -o Weather.py
, }3 I2 ?' c1 ~0 X8 A其中,我们需要把两个按钮绑定的槽函数:! \& E# d' s4 B! y

+ X, I: d* j' a( V. O1 @0 r2 \& }2 T\# self.queryBtn.clicked.connect(Dialog.accept)- _7 X' }( t  o# a! Y  i. x
  @2 z. c7 ~, t% }2 l9 @$ D; Q
\# self.clearBtn.clicked.connect(Dialog.accept)
  N% t4 u8 L, V" I' F
+ l1 {/ d3 p; @3 @3 k\# 修改为:) C: ?1 k$ ~2 o7 k

% Z0 G9 |  `& o/ u) ]5 Rself.queryBtn.clicked.connect(Dialog.queryWeather)' m2 K+ N7 M" \
: j9 Y3 h1 Q: z2 F8 h
self.clearBtn.clicked.connect(Dialog.clearText)
. h0 q' e% }: w, S最终的Weather.py内容如下:6 y  ~: v3 U, m  T; M* n
8 g5 q9 |6 G& }6 C; L6 ]) q& F
\# -*- coding: utf-8 -*-
& R% B! D" X3 R# q, V9 w
4 V  W, d( z4 i+ c/ x! z& [\# Form implementation generated from reading ui file 'Weather.ui'7 X, x+ Z& V8 q+ }5 Z" |8 v
: K5 `% P1 y" Z  q
\#0 c' f( |  ^# {) A" O0 v

/ h3 q: W/ F4 p6 u7 w\# Created by: PyQt5 UI code generator 5.15.4
8 x, T0 @  l2 I" x5 s$ K% B; M; H( J9 F9 h1 }
\#1 S* i2 t' L$ e' K  s
$ u' o5 w9 L/ G- T' q- E
\# WARNING: Any manual changes made to this file will be lost when pyuic5 is
, I( y: }& t) v7 h  l
4 i/ X# u0 T& U4 J/ K5 y! d% M\# run again.  Do not edit this file unless you know what you are doing.
, l5 e) N! @( q/ R0 R1 P% }1 i- a
7 o9 I1 a- v5 P% Jfrom PyQt5 import QtCore, QtGui, QtWidgets
" u0 b. U8 J: V& s  X4 o# Y0 I! }: B0 p' Q2 K+ x
class Ui_Dialog(object):( {( v) T: p/ E) A3 R, X
+ y/ G) ?* Q' S4 g. o
    def setupUi(self, Dialog):
5 c$ R7 _( z' j  [0 j2 Y& X; ~3 f- y& M' M2 B
        Dialog.setObjectName("Dialog")/ z" l1 T( ~- i$ H
* {6 w/ p/ B2 l+ O- |+ M& z
        Dialog.resize(600, 600)
% l1 w& u4 K+ l" p: e" Y, U, F9 s% f% W
        self.groupBox = QtWidgets.QGroupBox(Dialog)+ x9 T# T2 E7 X( r; Y$ x
8 d& O$ x0 C* ?: s2 l) I
        self.groupBox.setGeometry(QtCore.QRect(30, 20, 551, 511))
8 J4 b1 w5 j% c
6 T7 p- K2 K" ]5 ^- R5 Q: p# u; O. r        self.groupBox.setObjectName("groupBox")
' T- w9 d0 w" U: |& ]7 W6 U1 w* _# u+ F% M( m2 \9 x. u1 O' I1 {. @
        self.label_2 = QtWidgets.QLabel(self.groupBox)& @( T6 s9 {! x- R9 h
+ o0 `  n* T6 X/ x& p; p' V
        self.label_2.setGeometry(QtCore.QRect(20, 30, 31, 16))
( I$ S! Q3 i2 D5 E. w
. v. ^3 S) U8 q# u8 j+ r8 z( Z        self.label_2.setObjectName("label_2")
, F+ [; d3 }# W$ m" ?, S8 J  }$ @( N. |  f; h2 P1 E+ A/ v
        self.comboBox = QtWidgets.QComboBox(self.groupBox)
! ~/ c, Y( O7 `5 A
/ N: F& A% x2 h3 Q        self.comboBox.setGeometry(QtCore.QRect(70, 30, 87, 22))7 }4 g! a" G4 N5 ?
, }1 p7 j8 P9 K4 b6 G/ C
        self.comboBox.setObjectName("comboBox")
8 Z4 P6 _% |4 `0 y3 v0 L( y' n1 v; q/ s: R
        self.comboBox.addItem("")8 g7 F: j# d" e+ h/ w: H

" a' R9 X7 k- p* H        self.comboBox.addItem("")
! O  c$ w# Z& M& }7 n6 _
  F8 u  D0 I$ w$ e% \: ^        self.comboBox.addItem("")$ q; X$ q: k! T) K

. @" \7 J- R7 q/ h        self.textEdit = QtWidgets.QTextEdit(self.groupBox)
1 v6 R. _4 K$ s
0 l6 A+ A4 D# v3 F0 b        self.textEdit.setGeometry(QtCore.QRect(20, 70, 491, 411))5 E6 X  V, \3 \1 M1 n4 |1 ]% E

3 m6 ^* B8 I3 |! @) Q5 g5 I3 g        self.textEdit.setObjectName("textEdit"): u3 b- t. O  [' D# Z- i, n
* @2 H* ]  Y+ g, u# J
        self.queryBtn = QtWidgets.QPushButton(Dialog)
. R1 Z7 J1 s) r& t& z/ Y3 s9 I* @' e# w/ i" x% U* F) G/ r
        self.queryBtn.setGeometry(QtCore.QRect(490, 560, 93, 28))4 _+ Q$ h0 D, B, e1 L+ T
! a! S' c9 A0 P$ d' T
        self.queryBtn.setObjectName("queryBtn")
& y1 U  a$ N6 k
" u7 k$ A2 }6 W2 x( n        self.clearBtn = QtWidgets.QPushButton(Dialog)
2 M7 j/ n% m  \. U1 B" U( d- T! L! |0 }) w& g
        self.clearBtn.setGeometry(QtCore.QRect(30, 560, 93, 28))' L( t7 Y1 }5 F

( o6 T. Q" z" g. b+ b        self.clearBtn.setObjectName("clearBtn")9 _( p6 [" z# ~4 F
( U; K/ T2 W# C2 m( K& \
        self.retranslateUi(Dialog); e& t* J( U. {5 Y3 a& ]
8 }. U, `5 i; u. b: `) R
        self.clearBtn.clicked.connect(Dialog.clearText)! G5 k2 Z% s! j. o
# P2 K% V4 m0 k2 F; Y& `" f/ D
        self.queryBtn.clicked.connect(Dialog.queryWeather)6 ~* y! i+ c/ w: o) Q  b5 r
3 k8 |# d. {$ a& u  J3 q( _. `. t
        QtCore.QMetaObject.connectSlotsByName(Dialog)( b9 o5 y% e% ]) e& N9 B8 ~4 K7 A' n
9 E4 A1 O( P' {1 ~9 k0 M& e( ]
    def retranslateUi(self, Dialog):
! w5 {& `, j% \  u# t6 j5 t1 K1 U1 |# x: M+ N: C2 ^3 j7 L
        _translate = QtCore.QCoreApplication.translate: M$ ]: S, Z: C5 O+ K6 w3 f0 {

* t( e4 ^* n: j$ `1 ?! S0 h        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))" u9 y+ S( b. n' r

2 m: J( M5 {+ R: ?2 O4 J" b- M        self.groupBox.setTitle(_translate("Dialog", "城市天气预报"))2 B5 h+ m9 V) w6 c& o! |! y
& |" B- C( G/ d& Z: r. X, }
        self.label_2.setText(_translate("Dialog", "城市"))$ D$ F2 M! y( K2 {6 V/ Z$ \
; g+ e% b2 j5 ]* P* e  ?$ A7 h& L
        self.comboBox.setItemText(0, _translate("Dialog", "北京"))
. B: y: r. b' g, {6 T: \* D5 S6 q  }* ]0 A! M9 A
        self.comboBox.setItemText(1, _translate("Dialog", "苏州")): s/ F* `* }1 j6 P5 j1 x) C+ _$ M
! F$ c8 g  V' ?+ b4 ?+ k
        self.comboBox.setItemText(2, _translate("Dialog", "上海"))
* P+ ]$ Q" t' }6 c3 e9 D( E5 R8 W3 d9 Q- x; a+ o5 r
        self.queryBtn.setText(_translate("Dialog", "查询"))9 f- Q! r- h+ r; J

" E" ^/ h' k8 r5 [/ x: f' ?        self.clearBtn.setText(_translate("Dialog", "清空"))
  Q/ U" S/ ~2 O3 Y2 M
( i; T) }2 d4 c( }+ x! J1 d3、调用MainDialog
# x! o. b6 U+ D6 B5 o
; I" X, T- V; S4 V9 o, i在MainDialog中调用界面类Ui_Dialog,然后在其中中添加查询天气的业务逻辑代码,这样就做到了界面显示和业务逻辑的分离。新增demo.py文件, 在MainDialog类中定义了两个槽函数queryWeather()和clearText(),以便在界面文件Weather.ui中定义的两个按钮(queryBtn 和clearBtn) 触发clicked 信号与这两个槽函数进行绑定。. p" |1 F, h& Z4 c$ A- Y

# Y8 \$ P7 J$ E! q完整代码如下:* F+ r5 `! J7 [- K( r3 r

5 d7 d+ Z, h' Pimport sys7 B( E' {1 s& N5 ~
! m/ y6 Y6 O. W, K( n2 ^7 W
import Weather
" |/ T" i: ?0 n$ y7 P# _+ o0 ^
9 f' Y' @+ B# h7 u2 L/ G1 wfrom PyQt5.QtWidgets import QApplication, QDialog( W2 x8 p& \+ ]. ^3 G6 h
0 }0 o8 x5 s8 X4 K& m
import requests* t) _, ~+ y! k! [
# |0 @. f/ P9 e' b% Y7 g
class MainDialog(QDialog):
2 u& t: c8 p0 |" h6 L( ^
/ m% d) y3 {' K' z# C( h! ~    def __init__(self, parent=None):
: d  }& W1 C& p4 I  _+ I
: ^2 v5 ?, ]9 W: S  o. J        super(QDialog, self).__init__(parent)
4 v  \3 O8 O4 {4 z
$ f2 r' @% p9 Y6 R        self.ui = Weather.Ui_Dialog()* w6 _' d4 v: V, [! T" c) k

& a+ p+ Y" W2 e/ J- `7 {        self.ui.setupUi(self)+ i/ {& Q) D  e5 Z# O+ }4 L) J
) Q  k% x1 _( d% Y
    def queryWeather(self):) @0 v* W3 c3 O5 o) q
: U0 D* P9 j* t% A3 ?5 D
        cityName = self.ui.comboBox.currentText()
9 o2 ^: W$ f" a+ p( S0 o8 w) P
3 P# K$ R1 a% m, M" `1 h' C. r        cityCode = self.getCode(cityName)
: k( I. e# D" r4 F. N
! i% e. C! Q2 t9 {8 _- a6 o! h* `        r = requests.get(, }. |% _' d# `
% f, K% m' \$ h; K4 h+ [" {
            "https://restapi.amap.com/v3/weather/weatherInfo?key=f4fd5b287b6d7d51a3c60fee24e42002&city={}".format(
0 P; m8 s) J( Y: i6 i  }; ^' o. V) w. ]/ R
                cityCode))2 a# b; v# A) ^; P

' \+ [# N, n( B1 M        if r.status_code == 200:
3 ^* D5 ?2 {+ Q- P  s1 ]4 @* @- L1 c
( d  [0 s1 ?, y' b5 I" R6 o/ R" N            data = r.json()['lives'][0]
" b, B3 U/ U% t) f, |
" H( S; l% R& {& |& _            weatherMsg = '城市:{}\n天气:{}\n温度:{}\n风向:{}\n风力:{}\n湿度:{}\n发布时间:{}\n'.format(9 Z# w; [2 U7 Q
. y% h: p' F3 P" s- `- e7 ?
                data['city'],
3 G9 o2 u/ @  g& V( K  B# v1 u% h. ?9 i/ }
                data['weather'],/ s5 J( X2 B9 g$ v# b
2 s6 W- M" {9 X3 w
                data['temperature'],
% J" i4 C" j" l8 ^' U! y" j
1 K4 c: l; f. _" {( Y                data['winddirection'],
3 b: r. e, {1 B7 h3 o& x4 `4 ~5 R( G4 i0 W- C5 J5 T
                data['windpower'],
; t3 O( _; h5 t; a* b6 K* h4 N- F% b$ j" r+ k  e0 |
                data['humidity'],& {0 ^" L' E. a# l6 F
4 i3 i, w  W9 |! ]) O# E" u- R
                data['reporttime'],/ U$ q5 ?. H% U6 r) o: F
! H; [- |- O: K
            )
9 w& J9 h4 d; K" a" G+ [! {$ E1 q, s! K6 ]5 [. s
        else:
& G1 [5 D2 L  x# d7 B2 C& q3 \5 E, w4 e1 y+ A: k
            weatherMsg = '天气查询失败,请稍后再试!'+ ]* a$ A0 v! U

* h5 `) Q5 f& u( R8 J+ Z: }        self.ui.textEdit.setText(weatherMsg)- T* n2 q- V( ]( ]
# ^# R2 ~& H" u# P. X& z
    def getCode(self, cityName):  ^, g7 D2 L! ^1 B3 n5 e2 @+ N% S
7 I8 g6 F+ v- @, o1 q' [8 b
        cityDict = {"北京": "110000",
) T/ l$ b/ X6 ]7 o. H+ W, n) h" h" |$ x  k6 N: X
                    "苏州": "320500",$ N/ w# l% E% G! @5 N2 P2 ?

5 Q6 X& Y, Z8 b. o* z' n9 ]                    "上海": "310000"}
* ^3 i8 y& U' o' o2 v
0 M& f, L1 B, `        **return** cityDict.get(cityName, '101010100')
; I4 v! t+ G! I7 s* z/ @5 H- N/ G7 I' O$ E9 T% v4 j2 a
    def clearText(self):: e0 N$ p# P% s( ]
  Y# m2 ^/ v1 w+ _$ D
        self.ui.textEdit.clear()$ M) U) R- g. H3 C' Z0 }
# ~: ?  m: E( J: c+ L
if __name__ == '__main__':
! Q* `* t8 f, o: D6 |6 Z7 s+ C6 @! [1 X0 X- Y5 B2 V5 d1 R
    myapp = QApplication(sys.argv)& \) j$ ]* I2 i

/ b# y& w; `2 p2 r    myDlg = MainDialog()
3 ~  h5 a5 K8 n* J
" \* p* X) ~# U  {7 E( G. N' L+ ]    myDlg.show()5 P; c7 {7 L: R' Z

, Q" @7 r( n8 Q1 X. L( {) Y# F' D    sys.exit(myapp.exec_())( j* J, u3 S2 o. L3 r8 \' S4 c' d
5 v& S( W- s5 R
运行demo.py并执行查询后的效果:
0 \! w+ V, M3 `( _
( L# Q# B7 j8 f2 r2 ]6 f/ g" h# Y4 M8 t' l0 c, m

/ z% ]$ Y& l  @: S8 m4、将代码打包成exe文件
* N- r$ X5 H  v) Y
( {  ?+ A5 j" j将.py文件打包成可执行的exe在Python中称为freezing,常用的工具有:PyInstaller, py2exe, cx_Freeze, bbfreze, py2app等。功能对比:
9 j( u8 M" C4 O$ N6 F7 u
) l% B2 ]: F- F& `6 d. l. x& L5 |

1 F3 B6 h; ^5 Zpy2exe:软件更新已经不活跃,因此也就略过。
( o. @- Z3 C: f$ B9 J# q- V+ [- w0 x
pyinstaller:明确支持win8、win10、理论上支持win7,,支持apple Macos, linux。pyinsaller可以打包成文件夹形式内含exe入口执行文件的形式,也可以是一个单独的exe文件。
. S7 o. U( F9 r9 _: E- I$ C6 V7 f# h
fbs:基于PyInstaller,使用起来更加方便
" ~# O5 Z; o+ d* A. z' |# j+ [) \# \
% f; S3 r; _9 y# M- C7 O这里选择了fbs来打包。fbs的安装方法:
9 y0 g' \+ {! a8 y1 @- U8 [& X! o4 _( Y* ^. G9 _
pip install fbs9 M- J2 u2 q  @# r
使用方法,在命令行中输入:
. ?& J3 ~, b- y5 V! ~/ f; s* `: z8 p" j3 r5 R) M7 \
fbs startproject. y; h/ w. F: J4 d$ R  M
执行完成后需要输入一些APP的名称等。完成后会生成如下目录:: t7 T  ^& c6 h! K9 P3 x- P

2 t5 S2 q) F8 y, @0 Y2 S. s6 a' v/ J# C7 U  u- \2 w1 m% @

3 i& {$ m& A* i1 g6 |: |将刚才编写的PyQt5的代码(demo.py和Weather.py)拖到src/main/python文件夹下,删除原有的main.py,并将demo.py修改为main.py。然后打开 main.py,在文件头部添加如下代码:! Z  B% x% m( Z4 j

, J: v3 x! C( i. W4 O  rfrom fbs_runtime.application_context.PyQt5 import ApplicationContext# F6 U) E1 I/ z6 V! i2 p
```' W! p& |# T# @! k* x
完成后执行:
+ C5 _7 R% U+ S! T4 J8 B7 f```5 ]2 I. b# v( R' e- y# D8 Q/ M. `0 k, U
fbs freeze
9 j1 @- q5 d8 R) Q```+ V$ o! `, Q4 R
即可实现打包。生成的exe可执行文件在\target\MyApp文件下。
. M+ F: v4 y2 S! s8 l2 @1 w- Q
9 k# h9 r7 @# O) Z* ?  c$ f# q' D————————————————# }. C: P1 J% R
版权声明:本文为CSDN博主「宋宋讲编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
; u  \8 t! l" ^+ x5 S, O7 r: M原文链接:https://blog.csdn.net/qiqi1220/article/details/1262896673 Y8 [5 Z! T; `# k% S/ m, y2 w# E
3 Y- c" C/ G) ]2 d+ R
3 D1 s1 p' T% R. O4 D/ \2 y0 k2 n  s4 {

$ }2 C4 H, Q2 ~




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