Python Qt | 白月黑羽

# Install

1
2
pip install pyside2
pip install pyside2 -i https://pypi.douban.com/simple/

# Qt Designer UI

运行 C:\Program Files\Python36\Scripts\pyside2-designer.exe 创建 User Interface: httpclient.ui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>615</width>
<height>591</height>
</rect>
</property>
<property name="windowTitle">
<string>HTTP 接口测试</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,8,1">
<item>
<widget class="QComboBox" name="methodBox"/>
</item>
<item>
<widget class="QLineEdit" name="urlEdit"/>
</item>
<item>
<widget class="QPushButton" name="sendButton">
<property name="text">
<string>发送</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="3,1,4">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>消息头</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addHeaderButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>+</string>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="removeHeaderButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="headerTable">
<attribute name="verticalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>名称</string>
</property>
</column>
<column>
<property name="text">
<string></string>
</property>
</column>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="topMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>消息体</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="bodyText"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="resultText">
<property name="placeholderText">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="clearButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>清除</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

# Progam Logic

httpclient.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from PySide2.QtWidgets import QApplication
from PySide2.QtUiTools import QUiLoader
from PySide2.QtGui import QIcon
from requests import Request, Session


class HttpGui:

def __init__(self):
self.ui = QUiLoader().load('httpclient.ui')
self.ui.methodBox.addItems(['GET', 'POST', 'PUT', 'DELETE'])
self.ui.headerTable.setColumnWidth(0, 110)
self.ui.headerTable.setColumnWidth(1, 110)

self.ui.urlEdit.setText('http://localhost:3000/users')

self.ui.sendButton.clicked.connect(self.handle_request)
self.ui.clearButton.clicked.connect(self.handle_clear)
self.ui.addHeaderButton.clicked.connect(self.handle_header_add)
self.ui.removeHeaderButton.clicked.connect(self.handle_header_remove)

def handle_header_add(self):
current_row = self.ui.headerTable.currentRow()
self.ui.headerTable.insertRow(current_row + 1)

def handle_header_remove(self):
current_row = self.ui.headerTable.currentRow()
self.ui.headerTable.removeRow(current_row)

def handle_request(self):
self.ui.resultText.clear()
method = self.ui.methodBox.currentText()
url = self.ui.urlEdit.text()
headers = {}
ht = self.ui.headerTable
for row in range(0, ht.rowCount()):
key = ht.item(row, 0).text()
value = ht.item(row, 1).text()
if not key.strip():
continue
headers[key] = value
data = self.ui.bodyText.toPlainText()
session = Session()
req = Request(method, url, headers=headers, data=data)
prepped = req.prepare()
resp = session.send(prepped)
self.ui.resultText.appendPlainText(resp.text)

def handle_clear(self):
self.ui.resultText.clear()


app = QApplication([])
app.setWindowIcon(QIcon('logo.png'))
stats = HttpGui()
stats.ui.show()
app.exec_()

# Publisher

在 Windows 上只需要执行下面的命令,即可制作独立 exe 程序。这样就会在当前目录下产生一个名为 dist 的目录。里面就有一个名为 httpclient 的目录,我们的可执行程序 httpclient.exe 就在里面。

1
pyinstaller httpclient.py --noconsole --hidden-import PySide2.QtXml

--noconsole 指定不要命令行窗口,否则我们的程序运行的时候,还会多一个黑窗口。 但是我建议大家可以先去掉这个参数,等确定运行成功后,再加上参数重新制作 exe。因为这个黑窗口可以显示出程序的报错,这样我们容易找到问题的线索。
--hidden-import PySide2.QtXml 参数是因为这个 QtXml 库是动态导入,PyInstaller 没法分析出来,需要我们告诉它,

最后,别忘了,把程序所需要的 ui 文件拷贝到打包目录中。因为 PyInstaller 只能分析出需要哪些代码文件。 而你的程序动态打开的资源文件,比如 图片、excel、ui 这些,它是不会帮你打包的。在 dist/httpclient 中添加 logo.png, 示例代码需要从 httpclient.ui 中加载界面,也需要手动拷贝到 dist/httpclient 目录中。然后,再双击运行 httpclient.exe ,完美!

Edited on