数学建模社区-数学中国

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

作者: 杨利霞    时间: 2022-9-15 12:26
标题: 超全!Python图形界面框架PyQt5使用指南!
超全!Python图形界面框架PyQt5使用指南!8 k( x2 @% P3 }9 S6 [
$ {6 ?1 n4 q! I$ s3 s# L
使用Python开发图形界面的软件其实并不多,相对于GUI界面,可能Web方式的应用更受人欢迎。但对于像我一样对其他编程语言比如C#或WPF并不熟悉的人来说,未必不是一个好的工具。) U$ _; e0 ~8 j4 j% w0 A
# ?4 M- D/ d* o8 \
常见GUI框架
4 }# V4 S( k' Y: _' {5 L1 x. X3 l9 i! `
PyQt5:Qt是一个跨平台的 C++图形用户界面库。QT一度被诺基亚拥,后出售给芬兰的软件公司Digia Oyj。PyQt5是基于Digia公司Qt5的Python接口,由一组Python模块构成。PyQt5本身拥有超过620个类和6000函数及方法。在可以运行于多个平台,包括:Unix, Windows, and Mac OS。
* g4 N$ Q. {0 Q
6 }, A; U1 S) `Pyside6:Pyside是QT公司官方提供的Python包,上一版本为Pyside2,对应的是QT5,最新版命名规则进行了调整,更改为Pyside6,对应的是QT6版本。由于官方出品的比较看好,缺点是发布比较晚,网上的资料没有PyQt5多。* {) H" H( x) d
/ M) r$ q, d! [+ x
Tkinter:Python内置的GUI框架,使用TCL实现,Python中内嵌了TCL解释器,使用它的时候不用安装额外的扩展包,直接import,跨平台。不足之处在于UI布局全靠代码实现,只有15种常用部件,显示效果简陋。
4 ]) o: X9 L; y  f7 O
( |( O- L* \  [1 a" i+ tPySimpleGUI:PySimpleGUI 是 Tkinter 一层包装。使用 PySimpleGUI 实现自定义 GUI 所需的代码量要比使用 Tkinter 直接编写相同的 GUI 要少得多。
% E4 j8 X( X* W7 q$ T2 h. }- U& C2 h& F2 S, _
WxPython:wxPython是Python语言对流行的wxWidgets跨平台GUI工具库的绑定。用得比较广泛,跨平台,C++编写,文档少,用户可能就需要根据编程内容对不同平台中的GUI代码做一些调整。遇到问题不好解决,代码布局控件,不直观。, B; E) f! l: B( i0 O8 L
) Z% r. P) ]+ O
Wax:基于wxPython ,为克服wxPython的问题而制作的一个包。  Z2 _7 F% ?" V  `
- p5 }* B7 k" S6 c) g* o  E
Kivy:主要针对多点触控程序,智能手机平板等,也可以在没有触屏功能的系统上,全平台支持(Windows, Linux, Mac OS X, Android and iOS.)使用Python和cython编写,中文支持差,需要自己下载中文库并且制定路径。1 T( T) m8 m# q1 {8 v# S' j/ x
0 m( R3 K/ u/ K
BeeWare:Write once. Deploy everywhere.需要与Kivy配合使用。: p( x0 e7 G# W$ ~, }8 p

/ M8 S! y" s: J% [3 KToga:一个使用Python开发原生APP的GUI工具包。Toga由一个具有共享接口的基础组件库组成,以简化与平台无关的GUI开发。Toga适用于Mac OS、Windows、Linux(GTK)以及Android和iOS等移动平台。
. Y" c" s6 @% v* M
$ U2 q2 _. Z2 lEel:一个轻量的 Python 库,用于制作简单的类似于 Electron(但是比它更轻量) 的离线 HTML/JS GUI 应用程序,并具有对 Python 功能(capabilities)和库的完全访问权限。
( h) D3 l; X( [. p, ]  B/ z  O* X3 r
Flexx:一个纯 Python 工具包,用来创建图形化界面应用程序。其使用 Web 技术进行界面的渲染。你可以用 Flexx 来创建桌面应用,同时也可以导出一个应用到独立的 HTML 文档。因为使用纯 Python 开发,所以 Flexx 是跨平台的。只需要有 Python 和浏览器就可以运行。
7 w' [6 e; q( L
; D; |4 _  y- ~& H+ spywebview是围绕 webview 组件的轻量型跨平台包装器(wrapper),它允许在其自己的本机 GUI 窗口中显示 HTML 内容。它使您可以在桌面应用程序中使用 Web 技术,同时尽最大可能隐藏使用浏览器构建GUI的事实。6 N& ]" y# S1 a+ w! V5 ~/ O7 d
: y! M& L, X* B4 _
enaml:一种能够让你用最小的努力就可以实现高质量GUI界面的的Python框架,也是一种独特的编程语言。enaml将声明性语言与基于约束的布局系统结合在一起,使用户可以轻松地定义灵活布局的UI。enaml应用程序可以在任何支持Python和Qt的平台上运行。
* V" j6 L# h7 r5 H  C$ r. ?4 d( C' _' m; ~" l  q% j) E5 d
个人想法:太多学不完,先学PyQt5,原因是资料多,学有余力再学pyside6,最后看下PySimpleGUI,看能否解决一些简单问题。4 x, x, p/ V' j. {" W. B) f
! ?- a( m/ b2 @6 M% s* C# N* O
PyQt5简介! l& c3 x! T, [
) U1 H$ c6 ?* W! h) f! v7 U6 |( A
PyQt是Qt框架的Python语言实现,由Riverbank Computing开发,是最强大的GUI库之一。PyQt提供了一个设计良好的窗口控件集合,每一个PyQt控件都对应一个Qt控件,因此PyQt的API接口与Qt的API接口很接近,但PyQt不再使用QMake系统和Q_OBJECT宏。( i# j  n; K3 ?; K% b
5 d8 Y# s3 j7 J' V* m2 O; O- X  q. l
PyQt5提供GPL版和商业版证书,自由开发者可以使用免费的GPL许可,如果需要将PyQt用于商业应用,则必须购买商业许可。
, ^2 t- e# o, C7 _
! ]: u, H: A8 R: Q& Y3 a% B3 kPyQt5特性如下:
1 h+ d  L* f2 }7 [0 {% u1 |; N( k( r" k* ~7 }
基于高性能的Qt的GUI控件集。
$ [, W; l9 Q1 m/ a8 G5 F- y/ N8 B4 @  Z
能够跨平台运行在Linux、Window和Mac OS系统上。
5 g  g0 `. K5 Y- O8 o& L
5 P9 S0 H3 Q  b% I  S) C- a使用信号槽机制进行通信。
# \5 v& M3 I& H4 {0 p3 J7 S* k! h7 Z4 P$ N0 C
对Qt库进行完全封装。
: Z; O) ?( @3 w8 U+ r9 s
% p' ]# X& Q2 ^; f0 D$ y0 L可以使用成熟的IDE进行界面设计,并自动生成可执行的Python代码。% ^( D6 X" H, e+ k: K( m' c" T

! Z/ }2 L7 t% t提供一整套种类齐全的窗口控件。" u1 Y# z: P1 y! |
( n: Z, @4 d' v  k
PyQt5是由一系列Python模块组成,有超过620个类,6000个函数和方法,主要模块如下:. ^: v5 i! h) P. s

, K! ?" Q1 T# ^9 l0 r' KQtCore:包含了核心的非 GUI 的功能。主要和时间、文件与文件夹、各种数据、流、URLs、mime 类文件、进程与线程一起使用。7 t* a( `* S1 ~  y1 e5 u8 e
7 i* B5 Z3 S+ {
QtGui:包含了窗口系统、事件处理、2D 图像、基本绘画、字体和文字类。* a1 [8 B3 U. p- @6 o& t- _
9 B7 Z: m: k3 ^: _. j
QtWidgets:包含了一系列创建桌面应用的 UI 元素。& X; }; Y/ }  B. e8 A
$ @+ n, A% K. K4 ~
QtMultimedia:包含了处理多媒体的内容和调用摄像头 API 的类。" H& k- n0 X$ ]: I  X# x) j8 J
- R6 m. p# \9 |7 I# T1 F
QtBluetooth:包含了查找和连接蓝牙的类。5 q0 _% l! a/ Q
7 I- _+ k5 @0 Z9 r- r) a* v! a0 @
QtNetwork:包含了网络编程的类,这些工具能让 TCP/IP 和 UDP 开发变得更加方便和可靠。. z. d; k4 }% _7 M# c

' z9 S# m6 w# C* |QtPositioning:包含了定位的类,可以使用卫星、WiFi 甚至文本。4 u" t0 T2 u& y4 s. P

! K: u4 C; [8 x6 I. ~( ?Enginio:包含了通过客户端进入和管理 Qt Cloud 的类。
9 c( F' w- y) o- O- N7 y' U8 y  B. P% Y  ~1 I
QtWebSockets:包含了 WebSocket 协议的类。  D" n, a" k6 ~7 r% G0 o

0 M& n9 s! y8 [# u/ ?2 B  d1 UQtWebKit:包含了一个基 WebKit2 的 web 浏览器。
0 r; R2 M1 L( e- }$ j2 c
$ m! T: n. G: M1 I* r, TQtWebKitWidgets:包含了基于 QtWidgets 的 WebKit1 的类。* H/ P5 |, _* W7 D8 R7 ^# F- F
3 ~+ J2 v/ r4 N
QtXml:包含了处理 xml 的类,提供了 SAX 和 DOM API 的工具。- ~) Q% Z3 N8 v. j3 }0 h$ @0 k
3 l. }" ~2 W7 N* n) r1 L6 A
QtSvg:提供了显示 SVG 内容的类,Scalable Vector Graphics (SVG) 是一种是一种基于可扩展标记语言 (XML),用于描述二维矢量图形的图形格式(这句话来自于维基百科)。
* U+ y% v3 p: h1 r" M8 i: S3 X, q: c  A# C% F) ^
QtSql:提供了处理数据库的工具。
  \* {) j# Z9 h2 Y3 b) e0 ?' P+ e2 K. @- Q5 b* o# Q
QtTest:提供了测试 PyQt5 应用的工具。% x6 J8 @' z2 p3 \( Z8 D5 k

+ G0 Y, l: q+ Y0 a$ B$ S4 [, w" TPyQt5的安装
; m; O8 j) _7 D' W% h. n% a+ Y6 z9 y+ Y
由于后期要使用fbs进行打包,fbs对Python 3.7以后的版本可能存在兼容问题,所以我选择了Python 3.6.8进行了整个环境的搭建。主要内容为:Python + PyCharm + PyQt5
9 K2 z5 \4 R$ |- N2 j2 K" Z8 q& L& S+ b8 h9 U) a# f
安装PyQt52 |) I3 a2 q# J" O
9 y; o$ S% t( V  `! u9 Z4 ^
pip install pyqt5
& j1 t) w, @3 \9 p, g; U0 w9 \" F% G! f8 t
pip install pyqt5-tools' Z5 k% g" u% M9 M
其中pyqt5-tools为Qt Designer拖拽式的界面设计工具。安装过程中可能会报如下错误:/ G  H7 O: p  s; x

8 G" R: a/ ]% p* V, Y" d0 x! A) iqt5-tools 5.15.2.1.2 has requirement click~=7.0, but you'll have click 8.0.1 which is incompatible.
" d9 A2 b8 s: q, ^7 ?: Y解决方案:
4 Z8 ?- A& h5 {. F3 x; A5 X! \5 A9 {9 H0 i' {# \+ T8 k8 p) A
pip install click~=7.04 Q) ~: I2 a1 N" J, `0 n4 F
Qt Designer的配置' {: ]4 ?7 g' l/ y+ P1 w+ n
: @4 C4 p6 M% M, S4 n' K- E  e
Qt Designer 是通过拖拽的方式放置控件,并实时查看控件效果进行快速UI设计。
3 T( |0 \" D# H0 x1 N) ~8 b: k  R2 c  }4 Z  n

9 I; a: Y* ^- J% I. i$ G
% ]4 g! m; q9 }) Y3 f; [8 E整个画面的构成:, [2 v' Z/ ^1 v) `/ U4 L$ h
/ }$ s, n3 r/ R) ]* f; M* q, U- M
左侧的“Widget Box”就是各种可以自由拖动的组件3 }0 w3 e* T: U" ]) g

. C2 o0 w' I" g, o& j中间的“MainWindow – untitled”窗体就是画布% K7 C1 t8 j  `! _

, L4 G& M. e: \' n0 _8 l5 w右上方的”Object Inspector”可以查看当前ui的结构
/ m" m5 P  `9 J3 `/ z+ ?& r2 B# n) V, V- {( J; ^) c
右侧中部的”Property Editor”可以设置当前选中组件的属性
8 `2 S) K6 _( |0 p- }, a( R' t( P0 {6 V* P5 p: i4 X1 m6 ~$ O- {
右下方的”Resource Browser”可以添加各种素材,比如图片,背景等等
+ n  r: t+ a$ A4 f  ?  J
2 D1 D$ m0 K& J' D/ i2 C8 k$ f最终生成.ui文件(实质上是XML格式的文件),可直接使用,也可以通过pyuic5工具转换成.py文件。
% G  `) B& a! \+ ~1 {& }# T+ f
& v; l* ]1 L+ dQtDisigner配置$ [: E  d2 U0 [
) m. y3 k! h7 {+ b
在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:' A" `- N, ~5 M/ r! C9 P

5 i$ n/ ?+ S2 r5 pName: QtDisigner; d: r0 B1 C0 x. V: X
" C  N; u8 n8 q
Program : D:\Program Files\Python36\Lib\site-packages\qt5_applications\Qt\bin\designer.exe # 请根据实际修改
( x) G( U2 z4 a4 X7 f( z$ I
3 M$ m  c: g3 A! U1 ?, XWorking directory: $FileDir$
4 J/ i4 s8 i) |/ m" Q% c) J' P0 JPyUIC配置
$ J$ c6 J" O' U- R9 \: B. H
2 \" }% z* v3 b$ Q: }+ S3 IPyUIC主要是把Qt Designer生成的.ui文件换成.py文件。
! Y# x# r3 q+ \- c" H0 Q  Y) ?# a6 O4 s& s
在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:
1 P% t4 H/ m1 `8 |. _! k! m& ^7 }
; \6 J0 X. U# U5 ~Name: PyUIC
& ^. r  \* P/ l* R. M2 W# i9 a" t1 k# o% j# n3 N4 j. D- T
Program : D:\Program Files\Python36\python.exe # 当前Python目录,请根据实际修改
& e$ S( w; W* `* j  u5 M& I4 ~
! V2 J* F- m' Z6 ^9 y  }Arguments: -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py
+ R3 W$ z: t1 T  v6 e" ^2 m# \) }: x8 n" C. ?# W
Working directory: $FileDir$5 I8 b6 U3 i6 |2 _' Z. d. c$ j( P! ^0 ], b
PyRCC配置
. F! ?$ ^8 g# |8 k* D( N- T0 x+ W) e" }! c* U
PyRCC主要是把编写的.qrc资源文件换成.py文件。在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:* f2 `6 D, Q# |( K
( t2 c3 ?0 u2 ~+ L9 k5 ?* D
Name: PyRCC
  U7 E$ h! P+ Q7 k# e/ r4 M# C* I3 U
' {$ s( s, [# HProgram: D:\Program Files\Python36\pyrcc5.exe # 当前rcc工具目录,请根据实际修改
# t9 f/ x/ I! @+ [2 l5 |; c9 t
: p! g+ V+ B7 ?1 p" o# bArguments: $FileName$ -o $FileNameWithoutExtension$_rc.py) _' v  p" n' c7 T" m9 R: r
* E3 k/ N) H7 U8 G- a
Working directory: $FileDir$& J4 r' M, D" q0 C" x
PyQt5使用示例7 c) h& P( K8 ~. I5 E2 j

  @$ S: f9 H7 o) F3 e创建一个空白的界面:
9 h, j* ]: e4 d. @; d, I! V+ ^% H2 P
import sys) a! E9 _' Q- J* z- `

, R& `% w" H; yfrom PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
% L6 N, o& S. x# ?7 q0 }) ^
1 [( ?1 w7 T) Iapp = QApplication(sys.argv); n9 D9 k% J3 C4 v& J, J' L; p- \

' o; y7 s6 I3 U7 ]# i/ h7 @win = QMainWindow()7 q& ~! U/ A. d6 ]

1 C/ R7 ]- u% L  Dwin.setGeometry(400, 400, 400, 300)" m9 t- o% ?! g; z

* j+ B' Q5 |: y% f; M% S5 y2 Cwin.setWindowTitle("Pyqt5 Tutorial")
/ ^( T, g3 n1 x0 \  S% [
6 `& N; V- L/ U5 p3 q  [win.show()
+ E. L$ h; N' u2 ~! |
; e0 g5 L; H! w( C9 ?) k, vsys.exit(app.exec_())/ C) _* z7 S. K# ?3 }' t

4 \0 G5 \! i: P! G( Z) p5 q0 P8 n: V/ |( S: C
8 c3 K& Q- u, Z0 N9 t2 p0 ?: C
其中:
3 t" t- x1 A3 d# |7 }  I' I! E% N1 W7 F0 V6 J+ f
Qapplication():每个GUI都必须包含一个Qapplication,argv表示获取命令行参数,如果不用获取,则可以使用[]代替。
* X8 z- \0 V( k. @- A" _/ i+ ~  P% Y, k! v
QMainWindow():类似一个容器(窗口)用来包含按钮、文本、输入框等widgets。arg标识可以获取命令行执行时的参数。# R( \0 c$ Y( P4 c; V) J# @
' i! c# ?+ R& E2 Z5 C
SetGeometry是用来定义 QMainWindow() 窗口的尺寸, 语法:setGeometry(x, y, width, height ),其中x,y为屏幕上的坐标点。
2 _: j) c  F* ~( l5 B. P, W8 N; s3 c/ T9 G! |; T
show():用来显示窗口
, M/ T0 r5 i! C5 m/ z( m! O9 p
, E4 u( E: m, texit(app.exec_()):设置窗口一直运行指导使用关闭按钮进行关闭
9 a4 s* \  U3 P
; e5 p( D3 _5 r- j' c! Q; MPyQt5支持的常见Widgets有:3 m7 i; h' P! K9 [

6 \* X! d& U0 K, }1 q, |
: X5 u; r! A$ ~& w
  Z/ N6 {' E) S: w5 V从上到下,从左到右依次为:Qlabel、QcomboBox、QcheckBox、QradioButton、QpushButton、QtableWidget、QlineEdit、Qslider、QProgressBar6 S' H+ J2 a! l9 q# R5 z
$ W7 `: ~$ i4 C
对于使用Pyqt5设置文本内容,我们使用Qlabel:$ w4 v$ ], m( c. ~8 N
7 L: ~# _0 `4 `( U# @
import sys" }. Q( v- k/ ^' b

+ x. B$ v* a& }from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel' o( N5 Y4 T8 ~6 `, e, s5 G
/ G; |0 e, Y, X& g; Z  \* g
app = QApplication(sys.argv)4 w8 a2 W3 e( @# R; K/ W* b
% y5 I6 r* N' @& M+ J" ]( E" O
win = QMainWindow()% k! A9 [% ~4 y, B+ l9 {, B
7 ]2 v, I  X" G
win.setGeometry(400, 400, 400, 300)
4 @2 x& v# x2 S* S' l' q: F1 n- r. \- n3 i8 h; C! I
win.setWindowTitle("Pyqt5 Tutorial")3 i2 p* {# }( F- Q6 z) }! q

- H9 Q$ F# Z6 A' B\# Label Text' q  l5 [. v! [( z; d

7 k* x# h$ N6 u5 ~label = QLabel(win)
% z0 N# g$ g5 K6 ?4 j: C! u8 w* }" o4 ~/ R3 y; ~. n
label.resize(200, 100)
/ G; C/ O3 U  y. o. S& \4 @
7 c2 g% N5 `  elabel.setText("Hi this is Pyqt5")
; E0 D# Y9 r0 V0 F
* R8 t7 f' G' ^- j3 ^" tlabel.move(100, 100)9 v8 m' K& w3 R2 }
* s0 }4 S3 ~; G  w- V  p" u; |% F
win.show()' a6 `6 w4 J- [  f

- B5 c* m& F% a3 G$ H8 M+ _& zsys.exit(app.exec_())% k, w/ D; s3 ~8 ?! y# Y% k4 \
* ^; a* w! c% x2 D  l' o# C

. }$ u7 K' c  C' d. T* E4 j- l5 G/ `/ T% ~" g! ]: T, N$ M
按钮与事件:
6 h/ ~; P! L; J0 B
% T4 B2 F' S8 `$ i1 Vimport sys
4 B* T" N: [0 {: O+ L5 O8 J4 g$ W; H8 r
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
6 {: i8 I7 H" `6 S8 I7 m
9 S6 y% g% p$ h2 X" R: ]def click():7 _: [1 ~# [; C! t. G+ O" P. S
% @( B6 |8 C: L3 s
    print("Hy Button is clicked!")
) [/ a9 n  c" q' J- M- ~. R0 F) m' C% G5 [+ T* S: `5 w
app = QApplication(sys.argv)" T3 h' T0 ]4 L! U

( u7 Q5 C7 u! E! F/ v1 Z2 g* Zwin = QMainWindow(). [3 l# E+ n1 W9 e% a& V1 c) Y) F; ?6 f

/ C  ]2 |9 p4 p6 R/ U# }3 ]win.setGeometry(400, 400, 400, 300)8 {! F) F! j6 k1 x& Q  k/ F$ T

2 R+ K3 b/ F. C) twin.setWindowTitle("Pyqt5 Tutorial")
- G5 I) e; w! k4 u8 q6 I8 p
1 a) Y6 i: ]/ ~% v\# Button  B9 O) |, ^$ z
, U1 ^! w$ _! ?  T7 s7 {; r
button = QPushButton(win)9 B* y) o9 e5 K/ w
8 q) h# B$ i4 o7 @, J$ [9 v9 m: @
button.resize(200, 100)5 D( \& u% p- _( o+ ^2 }

. o" L, ?% f9 ~) gbutton.setText("Hi! Click Me")
7 y" T, w" ?7 i$ V  N3 o) P# ~5 C# G+ L# o  M5 D5 U2 a: w
button.move(100, 100)# B7 ?( B) U9 v4 p4 S

+ M1 o0 s" O- Q/ K& E- ?# Sbutton.clicked.connect(click)
  [: q* u; E1 c$ _7 }( y5 }  I. H2 U( J  k8 A. [' z& _
win.show()4 b) }( b* o( g) |
$ T) D! D7 o% H
sys.exit(app.exec_())4 b3 W+ l1 Q, {
: R9 J* V* V$ ]! }/ n

3 g' d: P2 L  s1 D3 x8 c' ~. t8 G6 I  |" O' H+ z: Q1 Q9 p
button.clicked.connect() 在按钮点击后执行特定的事件。* e1 R- U$ U6 m& ^

! A$ t" y0 y8 j, X% F' xPyQt5实战5 a( |1 M! s- x
2 i5 r6 \4 U& ^8 e$ i2 r5 ^/ Z
实战项目:简易的天气查询软件5 O! j: O! v( \6 Q, S! \9 l
7 k6 t  F6 b$ X  L& Y! D
1、使用Qt Designer设计一个界面
! G, x) ?5 c2 d5 T, q* h, Y
$ Q$ f/ {4 E$ s" x/ H* `/ E9 p4 j& u2 ?0 V. p
  L, C2 p8 E9 ?6 [( W
用到的控件有Button, GroupBox, Label,ComboBox,TextEdit,同时定义了两个按钮queryBtn及clearBtn,分别用来查询及清空天气数据。我们需要绑定槽函数,方法如下:3 I0 {5 x# {4 ~; a
2 S2 }+ [( J# z" Y+ H1 X6 }8 O
在Qt Designer右下角选择 信号/槽编辑器,点击+号新增, e2 t# k( A! y, ]+ D
; K* G" i; }; u9 W9 Q$ C
分别选择queryBtn及clearBtn,选择信号 clicked(), 接收者 Dialog 及槽 accept(),(槽函数这里不知道如何定义,后期在代码里再进行修改)
, V' f$ T1 S# B0 E1 J$ p5 c! s) }/ E: \6 j( m' q, H
以上完成后保存为Weather.ui文件。
3 r/ y6 J' D/ I/ ^# G5 R. `5 I. p* S0 P9 \3 {: }% ~
2、转换.ui文件为.py文件5 n" }, R2 `: {1 F8 @: ^  G2 s- g) t

- u4 c1 J0 g9 S, H* cPyQt5支持直接使用.ui文件:( d4 k# a$ B, Q
% F% T( p6 \  ]( M/ G& }
import sys
! M8 E$ z, V" Z3 I% C9 M/ b& r
0 u- \, J, B5 Rfrom PyQt5 import QtWidgets, uic9 }; g  j) H9 q: N: b6 O% Y. |

: X" |7 p' h; @& q9 F3 V% wapp = QtWidgets.QApplication(sys.argv)" ^/ u  N7 Z! w7 X9 k$ R, }9 u

" a4 m6 t$ ]8 u: ^, M2 ?window = uic.loadUi("mainwindow.ui")
# c2 h( p# G% j  B0 B: G0 w( K. m5 G
window.show()
: s7 k2 P1 F3 o+ R0 t& n
: \0 u, }2 j# @8 |( @5 Zapp.exec()7 d! G7 \+ o* ~4 N) R( D, w
但是为了更好的自定义及修改上面的槽函数,可以使用External Tools – PyUIC,即可生成Weather.py,实际运行命令如下:
+ _7 ?4 F5 L+ ~" B4 V
4 @* X/ z' V6 T  CD:\Program Files\Python36\python.exe -m PyQt5.uic.pyuic Weather.ui -o Weather.py
& O% R1 a2 w& i其中,我们需要把两个按钮绑定的槽函数:2 C- }6 G1 S+ c6 ]. q

9 P( ~* R& d# s4 K\# self.queryBtn.clicked.connect(Dialog.accept)
3 f. r4 X1 a4 m, j* @' Q: e" V# u8 y& I* D' U8 O
\# self.clearBtn.clicked.connect(Dialog.accept)
# e; @7 K0 ?8 z# x# E& b0 [- U- _8 l+ G% x
\# 修改为:  g( u, q! `$ s
9 R0 `/ T- g$ K' W( X
self.queryBtn.clicked.connect(Dialog.queryWeather)% J6 N- `/ f( F1 f: B4 o
! x% K7 d! N$ `% ~( T
self.clearBtn.clicked.connect(Dialog.clearText), y9 C; N+ k6 g" c* W" u; P% y
最终的Weather.py内容如下:
5 d1 {4 T/ q, @) S0 S
6 M$ o+ E: l; Y( |' ^1 w( h\# -*- coding: utf-8 -*-
- p/ f- L6 h: P- W& T2 c, W2 _2 W$ l6 |  d# w, P  V
\# Form implementation generated from reading ui file 'Weather.ui'0 y- a: S5 x6 i0 r7 }% h
& ?/ h6 w8 K2 x$ \1 z
\#
) [" M+ z7 d: ~& P; O/ y2 A
  B5 S$ s4 D2 A$ ]+ \3 @\# Created by: PyQt5 UI code generator 5.15.45 W1 S) \: m) n4 @! Q7 s  h! k

# u. i5 _" y) e+ u! o: U\#$ G/ L, f$ W0 y9 G7 L$ E
, |0 k5 B- I0 w) D. Y$ P
\# WARNING: Any manual changes made to this file will be lost when pyuic5 is: D3 t- x8 C2 S- y- Y7 p

4 d4 m$ _$ {" b. S2 b\# run again.  Do not edit this file unless you know what you are doing.& ~) B4 }9 W2 Q" C' r. X8 B

5 d9 b) Z! N7 Q* s3 x" Ufrom PyQt5 import QtCore, QtGui, QtWidgets
3 u& H' e1 ~0 S, H# n
3 M4 l8 d' r2 D1 ]class Ui_Dialog(object):
6 S# O" |. V! a3 V) q7 n" k: l5 X$ }2 V. F# V
    def setupUi(self, Dialog):* N+ e2 T8 V' x& B2 K; N
* J/ h: H( c& U) W  d% b3 ~. |! C5 R
        Dialog.setObjectName("Dialog")
9 u( u4 l5 U4 o: e6 _; U! @7 ~' [
' I4 C+ o# u3 }) Z; K        Dialog.resize(600, 600)4 P, h: a# c8 V6 u

! r3 D4 T! [& X4 N, B        self.groupBox = QtWidgets.QGroupBox(Dialog)
7 e% }: c* u4 X- V& Q( l( [1 i/ ]: J
        self.groupBox.setGeometry(QtCore.QRect(30, 20, 551, 511))! \7 o% b- j+ k
7 _8 R: m: S5 Y1 X) j  U* d
        self.groupBox.setObjectName("groupBox")- |5 o8 l; n( _- ]& |1 b# Z
; d0 k6 y& @' w3 n+ m
        self.label_2 = QtWidgets.QLabel(self.groupBox)
! o6 Y! \- O4 v: z: M9 F. `. D& k) q: U" ?( V* R& h
        self.label_2.setGeometry(QtCore.QRect(20, 30, 31, 16))7 r. m1 u# C$ j; }

  d2 b2 Y' B( ?* T. a! B/ l* O        self.label_2.setObjectName("label_2")
0 m: i# G) e8 y, E
# m2 ?1 k* z; U4 ~4 O* `        self.comboBox = QtWidgets.QComboBox(self.groupBox)% H! s$ P& Y+ p; t
7 c2 g. L/ }/ e' f
        self.comboBox.setGeometry(QtCore.QRect(70, 30, 87, 22))3 L; @; q' s+ O0 u4 h
( \; V) J( }; t! [8 S* W2 u: ~
        self.comboBox.setObjectName("comboBox")! o. p  Z- v) |& U. O: e3 p
$ w3 h% F# a) N$ x. N+ _: P
        self.comboBox.addItem("")
8 d2 w4 q8 l( m3 B
/ A# E* }' t' E% ]; U        self.comboBox.addItem("")6 h# s6 p( E' a) Y. Q# H9 B

+ m2 v# c5 Q0 @( x" }" P* \        self.comboBox.addItem("")
" E$ Y; C1 v$ Q8 i( o. C. {1 }# y" s( e8 Q) z
        self.textEdit = QtWidgets.QTextEdit(self.groupBox): o# R+ W/ [6 `; r5 |4 c" X

1 M. v* M& s$ y! r        self.textEdit.setGeometry(QtCore.QRect(20, 70, 491, 411))6 Q. n0 D* W' Z$ c7 }, i  @
) O7 ^0 r; G: t) ?" p
        self.textEdit.setObjectName("textEdit")
# x5 T) r2 y1 `2 |7 L; |$ j
6 h! {. s/ X: s5 W# c        self.queryBtn = QtWidgets.QPushButton(Dialog)
% y( b# ]7 i9 ^1 ^9 U: o4 k! D7 B; C/ ^! Z( Q
        self.queryBtn.setGeometry(QtCore.QRect(490, 560, 93, 28))7 F. F0 l7 B8 N) g

8 b, R# `: K% ?6 g- z        self.queryBtn.setObjectName("queryBtn")
7 x6 j5 x& p% f) G( o& A# r9 o( q8 L' u/ e6 W+ X
        self.clearBtn = QtWidgets.QPushButton(Dialog)
4 E" x; }2 A5 O& d% G! O8 O" B$ z0 Q. t2 G8 r
        self.clearBtn.setGeometry(QtCore.QRect(30, 560, 93, 28)). c) V9 v- F+ `3 ^8 V

2 r  p; F4 R6 ^        self.clearBtn.setObjectName("clearBtn")8 U2 C' m1 e% d2 w8 q  \
2 i- P$ Q9 G4 o4 Q
        self.retranslateUi(Dialog)/ p$ W' H; y: G8 H

4 t6 M8 a/ T% S1 j        self.clearBtn.clicked.connect(Dialog.clearText)6 Q9 V2 d! X- {1 ?; m# y2 i! N9 z

+ }; @5 Q0 X$ P& {% {# ^+ {; v        self.queryBtn.clicked.connect(Dialog.queryWeather)
% a: R$ k; D* B0 T( z: e5 Y, V
! x: F) t3 b6 B: w' N        QtCore.QMetaObject.connectSlotsByName(Dialog)
$ o6 D) Q$ V  ~6 X6 I4 O7 W9 c, `: G# t6 f
    def retranslateUi(self, Dialog):  }9 o  C6 c0 e/ S% }1 c! f

, B1 ?/ O) s& ?        _translate = QtCore.QCoreApplication.translate
& p- A) m) h0 x0 E" R
* }! ^* y4 u% C6 k3 Y4 J4 I        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
$ a$ T4 L0 r2 t. }+ x5 d1 b* p% l# A+ X/ Q8 f  Q* @. g
        self.groupBox.setTitle(_translate("Dialog", "城市天气预报"))- [; Q0 D9 T/ x- b4 J  h/ s
2 H: R, U" I2 N$ S
        self.label_2.setText(_translate("Dialog", "城市"))
& E: q7 f) k% x# H1 X# a1 C3 x# I) t# l3 U8 A
        self.comboBox.setItemText(0, _translate("Dialog", "北京"))# ]- J( d# E( p+ z2 i: c, \
; I& P( K) E3 G( l( H8 y
        self.comboBox.setItemText(1, _translate("Dialog", "苏州"))
3 h/ K5 J3 w  g: z6 f' p0 W3 t+ U
" Y9 B! o& ~: I% _! [' u        self.comboBox.setItemText(2, _translate("Dialog", "上海"))
" q$ d/ I4 C7 G
/ R  d8 y  x0 G! G% D" [, w        self.queryBtn.setText(_translate("Dialog", "查询"))9 c' y) ]+ _' }: _. p

6 D5 f1 m* U( R( S; k) b        self.clearBtn.setText(_translate("Dialog", "清空"))
( r1 {4 ?+ M2 M" @% Y- `9 A5 E1 I7 ^% h* r% h9 p0 y; ?
3、调用MainDialog% j8 p$ @! L4 e& _1 A7 e
9 a/ W; _5 y" [% K% X* F  z
在MainDialog中调用界面类Ui_Dialog,然后在其中中添加查询天气的业务逻辑代码,这样就做到了界面显示和业务逻辑的分离。新增demo.py文件, 在MainDialog类中定义了两个槽函数queryWeather()和clearText(),以便在界面文件Weather.ui中定义的两个按钮(queryBtn 和clearBtn) 触发clicked 信号与这两个槽函数进行绑定。& J( N5 Y; o3 L0 i+ q/ o3 K* {

& x* Z6 l% ~! B6 S2 |  e完整代码如下:" M9 Z* R' J& K8 z

  [  Y- v; E7 l& h, Z" jimport sys6 o$ t0 T+ ^( V* M' i& F

8 Q, @1 p4 W6 w7 s# ~; Jimport Weather
' x2 X6 z9 y7 Y2 o, Y* i
% k( \3 k$ W" C! Y/ ]: E( X; D. H% ifrom PyQt5.QtWidgets import QApplication, QDialog
/ L; Z7 }6 L' o: I; X( t9 b, T* s5 _4 I$ T9 _" X
import requests
$ L% G4 B0 K2 p- t
+ r0 F* p: l; [4 X1 iclass MainDialog(QDialog):$ b2 K' Z7 J2 u8 q( h' V

2 X2 ]1 J  s1 {    def __init__(self, parent=None):) e* Z5 C& d% [( k6 u
2 l9 B. V; H8 Y. F/ _$ O  N
        super(QDialog, self).__init__(parent)7 d$ X3 w1 d) L' o3 P
- o, ]5 N! y' ~- p$ g  S
        self.ui = Weather.Ui_Dialog()) i" V; i% B. b6 n5 U" W
- a; n+ y* J3 o) U" h, P
        self.ui.setupUi(self)! }; a; G' A) L2 a0 f

6 ]+ a/ N! `, t) V    def queryWeather(self):9 Y9 X7 V$ s  x+ U' x8 ^: d- M

+ _4 y& d+ e/ @( N9 B        cityName = self.ui.comboBox.currentText()
7 Z( l; |: w( T' n" z2 `1 V3 z% O1 V8 n+ |4 L, }. I6 B+ A) N
        cityCode = self.getCode(cityName)
' [4 k6 G5 i; }! s8 p2 r' c* B6 `" A9 s+ f. s8 |$ X/ N$ P
        r = requests.get(8 a6 S: _' U/ C/ B  {, r# s
# B8 m" t0 h+ C. p2 H, n; k. W
            "https://restapi.amap.com/v3/weather/weatherInfo?key=f4fd5b287b6d7d51a3c60fee24e42002&city={}".format(
/ H5 v6 H3 K( x5 x! {9 f: e; g8 I4 m+ u4 {5 j; A
                cityCode))
$ X( V# k& b4 w! a3 G) ~9 Z0 _0 C+ R% H; F2 U! c+ L
        if r.status_code == 200:* V# f1 H" E& e% U# [
) h6 ?0 g! Y0 I* v8 L) ]
            data = r.json()['lives'][0]: `8 J; G' g& d( w# X- S

" {$ @# M8 q9 |. P: [+ b            weatherMsg = '城市:{}\n天气:{}\n温度:{}\n风向:{}\n风力:{}\n湿度:{}\n发布时间:{}\n'.format(% y$ ~' a/ T5 J: L8 ~, k$ A2 f- |

% _9 I- _. q$ e' Q- x" T) s                data['city'],+ T* a3 D. x* X) x2 a/ V; o
1 K. n1 E' K2 D8 S4 R1 n7 a$ l, d) r: A
                data['weather'],
. h9 c- Q% Q+ Z( }# t7 {! X
- I# s2 |, H; ~8 g                data['temperature'],6 u# k5 C4 l+ b# Z  a
; K" w& _6 N: a6 v* w& z, Y2 W
                data['winddirection'],6 L5 r; G( V% C* n: A- R

* o' @4 G$ g0 A0 s! c0 ]$ t                data['windpower'],$ f; E) A# b1 N' G- E0 X+ u

% S* ~( K% I5 y: h                data['humidity'],
! @7 v2 F5 t: v! k5 f7 U, z' U7 q4 o3 l' y- I) c
                data['reporttime'],+ ]* I' Z8 Q: ~: c

: q2 Y3 p; K: q' l% a# c2 f            ); D% t, [$ i' _: [. H7 S2 ^
5 L% A) L8 D% b8 U& T
        else:
! o# x4 G" h6 x. C+ c% T
0 O0 _2 W+ ~. I7 q- ?( z            weatherMsg = '天气查询失败,请稍后再试!'! Z2 _1 ?0 d3 A8 ~2 [+ f  U( m: w3 v3 O
; s+ P7 ^+ X; t/ N% N
        self.ui.textEdit.setText(weatherMsg)! V5 O& G) M8 Q! l0 i

( x7 J# T; D6 G& z) n7 d2 H    def getCode(self, cityName):( Y6 G3 D' D3 ]0 `  n) G

5 P$ x, w* k& z3 g* g        cityDict = {"北京": "110000",# g( Z) \' g7 c0 E

9 L- ]( L( m1 s                    "苏州": "320500",0 Q" g, y) F$ d  B7 j

& N" w0 D! T  d5 }                    "上海": "310000"}+ h4 {! |- O+ X: |( S. z% d

" X6 K6 z8 d: N- {* q        **return** cityDict.get(cityName, '101010100')4 g: |% t3 F! ]) [, f' P
+ S( S; z3 ]6 _% X9 _
    def clearText(self):6 V" k$ M+ D% e

6 ^0 Q/ j# E, o+ I" `+ t% \        self.ui.textEdit.clear()4 a2 V$ f! Q6 Y! Y
8 o1 J% z0 j( M: m! l+ t- t
if __name__ == '__main__':
+ p! [  C& f! e& U2 v2 X' u3 k$ ?/ k- q0 X0 S9 M" a% C0 |
    myapp = QApplication(sys.argv)
3 t5 N3 o7 \" p$ m- \3 E% Y+ \3 V( b2 W
    myDlg = MainDialog()
1 [1 V6 g: Z! d5 ^4 S0 g* s% @& e9 R% Z% @+ H% d3 @9 K9 l5 C
    myDlg.show()
( i* n$ e% m3 a( J- x( h7 b; a4 ~2 ~  p* t
    sys.exit(myapp.exec_())) w9 |( n3 Y) Z( h& O
4 l- w" s+ y/ G+ \
运行demo.py并执行查询后的效果:* N, r7 o, m' @5 g% f

' F& |: z" P1 ?, _6 Z. f' P+ @: v: g. P0 s/ H. S

7 T% T) G3 U( y0 x4、将代码打包成exe文件) k6 a/ S7 x; l0 |

% R- c. t* y. {: ^- S将.py文件打包成可执行的exe在Python中称为freezing,常用的工具有:PyInstaller, py2exe, cx_Freeze, bbfreze, py2app等。功能对比:
, ]: m+ T9 z2 ^% r4 f' i" U3 p0 f6 n
0 E0 B: `/ ?! `3 O4 }

! E# I) _5 H3 _" w. F# Opy2exe:软件更新已经不活跃,因此也就略过。5 Z' n/ b* p" ?

* \* w  m( I/ x+ N2 J/ X9 |2 B% Jpyinstaller:明确支持win8、win10、理论上支持win7,,支持apple Macos, linux。pyinsaller可以打包成文件夹形式内含exe入口执行文件的形式,也可以是一个单独的exe文件。
* R+ X" Y- |0 m# m* J6 ^; K- A1 c1 Y8 k+ `: J
fbs:基于PyInstaller,使用起来更加方便
) d( O2 @' C6 f( p, |1 c/ b: }
; \% M) l2 Q  U( v! Q. K& g这里选择了fbs来打包。fbs的安装方法:4 X, T" R* |% f1 {( h+ U% V
, S2 g# \+ w: B% G" w  l
pip install fbs, u9 u. B& ?0 n: |- V* B
使用方法,在命令行中输入:
- @+ W2 x  A# R+ n8 q  x8 }* ~- ^/ F/ [6 s5 A) a. t* r1 W
fbs startproject
8 ~% ]/ c5 C$ `5 _* p; u* P执行完成后需要输入一些APP的名称等。完成后会生成如下目录:! x  h4 N' p  }) m- G( F, w
: H6 T! F, u1 w+ h/ N8 @5 E

6 @1 P6 m$ B, _
0 h  p  q  ^6 }$ ~% j将刚才编写的PyQt5的代码(demo.py和Weather.py)拖到src/main/python文件夹下,删除原有的main.py,并将demo.py修改为main.py。然后打开 main.py,在文件头部添加如下代码:4 z$ @6 C; v- [# a0 i) M

0 b7 J/ C3 H5 ufrom fbs_runtime.application_context.PyQt5 import ApplicationContext. E: P* `5 a) M  Y, ^5 t
```4 q$ J% r/ s& G+ s4 f1 R- ~3 ?  @
完成后执行:
( V  M: l  j6 }& b. m```' T5 ~4 E, D& F# q. _. ?4 D
fbs freeze
( f8 }$ p  P& X* }, C```. z. U# x% s& L' d9 G6 D2 `- I
即可实现打包。生成的exe可执行文件在\target\MyApp文件下。8 }% u5 h8 C; T7 U0 K2 l( R1 ~0 D
3 S  J. ^- S  q6 T2 H
————————————————7 Q8 I: J+ N+ b  N
版权声明:本文为CSDN博主「宋宋讲编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。7 m6 @) a$ l8 ^% d* n
原文链接:https://blog.csdn.net/qiqi1220/article/details/1262896673 {( R/ m: |5 e- m' X

/ p' b0 F' `2 a0 x* }- o6 y: r0 p1 Q4 k7 B5 e" K9 `0 `* }

1 W# [7 _4 O7 O




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