github star gitee star atomgit star PyPI Downloads AI编程 AI交流群

大家好,这里是程序员晚枫,正在all in AI编程实战

第5讲:点石成金:用PySide6为你的脚本生成专业图形界面

大家好,我是程序员晚枫。在上一讲中,我们学习了如何用python-office库快速实现办公自动化。今天,我将教你如何用PySide6将这些实用的脚本包装成专业的桌面应用程序,让你的工具从命令行程序变身成谁都能使用的专业软件。

为什么选择PySide6?

在为企业客户开发python-office的配套工具时,我发现了PySide6的独特优势:

  • 工业级标准:基于Qt框架,被众多商业软件采用
  • 极致性能:C++底层,处理大文件时优势明显
  • 组件丰富:提供真正专业的UI控件
  • 无缝打包:打包成exe后体积更小,启动更快
  • 完美兼容:与python-officepopdf等库无缝集成

环境准备:安装必要的库

1
2
3
4
5
# 安装PySide6和popdf
pip install pyside6 popdf -i https://pypi.tuna.tsinghua.edu.cn/simple

# 验证安装
python -c "import PySide6, popdf; print('环境配置成功!')"

实战:30分钟创建一个给视频加水印的程序

案例1:PDF转Word工具(基于popdf + PySide6)

完整代码实现:

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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
import sys
import os
from pathlib import Path
from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout,
QHBoxLayout, QWidget, QPushButton, QLabel,
QLineEdit, QProgressBar, QMessageBox, QFileDialog,
QGroupBox, QTextEdit)
from PySide6.QtCore import QThread, Signal
from PySide6.QtGui import QIcon
import popdf
import office

class ConversionThread(QThread):
"""转换线程,避免界面卡顿"""
progress_signal = Signal(int)
finished_signal = Signal(bool, str)
log_signal = Signal(str)

def __init__(self, input_path, output_path, operation_type):
super().__init__()
self.input_path = input_path
self.output_path = output_path
self.operation_type = operation_type

def run(self):
try:
self.log_signal.emit(f"开始{self.operation_type}操作...")

if self.operation_type == "pdf2docx":
# 使用popdf进行PDF转Word
popdf.pdf2docx(input_file=self.input_path, output_file=self.output_path)
elif self.operation_type == "word2pdf":
# 使用python-office进行Word转PDF
office.word2pdf(input_path=self.input_path, output_path=self.output_path)
elif self.operation_type == "merge_pdf":
# 合并PDF文件
popdf.merge_pdf(input_path=self.input_path, output_file=self.output_path)

self.log_signal.emit(f"{self.operation_type}操作完成!")
self.finished_signal.emit(True, "操作成功!")

except Exception as e:
error_msg = f"{self.operation_type}操作失败: {str(e)}"
self.log_signal.emit(error_msg)
self.finished_signal.emit(False, error_msg)

class OfficeToolsSuite(QMainWindow):
"""python-office工具套件 - 基于PySide6"""

def __init__(self):
super().__init__()
self.current_thread = None
self.init_ui()

def init_ui(self):
"""初始化界面"""
self.setWindowTitle("python-office 专业办公套件 - 程序员晚枫")
self.setGeometry(100, 100, 800, 600)

# 创建选项卡
tab_widget = QTabWidget()

# PDF工具选项卡
pdf_tab = self.create_pdf_tools_tab()
tab_widget.addTab(pdf_tab, "PDF工具")

# 文件处理选项卡
file_tab = self.create_file_tools_tab()
tab_widget.addTab(file_tab, "文件处理")

# 日志选项卡
log_tab = self.create_log_tab()
tab_widget.addTab(log_tab, "操作日志")

self.setCentralWidget(tab_widget)

# 状态栏
self.statusBar().showMessage("就绪 - python-office工具套件")

def create_pdf_tools_tab(self):
"""创建PDF工具选项卡"""
widget = QWidget()
layout = QVBoxLayout(widget)

# PDF转换组
conversion_group = QGroupBox("PDF格式转换")
conversion_layout = QVBoxLayout(conversion_group)

# PDF转Word
pdf_to_word_layout = QHBoxLayout()
self.pdf_input = QLineEdit()
self.pdf_input.setPlaceholderText("选择PDF文件...")
pdf_to_word_layout.addWidget(QLabel("PDF文件:"))
pdf_to_word_layout.addWidget(self.pdf_input)

self.pdf_browse_btn = QPushButton("浏览...")
self.pdf_browse_btn.clicked.connect(lambda: self.select_input_file(self.pdf_input, "PDF文件 (*.pdf)"))
pdf_to_word_layout.addWidget(self.pdf_browse_btn)

self.docx_output = QLineEdit()
self.docx_output.setPlaceholderText("选择Word输出位置...")
pdf_to_word_layout.addWidget(QLabel("Word文件:"))
pdf_to_word_layout.addWidget(self.docx_output)

self.docx_browse_btn = QPushButton("浏览...")
self.docx_browse_btn.clicked.connect(lambda: self.select_output_file(self.docx_output, "Word文档 (*.docx)"))
pdf_to_word_layout.addWidget(self.docx_browse_btn)

self.pdf_to_word_btn = QPushButton("PDF转Word")
self.pdf_to_word_btn.clicked.connect(self.convert_pdf_to_word)
pdf_to_word_layout.addWidget(self.pdf_to_word_btn)

conversion_layout.addLayout(pdf_to_word_layout)

# Word转PDF
word_to_pdf_layout = QHBoxLayout()
self.word_input = QLineEdit()
self.word_input.setPlaceholderText("选择Word文件...")
word_to_pdf_layout.addWidget(QLabel("Word文件:"))
word_to_pdf_layout.addWidget(self.word_input)

self.word_browse_btn = QPushButton("浏览...")
self.word_browse_btn.clicked.connect(lambda: self.select_input_file(self.word_input, "Word文档 (*.docx)"))
word_to_pdf_layout.addWidget(self.word_browse_btn)

self.pdf_output = QLineEdit()
self.pdf_output.setPlaceholderText("选择PDF输出位置...")
word_to_pdf_layout.addWidget(QLabel("PDF文件:"))
word_to_pdf_layout.addWidget(self.pdf_output)

self.pdf_output_browse_btn = QPushButton("浏览...")
self.pdf_output_browse_btn.clicked.connect(lambda: self.select_output_file(self.pdf_output, "PDF文件 (*.pdf)"))
word_to_pdf_layout.addWidget(self.pdf_output_browse_btn)

self.word_to_pdf_btn = QPushButton("Word转PDF")
self.word_to_pdf_btn.clicked.connect(self.convert_word_to_pdf)
word_to_pdf_layout.addWidget(self.word_to_pdf_btn)

conversion_layout.addLayout(word_to_pdf_layout)
layout.addWidget(conversion_group)

# PDF操作组
pdf_ops_group = QGroupBox("PDF高级操作")
pdf_ops_layout = QVBoxLayout(pdf_ops_group)

# PDF合并
merge_pdf_layout = QHBoxLayout()
self.merge_input = QLineEdit()
self.merge_input.setPlaceholderText("选择PDF文件夹...")
merge_pdf_layout.addWidget(QLabel("PDF文件夹:"))
merge_pdf_layout.addWidget(self.merge_input)

self.merge_browse_btn = QPushButton("浏览...")
self.merge_browse_btn.clicked.connect(self.select_merge_folder)
merge_pdf_layout.addWidget(self.merge_browse_btn)

self.merge_output = QLineEdit()
self.merge_output.setPlaceholderText("选择合并后PDF位置...")
merge_pdf_layout.addWidget(QLabel("输出文件:"))
merge_pdf_layout.addWidget(self.merge_output)

self.merge_output_browse_btn = QPushButton("浏览...")
self.merge_output_browse_btn.clicked.connect(lambda: self.select_output_file(self.merge_output, "PDF文件 (*.pdf)"))
merge_pdf_layout.addWidget(self.merge_output_browse_btn)

self.merge_pdf_btn = QPushButton("合并PDF")
self.merge_pdf_btn.clicked.connect(self.merge_pdfs)
merge_pdf_layout.addWidget(self.merge_pdf_btn)

pdf_ops_layout.addLayout(merge_pdf_layout)
layout.addWidget(pdf_ops_group)

# 进度条
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
layout.addWidget(self.progress_bar)

layout.addStretch()
return widget

def create_file_tools_tab(self):
"""创建文件处理选项卡"""
widget = QWidget()
layout = QVBoxLayout(widget)

# 文件整理组
file_org_group = QGroupBox("文件整理工具")
file_org_layout = QVBoxLayout(file_org_group)

# 文件批量重命名
rename_layout = QHBoxLayout()
self.rename_folder = QLineEdit()
self.rename_folder.setPlaceholderText("选择要重命名的文件夹...")
rename_layout.addWidget(QLabel("文件夹:"))
rename_layout.addWidget(self.rename_folder)

self.rename_browse_btn = QPushButton("浏览...")
self.rename_browse_btn.clicked.connect(self.select_rename_folder)
rename_layout.addWidget(self.rename_browse_btn)

self.rename_btn = QPushButton("智能重命名")
self.rename_btn.clicked.connect(self.batch_rename_files)
rename_layout.addWidget(self.rename_btn)

file_org_layout.addLayout(rename_layout)

# 文件分类整理
organize_layout = QHBoxLayout()
self.organize_folder = QLineEdit()
self.organize_folder.setPlaceholderText("选择要整理的文件夹...")
organize_layout.addWidget(QLabel("文件夹:"))
organize_layout.addWidget(self.organize_folder)

self.organize_browse_btn = QPushButton("浏览...")
self.organize_browse_btn.clicked.connect(self.select_organize_folder)
organize_layout.addWidget(self.organize_browse_btn)

self.organize_btn = QPushButton("智能整理")
self.organize_btn.clicked.connect(self.organize_files)
organize_layout.addWidget(self.organize_btn)

file_org_layout.addLayout(organize_layout)
layout.addWidget(file_org_group)

layout.addStretch()
return widget

def create_log_tab(self):
"""创建日志选项卡"""
widget = QWidget()
layout = QVBoxLayout(widget)

self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
layout.addWidget(self.log_text)

clear_layout = QHBoxLayout()
clear_layout.addStretch()

clear_btn = QPushButton("清空日志")
clear_btn.clicked.connect(self.log_text.clear)
clear_layout.addWidget(clear_btn)

layout.addLayout(clear_layout)
return widget

def select_input_file(self, line_edit, file_filter):
"""选择输入文件"""
file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", file_filter)
if file_path:
line_edit.setText(file_path)

def select_output_file(self, line_edit, file_filter):
"""选择输出文件"""
file_path, _ = QFileDialog.getSaveFileName(self, "保存文件", "", file_filter)
if file_path:
line_edit.setText(file_path)

def select_merge_folder(self):
"""选择合并文件夹"""
folder_path = QFileDialog.getExistingDirectory(self, "选择PDF文件夹")
if folder_path:
self.merge_input.setText(folder_path)
# 自动生成输出路径
output_path = os.path.join(folder_path, "合并文档.pdf")
self.merge_output.setText(output_path)

def select_rename_folder(self):
"""选择重命名文件夹"""
folder_path = QFileDialog.getExistingDirectory(self, "选择文件夹")
if folder_path:
self.rename_folder.setText(folder_path)

def select_organize_folder(self):
"""选择整理文件夹"""
folder_path = QFileDialog.getExistingDirectory(self, "选择文件夹")
if folder_path:
self.organize_folder.setText(folder_path)

def log_message(self, message):
"""添加日志消息"""
self.log_text.append(f"{message}")
self.log_text.ensureCursorVisible() # 自动滚动到底部

def start_operation(self, input_path, output_path, operation_type):
"""开始操作"""
if not input_path:
QMessageBox.warning(self, "警告", "请选择输入文件/文件夹")
return

if not output_path and operation_type != "organize_files":
QMessageBox.warning(self, "警告", "请选择输出位置")
return

# 禁用按钮,显示进度
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0) # 无限进度条

# 在新线程中执行操作
self.current_thread = ConversionThread(input_path, output_path, operation_type)
self.current_thread.finished_signal.connect(self.operation_finished)
self.current_thread.log_signal.connect(self.log_message)
self.current_thread.start()

def operation_finished(self, success, message):
"""操作完成回调"""
self.progress_bar.setVisible(False)

if success:
self.statusBar().showMessage("操作完成")
QMessageBox.information(self, "成功", message)
else:
self.statusBar().showMessage("操作失败")
QMessageBox.critical(self, "错误", message)

def convert_pdf_to_word(self):
"""PDF转Word"""
self.start_operation(
self.pdf_input.text(),
self.docx_output.text(),
"pdf2docx"
)

def convert_word_to_pdf(self):
"""Word转PDF"""
self.start_operation(
self.word_input.text(),
self.pdf_output.text(),
"word2pdf"
)

def merge_pdfs(self):
"""合并PDF"""
self.start_operation(
self.merge_input.text(),
self.merge_output.text(),
"merge_pdf"
)

def batch_rename_files(self):
"""批量重命名文件"""
folder_path = self.rename_folder.text()
if not folder_path:
QMessageBox.warning(self, "警告", "请选择文件夹")
return

try:
# 使用python-office进行批量重命名
office.file.replace4filename(
path=folder_path,
del_content="",
replace_content="new_"
)
self.log_message("✓ 文件重命名完成")
QMessageBox.information(self, "成功", "文件重命名完成!")
except Exception as e:
error_msg = f"✗ 文件重命名失败: {str(e)}"
self.log_message(error_msg)
QMessageBox.critical(self, "错误", error_msg)

def organize_files(self):
"""整理文件"""
folder_path = self.organize_folder.text()
if not folder_path:
QMessageBox.warning(self, "警告", "请选择文件夹")
return

try:
# 使用python-office进行文件整理
office.file.group_by_name(path=folder_path)
self.log_message("✓ 文件整理完成")
QMessageBox.information(self, "成功", "文件整理完成!")
except Exception as e:
error_msg = f"✗ 文件整理失败: {str(e)}"
self.log_message(error_msg)
QMessageBox.critical(self, "错误", error_msg)

def main():
"""主函数"""
app = QApplication(sys.argv)

# 设置应用程序信息
app.setApplicationName("python-office专业办公套件")
app.setApplicationVersion("1.0")
app.setOrganizationName("python-office")

window = OfficeToolsSuite()
window.show()

sys.exit(app.exec())

if __name__ == "__main__":
main()

PySide6开发的核心优势

1. 真正的原生体验

1
2
3
4
5
# 系统原生文件对话框
file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "PDF文件 (*.pdf)")

# 系统原生消息框
QMessageBox.information(self, "操作完成", "文件转换成功!")

2. 强大的多线程支持

1
2
3
4
5
6
7
8
9
10
11
12
from PySide6.QtCore import QThread, Signal

class WorkerThread(QThread):
finished_signal = Signal(bool, str)

def run(self):
try:
# 执行python-office或popdf操作
popdf.pdf2docx(input_file="input.pdf", output_file="output.docx")
self.finished_signal.emit(True, "成功")
except Exception as e:
self.finished_signal.emit(False, str(e))

3. 专业的UI控件

1
2
3
4
5
6
7
# 选项卡控件
tab_widget = QTabWidget()
tab_widget.addTab(widget, "选项卡名称")

# 分组框
group_box = QGroupBox("功能组")
group_layout = QVBoxLayout(group_box)

AI辅助PySide6开发提示词

组件级生成

1
2
用PySide6创建一个[组件类型],包含[具体功能],
使用[样式要求],处理[用户交互]。

对话框生成

1
2
创建一个PySide6对话框,用于[对话框用途],
包含以下控件:[控件列表],处理[确认/取消]逻辑。

布局生成

1
2
用PySide6创建一个[布局类型]布局,包含[组件列表],
实现[响应式行为],符合[设计规范]。

调试技巧:PySide6专属

1. 内存管理

1
2
3
4
5
6
# 正确释放资源
def closeEvent(self, event):
if self.current_thread and self.current_thread.isRunning():
self.current_thread.quit()
self.current_thread.wait()
event.accept()

2. 信号槽连接

1
2
3
4
5
6
7
8
9
# 安全的信号连接
self.button.clicked.connect(self.handle_click)

# 断开连接避免重复
try:
self.signal.disconnect()
except:
pass
self.signal.connect(self.slot)

3. 异常处理

1
2
3
4
5
def safe_office_operation(self):
try:
office.some_operation()
except Exception as e:
QMessageBox.critical(self, "操作错误", str(e))

打包发布

使用PyInstaller打包PySide6应用:

1
2
pip install pyinstaller
pyinstaller --onefile --windowed --name="python-office工具套件" your_script.py

实战作业:创建企业级工具

任务: 使用PySide6创建一个集成了多个python-office功能的办公工具

要求:

  1. 使用PySide6框架
  2. 集成至少3个不同的python-officepopdf功能
  3. 实现多线程操作
  4. 添加完整的错误处理和日志记录
  5. 设计专业的用户界面

企业级特性:

  • 添加配置持久化
  • 实现操作日志
  • 添加批量处理能力
  • 支持拖拽操作

程序员晚枫的PySide6实战心得

在为企业开发python-office配套工具时,我总结了几个关键经验:

  1. 组件复用:将常用功能封装成自定义组件
  2. 资源管理:正确管理线程和内存资源
  3. 用户体验:提供即时反馈和进度指示
  4. 错误恢复:设计完善的错误处理和恢复机制

记住:专业工具的核心是稳定性和用户体验。

下一讲预告

在第6讲中,我们将学习如何将你的PySide6桌面应用转换为网页服务,创建一个"AI插件大全"网站,让更多人能够在线使用你开发的工具!


本节课的收获:

  • 掌握了PySide6框架的核心概念
  • 学会了创建专业的桌面应用程序
  • 了解了多线程编程的最佳实践
  • 能够将python-officepopdf功能包装成企业级工具

课后任务:

  1. 使用PySide6创建一个集成了多个功能的办公工具
  2. 打包生成可执行文件
  3. 在课程群分享你的作品截图

我是程序员晚枫,我们下一讲见!


本文涉及的PySide6开发模式已在多个python-office企业版工具中验证,具有极高的稳定性和专业性。遇到PySide6相关问题,欢迎在课程群中交流。

关于课程

我会尽我所能,把AI编程的知识分享给你。

因为对于我来说,给小白的《30讲 · AI编程训练营》是我能力范围内,最有机会抓住AI趋势的一套课。

前3讲可以试听,试听链接:https://www.bilibili.com/cheese/play/ss982042944

所以这套课的内容,只会比我承诺的更多,不会更少;只会比你预期的更用心,不会割韭菜。

同时,我也欢迎大家找我沟通,我会尽力解答你的问题。

联系作者

程序员晚枫专注AI编程培训,小白看完他和图灵社区合作的教程《30讲 · AI编程训练营》就能上手做AI项目。