python开发的桌面程序代码详解

上篇文章展示了这个简单的桌面程序,功能虽然很简单,开发过程还是遇到了很多坑,用了两天时间基本把坑给填了,其中两个主要影响的坑,python开发gui的的扩展库选择和python gui程序打包出现的问题,这两个问题我觉得也有必要单独说下,这里就只说代码。

环境准备

你永远不可能知道我要写这个小程序的初衷,你不会知道我原本的打算是要写一个h5专题生成系统(本人之前使用php写过),想到需要用到切割图片就看了一下python关于图片的处理库PIL,后来想到切割数量较多的时候客户等待体验非常不好,就又关注了下RabbitMQ消息队列的使用;考虑到h5专题系统稍微复杂点先写个自己日常会使用的图片切割桌面app;奈何本人没有桌面应用开发的经验,只知道python有这么方面的扩展包,先找了tkinter,发现能实现的界面实在是太简单了发现了pyqt5;功能实现后又要用到打包成exe,又找到了打包的扩展包pyinstaller,打包过程的坑上面讲了后面会说。

总结以上来说我们用到的扩展包有:sysosPyQt5PILpyinstaller

代码展示

代码中一些地方我已经加过了注释,故本人觉得没必要在一行行解释,后面我会贴上关于pyqt5可参考:pyqt5安装及使用

功能代码win.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
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import sys
import os
from PyQt5.QtWidgets import QWidget, QApplication, QGroupBox, QPushButton, QLabel, QHBoxLayout, QVBoxLayout, QGridLayout, QFormLayout, QLineEdit, QTextEdit, QInputDialog, QFileDialog, QMessageBox, QDesktopWidget
from PyQt5.QtGui import QIntValidator, QDoubleValidator, QRegExpValidator, QTextCursor, QIcon, QPixmap
from PyQt5.QtCore import QRegExp
from PIL import Image


class ImgCut(QWidget):

def __init__(self):
super(ImgCut, self).__init__()

self.file_label = QLabel('选择图片:')
self.file_text = QLineEdit()
self.file_text.setDisabled(True)
self.file_btn = QPushButton('选择文件…')

# 头部
self.head_label = QLabel('切除头部:')
self.head_text = QLineEdit()
self.head_text.setPlaceholderText('要丢弃的顶部高度')
# self.head_text.setInputMask('99')
# 验证带范围的正整数
self.head_text.setValidator(QIntValidator(0, 500))

# 底部
self.foot_label = QLabel('切除底部:')
self.foot_text = QLineEdit()
self.foot_text.setPlaceholderText('要丢弃的底部高度')
# 验证带范围的正整数
self.foot_text.setValidator(QIntValidator(0, 500))
# 切割份数
self.nums_label = QLabel('切割份数:')
self.nums_text = QLineEdit()
self.nums_text.setPlaceholderText('要剪切的份数')
# 验证带范围的正整数
self.nums_text.setValidator(QIntValidator(2, 50))
# 文件名前缀
self.pre_label = QLabel('图片前缀:')
self.pre_text = QLineEdit()
self.pre_text.setPlaceholderText('要保存的图片名前缀')
reg = QRegExp('[a-zA-Z0-9]{1,15}$')
# 自定义文本验证器
pValidator = QRegExpValidator(self)
# 设置属性
pValidator.setRegExp(reg)
self.pre_text.setValidator(pValidator)
# 存储位置
self.forder_label = QLabel('存储位置:')
self.forder_text = QLineEdit()
self.forder_text.setDisabled(True)
self.forder_btn = QPushButton('选择存储位置…')

# 确认按钮
self.submit_btn = QPushButton('开始切割')
self.submit_btn.setStyleSheet("QPushButton{padding:20px 4px}")

# 图片label
self.img_label = QLabel()

# 复制按钮
self.copy_btn = QPushButton('复制代码')
self.copy_btn.setStyleSheet("QPushButton{padding:20px 4px}")
# 显示生成的文件html
self.res_teatarea = QTextEdit()

self.initUi()

def initUi(self):
self.createGridGroupBox()
self.creatVboxGroupBox()
self.creatFormGroupBox()
mainLayout = QVBoxLayout()
hboxLayout = QHBoxLayout()
# hboxLayout.addStretch()
hboxLayout.addWidget(self.gridGroupBox)
hboxLayout.addWidget(self.vboxGroupBox)
mainLayout.addLayout(hboxLayout)
mainLayout.addWidget(self.formGroupBox)
self.setLayout(mainLayout)
# 禁止最大化
# self.setFixedSize(self.width(), self.height())

# 参数区域
def createGridGroupBox(self):
self.gridGroupBox = QGroupBox("基本参数")
layout = QGridLayout()

# 点击选择文件按钮
self.file_btn.clicked.connect(self.selectImg)
# 点击选择保存路径按钮
self.forder_btn.clicked.connect(self.savePath)
# 点击提交按钮
self.submit_btn.clicked.connect(self.submit)
layout.setSpacing(10)
# 网格布局
layout.addWidget(self.file_label, 1, 0)
layout.addWidget(self.file_text, 1, 1)
layout.addWidget(self.file_btn, 1, 2)
layout.addWidget(self.head_label, 2, 0)
layout.addWidget(self.head_text, 2, 1, 1, 2)
layout.addWidget(self.foot_label, 3, 0)
layout.addWidget(self.foot_text, 3, 1, 1, 2)
layout.addWidget(self.nums_label, 4, 0)
layout.addWidget(self.nums_text, 4, 1, 1, 2)
layout.addWidget(self.pre_label, 5, 0)
layout.addWidget(self.pre_text, 5, 1, 1, 2)
layout.addWidget(self.forder_label, 6, 0)
layout.addWidget(self.forder_text, 6, 1)
layout.addWidget(self.forder_btn, 6, 2)
layout.addWidget(self.submit_btn, 7, 0, 1, 3)
layout.setColumnStretch(1, 10)
self.gridGroupBox.setLayout(layout)
self.setWindowTitle('图片切割')
self.setWindowIcon(QIcon(r'E:\site\python\cutimg\favicon.ico'))

# 图片预览区域
def creatVboxGroupBox(self):
self.vboxGroupBox = QGroupBox("图片预览")
layout = QVBoxLayout()
pixmap = QPixmap(r"E:\site\python\cutimg\default.jpg").scaled(300, 300)
self.img_label.setPixmap(pixmap) # 在label上显示图片
layout.addWidget(self.img_label)
self.vboxGroupBox.setLayout(layout)

# 代码区域
def creatFormGroupBox(self):
self.formGroupBox = QGroupBox("代码生成")
layout = QGridLayout()

layout.addWidget(self.res_teatarea, 1, 0)
layout.addWidget(self.copy_btn, 2, 0)
# 点击选择保存路径按钮
self.copy_btn.clicked.connect(self.copyText)

self.formGroupBox.setLayout(layout)

# 显示消息
def showMsg(self, tit, content, icon=3):
box = QMessageBox(QMessageBox.Question, tit, content)
# 设置左上角消息框图标
box.setWindowIcon(QIcon(r'E:\site\python\cutimg\favicon.ico'))

# 添加按钮,可用中文
yes = box.addButton('确定', QMessageBox.YesRole)
# 设置消息框中内容前面的图标
box.setIcon(icon)
# 显示该问答框
box.exec()

# 选择图片
def selectImg(self):
# 获取文件路径和图片类型
imgName, imgType = QFileDialog.getOpenFileName(self, "请选择您要切割的图片", "", "*.jpg;;*.png;;All Files(*)")

# 判断选择的文件是否存在
if os.path.exists(imgName):
# 将文件url放入路径文本框中
self.file_text.setText(imgName)
# 缩放图片,放入右侧label,缩放为300
pixmap = QPixmap(imgName).scaled(300, 300)
self.img_label.setPixmap(pixmap) # 在label上显示图片
else:
self.showMsg('错误', '您选择的文件不存在,请重新选择!')

# 选择保存文件夹
def savePath(self):
path = QFileDialog.getExistingDirectory(self, "请选择您要保存的位置")
# 判断选择的文件是否存在
if os.path.exists(path):
# 将保存url放入路径文本框中
self.forder_text.setText(path)
else:
self.showMsg('错误', '您选择的保存路径不存在,请重新选择!')

# 提交切割
def submit(self):
# 判断是否上传文件和选择保存目录
if not self.file_text.text():
self.showMsg('错误', '您还没有上传文件')
return False
if not self.forder_text.text():
self.showMsg('错误', '您还没有选择保存目录')
return False
# 获取分割份数
nums = self.nums_text.text()
# 判断是否填写分割份数
if nums:
# 获取文件路径
file = self.file_text.text()
# 获取要切除的头部高度,未填写则为0
head = int(self.head_text.text()) if self.head_text.text() else 0
# 获取要切除的底部高度,未填写则为0
foot = int(self.foot_text.text()) if self.foot_text.text() else 0
# 获取要保存的文件名前缀,默认为“image_”
pre = self.pre_text.text() + '_' if self.pre_text.text() else 'image_'
# 获取保存路径
forder = self.forder_text.text()
# 执行切割
self.cutImg(file, head, foot, pre, forder, int(nums))
else:
self.showMsg('错误', '必须填写要切割的份数')

# 复制代码
def copyText(self):
# 获取代码框内容
data = self.res_teatarea.toPlainText()
# 判断代码框是否有内容
if data:
# 如果有内容将内容添加至剪贴板
clipboard = QApplication.clipboard()
clipboard.setText(data)
self.showMsg('信息', '内容以成功复制到剪贴板', 1)
else:
self.showMsg('错误', '代码为空,没有代码可以复制')

def cutImg(self, file, head, foot, pre, forder, nums):
# 打开图片
img = Image.open(file)
# 图片宽度/高度
width, height = img.size
# 每份高度(减去废弃的头部和底部高度)
item_height = (height - head - foot) / nums
for i in range(nums):
# crop参数:剪切起始点x坐标,起始点y坐标,终点x坐标,终点y坐标
croped = img.crop((0, (head + i * item_height), width, (i + 1) * item_height + head))
# 图片名称从1开始
i += 1
# 格式化图片名称,图片名称两位数,不足两位前面补0
if i < 10:
i = '0{}'.format(i)
# 格式化图片名称,添加图片名前缀
imgName = '{}{}.jpg'.format(pre, i)
# 代码框光标移动至末尾
self.res_teatarea.moveCursor(QTextCursor.End)
# 每次打印的图片标签
shtml = '<img src="{}" />\n'.format(imgName)
# 将代码粘贴至代码框光标位置
self.res_teatarea.insertPlainText(shtml)
# 连接保存路径和图片名
files = os.path.join(forder, imgName)
# 保存图片
croped.save(files)
# 切割完成提示
self.showMsg('成功', '图片切割完成,请到目标文件夹查看吧', 1)


if __name__ == '__main__':
app = QApplication(sys.argv)
ex = ImgCut()
ex.show()
sys.exit(app.exec_())

入口代码main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sys, os

if hasattr(sys, 'frozen'):
os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH']
import win
from PyQt5.QtWidgets import QApplication, QMainWindow

if __name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = win.ImgCut()
ui.show()
sys.exit(app.exec_())

打包

关于打包,这里是有一下坑的,安装pyinstaller扩展包可以用来打包成exe文件,本人打包命令如下:

1
pyinstaller.exe -F -w -i tony1.ico --version-file=file_version_info.txt E:\site\python\cutimg\main.py

更多关于pyinstaller的使用请参考:python打包文件之pyinstaller

总结

以上就是此程序的所有代码200行左右,代码写的有点乱,如用到自己整理。后面打包有坑,另开文章说。