数学建模社区-数学中国

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

作者: 杨利霞    时间: 2022-9-15 12:26
标题: 超全!Python图形界面框架PyQt5使用指南!
超全!Python图形界面框架PyQt5使用指南!
5 ^! K9 o, Y' H1 x
! \# t+ P7 V$ ^1 A) r- w使用Python开发图形界面的软件其实并不多,相对于GUI界面,可能Web方式的应用更受人欢迎。但对于像我一样对其他编程语言比如C#或WPF并不熟悉的人来说,未必不是一个好的工具。) ]( [. p9 ?' T: k' ]0 l
6 O, k6 T8 c% }$ c- m
常见GUI框架
/ K" W6 [. Y, a0 o; \: h1 z, w# v, o+ S1 x1 V( U5 M
PyQt5:Qt是一个跨平台的 C++图形用户界面库。QT一度被诺基亚拥,后出售给芬兰的软件公司Digia Oyj。PyQt5是基于Digia公司Qt5的Python接口,由一组Python模块构成。PyQt5本身拥有超过620个类和6000函数及方法。在可以运行于多个平台,包括:Unix, Windows, and Mac OS。
: [1 B  f$ J7 M; R% @3 V: B1 T2 Z6 a) ?: O1 j5 c
Pyside6:Pyside是QT公司官方提供的Python包,上一版本为Pyside2,对应的是QT5,最新版命名规则进行了调整,更改为Pyside6,对应的是QT6版本。由于官方出品的比较看好,缺点是发布比较晚,网上的资料没有PyQt5多。4 o* G' I/ g2 ?3 t# o

. L- w1 [& d: aTkinter:Python内置的GUI框架,使用TCL实现,Python中内嵌了TCL解释器,使用它的时候不用安装额外的扩展包,直接import,跨平台。不足之处在于UI布局全靠代码实现,只有15种常用部件,显示效果简陋。* @, I: y( d( B% z0 a0 g) b
- ?2 d* y$ d2 w5 ~  }
PySimpleGUI:PySimpleGUI 是 Tkinter 一层包装。使用 PySimpleGUI 实现自定义 GUI 所需的代码量要比使用 Tkinter 直接编写相同的 GUI 要少得多。
0 j% v% W& z+ z5 P: |+ F0 E
7 ]* V6 U' I  |& k0 k6 NWxPython:wxPython是Python语言对流行的wxWidgets跨平台GUI工具库的绑定。用得比较广泛,跨平台,C++编写,文档少,用户可能就需要根据编程内容对不同平台中的GUI代码做一些调整。遇到问题不好解决,代码布局控件,不直观。
/ Y' H; Q9 B5 r4 m" B
0 e5 h- s) \8 p! T6 G; d+ J, wWax:基于wxPython ,为克服wxPython的问题而制作的一个包。
% [, q/ P2 I2 b4 ?/ ]( X0 A2 D1 j
  I1 y6 o8 v# j' t8 G/ U( I, ZKivy:主要针对多点触控程序,智能手机平板等,也可以在没有触屏功能的系统上,全平台支持(Windows, Linux, Mac OS X, Android and iOS.)使用Python和cython编写,中文支持差,需要自己下载中文库并且制定路径。$ l( W& L' X' G% o
/ B6 ^  \# y6 d! {7 y. S& Q
BeeWare:Write once. Deploy everywhere.需要与Kivy配合使用。2 {, v& ~' t+ l9 [& }, r
4 e6 Q3 g* N6 c  n
Toga:一个使用Python开发原生APP的GUI工具包。Toga由一个具有共享接口的基础组件库组成,以简化与平台无关的GUI开发。Toga适用于Mac OS、Windows、Linux(GTK)以及Android和iOS等移动平台。" o% N7 v) {8 k  b! i
2 [/ ?4 ~9 Y  v1 W6 c
Eel:一个轻量的 Python 库,用于制作简单的类似于 Electron(但是比它更轻量) 的离线 HTML/JS GUI 应用程序,并具有对 Python 功能(capabilities)和库的完全访问权限。, x1 L- _6 M: I9 q
( p0 r, ]2 o2 ^2 I4 H' `
Flexx:一个纯 Python 工具包,用来创建图形化界面应用程序。其使用 Web 技术进行界面的渲染。你可以用 Flexx 来创建桌面应用,同时也可以导出一个应用到独立的 HTML 文档。因为使用纯 Python 开发,所以 Flexx 是跨平台的。只需要有 Python 和浏览器就可以运行。
" \8 a8 K2 R; a  l, G
! S& i' Y# A. M6 J! t* }  n/ G6 cpywebview是围绕 webview 组件的轻量型跨平台包装器(wrapper),它允许在其自己的本机 GUI 窗口中显示 HTML 内容。它使您可以在桌面应用程序中使用 Web 技术,同时尽最大可能隐藏使用浏览器构建GUI的事实。" Z  L$ I4 ~3 S. W$ w
( ~; C2 a1 x9 I: |5 [4 b
enaml:一种能够让你用最小的努力就可以实现高质量GUI界面的的Python框架,也是一种独特的编程语言。enaml将声明性语言与基于约束的布局系统结合在一起,使用户可以轻松地定义灵活布局的UI。enaml应用程序可以在任何支持Python和Qt的平台上运行。8 x( d/ A4 j. u

8 T1 G* a. x4 Q; b3 x* w( u$ z个人想法:太多学不完,先学PyQt5,原因是资料多,学有余力再学pyside6,最后看下PySimpleGUI,看能否解决一些简单问题。, O4 R5 X3 _+ ^
" Y4 `6 }; L+ x$ l- c+ M8 W, A5 N3 i0 |
PyQt5简介; {) R, u# }8 |0 Z$ k

% t8 s& F1 ?) I  R0 G5 T6 |PyQt是Qt框架的Python语言实现,由Riverbank Computing开发,是最强大的GUI库之一。PyQt提供了一个设计良好的窗口控件集合,每一个PyQt控件都对应一个Qt控件,因此PyQt的API接口与Qt的API接口很接近,但PyQt不再使用QMake系统和Q_OBJECT宏。5 p% s! P, A$ w
$ H* J9 b6 u  Y/ c+ A+ H
PyQt5提供GPL版和商业版证书,自由开发者可以使用免费的GPL许可,如果需要将PyQt用于商业应用,则必须购买商业许可。7 D1 s( q+ p8 z
1 }# G+ C( `) C. a
PyQt5特性如下:
) h" V- R- T5 [% {  g4 ^9 |" _/ Q* C' v' u$ q8 P: d
基于高性能的Qt的GUI控件集。# m* ]2 G& U$ p( Z3 ?7 G* N) q

' W6 I# E# O9 B" T0 \% F能够跨平台运行在Linux、Window和Mac OS系统上。
  B7 P7 P1 c( r2 |- s' F
" }5 y& i2 G) j* l使用信号槽机制进行通信。6 h( S+ ?) U) C; U
9 t& h% b$ R: k& x1 v
对Qt库进行完全封装。! N! z" y1 R5 ~( o+ r3 t

$ P, X; D  K! q8 H; v' p可以使用成熟的IDE进行界面设计,并自动生成可执行的Python代码。4 P4 K, H1 M0 M% y! [: g
& \  X) Y2 p- I4 c# g2 x
提供一整套种类齐全的窗口控件。
& `7 g# \$ D4 L: }( e/ W3 N* b5 W0 @. [1 L: K- q9 U( }/ D( ^
PyQt5是由一系列Python模块组成,有超过620个类,6000个函数和方法,主要模块如下:! v; f+ ?- ?7 @, L8 }7 c, K3 a

: f7 c3 |3 {8 ^+ `7 F* rQtCore:包含了核心的非 GUI 的功能。主要和时间、文件与文件夹、各种数据、流、URLs、mime 类文件、进程与线程一起使用。$ [/ R4 u" @. p$ U' g/ y' ?0 r
0 A$ {3 {1 U1 H* G+ V, o7 J
QtGui:包含了窗口系统、事件处理、2D 图像、基本绘画、字体和文字类。
0 `2 L1 ^9 ^- j. F" g) H" N; C! ]/ L6 @' H( h& ?4 G2 I
QtWidgets:包含了一系列创建桌面应用的 UI 元素。# b9 d& R! i5 x! a2 R6 M
# ~$ ?& `* ]1 k4 K
QtMultimedia:包含了处理多媒体的内容和调用摄像头 API 的类。
9 E2 }" G- \& {2 p( O" n; ^& M
: ^' k$ W( z$ x* p2 CQtBluetooth:包含了查找和连接蓝牙的类。: z2 V, p" q1 J% C
4 ^0 x) w( z7 ]
QtNetwork:包含了网络编程的类,这些工具能让 TCP/IP 和 UDP 开发变得更加方便和可靠。, F6 f+ w/ `0 E( B

9 e: L/ f* S" W1 [8 k8 Y# [/ aQtPositioning:包含了定位的类,可以使用卫星、WiFi 甚至文本。
9 C8 H4 x7 M/ O% u( r( z2 S2 q5 d$ M/ ?4 G  }# V. _
Enginio:包含了通过客户端进入和管理 Qt Cloud 的类。
# k' @6 j9 Q: F& T, q; _1 T* k3 b+ M" F
QtWebSockets:包含了 WebSocket 协议的类。2 [* Q( _; s( h  [; E& ^4 w

# D# ?6 B$ S" K/ tQtWebKit:包含了一个基 WebKit2 的 web 浏览器。/ v9 @& @- z5 g4 J* s$ x

" p; ]: Y2 z; s7 J9 ^QtWebKitWidgets:包含了基于 QtWidgets 的 WebKit1 的类。% W# o9 e2 g6 i4 w3 x1 G9 q

- D1 n- f" W% TQtXml:包含了处理 xml 的类,提供了 SAX 和 DOM API 的工具。$ j( y  z8 Y& f
7 q8 O, f# i5 b2 j; @. J% n
QtSvg:提供了显示 SVG 内容的类,Scalable Vector Graphics (SVG) 是一种是一种基于可扩展标记语言 (XML),用于描述二维矢量图形的图形格式(这句话来自于维基百科)。
1 X  M! t' \: B1 f, D% N6 w) n1 j4 `
. e+ U; e/ w" q, zQtSql:提供了处理数据库的工具。
( x- |. N  G$ j" C$ a6 a) i1 N6 M) g
QtTest:提供了测试 PyQt5 应用的工具。
) m  s% F" n: ^# |. H2 V8 W2 d* r* J7 K% _, V; G
PyQt5的安装
6 t, ?2 [; C  v$ b6 v( d+ o; }1 y- S1 m+ P# W: {
由于后期要使用fbs进行打包,fbs对Python 3.7以后的版本可能存在兼容问题,所以我选择了Python 3.6.8进行了整个环境的搭建。主要内容为:Python + PyCharm + PyQt5
  o2 g7 ?/ e' z& s! \* |' t( V2 C7 {; n1 _! C  k
安装PyQt5$ \# o% g6 F& c2 L& _; |. q

) \2 s/ k6 T' ]3 Y) R8 Apip install pyqt5; {5 ?9 G9 B( _0 p9 c# Y6 h
8 a* A! Y- m+ k0 p7 G7 `: N1 q
pip install pyqt5-tools3 j! I( M5 X4 d' x% z2 G3 X+ y
其中pyqt5-tools为Qt Designer拖拽式的界面设计工具。安装过程中可能会报如下错误:
( ?0 \' R$ A: p$ ~7 E9 ]
" N1 @) S5 y* E2 U( O5 v! _- pqt5-tools 5.15.2.1.2 has requirement click~=7.0, but you'll have click 8.0.1 which is incompatible.* d. z  O% w6 M
解决方案:
  h; [6 ~8 n7 ^* a$ Q( p
0 k; J# I$ c+ J) Y) Cpip install click~=7.0
9 j% x4 h2 l. z. ]8 FQt Designer的配置
0 `2 H: O; F9 ^6 ]& |# s1 d5 R8 D; K8 z, f" u# k# k+ J, Z5 C. |' G9 ]
Qt Designer 是通过拖拽的方式放置控件,并实时查看控件效果进行快速UI设计。* o/ J1 `5 _3 L* E4 j1 M0 f+ }

0 }  x  [" k" B- H. i+ r" d5 T9 k$ l6 b9 s9 z6 ?

( R8 N2 E! ~2 w" ~! U整个画面的构成:
8 M5 w, r1 M: S: M! A
* A  P0 B% o/ \左侧的“Widget Box”就是各种可以自由拖动的组件9 X6 {* o& B: `1 c+ ]9 u
3 B) {* _/ {) k- x. O/ p9 I6 m
中间的“MainWindow – untitled”窗体就是画布9 \8 N! \  [1 t4 g6 e( \9 ^

! L+ a; ~6 L4 U# \右上方的”Object Inspector”可以查看当前ui的结构0 K+ E, q$ u6 H0 M5 n

; K/ G+ k% N: t' ^# v% M# ]1 b& e右侧中部的”Property Editor”可以设置当前选中组件的属性' Z! |' N; Q$ W
6 n  G3 N" K! d. c7 y0 ]/ Y- [
右下方的”Resource Browser”可以添加各种素材,比如图片,背景等等
6 m: ?- ]+ J( t- ^( M+ D+ c, E7 v- N3 U4 N! a, r( K
最终生成.ui文件(实质上是XML格式的文件),可直接使用,也可以通过pyuic5工具转换成.py文件。
# _! ]7 _0 Q" X# ?" h' I/ ~2 }
4 B; I. h' e- j; ~( O1 w& q) \QtDisigner配置
" ]' U% p: z& U$ {2 |, L6 w) Z7 ?. E7 D" Z
在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:
9 u# |; ~" ^9 g$ L% E" }) U# e
8 c* _+ R/ e7 r: Y7 O. q# MName: QtDisigner
& L* H: a+ @5 _( Y6 |) K5 K5 `& B# W# v! Z' F' S3 m. x7 G
Program : D:\Program Files\Python36\Lib\site-packages\qt5_applications\Qt\bin\designer.exe # 请根据实际修改( w( A$ D4 u" l

- v" {% ?: ~2 {8 Q" n/ Y+ OWorking directory: $FileDir$4 I4 [5 U- j& E, f! G  [
PyUIC配置) h+ q5 `7 T* O6 C2 d( n

9 T6 f/ D! N( ^) Y% JPyUIC主要是把Qt Designer生成的.ui文件换成.py文件。
& d6 |2 q  l; t& A) v# @
7 U/ V% |: H0 `& R( s# [在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:! T/ B( F8 z2 H/ B! |9 b" F
" r0 I+ u2 W8 x6 S
Name: PyUIC. X, K  q/ f; i! R- }

  W- j7 L8 F9 d9 q2 Y5 p% Z1 E& oProgram : D:\Program Files\Python36\python.exe # 当前Python目录,请根据实际修改
. B% l8 g: P0 ~# |7 L$ t" w. k0 g; z- G. O. t7 c. Y7 ^
Arguments: -m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py
# S' r0 e: |# y& V! c8 h7 A
# V" Q" e, [" GWorking directory: $FileDir$
; q6 I* x9 H! c( Z0 ZPyRCC配置$ |& B) G8 X6 @$ \6 n
* ]) u9 a9 F# a! b7 Z# Q: W$ U9 G/ m. o
PyRCC主要是把编写的.qrc资源文件换成.py文件。在Pycharm中,依次打开 File – Settings – Tools – External Tools,点击 + Create Tool,配置如下:8 y) Z6 F9 G2 }

! e; t  C" `, m: A3 XName: PyRCC
' {* V7 n. a9 O- h( E/ q# O! P1 v: ]% L( j# B
Program: D:\Program Files\Python36\pyrcc5.exe # 当前rcc工具目录,请根据实际修改  Q' n0 k( Q$ ?- F1 g* C# C0 [
% M7 N3 C( B+ ]8 q
Arguments: $FileName$ -o $FileNameWithoutExtension$_rc.py# p( C  M/ `, y4 q/ ~9 Y1 X( t

2 m/ M" J- i; s3 D% CWorking directory: $FileDir$
! E  ^* d9 w% O& H" @1 dPyQt5使用示例
2 y+ C3 ]2 Q* }; y" S  n9 H
7 ~$ A0 R! E5 [  E$ A创建一个空白的界面:
3 @! Y1 F$ T7 O. S% L9 t
2 |4 p# z  q' q! I/ Wimport sys
6 |9 h( C2 m6 r) Z. [  a( Y/ b" J- D2 r
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
9 X: Y; M4 i* H; F, d( N$ g
* O" ^# H. w3 M1 e  fapp = QApplication(sys.argv)
+ g0 K9 ^) b  g' k( X  x8 ~: [. C& Z+ W/ o$ w
win = QMainWindow()0 c; ?( X, a9 S

- z! e- p1 P$ K) `5 ]win.setGeometry(400, 400, 400, 300)0 G/ c$ u6 b. x2 W. M. G! U5 P
2 M: m0 }9 i; i5 M# w2 C, j' m
win.setWindowTitle("Pyqt5 Tutorial")  x$ e( {0 G: F. H* U) j5 c
( k7 _) l. ?. B0 a; I/ X# d/ i
win.show()
  z" t& t' g4 P1 O! V. Y. z0 ^+ c! F
sys.exit(app.exec_()); M, K" L+ Z$ v7 }) W. J0 M! p5 w
& u  Y  C+ Z! F( |1 @
0 R+ _. E2 p& ~$ i

) ]6 Y) k% x+ z2 ]0 G# g6 b- ]其中:: V# C2 y1 t$ l/ w; a

) A* O) |6 C8 R0 Q) _+ LQapplication():每个GUI都必须包含一个Qapplication,argv表示获取命令行参数,如果不用获取,则可以使用[]代替。
; A9 }& a- Z$ e% y0 r0 K" ?2 i/ M' O! [) i) K2 d
QMainWindow():类似一个容器(窗口)用来包含按钮、文本、输入框等widgets。arg标识可以获取命令行执行时的参数。1 B5 M8 Y/ K, X- w" p  ]! n# f

9 p1 O& ]( ~( R1 ~/ _SetGeometry是用来定义 QMainWindow() 窗口的尺寸, 语法:setGeometry(x, y, width, height ),其中x,y为屏幕上的坐标点。
: q) @  _0 X/ \& w8 {) p& l+ v3 P6 Z3 D) i
show():用来显示窗口1 M8 U7 Q+ A1 g
& H& p" W1 Z( o$ c( J
exit(app.exec_()):设置窗口一直运行指导使用关闭按钮进行关闭
. f! e* M( F8 ?: P0 u
8 W4 M; v& u( ~3 CPyQt5支持的常见Widgets有:
* V( R' p' i3 c, q, N" g. `4 O$ p" B4 @" J. Y

2 j$ Q9 ?4 T9 @% q# o5 U5 i
: K& h$ b# b: {4 k! _% v9 t8 b* o6 u从上到下,从左到右依次为:Qlabel、QcomboBox、QcheckBox、QradioButton、QpushButton、QtableWidget、QlineEdit、Qslider、QProgressBar- I+ q6 o  X* ^8 H8 I2 j) q
2 E. p+ s* P; `9 g
对于使用Pyqt5设置文本内容,我们使用Qlabel:
9 m2 o0 S8 n) F) x' b1 b) A- O0 {! z: l/ M2 b' u( |
import sys  P0 Q8 h! A. A2 B. v

, P! W6 }/ M8 k! f" n( p* ~from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
& i) R) k) h5 J, x3 }  {# d8 E
! P0 Y% F9 g% o5 K9 h2 Aapp = QApplication(sys.argv)8 I% Q" {8 u! o9 d& ?( Z( t# I- T- t

* ]' C! F) o  N+ X$ N3 d& ~2 @win = QMainWindow()$ o# [9 Y; l9 M5 e

' a/ `% O* A4 ?win.setGeometry(400, 400, 400, 300); k  H% K: z# i$ s9 O: W4 F

) i8 D3 e7 r2 g& O4 A0 H( c( @win.setWindowTitle("Pyqt5 Tutorial")7 l1 S8 s9 q* O
- S7 I" [. f+ r! l$ _2 X4 Q' j
\# Label Text  D3 r, [* Q  T
8 D4 d2 A5 l+ ~/ k6 }; h7 |  ~
label = QLabel(win)
$ G5 p/ J- O3 E8 T6 S  |
& R- }  a- u2 y. Qlabel.resize(200, 100)  C3 R6 L, J$ C
  P6 E* I( A9 |1 w# L; {! w+ I
label.setText("Hi this is Pyqt5")2 |3 H! }+ `- P6 N. E9 h9 Y0 j

2 o+ ]  B, Z& `2 }4 f: elabel.move(100, 100)& K3 N/ u- J! O2 s

/ M& E! U( @0 bwin.show()! m8 V" t" }6 V8 W

" {5 D6 R; G* _sys.exit(app.exec_())
4 @9 q) e# ~3 g- r/ C8 e7 x9 j2 Z& Z1 M+ [; V6 O& j# y6 ?% w( r% L
0 O# z+ i& Z0 g4 O4 N
0 ~% V2 F" }+ s, o+ @) H
按钮与事件:' Z$ p, c3 \2 }0 C, H* T
/ C1 H, v. Y2 c6 m4 l$ T
import sys
* `- C4 g5 u" ~. Q/ e7 y5 h$ y# o- r! C& V7 B' ^0 \% O
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
6 _* u( q! I# B) p0 S" F
& P: G' E: g  T* W- l4 T8 {def click():
; B* o9 J% `- p
! y& f* l" f$ i4 V1 E1 w! q    print("Hy Button is clicked!"): I# U, D; l! k. l' ~0 I- {, G. l8 _
( E" |/ q# |$ F( d
app = QApplication(sys.argv)9 Z- V/ W* B0 ^) U, D6 ?7 k
& h) j/ W; k& m/ e
win = QMainWindow()' j* b( w( f, u+ y
0 x  Z7 x, D( G
win.setGeometry(400, 400, 400, 300)
' o* Q* A; f/ W) s$ T7 A/ h4 |4 j  X: X! n
win.setWindowTitle("Pyqt5 Tutorial"); C; Y/ D7 \( x& c
1 X: m$ I, m& v4 Y, b9 B- G6 D5 e
\# Button
: ~: y0 `" V( U6 G  P3 I7 Q! U! a3 j. }! r& h9 ?8 c
button = QPushButton(win)
; f  E# R1 ~4 \& l8 {1 Z; U
$ Q- B3 R7 F0 a7 @2 X# gbutton.resize(200, 100)
3 i- h5 j* B' K  G# B/ g
& l/ |4 A# i+ J( p- ]; {button.setText("Hi! Click Me")  h" r/ {5 ?5 g6 V1 k
' [0 u& N5 j/ w. D! x" i: R
button.move(100, 100)
1 O  T4 L7 t/ q! F+ Y, }; w/ n* _5 Y$ V' X' T" J! B3 v/ V1 s0 r
button.clicked.connect(click)
  ]. s5 D5 H! Q! r$ G$ a& f
* \3 q  f5 T$ S9 W$ `( S  gwin.show()2 Q. b- m1 j; W9 U) a: e( y
( M3 ]6 ^1 J! \3 M  R% ]
sys.exit(app.exec_())
' `  {5 p( W" W9 [* B7 R$ W6 ^5 n6 U7 D: [% v
2 e$ T4 x. Q& ]7 A5 w' w
! [3 w8 k2 p6 z& k
button.clicked.connect() 在按钮点击后执行特定的事件。
! |# q, x) A' Z1 S+ _* H8 z% k9 w2 O
PyQt5实战
' T4 a6 d/ A  g" E0 V5 t% u  g# `: N& z5 o
实战项目:简易的天气查询软件
) j- L) d* C8 h2 a
+ G, I1 a- C7 M1 @1、使用Qt Designer设计一个界面
4 k+ Y! E0 @% O' ^0 [4 X: m. n$ x; ]  R7 O
9 h6 P5 h& y9 R7 K$ {9 A

  A& W/ b4 Y7 n- C用到的控件有Button, GroupBox, Label,ComboBox,TextEdit,同时定义了两个按钮queryBtn及clearBtn,分别用来查询及清空天气数据。我们需要绑定槽函数,方法如下:
% w0 T+ x2 A" |9 z5 b' \# a6 }, Y- K  a* g# ^
在Qt Designer右下角选择 信号/槽编辑器,点击+号新增. Y  B( n0 C# P* d4 W' W- a* u

/ G1 d' j* h6 {5 p分别选择queryBtn及clearBtn,选择信号 clicked(), 接收者 Dialog 及槽 accept(),(槽函数这里不知道如何定义,后期在代码里再进行修改)4 s2 W0 }: n/ T

( p$ I1 M; P: H/ x7 \4 }; {以上完成后保存为Weather.ui文件。8 F1 \# q; o1 i2 A' |, l
% Y3 C6 z) Q: I: ^. R; w
2、转换.ui文件为.py文件
; P( C7 S3 M- o
+ o6 E. }+ X8 N. z. l. f# ^% gPyQt5支持直接使用.ui文件:9 e) o; w7 S4 {
9 }- T3 {: F$ t% w& B. k" E" R
import sys- v6 I# {0 b: `, d% W- O
3 h$ |5 [6 k6 _+ I* V8 @! W# ~8 Y
from PyQt5 import QtWidgets, uic* [$ I9 {& k1 c0 f3 k: n. u2 O

. |1 c& Q, ?( b5 e2 t4 Zapp = QtWidgets.QApplication(sys.argv)
3 h4 Q. ?9 Q) N) V- X
0 w' }, i3 w4 ^& P" Q$ Cwindow = uic.loadUi("mainwindow.ui")& m6 i: `0 M9 }% B4 V: z2 T
+ e7 U$ L, u9 B0 |/ _4 }
window.show()
. n. B+ v, X3 \) @$ `; {5 y3 ^: d% v) U* y$ G" J/ _
app.exec()
5 W7 ?8 ]$ l. d) W+ m$ ^但是为了更好的自定义及修改上面的槽函数,可以使用External Tools – PyUIC,即可生成Weather.py,实际运行命令如下:, ]9 C8 ]2 c6 d' H+ [
% D! v" h$ o+ ]; G5 r
D:\Program Files\Python36\python.exe -m PyQt5.uic.pyuic Weather.ui -o Weather.py- j  \: J# P% \1 ^) a- R  H/ Q. I8 n
其中,我们需要把两个按钮绑定的槽函数:4 H1 q6 v5 ^/ `1 M6 i1 N- R' Y
  B7 H9 ^7 [+ `
\# self.queryBtn.clicked.connect(Dialog.accept)
" N. Q8 `  k0 L. ?9 p. T: Q; i9 J1 ?; u( R7 X/ M6 i0 K' z$ _
\# self.clearBtn.clicked.connect(Dialog.accept)/ S! ]0 \" t' L& T8 _* `' t7 _' J, f3 l
0 O6 D( ?& s* T! D2 U
\# 修改为:
3 L! n; ?  c5 m. Z# n' n
; Z) ]% K; X3 U! J6 P& K, J4 eself.queryBtn.clicked.connect(Dialog.queryWeather)
; Q; k( i: U, ^: H6 C! I5 ^4 A5 J3 \8 w; K4 B
self.clearBtn.clicked.connect(Dialog.clearText)
  n" P8 Y% W, s0 i& m# d) p最终的Weather.py内容如下:6 w! @- `5 l! m' @+ ?1 o, t

2 V( Z4 f) C; B& T8 i1 C7 Q\# -*- coding: utf-8 -*-6 O2 d, [. V# s& H0 m& C4 b
" o' u5 t' r' J, @6 S+ A; ^2 ~
\# Form implementation generated from reading ui file 'Weather.ui'
1 i/ i* n* D$ T! z3 K% m& [3 K, j+ F$ {1 f, E; P
\#
: U7 b) g0 @' _: R1 c0 [! I. H# S6 ^
\# Created by: PyQt5 UI code generator 5.15.4; q) y- B( |, W* a5 K5 U, d

) n# K( m( {4 }" i0 x2 g$ [$ B\#
+ c. C( ]$ @, N' c  o
  n; S( k. F6 c2 Y/ E: r% B9 A\# WARNING: Any manual changes made to this file will be lost when pyuic5 is: W, b$ `; m6 A; C" ]3 t
* y1 U# p8 g- Z; l# q
\# run again.  Do not edit this file unless you know what you are doing.
% V; K! S; c8 \& \$ s7 ?' l% R. s6 o5 }7 ~5 l
from PyQt5 import QtCore, QtGui, QtWidgets$ n, L5 a: o2 K0 S5 h# Y
5 ?8 k3 Q. f# [# P
class Ui_Dialog(object):, N; U( `0 b0 K5 K0 Z: H
$ B& x% B) i8 \6 g' u- w& I
    def setupUi(self, Dialog):
5 A( S8 \+ e% X$ d* H0 ?" k& c9 _  j# O. `
        Dialog.setObjectName("Dialog")- Z$ ~- u' O8 E# M% L/ ^3 ]0 D; P

  K* K8 d0 x( e) _6 a* p        Dialog.resize(600, 600)- D1 U4 G# R8 {  n. i; J4 l
. g% w+ B. J7 V( ?8 d$ n  Y7 _
        self.groupBox = QtWidgets.QGroupBox(Dialog)
. u' p+ ]8 |) u9 h. m( G6 {
( r2 ^- L" ^, ~: @* u        self.groupBox.setGeometry(QtCore.QRect(30, 20, 551, 511))  s9 ~* M" r. y/ L0 n

% |" w+ i  J7 Y+ _0 w2 N) a. f& U        self.groupBox.setObjectName("groupBox")1 }# f0 W% S$ U7 u! }. A4 A+ J+ @

- X" R0 G4 F9 |        self.label_2 = QtWidgets.QLabel(self.groupBox), v" N" U& Y( o5 k6 S0 Z4 ~
7 k) y! D4 F3 n* q% B  y3 J
        self.label_2.setGeometry(QtCore.QRect(20, 30, 31, 16))6 W$ Q4 z! q8 a  R! O
; j( z8 V2 O& B) D* n
        self.label_2.setObjectName("label_2")7 v+ w0 s* _/ a# v+ q+ _( w1 \
% V) g; O2 h1 f7 N1 J
        self.comboBox = QtWidgets.QComboBox(self.groupBox)4 _  B$ i/ j# }
! A9 a* m( g7 ~' L
        self.comboBox.setGeometry(QtCore.QRect(70, 30, 87, 22))
1 O/ `9 Q( w: V
0 o8 u1 ~! A3 a9 N+ \        self.comboBox.setObjectName("comboBox")
. l5 J5 _$ q$ U0 Y! x: |) m0 N( j' \  @
        self.comboBox.addItem("")
. x1 x) ?9 v. Y) |) _3 _- W4 q/ Q
& D2 F6 n0 E& o. }" I; @# D+ m        self.comboBox.addItem("")
( b) }' D7 i7 h
/ S9 v- a; V3 ]# U' g5 x6 J+ e  X        self.comboBox.addItem("")
/ d' k8 _" a. L# W; d. [8 z) N3 e
        self.textEdit = QtWidgets.QTextEdit(self.groupBox)" S: |% i6 ]0 [! w: D+ T

. ]! m" V5 l! [        self.textEdit.setGeometry(QtCore.QRect(20, 70, 491, 411)); o# \* t% O6 Q5 I

9 s8 u- p+ J: Q0 x+ k$ T# V/ B        self.textEdit.setObjectName("textEdit")
! @2 D* F3 q  F, O/ i* K  Z
/ m& F7 L$ Z* {# m/ k' K        self.queryBtn = QtWidgets.QPushButton(Dialog)# }6 s$ {4 @$ q% @% W  L

0 F1 u& N, ^' v% t8 G        self.queryBtn.setGeometry(QtCore.QRect(490, 560, 93, 28))
, H1 P3 @0 \; H4 M% k  y
4 a1 o, f9 f1 X8 V1 x6 l; B  A        self.queryBtn.setObjectName("queryBtn")2 `2 @+ A! N# G  B& P

+ l; u& ]' ~. n% C' S        self.clearBtn = QtWidgets.QPushButton(Dialog)
; L2 L& H$ X# _7 _9 K
1 ~' g5 i0 R, I  q2 v        self.clearBtn.setGeometry(QtCore.QRect(30, 560, 93, 28))
; f9 R7 H1 S* l% e2 W! M! ?) G4 H% h9 F  Z5 r/ M! r1 U
        self.clearBtn.setObjectName("clearBtn")
2 g* f& p! U' q+ r) y. Y% O0 b: r% b/ G* D7 r4 ~  j: M
        self.retranslateUi(Dialog)% @- G3 L; |9 A3 l7 C2 R
% d6 Q6 i% @, r
        self.clearBtn.clicked.connect(Dialog.clearText)
; M) H6 \; w$ V4 ~3 M7 f0 Y! Y3 J  d8 c: R
        self.queryBtn.clicked.connect(Dialog.queryWeather)
( B' z0 y# v+ H: y8 C1 u; S' @6 U3 i+ m5 |% v5 J# Y" [
        QtCore.QMetaObject.connectSlotsByName(Dialog)' b+ n. Q+ q2 ^1 Q, B" d' C( i
" a+ F$ Z' }0 w  X/ z* D  T
    def retranslateUi(self, Dialog):2 U2 P4 Y  _7 Y2 q$ l( W: Y+ D. U9 X

6 B* [) U/ U* T% Q+ }( |2 |        _translate = QtCore.QCoreApplication.translate' }  y# @2 O0 g! K
( N* Z9 I% ~( A5 M
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))$ u  |+ U5 \% h  O6 q
- B6 W( {  G% Z" \$ w1 W5 Z
        self.groupBox.setTitle(_translate("Dialog", "城市天气预报"))
: V/ b' H3 E, i4 E8 P8 _" W8 S$ U3 z- ~
        self.label_2.setText(_translate("Dialog", "城市"))& i/ L8 i" v6 ^

9 Y+ r( j( X( R        self.comboBox.setItemText(0, _translate("Dialog", "北京"))& e4 n: o: i. Y

& M1 ?9 x# a% \. K5 i3 s4 t9 V0 O        self.comboBox.setItemText(1, _translate("Dialog", "苏州"))
1 F. P2 p# |* v1 G! e* A, X  |8 j% H% K9 D" C: X. j; @
        self.comboBox.setItemText(2, _translate("Dialog", "上海"))
' q6 L: ^% v$ H
8 T9 |$ s1 v2 U$ }  y        self.queryBtn.setText(_translate("Dialog", "查询"))9 o& A& S0 i7 Q4 {) Q: Z

: [9 B& x5 y5 w7 U% M6 h' j' P8 C% _1 V        self.clearBtn.setText(_translate("Dialog", "清空"))6 i- M  N6 j, h
3 ]* ?8 ]; U* `4 z8 g! N
3、调用MainDialog
; O7 a! j. }8 W* D
) _  T/ a& s/ W在MainDialog中调用界面类Ui_Dialog,然后在其中中添加查询天气的业务逻辑代码,这样就做到了界面显示和业务逻辑的分离。新增demo.py文件, 在MainDialog类中定义了两个槽函数queryWeather()和clearText(),以便在界面文件Weather.ui中定义的两个按钮(queryBtn 和clearBtn) 触发clicked 信号与这两个槽函数进行绑定。
0 m& D0 |: F$ s7 @1 ]5 r9 |& S8 Q" {% G( ?2 Y3 ~, k6 c
完整代码如下:; d/ g2 F* v0 V5 d
, ?$ d" @: C9 ^* E& M8 p0 n9 Z
import sys/ T: l: m$ n  c# N3 e5 z" _

/ d5 a( C6 w* F% V# {- y' }* Oimport Weather( U- a- _" l  l2 \0 \3 V: M( Q# D
) Z  H3 x6 ]3 K2 @( p: m8 e
from PyQt5.QtWidgets import QApplication, QDialog+ z- F1 b- q/ t. Q2 l: T

" l. T8 u3 n! @# f& w( Qimport requests' `+ E" l. I+ o0 b5 W3 v

; z1 H; X8 B$ i& aclass MainDialog(QDialog):8 c7 N* [. `1 P
& D1 \! F0 i1 A8 ?0 n- S# X
    def __init__(self, parent=None):
$ N: P; z* y4 Q. C  [$ [# [; o( ^- j1 e  |, [' O
        super(QDialog, self).__init__(parent)
* y- L! p8 f( V$ d7 X, D6 f6 G8 h9 E4 Q5 d) B4 A* j$ F
        self.ui = Weather.Ui_Dialog()
3 f; Y; B, Z% K* v3 w! W! u0 }+ ?! a; N2 L0 {
        self.ui.setupUi(self)& I2 w2 B* R/ ?4 ~

- e- k' [% P) P  D7 x    def queryWeather(self):
, t3 m: a0 }) z' r9 e9 w& D: {# Q$ N2 T* m9 O& O
        cityName = self.ui.comboBox.currentText()
/ ^$ A3 c  R3 y. m( z5 G! c+ X2 d2 b0 f6 Q! u4 F  p
        cityCode = self.getCode(cityName)8 `8 p6 T0 G- {+ W0 f% E
/ Q1 o: E  s: q" r$ L  Q. o
        r = requests.get(! V8 g8 f& K! E9 S5 i3 w$ W. ^

$ r" _. u7 e: p/ ?4 v# g6 G            "https://restapi.amap.com/v3/weather/weatherInfo?key=f4fd5b287b6d7d51a3c60fee24e42002&city={}".format(1 W6 `! Y& C( M7 |

6 Q1 C2 v7 e/ z$ K                cityCode))
/ j! Q) w1 ?% N% b4 V9 V' ?1 ?9 V
( N0 B1 O$ V- W3 j% L0 Z        if r.status_code == 200:# \' k. ^3 i) y' x% V8 ~

3 }9 o' i. z/ d8 ~5 [: n            data = r.json()['lives'][0]3 _5 l# u: @, z) R% V3 V' n+ p# z

9 ~# a4 ?0 y; I0 v  P+ I            weatherMsg = '城市:{}\n天气:{}\n温度:{}\n风向:{}\n风力:{}\n湿度:{}\n发布时间:{}\n'.format(: j* E5 @/ v7 }: M5 ^' ]
1 f& L" r6 J+ S) M
                data['city'],& T/ F4 u( Y# J; f+ f* `
1 R$ Q1 J& M" `; r1 l  p/ u
                data['weather'],+ g9 F) [1 S: g) \1 D
6 h6 r3 H0 g" S% K+ \
                data['temperature'],
( O- T' k$ r( @+ X# k& W) n
# q  u8 w2 B3 p( y                data['winddirection'],
$ G; r$ l1 u9 K8 ^# |% K, K" N  O% f2 ^
                data['windpower'],
, [8 i) E0 V# r% J* k8 }+ c3 G# f3 x
! b% A0 L$ [: v  }1 H* W                data['humidity'],' u' }  o7 s+ C' ^2 i7 w

* I! ~, R1 `8 {2 i  h. ?* B                data['reporttime'],6 c, l  F: y) y" m
2 y; ?) I( z4 b! Y8 s* f, m+ l
            ); M1 i3 E; g. Q. ?& x8 s, I

3 j* A! u5 b! K; y        else:% O1 I5 E# _: T! \& k; r$ c: Q
' @! n3 C" ?( u% |4 k$ N
            weatherMsg = '天气查询失败,请稍后再试!'
  `" M4 b1 r+ F
0 s5 h0 N; m: A2 E; V& _        self.ui.textEdit.setText(weatherMsg)
* L% n0 Y% l* {
  A2 Y5 ?9 g. B7 G    def getCode(self, cityName):2 }6 ]) d% V% B- P

4 E+ n/ k4 \* h: X8 S0 |: O1 s        cityDict = {"北京": "110000",
5 B- P5 f) R, @7 j  b7 i; G( `8 Z4 G: h3 X: _7 k4 T- e
                    "苏州": "320500",! f: W2 r7 W# U. A0 D

4 a: T; `' }$ o% g                    "上海": "310000"}1 }) L+ l8 Q" D. i; Y- |

+ s& z, c7 V! @- _: E        **return** cityDict.get(cityName, '101010100')
; M' s" t2 n/ ?0 n" u6 B
7 r* W* c( u  D6 w7 @1 \' X    def clearText(self):. u7 Q' x# w/ T% g, V

" O) O8 S( i- ?0 c% n- g# ]        self.ui.textEdit.clear()1 a: @! D4 a& s- M, z

9 B8 O5 e( Y$ o) j1 Kif __name__ == '__main__':! Q) S/ k# ]4 b# I
' X, g7 j. N+ S' j- X
    myapp = QApplication(sys.argv)
# ?" {2 o2 }: E. H9 O8 }+ C4 k% Y9 o( F2 I! |8 X
    myDlg = MainDialog()7 m* N$ ~1 N, ?

0 U$ Q8 r* N/ ~4 ?    myDlg.show()
( B5 e7 X% P, z7 x# I
5 G) E3 H! |  p# P    sys.exit(myapp.exec_())
; v7 [+ ~& {# P5 U- E; |5 V' O( F( c* q9 [( n6 Q
运行demo.py并执行查询后的效果:2 F8 w8 N& _8 `+ t
9 G) \# G5 ~  d9 v- G, n  @
( C; N9 B3 M0 [3 q1 p' g* `9 y
' I7 h7 M' V' z; X- ]5 Z
4、将代码打包成exe文件; R( }* c* K# C3 i7 P  y

; {0 N; g1 y1 b+ ~  Q7 V) {" m将.py文件打包成可执行的exe在Python中称为freezing,常用的工具有:PyInstaller, py2exe, cx_Freeze, bbfreze, py2app等。功能对比:
' D( H1 V$ ?- }( ^! H
7 W% C% A" z4 h+ `1 z' b
* K, d' F! K3 |- X! |
+ E. b6 ^4 h9 ppy2exe:软件更新已经不活跃,因此也就略过。
2 r1 M7 T; X) E1 B2 E" L
+ k+ L: \0 z6 e" m9 epyinstaller:明确支持win8、win10、理论上支持win7,,支持apple Macos, linux。pyinsaller可以打包成文件夹形式内含exe入口执行文件的形式,也可以是一个单独的exe文件。' f, X1 w7 J) b  r$ L  x

( D$ g% _8 o% a" ffbs:基于PyInstaller,使用起来更加方便
9 E3 {- Q& d$ ]8 n" f6 T  ~3 o7 G* p& M
这里选择了fbs来打包。fbs的安装方法:
/ h5 g5 u; U  h1 t* v& h$ \8 e. a' j0 T! O  Y$ @3 K7 F0 ]6 |
pip install fbs8 l2 C, D' S+ l* o) g9 h# X
使用方法,在命令行中输入:
) b2 `  H0 P; [6 F
$ d& i. N! q4 bfbs startproject0 C# E4 z0 c" [% D$ R% @2 A
执行完成后需要输入一些APP的名称等。完成后会生成如下目录:; }" b4 X8 b. F
' Z' j8 d, u: J. _5 M

, o9 l6 a. n& Q2 |5 n
  o6 V  ^, S4 h) }! i' H6 O5 F) j3 U将刚才编写的PyQt5的代码(demo.py和Weather.py)拖到src/main/python文件夹下,删除原有的main.py,并将demo.py修改为main.py。然后打开 main.py,在文件头部添加如下代码:
$ H1 j$ A" T' v* b, }) T
6 n/ G9 g! ~( n, wfrom fbs_runtime.application_context.PyQt5 import ApplicationContext
- H* I/ k: C4 e% \# ]```8 @6 ~! [7 O0 F& v$ [7 w( U
完成后执行:( J3 W/ a2 s. k$ S* L
```" ^' K$ a% o0 y
fbs freeze9 C. y# @5 i& N; c  l0 z, d
```1 m1 z1 a. ^& r1 H* P3 I, k7 Y4 Z6 R- ~
即可实现打包。生成的exe可执行文件在\target\MyApp文件下。
+ n2 w, @3 P# }7 w( A; _; g6 J! m1 q
————————————————
& o% V2 l% r/ A: H, Z* u# M  m版权声明:本文为CSDN博主「宋宋讲编程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。7 Y4 g- T  U4 x0 x2 c# u
原文链接:https://blog.csdn.net/qiqi1220/article/details/126289667
4 v% D0 V; q5 [1 {; j- z  h1 P% ^0 q7 O7 _7 P7 q+ m

- C5 `7 ]# x; R) G  m8 L, d9 r( s3 h7 P6 A5 n$ e8 C





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