数学建模社区-数学中国

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

作者: 杨利霞    时间: 2022-9-15 12:26
标题: 超全!Python图形界面框架PyQt5使用指南!
超全!Python图形界面框架PyQt5使用指南!" }! V" }* s& J# Y! o( W% u
$ r/ J7 d  z3 X2 _. N9 J
使用Python开发图形界面的软件其实并不多,相对于GUI界面,可能Web方式的应用更受人欢迎。但对于像我一样对其他编程语言比如C#或WPF并不熟悉的人来说,未必不是一个好的工具。
( C" J0 w+ ?% w! ]' _& ~  K9 q& S" T7 i0 R. T: c
常见GUI框架
' r* J: e$ S1 k7 v# j. j* r3 n2 L. S$ F( P# d6 D
PyQt5:Qt是一个跨平台的 C++图形用户界面库。QT一度被诺基亚拥,后出售给芬兰的软件公司Digia Oyj。PyQt5是基于Digia公司Qt5的Python接口,由一组Python模块构成。PyQt5本身拥有超过620个类和6000函数及方法。在可以运行于多个平台,包括:Unix, Windows, and Mac OS。  l) C! o3 j, \) X% h; X" Y
/ o9 G/ Y6 `. L& Z: s, `/ s$ Q
Pyside6:Pyside是QT公司官方提供的Python包,上一版本为Pyside2,对应的是QT5,最新版命名规则进行了调整,更改为Pyside6,对应的是QT6版本。由于官方出品的比较看好,缺点是发布比较晚,网上的资料没有PyQt5多。
% M& m! v' H; i3 j0 A5 n5 `$ n4 z/ L7 U3 t/ `$ m
Tkinter:Python内置的GUI框架,使用TCL实现,Python中内嵌了TCL解释器,使用它的时候不用安装额外的扩展包,直接import,跨平台。不足之处在于UI布局全靠代码实现,只有15种常用部件,显示效果简陋。7 C* {1 _1 P* X. B( {

  v0 R2 ?5 x: q1 a( T/ gPySimpleGUI:PySimpleGUI 是 Tkinter 一层包装。使用 PySimpleGUI 实现自定义 GUI 所需的代码量要比使用 Tkinter 直接编写相同的 GUI 要少得多。! {# ~  {' Z' x- H( _4 `* x

2 h/ d& K3 O1 Q# e+ Q& RWxPython:wxPython是Python语言对流行的wxWidgets跨平台GUI工具库的绑定。用得比较广泛,跨平台,C++编写,文档少,用户可能就需要根据编程内容对不同平台中的GUI代码做一些调整。遇到问题不好解决,代码布局控件,不直观。8 n0 s. M& L; l7 h# ^+ }
1 K% i( B# |+ @
Wax:基于wxPython ,为克服wxPython的问题而制作的一个包。
0 x) g0 d* O1 i) m/ W$ F! [* ]& H7 Y8 z
Kivy:主要针对多点触控程序,智能手机平板等,也可以在没有触屏功能的系统上,全平台支持(Windows, Linux, Mac OS X, Android and iOS.)使用Python和cython编写,中文支持差,需要自己下载中文库并且制定路径。9 b" t" t/ l6 b! M( w6 u& N

0 a$ v$ N' ]' K  o$ J" z& gBeeWare:Write once. Deploy everywhere.需要与Kivy配合使用。5 k/ @9 t3 P, O' d8 o0 Z
, S6 O6 _+ K1 T+ P; F3 Q
Toga:一个使用Python开发原生APP的GUI工具包。Toga由一个具有共享接口的基础组件库组成,以简化与平台无关的GUI开发。Toga适用于Mac OS、Windows、Linux(GTK)以及Android和iOS等移动平台。; h# q# b7 y6 T

' a3 |. C; z* i8 b( ]Eel:一个轻量的 Python 库,用于制作简单的类似于 Electron(但是比它更轻量) 的离线 HTML/JS GUI 应用程序,并具有对 Python 功能(capabilities)和库的完全访问权限。
. i, S' H! y. f8 y( a3 j- k" V1 J3 B" b
Flexx:一个纯 Python 工具包,用来创建图形化界面应用程序。其使用 Web 技术进行界面的渲染。你可以用 Flexx 来创建桌面应用,同时也可以导出一个应用到独立的 HTML 文档。因为使用纯 Python 开发,所以 Flexx 是跨平台的。只需要有 Python 和浏览器就可以运行。) l" ^0 F% ^/ ~9 w$ e

& X2 R$ M4 Z( J1 r+ T4 c. E& }pywebview是围绕 webview 组件的轻量型跨平台包装器(wrapper),它允许在其自己的本机 GUI 窗口中显示 HTML 内容。它使您可以在桌面应用程序中使用 Web 技术,同时尽最大可能隐藏使用浏览器构建GUI的事实。
0 B# |3 b5 V( S3 s* c7 e, |4 t+ ^$ K1 Z* J; D$ W
enaml:一种能够让你用最小的努力就可以实现高质量GUI界面的的Python框架,也是一种独特的编程语言。enaml将声明性语言与基于约束的布局系统结合在一起,使用户可以轻松地定义灵活布局的UI。enaml应用程序可以在任何支持Python和Qt的平台上运行。6 O) p# d; l. K: x+ W2 s4 ~
7 B2 N6 _9 Z( v& [4 g% @, A. P9 w$ i
个人想法:太多学不完,先学PyQt5,原因是资料多,学有余力再学pyside6,最后看下PySimpleGUI,看能否解决一些简单问题。  u9 w7 r* [3 X

& E2 u) V: K$ J; z  p" W" D' ZPyQt5简介
7 O. }! J2 t4 H& W
& e' x! ^- E$ O$ a1 uPyQt是Qt框架的Python语言实现,由Riverbank Computing开发,是最强大的GUI库之一。PyQt提供了一个设计良好的窗口控件集合,每一个PyQt控件都对应一个Qt控件,因此PyQt的API接口与Qt的API接口很接近,但PyQt不再使用QMake系统和Q_OBJECT宏。
5 j0 B+ ]! ^5 d( Q) ]! E; w" ^4 {1 H) z- j
PyQt5提供GPL版和商业版证书,自由开发者可以使用免费的GPL许可,如果需要将PyQt用于商业应用,则必须购买商业许可。8 J2 d( I, \# o9 t
; r. W  c1 l* U3 g' w0 i$ Y; j
PyQt5特性如下:
. M6 a3 N1 K2 m
. @, s" V& a& m+ P5 M: J& Z4 S基于高性能的Qt的GUI控件集。4 C7 o3 d8 A* U3 e

% E% E, D# d( q# S& \4 t- [$ o/ ^能够跨平台运行在Linux、Window和Mac OS系统上。% w/ @4 V2 D- O6 N

0 j( c, X! v8 q, M使用信号槽机制进行通信。
- m- E4 D% ~1 L. b. q+ \4 ^; N6 k2 t; l9 S
对Qt库进行完全封装。4 i0 ?% p2 J/ U( G
* O9 c5 q3 [& \4 e+ h
可以使用成熟的IDE进行界面设计,并自动生成可执行的Python代码。1 o7 z# B) ]) j- q9 A

9 s( e2 a- Y/ ~/ X) ]提供一整套种类齐全的窗口控件。5 R% ]' D' ~+ v% q3 A3 q  _
/ a5 @% X+ \0 P* h. u- ?
PyQt5是由一系列Python模块组成,有超过620个类,6000个函数和方法,主要模块如下:" o1 i: X5 B' B' |
5 b) U- B0 }6 F( ^( E7 C
QtCore:包含了核心的非 GUI 的功能。主要和时间、文件与文件夹、各种数据、流、URLs、mime 类文件、进程与线程一起使用。
* |8 t  ]& R, D( P0 ]/ O) e1 s0 p; ~/ e# A3 E
QtGui:包含了窗口系统、事件处理、2D 图像、基本绘画、字体和文字类。
8 p+ u# _7 }1 v1 h) N5 m7 e* J3 R2 {: D) D& l, ]  K- x
QtWidgets:包含了一系列创建桌面应用的 UI 元素。
! {2 i$ F) ~, U0 [) b, c: t, b& J
QtMultimedia:包含了处理多媒体的内容和调用摄像头 API 的类。; P6 L  ~$ S* l, J: f1 L2 H

  N* R/ o2 |$ G! U, Q4 {! t5 `QtBluetooth:包含了查找和连接蓝牙的类。
, C: l: x) f0 U3 ~
. b$ I% h0 r* ~1 n! s: Q/ g1 Z2 hQtNetwork:包含了网络编程的类,这些工具能让 TCP/IP 和 UDP 开发变得更加方便和可靠。9 f* ?4 X& t' X

# E4 m; c. M$ D, [5 ZQtPositioning:包含了定位的类,可以使用卫星、WiFi 甚至文本。
+ E2 ?" N/ {- I0 Q9 c% E  X! R6 r, s3 b; o# M4 E  f
Enginio:包含了通过客户端进入和管理 Qt Cloud 的类。
  c9 A9 o, z1 h1 G+ z2 G5 ?, v8 k3 |0 V, d# o1 o5 M
QtWebSockets:包含了 WebSocket 协议的类。1 O9 {9 y9 a) a/ u/ {
, K3 E2 w( j) W: F7 f2 d/ G2 h& ]1 I( d
QtWebKit:包含了一个基 WebKit2 的 web 浏览器。
" x% T& v. Y1 B
' Z8 t2 K4 n! c5 X3 ?QtWebKitWidgets:包含了基于 QtWidgets 的 WebKit1 的类。
% @0 [9 Y  e! G: Q: y' v+ b# {; r3 b6 c9 s7 m0 v
QtXml:包含了处理 xml 的类,提供了 SAX 和 DOM API 的工具。
0 g9 N6 H! l( O% E9 J4 P6 p
: x+ L$ F9 ~+ F/ y3 Z1 k7 KQtSvg:提供了显示 SVG 内容的类,Scalable Vector Graphics (SVG) 是一种是一种基于可扩展标记语言 (XML),用于描述二维矢量图形的图形格式(这句话来自于维基百科)。" f4 z0 k+ B' j

( g% ?" c/ s) h- ^4 S# zQtSql:提供了处理数据库的工具。
; G6 m: [) }$ p) t1 {2 r
3 G6 v8 x0 \  z; hQtTest:提供了测试 PyQt5 应用的工具。
- T, v. J  w. l& e7 N) n1 J- n* P2 n) V0 K
PyQt5的安装: k* _, s4 C4 i, S& v/ @: D
! m- D& ^+ K7 e* D
由于后期要使用fbs进行打包,fbs对Python 3.7以后的版本可能存在兼容问题,所以我选择了Python 3.6.8进行了整个环境的搭建。主要内容为:Python + PyCharm + PyQt5
! T" k1 X! U% a- g) g
* a/ M3 d# q5 h5 X安装PyQt5. b; d1 J/ K0 B

5 g: T3 y+ w9 ]( E/ upip install pyqt5# B' X0 t, H4 k, a/ P" W& C$ Y

7 K0 r7 E7 l9 g- ]pip install pyqt5-tools* @8 S: X9 g" {: b" W
其中pyqt5-tools为Qt Designer拖拽式的界面设计工具。安装过程中可能会报如下错误:' I) f; ?7 C1 R: @+ H6 s9 u5 X
3 A2 B# I5 H  i& _3 k
qt5-tools 5.15.2.1.2 has requirement click~=7.0, but you'll have click 8.0.1 which is incompatible.
& }' T0 f/ n& @% @解决方案:
9 U5 J2 Y/ W' z$ {  B1 f: l/ Z0 u- U' D3 K' K
pip install click~=7.0
  t7 r' Y: Q4 M: ^  T8 IQt Designer的配置; `# F. n( U3 [  o

9 f0 k' L" l5 m% T3 PQt Designer 是通过拖拽的方式放置控件,并实时查看控件效果进行快速UI设计。9 s( K* S4 w* ~8 {. D
0 u$ U0 F8 G8 x6 J0 T8 _

" s+ J, y5 I* b; i/ q9 [, L# ^3 d. t
/ ~3 ]7 z/ ]% o) j! I整个画面的构成:
5 b& T! R' ^$ \% c! H: u; Z- P" X- V7 l; T' r: _
左侧的“Widget Box”就是各种可以自由拖动的组件
* _: c. }! P$ Z
! Y! x0 Z' Z" M) J5 Z7 z中间的“MainWindow – untitled”窗体就是画布
6 v2 r& [' I1 t$ D# D) f" Q0 `& o3 M# P. g/ i5 J; K; [
右上方的”Object Inspector”可以查看当前ui的结构2 ]6 u, K7 K6 d7 B+ o+ W0 ~

% O7 E; b! j$ b* F+ X$ O右侧中部的”Property Editor”可以设置当前选中组件的属性
" [+ y) Q' G  G6 r5 I3 d0 a
$ T* z7 r; i2 J! L/ b右下方的”Resource Browser”可以添加各种素材,比如图片,背景等等
: n4 }3 x, Q, W; i: U) T0 W+ z8 q1 N6 o) a- w
最终生成.ui文件(实质上是XML格式的文件),可直接使用,也可以通过pyuic5工具转换成.py文件。; O$ E( L4 a& w& T& |# V
  ~) `& |* I- ?6 S( r
QtDisigner配置
/ U8 v' \" c. p0 Q, A/ J2 N: E( l/ l! i6 e
在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:6 g3 g9 g8 M0 u/ ~* n

& A* y# w' z0 U- W) S; nName: QtDisigner
. g" u" F" W0 D. e! _& _
5 ]. t; |" l! }. AProgram : D:\Program Files\Python36\Lib\site-packages\qt5_applications\Qt\bin\designer.exe # 请根据实际修改
8 w4 l/ i* W$ ~& x. A' @  e% X8 R( u' ?5 Y0 _$ c9 W. Q
Working directory: $FileDir$  z# }8 T5 r$ o/ N! X
PyUIC配置) l9 e. Q& N3 C; Y- G; d

, p# f$ a+ W: P4 EPyUIC主要是把Qt Designer生成的.ui文件换成.py文件。
4 D& O! m' B$ k4 t3 r, U  U  E& p3 [. Z% t
在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:1 ^3 @- z3 O3 k+ E4 M
( p8 m" ?+ k$ m# S. g! ^
Name: PyUIC
9 x2 y& K5 Y+ [8 Q& V5 d
4 y: M+ V3 N8 L2 w2 e" \Program : D:\Program Files\Python36\python.exe # 当前Python目录,请根据实际修改
- V% l  F! Y8 Q" L8 D$ C- ?7 F3 |- F5 ]" C
Arguments: -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py/ F4 H. N2 C* h. G
" h" v) F2 n5 v5 B
Working directory: $FileDir$
0 N& r3 Q1 l7 _6 z: a( i& Y: oPyRCC配置
& d. u  a5 G& y/ J( u4 ?7 I8 ~# l5 f" R9 P5 ?# e
PyRCC主要是把编写的.qrc资源文件换成.py文件。在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:
/ c0 V! {) R4 w  ?& j7 h' X9 R7 E# z2 @
Name: PyRCC. C: X/ e: h/ r9 I$ X; s& H
7 x+ J9 z/ \/ t% A: ~; e
Program: D:\Program Files\Python36\pyrcc5.exe # 当前rcc工具目录,请根据实际修改
. S1 E& T* r3 x% n- d9 a0 p! L/ V  n6 t: `& h
Arguments: $FileName$ -o $FileNameWithoutExtension$_rc.py
1 E* f9 |' I3 M% u+ y& u( p9 R& U' A! k! Y1 K& e
Working directory: $FileDir$/ l# h5 e9 Z( `7 S, X2 w' J7 ~
PyQt5使用示例
3 u/ d% b4 }2 O# m. M; d: b* Y: g5 a7 X" v
创建一个空白的界面:; ?+ v- c5 G5 q$ q

, l! _/ X) o# U3 D- Limport sys
0 x& m2 Y! B7 d) I2 B5 J6 C/ w( I6 E7 o# _( w) r2 h" z1 v. f& r
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
3 I3 z, d8 Y$ e( L
' G/ f* V% S8 c$ _" iapp = QApplication(sys.argv)
9 ~5 P: ^( o$ f' A+ u' W$ ^5 M* p
win = QMainWindow()
2 T6 a- Q+ K* G( K
9 C8 B; |8 b9 H: {win.setGeometry(400, 400, 400, 300)
2 Q7 ?& g1 c* O" z$ G" [4 y( ~: o$ m! S0 B
win.setWindowTitle("Pyqt5 Tutorial")8 w9 j) ~; o3 q) I% J$ C
5 y/ k  s  |# B8 Z
win.show()
! E  S' o! }/ V% J- w# ~- w4 o5 T/ ~# \% q9 Z1 T+ y$ c% x
sys.exit(app.exec_())$ n' C: `$ o6 f2 J3 ^# ^" i1 K) k; u( k
4 c9 i8 j+ b  [% g

+ l* e/ z9 C, G6 X/ [' w  \% b3 w. z- T8 e) D* Y4 o' `% s0 |
其中:
# v/ ]) r9 x  d* U! a( Q
* e6 b' I: z5 u% w' ]0 TQapplication():每个GUI都必须包含一个Qapplication,argv表示获取命令行参数,如果不用获取,则可以使用[]代替。& j7 j$ D, r4 f2 n/ g+ O8 N% n( D+ u

0 X3 ]; V4 @' S, b% p* ?" v6 rQMainWindow():类似一个容器(窗口)用来包含按钮、文本、输入框等widgets。arg标识可以获取命令行执行时的参数。
" |4 h5 ?( l4 {( z( l/ [
- i$ K  r5 I3 b5 ~9 A6 jSetGeometry是用来定义 QMainWindow() 窗口的尺寸, 语法:setGeometry(x, y, width, height ),其中x,y为屏幕上的坐标点。5 F7 n5 Y! c' y8 H
+ M4 O+ Y' Q! U+ o3 _
show():用来显示窗口$ z& L3 B7 f- V$ M# o1 V: T$ Z1 e

% _* E; j' y% B8 ]! qexit(app.exec_()):设置窗口一直运行指导使用关闭按钮进行关闭
4 P, I/ ^8 J  m- ]3 F/ L6 n' U2 n% i* B( F
PyQt5支持的常见Widgets有:0 k* e3 k6 h! n5 M+ A
: i+ t, v  V$ B  F, r& @7 K

3 W! c. \: W, r8 i/ o6 \0 T, s( j8 y2 w
从上到下,从左到右依次为:Qlabel、QcomboBox、QcheckBox、QradioButton、QpushButton、QtableWidget、QlineEdit、Qslider、QProgressBar
4 T4 X; [/ I5 |0 Y2 ~0 u
. @0 U5 P* b: `5 Z6 W- v7 U对于使用Pyqt5设置文本内容,我们使用Qlabel:
' @% L, K, ?; R0 `! L$ I4 s# ?- I1 v; H5 w' w; o/ f
import sys
4 s, _, l2 W- u- O* U( Z* K" K/ {; B8 y1 g
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel0 T. Q' Q; u+ q; {8 n

4 m5 Y0 k1 W& T2 b+ n9 Bapp = QApplication(sys.argv)
- o$ C+ ^. j" U6 B; m/ c' d$ ]6 G) a% h: G; M: |& d; ]
win = QMainWindow()
- ?- [4 q, {! r+ N4 l$ [% K9 c. W! t6 ?- {; H9 s8 y, P
win.setGeometry(400, 400, 400, 300)
# u. b$ R8 M" X9 m
5 w+ I1 ]9 ]/ W8 hwin.setWindowTitle("Pyqt5 Tutorial")
- P; X8 a% u% K- o' l: V/ Y3 V4 c/ K* j" ^: ]  m
\# Label Text) U4 T, Q  V; `; D7 p

4 g6 ^2 [5 N# O( ylabel = QLabel(win)
) T6 L) n# v. c6 E# g( p( y2 }3 F3 T+ _, ]9 A/ z& c! p
label.resize(200, 100)+ A  A# x3 c# z, f1 K1 l1 f8 h

6 k/ g- B; x' T0 v" @label.setText("Hi this is Pyqt5")% T( g& ?& `# f" t* G

$ m  d" Y) j3 `% ~- `label.move(100, 100)
" n  y' b9 q, P8 ~/ ^0 r0 e# d5 `# E. U- z! B/ z, Q
win.show()6 |/ `- Z( Q" t) V! m" h
0 l5 ~! I# V5 x8 x# ^) @; G2 P
sys.exit(app.exec_())
8 C! q. |8 }- [9 Q% z: [2 w% i& }( p! {0 f" N4 |. V

4 s3 y7 _" e% ?" Z; @7 N8 M8 F% p" O) T3 [
按钮与事件:
$ H4 P. |8 M+ \% ?
* c, ^1 ?& r$ C( l& S1 Gimport sys& }4 ?/ }  [8 y3 l$ r$ J7 ]

3 x" H& h1 P2 ^' U; H1 s0 W0 Zfrom PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton* ~3 |: P% s) T' Q+ X( P. W
3 G1 U# P, t* o, h2 P" f% m5 `
def click():
% p2 D  }" x6 |# X% V4 s8 Q& [7 ~1 Y
    print("Hy Button is clicked!")
5 f# Y( \' S$ i# M4 ?
5 [9 ~" Q/ E8 |0 G2 Z5 wapp = QApplication(sys.argv)  T, s, |$ D8 C  _

4 r) S( U+ w" ]% E4 V& Z  ]& k2 Y: ywin = QMainWindow()1 M' y- l3 U# q: R

" n# e0 N% j9 Swin.setGeometry(400, 400, 400, 300), ]" g8 T& L3 w+ y! s# L

$ }+ q* r0 w% I6 ywin.setWindowTitle("Pyqt5 Tutorial")+ f. ~5 Y( C. }3 d/ R  m( ?$ R

/ [7 w+ q: Q" B4 u( ~  ]. D\# Button: V# w( Z5 y7 M) k# `

& y/ {; c( S  p0 n+ i. T1 O/ ?button = QPushButton(win)+ m- r: O6 T0 o! b
$ Z. D) b: D" [* m8 H  j
button.resize(200, 100)
8 g  j# y0 O% }' S* L3 h% Q( d
button.setText("Hi! Click Me")4 X2 Z/ o/ ?, A3 ~% g

- t8 V! m5 S: W. E& w. gbutton.move(100, 100)
, m* Q: y- [9 W
; `5 I/ ?' [  B% D0 Abutton.clicked.connect(click)2 x7 \- h3 P' O$ \$ p, ^* S2 X

' T! I  k5 i' g; o7 Uwin.show()
0 q+ B3 K" \6 y5 _2 z% ?9 u, V. E- I5 N7 t: j8 A: }0 M
sys.exit(app.exec_()). w  ^: k( i" n0 g8 d
; \0 Q! ]) W$ W+ t( _
4 a1 `" T: N% U1 |  i5 n3 @

+ w& `2 ~7 {+ A! g% Ybutton.clicked.connect() 在按钮点击后执行特定的事件。
* i6 q) V- s' Z" e% ^7 a1 c: ?% H  Y* R' p
PyQt5实战' \6 N) ?) g6 D2 x8 W
% v3 `8 R  ~4 A/ {) d
实战项目:简易的天气查询软件
2 J! }! x1 Y' e: z" ~8 g& b- ]6 o
- z5 K, x' j# H5 D" T! m1、使用Qt Designer设计一个界面
, Q7 Q: Y0 ^# r1 T% z; ^4 [  c
( d; g6 y* d8 G* D, o; I
" c' P. h/ K5 ~/ \0 c2 B) v! o+ u
6 q5 y1 C$ f/ o* s0 L9 q$ z用到的控件有Button, GroupBox, Label,ComboBox,TextEdit,同时定义了两个按钮queryBtn及clearBtn,分别用来查询及清空天气数据。我们需要绑定槽函数,方法如下:
  `. d0 U7 j6 D; W2 N' P' x$ V  N; O* E/ f* x$ b1 t
在Qt Designer右下角选择 信号/槽编辑器,点击+号新增; O2 E! ]+ r( S! X  l) w
( v" u. I0 |+ c7 P1 V3 _" [
分别选择queryBtn及clearBtn,选择信号 clicked(), 接收者 Dialog 及槽 accept(),(槽函数这里不知道如何定义,后期在代码里再进行修改)8 ^8 o4 r$ }& h. X% \* ]

1 |& R6 }% S0 K0 g& k以上完成后保存为Weather.ui文件。% T+ n0 q; K! f( l2 F$ M$ e

& ], P8 J7 N. Q$ j( s+ o6 e2、转换.ui文件为.py文件
! q0 W: J$ k: |2 A* U5 I; B4 v  U2 A! L, L& w2 C
PyQt5支持直接使用.ui文件:; U% J) [0 c. `
9 M+ F% k8 D2 R6 W" M8 u  m9 R% [, B
import sys1 `, t$ A$ V; u- b
- [2 Q6 O7 M. G' Y+ F
from PyQt5 import QtWidgets, uic
1 w, u/ V$ L; X; S
: _  G7 L  n* u" H4 U7 mapp = QtWidgets.QApplication(sys.argv)2 Z% t: `: {' Z
; k- [7 e. D) H, r* m4 h& @
window = uic.loadUi("mainwindow.ui")/ ?4 t  h  ~) H5 W/ a

5 i! @. u1 h' j  P, N2 D- P5 Rwindow.show()" f8 s. Y8 s4 ~) Q0 |
0 J- s3 L" i! Q- w. l/ Q7 u# @+ [
app.exec()" {. _6 _1 r$ f( W
但是为了更好的自定义及修改上面的槽函数,可以使用External Tools – PyUIC,即可生成Weather.py,实际运行命令如下:# R* ~/ `, q+ v* g4 I/ V' R
! s# G$ O+ a3 }* b& v
D:\Program Files\Python36\python.exe -m PyQt5.uic.pyuic Weather.ui -o Weather.py
: c# K; \/ ~% s& o其中,我们需要把两个按钮绑定的槽函数:
( N9 m2 G+ w& @% C# K8 [; e6 W; V: ]( `( H: M
\# self.queryBtn.clicked.connect(Dialog.accept)$ n. B+ T7 B3 ^8 R3 w2 C9 ~5 R
" j$ q) p1 w+ D& z8 ]' M8 `7 F
\# self.clearBtn.clicked.connect(Dialog.accept)
1 Z' l( `, W0 b  z6 ?7 P- i/ W
. p8 a& `. P& y, p  F+ ~9 c\# 修改为:
3 i* ?! q% q0 b( i: @* D& G! ~
4 Z% ~: s+ l( G3 w4 iself.queryBtn.clicked.connect(Dialog.queryWeather)
  y6 l$ V/ _- b6 V& Q1 J+ l) \
, Y* o: Q; ~" y) Y5 Vself.clearBtn.clicked.connect(Dialog.clearText)& c; I% U3 Q) T9 C8 @  Z# ~
最终的Weather.py内容如下:
& q: o$ y, i) g! w! ]8 P, m( `1 g: Z" d& u2 L9 M2 a( z8 |8 |( T
\# -*- coding: utf-8 -*-: f/ H7 N7 B: _0 q
2 c% U5 n& j9 e" P
\# Form implementation generated from reading ui file 'Weather.ui'  \4 r! h0 H" O

" x9 M4 F4 b2 Q4 Y+ c3 p\#
5 j' l! M/ _+ j; t/ |' L" }$ X7 O+ o! L- S9 ^- ^4 D4 n6 L' a" r) u  }4 C
\# Created by: PyQt5 UI code generator 5.15.46 k0 z3 c" E% M  i! ]1 u% q

! f# m' Y5 J$ X5 N\#
& ]: f& m. v) Q1 T. L* g
; K& P2 o& [( [! D* f\# WARNING: Any manual changes made to this file will be lost when pyuic5 is; h: o0 r0 e, [- @' j8 c
- c2 M, o1 Q- p
\# run again.  Do not edit this file unless you know what you are doing.
+ m" \% T, r/ K5 n; H  ^1 Q+ l6 P% Z7 _/ p  n4 p( _
from PyQt5 import QtCore, QtGui, QtWidgets
: M- }# l2 I3 q) G5 V8 M4 e6 w: p$ }
class Ui_Dialog(object):
; t  [8 q8 p; x; v" `3 r* U3 r5 J4 \* s" F% }& e2 A. y
    def setupUi(self, Dialog):3 L: n6 Z5 r. Y

* V, U" Z2 J3 K2 v- R# G' k        Dialog.setObjectName("Dialog"). O& e. f3 r  v) L+ `9 x2 O5 C
7 A) x4 O8 I3 C& t4 }( r7 B4 C0 x
        Dialog.resize(600, 600)& _7 Q2 d; {7 s+ ?0 Q7 m1 a3 L; |
$ s2 J/ o# b1 P" V# Y/ I% H
        self.groupBox = QtWidgets.QGroupBox(Dialog)
7 j+ S* h& x$ n$ h" z8 J. B5 n" j! r# W7 [3 d& A& O) C5 P( C1 z3 K
        self.groupBox.setGeometry(QtCore.QRect(30, 20, 551, 511))
' Y, A  R8 m3 q9 ~% a9 c2 R) O9 T
3 A% E  X( K+ q        self.groupBox.setObjectName("groupBox")
0 P8 `* G4 v% r9 _6 i
, b  K( O8 ]8 `( ^        self.label_2 = QtWidgets.QLabel(self.groupBox)
3 I3 M" z6 h1 }4 y5 g5 m8 R' K% _
4 B1 Q" x9 O' n! p        self.label_2.setGeometry(QtCore.QRect(20, 30, 31, 16))7 q% l+ h2 Y# F& ^0 d9 e

7 |$ Z( R" g& o% d3 ?+ {7 \5 w5 \        self.label_2.setObjectName("label_2")' J8 r9 M& N5 w9 Z

- d2 j# {" x* b, C        self.comboBox = QtWidgets.QComboBox(self.groupBox)
$ ~9 I5 W5 v- G- g1 u1 j
: H9 t6 k' X4 l1 L% W, e" I( s9 t! s        self.comboBox.setGeometry(QtCore.QRect(70, 30, 87, 22))
5 Z$ m) A  X* M$ M) J$ R( S' a/ j* G1 U4 W. T( r! j  s0 d
        self.comboBox.setObjectName("comboBox")" A7 s& }: a! L  J; E
' j, y' a/ }, p8 A
        self.comboBox.addItem("")
% ^* A  r& A. b0 i8 A  O; x7 g
0 H" {/ r- W- o0 l9 u* u        self.comboBox.addItem("")2 G3 B* I' [; n; K* F8 V. E

& U8 E( y5 I1 B" v0 N        self.comboBox.addItem("")8 _6 s' R8 I0 w9 a
' v$ v4 Y  Y! n  H. R3 |8 n
        self.textEdit = QtWidgets.QTextEdit(self.groupBox)9 R2 H: @# ?! c+ G- S1 K
7 N. ]6 i0 d7 `; e
        self.textEdit.setGeometry(QtCore.QRect(20, 70, 491, 411))9 g- q- ^5 X1 g% X) s: @
3 y: Q; `3 K* R8 W7 M% U
        self.textEdit.setObjectName("textEdit")* R) \, n# F5 J3 N6 V0 O$ }& w

; E2 }) u# @4 n& e, Z4 u% B% H. c$ p        self.queryBtn = QtWidgets.QPushButton(Dialog)
- ^7 i* g1 D% D+ x9 q4 [1 K2 ~/ N; i: n8 o
        self.queryBtn.setGeometry(QtCore.QRect(490, 560, 93, 28))' r% W, T; y" X4 I% j

* d! ^" b9 G1 v8 x5 S: y9 |: v        self.queryBtn.setObjectName("queryBtn")
4 M, I1 r! P6 j$ e' G* N# a& J: K; t" k) H
        self.clearBtn = QtWidgets.QPushButton(Dialog)
  r. p4 M# ?: ?7 }5 K, A! B; R& B; B$ B# k
        self.clearBtn.setGeometry(QtCore.QRect(30, 560, 93, 28))
" j% I4 L- C" v6 V% \/ U$ s2 s+ z9 u
        self.clearBtn.setObjectName("clearBtn")
* ^! a- x! U! `# k( u5 I0 Z; |0 r+ t5 L, x1 k+ F
        self.retranslateUi(Dialog)
, |2 w9 w# L% w& i
8 M! ~( i- b% X* h+ u9 P, m        self.clearBtn.clicked.connect(Dialog.clearText)
% k! S) R% D  s% U0 Y& a0 t5 `# a
9 @5 }4 }( |; N        self.queryBtn.clicked.connect(Dialog.queryWeather)
* g8 F3 [! y2 e# V; v! n! B) D0 w% x; S8 t% z' m
        QtCore.QMetaObject.connectSlotsByName(Dialog)( b) v- V2 W5 W: j
6 s( b$ o5 E' \* ~4 E) `, f
    def retranslateUi(self, Dialog):
6 M2 s0 w$ D0 Y. @( u; l: W, t8 V* u$ R( |- p$ |
        _translate = QtCore.QCoreApplication.translate
9 S' s" r+ R6 j( v1 l- D' j6 U/ E. F+ r" e! {$ w  x/ u, d
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
! i/ k7 {% f( v" a
' F5 e% J4 ?0 M9 b- h1 G        self.groupBox.setTitle(_translate("Dialog", "城市天气预报"))
5 ?8 @! q7 p2 p
! b7 a3 |& o  `/ V) T2 h        self.label_2.setText(_translate("Dialog", "城市"))
4 ?; R9 d3 A6 y& F( W( b% {* B5 R
- L' _% A! e5 r, B" v( f3 G        self.comboBox.setItemText(0, _translate("Dialog", "北京"))
& X1 G: d1 C* C/ g8 U& I, s2 {4 n) v5 C! w. w; v; d, j/ c  A' S
        self.comboBox.setItemText(1, _translate("Dialog", "苏州"))6 m, e  X2 u2 j- s# L% p
' D" E5 C$ l5 ]6 n# n) P! j- ?/ V' n
        self.comboBox.setItemText(2, _translate("Dialog", "上海"))
1 W7 h9 [# P8 M' q, ~% E' E6 l" B8 [
        self.queryBtn.setText(_translate("Dialog", "查询"))4 U6 N+ x$ A4 f9 h) Y4 ~* T" K
& C, I; }! r  |
        self.clearBtn.setText(_translate("Dialog", "清空"))  [1 ]5 s" J7 Z! W) M9 O& p8 q# g3 j

* n7 _4 ]+ {3 {; R, B* b8 G3、调用MainDialog
7 d! j7 F+ t# I# I! p) E, V0 N) L5 ~" w7 l- N, u  h9 l2 t' f
在MainDialog中调用界面类Ui_Dialog,然后在其中中添加查询天气的业务逻辑代码,这样就做到了界面显示和业务逻辑的分离。新增demo.py文件, 在MainDialog类中定义了两个槽函数queryWeather()和clearText(),以便在界面文件Weather.ui中定义的两个按钮(queryBtn 和clearBtn) 触发clicked 信号与这两个槽函数进行绑定。7 f: p" J5 q4 j3 n  i, K

+ q+ i/ Z1 d/ w/ W( E9 M# y" o5 B完整代码如下:, D& _0 f& h7 [9 u

8 Q, B- x8 s: X: e7 x# x  _& E2 Yimport sys) h  d3 {3 U5 T' O, h
; f2 b" x& N0 o9 e7 R
import Weather
- z: j5 O) S% D1 U3 t# U( L; \" J% X- k4 z% P% N% w: v. O1 ]
from PyQt5.QtWidgets import QApplication, QDialog
6 C  z& R6 H! C: y$ @* @
' j# I9 E# \( O0 S8 [; \' kimport requests5 ]7 A6 R+ j  \( V8 [" u  ^

3 r9 y2 h& w$ i, Y9 Q- Q7 `$ nclass MainDialog(QDialog):
8 n- u( d" \$ }' `/ R: V. {+ h( J9 q5 z6 Q- {% ]/ ]& X
    def __init__(self, parent=None):( ^4 P4 W1 F# z9 I0 h% a& M3 X
& c& G" u1 j+ T* X, X2 n  v+ l
        super(QDialog, self).__init__(parent)3 c6 C& Z  A: A4 f( B
$ M! T( [5 Z) c) ^
        self.ui = Weather.Ui_Dialog()
/ {, h( n8 C) m1 y( {9 C/ A! V- v! ]* C/ F$ A" B* G1 P7 T! F" c6 G
        self.ui.setupUi(self)0 s6 j8 q/ t; w0 Y+ v- M9 V

0 A# i8 W$ C+ `) ?" T    def queryWeather(self):# C2 P# x4 W3 P" z4 G6 E
  O% v# q  R* n
        cityName = self.ui.comboBox.currentText()9 q& b/ m: _8 b# e. j
: U" ?8 G7 G/ w# a
        cityCode = self.getCode(cityName)
+ ?% U9 a/ ~$ S6 a" l
4 v& V  c, w3 w# Y* E! O        r = requests.get(
! L' {$ n9 S7 X- k( d3 u2 @! z; Y" O: f1 ?, r' |# i
            "https://restapi.amap.com/v3/weather/weatherInfo?key=f4fd5b287b6d7d51a3c60fee24e42002&city={}".format(7 t( V/ [+ r) A* \" _$ M
: j: P% @0 O$ q% L5 `
                cityCode))
- @* _8 _, x. L  U7 w2 v# C1 V0 C$ g$ t* g* W4 c; y
        if r.status_code == 200:
/ j9 l# D$ E+ m+ f
7 O; p  @( q6 I6 `. V% a            data = r.json()['lives'][0]
# e0 k3 k7 Q6 U+ f' e) x3 P, B  p4 x* ?; Y7 U) q* t+ k
            weatherMsg = '城市:{}\n天气:{}\n温度:{}\n风向:{}\n风力:{}\n湿度:{}\n发布时间:{}\n'.format(
) r% `2 Z" B0 @7 \6 x* b+ J! |# G2 T5 Q" P
                data['city'],
: E% c, Q) t# l6 w6 A% M, \6 }2 |
$ q1 Q9 A* N% |                data['weather'],& ^) t2 J2 {7 T4 N1 p% l$ \9 @
3 Q. m1 a6 Q9 z  p( E, d$ }, J) x# T
                data['temperature'],
# K3 @& s3 O, _
4 r. {- g* B7 ^) o  P3 r" v                data['winddirection']," Y: q4 i& a5 M4 h0 [

$ l5 c$ ~! l8 g+ L                data['windpower'],' C& C6 Q& U/ Q, t5 U

& [6 E8 k' \7 C- d* K% a- N                data['humidity'],; Y! q3 h1 y) W

3 c7 K- J  U" h: b                data['reporttime'],2 {9 R$ {. ~( l/ E3 l4 z

5 ]+ m* B% V' f            )
8 J7 @8 P; V! |/ |4 h1 T
: r' m0 m4 ]# e2 Z  q        else:- |5 q% ?7 G" m# U2 T

5 K( l5 d: t! e: `4 c5 c            weatherMsg = '天气查询失败,请稍后再试!'
3 N5 W+ W+ s4 A# [/ F/ E
* |5 {4 G9 [0 w7 w5 N        self.ui.textEdit.setText(weatherMsg)% Z* z/ Y! I, B/ U% @: s% u- a6 i

! L  b' ]  N& [5 j. m6 B- x8 l    def getCode(self, cityName):
' _# }) c2 @, P1 ~0 U4 q" ~, d, P- S" D* @7 n7 |- D# A! I2 k/ }  j
        cityDict = {"北京": "110000",
. I- }3 U- H9 t. {8 R- E$ @' V( l5 A7 T
                    "苏州": "320500",9 v6 c9 k& d/ X0 U, B

9 S8 e" B) o2 o8 q: G" x8 v                    "上海": "310000"}
) p! s9 x/ ]/ V) e  }/ u7 u( ?3 {. d+ t  A, o3 g; E- [
        **return** cityDict.get(cityName, '101010100')2 W1 o4 j  }6 Q' k5 `4 d% h
' S4 Y8 W1 U2 x5 [3 p. l' x
    def clearText(self):
7 Z. f4 ~/ k+ Z% X9 {( n3 r
8 h) w: t- D' H        self.ui.textEdit.clear()) z; B# C+ Y7 k. J) J( |

- Z) }# v  m" D! q' a" ^if __name__ == '__main__':
- A- P- m4 _" U4 d! a6 [" X) t( E% s, O3 L9 b8 W& w( d
    myapp = QApplication(sys.argv)
/ z! v* k$ p& S: f
, y$ M2 z) l" a$ e( w    myDlg = MainDialog()' v" k# t1 {0 z8 R+ e3 E
& F! [' w$ U: Q4 k+ e4 f# E2 ~
    myDlg.show()
+ o. g( C1 ?; l; S) G
- x/ v' N2 l# I* R& Q    sys.exit(myapp.exec_())
* S) v2 s2 s% E7 |4 D/ Z7 _; e+ B. |0 N9 W3 r: x; v+ V1 K) J' D) S
运行demo.py并执行查询后的效果:$ ^+ O$ t/ }, ]2 u/ e
: W% a4 ~' I  A  O

. L, `3 T9 B. J0 s* _( H& }* S0 w) F( _" p5 p. M5 y
4、将代码打包成exe文件, r, Y: a( c8 x/ S; K# V

1 \9 _8 a6 \3 }6 ~# P" E将.py文件打包成可执行的exe在Python中称为freezing,常用的工具有:PyInstaller, py2exe, cx_Freeze, bbfreze, py2app等。功能对比:
5 g6 D2 T5 x0 \5 z! K) d/ h0 ^: [' U& c7 ?' y# a5 l$ ?- B* u
* i* r  U% C7 W

. ~3 R  V, U. P' I; v- tpy2exe:软件更新已经不活跃,因此也就略过。; a2 _1 D, B6 M0 V- j$ R

4 H* H: |& t- xpyinstaller:明确支持win8、win10、理论上支持win7,,支持apple Macos, linux。pyinsaller可以打包成文件夹形式内含exe入口执行文件的形式,也可以是一个单独的exe文件。8 c, J( m" O7 \" K' o( S: A

& g& r1 W% I( ?, }fbs:基于PyInstaller,使用起来更加方便
1 M9 N( v2 i! X6 j; r& X! Q& v
# _* }# w: E4 n' X  b  }4 O* w3 V这里选择了fbs来打包。fbs的安装方法:" A6 x$ B$ |% P5 Y& R' f4 R
! `2 f2 V7 C0 c# \
pip install fbs
. p1 q8 V% S$ i3 p! U使用方法,在命令行中输入:/ Y% m( T4 [9 e2 x& I
# Z/ K& i; Z/ o. S
fbs startproject" ^: \1 s) o; R& L8 s/ m9 q7 w
执行完成后需要输入一些APP的名称等。完成后会生成如下目录:
- c. Q% w4 }8 g# J" x  a& ^5 t' J5 e" D% B+ U" \. z8 L" E, O# f' r( W( O
: O5 n4 D1 Q* x3 }! j

( j* V3 b0 j3 v' i  F. _# v4 `将刚才编写的PyQt5的代码(demo.py和Weather.py)拖到src/main/python文件夹下,删除原有的main.py,并将demo.py修改为main.py。然后打开 main.py,在文件头部添加如下代码:; `) O" t3 J7 I/ j/ `

2 V2 Q7 \3 v1 U4 b4 D/ P7 Afrom fbs_runtime.application_context.PyQt5 import ApplicationContext
5 z4 w% F# r/ B  _* G3 y```) G: r' v, v2 k. S& d9 H. N
完成后执行:2 h0 @$ h1 ~+ ^* N, u  e) I, }
```
9 k+ N( K% F9 Y0 U6 wfbs freeze
8 I3 k3 n6 J$ ^- Q6 I- m! x```
( K& F& ^, w- d即可实现打包。生成的exe可执行文件在\target\MyApp文件下。
3 d( o3 [, ~2 U+ P4 w6 D; X
; V3 P/ I- d0 }————————————————+ ~- ?( O. D$ e. Y
版权声明:本文为CSDN博主「宋宋讲编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。9 V( j* I! z7 k; f
原文链接:https://blog.csdn.net/qiqi1220/article/details/126289667
  G! I- f; z+ h+ x! V  M, u9 ]; d6 _+ D0 E2 l4 B

: f1 L' y* ]9 e- @* M' h) F5 q
; J0 r* c# B6 A1 m( ?2 j




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