大家好,我是良逍,一个爱折腾的 95 后,目前是一个不知名的跨境电商产品经理,平时爱研究折腾一些新鲜事物也尝试过很多副业,也取得过一些成绩。后期将会在平台持续分享产品/Ai 编程/成长等相关内容,不一定都对,希望对大家有启发,可以一起讨论一起成长!
一、项目需求与初始配置
二、代码实现与调试
三、理解代码结构
四、学习总结
我们直接开始向 Ai 提问,如果是新朋友不知道用的什么工具,可以翻看以前的文章!
作为一个编程新手,首先提出了这样的需求:
把代码输入到 PyCharm 终端,先把环境配置好。
pip3 install PyQt5 PyPDF2 Pillow python-docx openpyxl python-pptx
2. 代码实现与调试
在完成环境设置后,你可以直接问他需要哪些指令才能够完成这个产品。
根据他的要求,把你的指令发给他,这里其实很像我们的产品需求文档,大家如果是产品应该会很快可以上手,如果是开发,那么也可以自己根据平时对产品文档的理解进行梳理。而且我们这个可以反复修正,其实后面影响不大,我这里也是一直在修正,会把修正过程也进行分享。
其他板块你们可以根据需求写,用户界面的指令我分享出来
1. 用户界面
* 期望的界面类型: 图形用户界面
* 窗口标题设置为"文件合并工具"。 在窗口顶部添加一个醒目的标题标签"文件合并工具",使用大号字体和粗体。 主界面布局: 使用主布局,确保元素垂直排列。 在标题下方添加一个简短的说明文字,解释工具的用途。 目录选择部分: 创建一个水平布局,包含一个只读的编辑线框和一个"浏览"按钮。 当用户点击"浏览"按钮时,打开一个文件选择弹窗让用户选择目录。 选择的目录路径显示在应用中。 添加一个"开始合并"按钮: 按钮应足够大,易于点击。 在合并操作开始前,检查是否已选择有效目录。 在底部添加一个状态栏: 用于显示操作进度、结果或提示信息。 添加一个进度条到状态栏中,用于显示合并进度,注意布局要合理大气。 样式和美化: 使用QSS来美化界面,包括设置字体、颜色、边距等。 为主要元素(如按钮、输入框)添加适当的图标。 确保窗口大小适中,并允许调整大小。 错误处理和用户反馈: 添加错误处理机制,例如当选择的目录无效或合并过程出错时显示错误消息到主界面,确保用户知晓合并情况。
请基于以上要求,提供完整的、可运行的Python代码。
根据指令会生成一段这样的代码;在 PyCharm 里面新建一个 Python 文件(不会建的可以翻看前面的内容),随后将内容复制进去,点击运行
代码:
import sys
import os
import shutil
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLineEdit, QLabel, QFileDialog,
QProgressBar, QTextEdit)
from PyQt5.QtGui import QIcon, QFont
from PyQt5.QtCore import Qt, QThread, pyqtSignal
import PyPDF2
from PIL import Image
from docx import Document
from openpyxl import load_workbook
from pptx import Presentation
import logging
class FileMerger(QThread):
progress = pyqtSignal(int)
log = pyqtSignal(str)
finished = pyqtSignal()
def __init__(self, source_dir, output_dir):
super().__init__()
self.source_dir = source_dir
self.output_dir = output_dir
self.file_types = {
'pdf': self.merge_pdfs,
'jpg': self.merge_images,
'png': self.merge_images,
'docx': self.merge_word,
'xlsx': self.merge_excel,
'pptx': self.merge_powerpoint
}
def run(self):
self.setup_logging()
self.process_files()
self.finished.emit()
def setup_logging(self):
log_file = os.path.join(self.output_dir, 'merge_log.txt')
logging.basicConfig(filename=log_file, level=logging.INFO,
format='%(asctime)s - %(message)s')
def process_files(self):
total_files = sum([len(files) for _, _, files in os.walk(self.source_dir)])
processed_files = 0
for root, _, files in os.walk(self.source_dir):
for file in files:
file_path = os.path.join(root, file)
file_ext = file.split('.')[-1].lower()
if file_ext in self.file_types:
self.file_types[file_ext](file_path)
else:
self.log.emit(f"Skipping unsupported file: {file}")
logging.info(f"Skipped unsupported file: {file}")
processed_files += 1
progress = int((processed_files / total_files) * 100)
self.progress.emit(progress)
self.log.emit("File merging completed.")
logging.info("File merging completed.")
def merge_pdfs(self, file_path):
output_pdf = os.path.join(self.output_dir, 'Merged_PDFs.pdf')
if not os.path.exists(output_pdf):
shutil.copy(file_path, output_pdf)
else:
merger = PyPDF2.PdfMerger()
merger.append(output_pdf)
merger.append(file_path)
temp_output = os.path.join(self.output_dir, 'temp_output.pdf')
with open(temp_output, 'wb') as f:
merger.write(f)
os.remove(output_pdf)
os.rename(temp_output, output_pdf)
self.add_separator(output_pdf, os.path.basename(file_path))
self.log.emit(f"Merged PDF: {file_path}")
logging.info(f"Merged PDF: {file_path}")
def merge_images(self, file_path):
output_pdf = os.path.join(self.output_dir, 'Merged_Images.pdf')
image = Image.open(file_path)
if image.mode == 'RGBA':
image = image.convert('RGB')
if not os.path.exists(output_pdf):
image.save(output_pdf, 'PDF', resolution=100.0)
else:
existing_pdf = PyPDF2.PdfReader(output_pdf)
output = PyPDF2.PdfWriter()
for page in existing_pdf.pages:
output.add_page(page)
image_pdf = PyPDF2.PdfReader(file_path)
output.add_page(image_pdf.pages[0])
with open(output_pdf, 'wb') as f:
output.write(f)
self.log.emit(f"Merged Image: {file_path}")
logging.info(f"Merged Image: {file_path}")
def merge_word(self, file_path):
output_docx = os.path.join(self.output_dir, 'Merged_Word.docx')
if not os.path.exists(output_docx):
shutil.copy(file_path, output_docx)
else:
master = Document(output_docx)
master.add_page_break()
slave = Document(file_path)
for element in slave.element.body:
master.element.body.append(element)
master.save(output_docx)
self.add_separator_word(output_docx, os.path.basename(file_path))
self.log.emit(f"Merged Word: {file_path}")
logging.info(f"Merged Word: {file_path}")
def merge_excel(self, file_path):
output_xlsx = os.path.join(self.output_dir, 'Merged_Excel.xlsx')
if not os.path.exists(output_xlsx):
shutil.copy(file_path, output_xlsx)
else:
master_wb = load_workbook(output_xlsx)
slave_wb = load_workbook(file_path)
for sheet_name in slave_wb.sheetnames:
if sheet_name in master_wb.sheetnames:
new_sheet_name = f"{sheet_name}_{os.path.basename(file_path)}"
master_wb.create_sheet(new_sheet_name)
master_sheet = master_wb[new_sheet_name]
slave_sheet = slave_wb[sheet_name]
for row in slave_sheet.iter_rows(values_only=True):
master_sheet.append(row)
else:
master_wb.create_sheet(sheet_name)
master_sheet = master_wb[sheet_name]
slave_sheet = slave_wb[sheet_name]
for row in slave_sheet.iter_rows(values_only=True):
master_sheet.append(row)
master_wb.save(output_xlsx)
self.log.emit(f"Merged Excel: {file_path}")
logging.info(f"Merged Excel: {file_path}")
def merge_powerpoint(self, file_path):
output_pptx = os.path.join(self.output_dir, 'Merged_PowerPoint.pptx')
if not os.path.exists(output_pptx):
shutil.copy(file_path, output_pptx)
else:
master_ppt = Presentation(output_pptx)
slave_ppt = Presentation(file_path)
for slide in slave_ppt.slides:
new_slide = master_ppt.slides.add_slide(master_ppt.slide_layouts[0])
for shape in slide.shapes:
el = shape.element
new_slide.shapes._spTree.insert_element_before(el, 'p:extLst')
master_ppt.save(output_pptx)
self.add_separator_ppt(output_pptx, os.path.basename(file_path))
self.log.emit(f"Merged PowerPoint: {file_path}")
logging.info(f"Merged PowerPoint: {file_path}")
def add_separator(self, pdf_path, file_name):
reader = PyPDF2.PdfReader(pdf_path)
writer = PyPDF2.PdfWriter()
for page in reader.pages:
writer.add_page(page)
packet = PyPDF2.PdfReader(pdf_path)
page = packet.pages[0]
page.merge_page(page)
writer.add_page(page)
with open(pdf_path, 'wb') as output_file:
writer.write(output_file)
def add_separator_word(self, docx_path, file_name):
doc = Document(docx_path)
doc.add_paragraph('-' * 50)
doc.add_paragraph(f"Source: {file_name}")
doc.add_paragraph('-' * 50)
doc.save(docx_path)
def add_separator_ppt(self, pptx_path, file_name):
prs = Presentation(pptx_path)
blank_slide_layout = prs.slide_layouts[6]
slide = prs.slides.add_slide(blank_slide_layout)
title = slide.shapes.title
title.text = f"Source: {file_name}"
prs.save(pptx_path)
class FileMergerGUI(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('文件合并工具')
self.setGeometry(300, 300, 500, 300)
layout = QVBoxLayout()
title_label = QLabel('文件合并工具')
title_label.setAlignment(Qt.AlignCenter)
title_label.setFont(QFont('Arial', 16, QFont.Bold))
layout.addWidget(title_label)
description = QLabel('这个工具可以合并多种类型的文件,包括PDF、图片、Word、Excel和PowerPoint。')
description.setWordWrap(True)
layout.addWidget(description)
input_layout = QHBoxLayout()
self.input_edit = QLineEdit()
self.input_edit.setReadOnly(True)
input_layout.addWidget(self.input_edit)
browse_button = QPushButton('浏览')
browse_button.clicked.connect(self.browse_folder)
input_layout.addWidget(browse_button)
layout.addLayout(input_layout)
self.merge_button = QPushButton('开始合并')
self.merge_button.clicked.connect(self.start_merge)
layout.addWidget(self.merge_button)
self.progress_bar = QProgressBar()
layout.addWidget(self.progress_bar)
self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
layout.addWidget(self.log_text)
self.setLayout(layout)
def browse_folder(self):
folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
if folder:
self.input_edit.setText(folder)
def start_merge(self):
source_dir = self.input_edit.text()
if not source_dir:
self.log_text.append("请先选择源文件夹")
return
output_dir = QFileDialog.getExistingDirectory(self, "选择输出文件夹")
if not output_dir:
return
self.merger = FileMerger(source_dir, output_dir)
self.merger.progress.connect(self.update_progress)
self.merger.log.connect(self.update_log)
self.merger.finished.connect(self.merge_finished)
self.merger.start()
self.merge_button.setEnabled(False)
def update_progress(self, value):
self.progress_bar.setValue(value)
def update_log(self, message):
self.log_text.append(message)
def merge_finished(self):
self.merge_button.setEnabled(True)
self.log_text.append("合并完成!")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = FileMergerGUI()
ex.show()
sys.exit(app.exec_())
2.3 代码修复
点击运行后,我这边出现了报错;目前来看是表格的字符数超出了,需要让他处理,我们也很简单,直接把报错复制给 Ai ,让他提供解决方案
UserWarning: Title is more than 31 characters. Some applications may not be able to read the file
这里如果是有编程经验可以直接改,如果不懂你也可以直接问 Ai 这里需要怎么修改,因为他知道你的完整代码,所以会很清楚地告诉你这段代码要如何添加,我们看到之后是可以直直接在 PyCharm 搜索这个类或者这个方法之后进行替换的。
后续运行的过程可能还会有新的问题,现在处理了表格,后面可能关于图片等格式也还会有问题,其实跟上面一样反复就行,或者你直接可以在提示词里面说明,请检查其他文件是否可能会存在该问题,让他一并修复,我们后面的提示词不需要加上很长的背景,只需要说明你的问题还有你的需求就可以。
import sys
import os
import shutil
import logging
from io import BytesIO
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLineEdit, QLabel, QFileDialog,
QProgressBar, QTextEdit
)
from PyQt5.QtGui import QIcon, QFont
from PyQt5.QtCore import Qt, QThread, pyqtSignal
import PyPDF2
from PIL import Image
from docx import Document
from openpyxl import load_workbook
from pptx import Presentation
class FileMerger(QThread):
progress = pyqtSignal(int)
log = pyqtSignal(str)
finished = pyqtSignal()
def __init__(self, source_dir, output_dir):
super().__init__()
self.source_dir = source_dir
self.output_dir = output_dir
self.file_types = {
'pdf': self.merge_pdfs,
'jpg': self.merge_images,
'png': self.merge_images,
'docx': self.merge_word,
'xlsx': self.merge_excel,
'pptx': self.merge_powerpoint
}
def run(self):
self.setup_logging()
self.process_files()
self.finished.emit()
def setup_logging(self):
log_file = os.path.join(self.output_dir, 'merge_log.txt')
logging.basicConfig(filename=log_file, level=logging.INFO,
format='%(asctime)s - %(message)s')
def process_files(self):
total_files = sum([len(files) for _, _, files in os.walk(self.source_dir)])
processed_files = 0
for root, _, files in os.walk(self.source_dir):
for file in files:
file_path = os.path.join(root, file)
file_ext = file.split('.')[-1].lower()
if file_ext in self.file_types:
self.file_types[file_ext](file_path)
else:
self.log.emit(f"Skipping unsupported file: {file}")
logging.info(f"Skipped unsupported file: {file}")
processed_files += 1
progress = int((processed_files / total_files) * 100)
self.progress.emit(progress)
self.log.emit("File merging completed.")
logging.info("File merging completed.")
def merge_pdfs(self, file_path):
output_pdf = os.path.join(self.output_dir, 'Merged_PDFs.pdf')
if not os.path.exists(output_pdf):
shutil.copy(file_path, output_pdf)
else:
merger = PyPDF2.PdfMerger()
merger.append(output_pdf)
merger.append(file_path)
temp_output = os.path.join(self.output_dir, 'temp_output.pdf')
with open(temp_output, 'wb') as f:
merger.write(f)
os.remove(output_pdf)
os.rename(temp_output, output_pdf)
self.add_separator(output_pdf, os.path.basename(file_path))
self.log.emit(f"Merged PDF: {file_path}")
logging.info(f"Merged PDF: {file_path}")
def merge_images(self, file_path):
output_pdf = os.path.join(self.output_dir, 'Merged_Images.pdf')
image = Image.open(file_path)
if image.mode == 'RGBA':
image = image.convert('RGB')
# 将图像转换为PDF
pdf_bytes = BytesIO()
image.save(pdf_bytes, format='PDF')
pdf_bytes.seek(0)
if not os.path.exists(output_pdf):
# 如果输出PDF不存在,直接保存转换后的图像
with open(output_pdf, 'wb') as f:
f.write(pdf_bytes.getvalue())
else:
# 如果输出PDF已存在,将新图像添加到现有PDF
existing_pdf = PyPDF2.PdfReader(output_pdf)
output = PyPDF2.PdfWriter()
# 添加现有页面
for page in existing_pdf.pages:
output.add_page(page)
# 添加新图像页面
new_page = PyPDF2.PdfReader(pdf_bytes).pages[0]
output.add_page(new_page)
# 保存更新后的PDF
with open(output_pdf, 'wb') as f:
output.write(f)
self.log.emit(f"Merged Image: {file_path}")
logging.info(f"Merged Image: {file_path}")
def merge_word(self, file_path):
output_docx = os.path.join(self.output_dir, 'Merged_Word.docx')
if not os.path.exists(output_docx):
shutil.copy(file_path, output_docx)
else:
master = Document(output_docx)
master.add_page_break()
slave = Document(file_path)
for element in slave.element.body:
master.element.body.append(element)
master.save(output_docx)
self.add_separator_word(output_docx, os.path.basename(file_path))
self.log.emit(f"Merged Word: {file_path}")
logging.info(f"Merged Word: {file_path}")
def merge_excel(self, file_path):
output_xlsx = os.path.join(self.output_dir, 'Merged_Excel.xlsx')
if not os.path.exists(output_xlsx):
shutil.copy(file_path, output_xlsx)
else:
master_wb = load_workbook(output_xlsx)
slave_wb = load_workbook(file_path)
for sheet_name in slave_wb.sheetnames:
new_sheet_name = self.get_unique_sheet_name(master_wb, sheet_name, file_path)
master_wb.create_sheet(new_sheet_name)
master_sheet = master_wb[new_sheet_name]
slave_sheet = slave_wb[sheet_name]
for row in slave_sheet.iter_rows(values_only=True):
master_sheet.append(row)
master_wb.save(output_xlsx)
self.log.emit(f"Merged Excel: {file_path}")
logging.info(f"Merged Excel: {file_path}")
def get_unique_sheet_name(self, workbook, sheet_name, file_path):
base_name = os.path.splitext(os.path.basename(file_path))[0]
new_name = (sheet_name[:20] + "_" + base_name[:10])[:31]
# 如果名称已存在,添加数字后缀
counter = 1
while new_name in workbook.sheetnames:
suffix = f"_{counter}"
new_name = (sheet_name[:20] + "_" + base_name[:5] + suffix)[:31]
counter += 1
return new_name
def merge_powerpoint(self, file_path):
output_pptx = os.path.join(self.output_dir, 'Merged_PowerPoint.pptx')
if not os.path.exists(output_pptx):
shutil.copy(file_path, output_pptx)
else:
master_ppt = Presentation(output_pptx)
slave_ppt = Presentation(file_path)
for slide in slave_ppt.slides:
new_slide = master_ppt.slides.add_slide(master_ppt.slide_layouts[0])
for shape in slide.shapes:
el = shape.element
new_slide.shapes._spTree.insert_element_before(el, 'p:extLst')
master_ppt.save(output_pptx)
self.add_separator_ppt(output_pptx, os.path.basename(file_path))
self.log.emit(f"Merged PowerPoint: {file_path}")
logging.info(f"Merged PowerPoint: {file_path}")
def add_separator(self, pdf_path, file_name):
reader = PyPDF2.PdfReader(pdf_path)
writer = PyPDF2.PdfWriter()
for page in reader.pages:
writer.add_page(page)
packet = PyPDF2.PdfReader(pdf_path)
page = packet.pages[0]
page.merge_page(page)
writer.add_page(page)
with open(pdf_path, 'wb') as output_file:
writer.write(output_file)
def add_separator_word(self, docx_path, file_name):
doc = Document(docx_path)
doc.add_paragraph('-' * 50)
doc.add_paragraph(f"Source: {file_name}")
doc.add_paragraph('-' * 50)
doc.save(docx_path)
def add_separator_ppt(self, pptx_path, file_name):
prs = Presentation(pptx_path)
blank_slide_layout = prs.slide_layouts[6]
slide = prs.slides.add_slide(blank_slide_layout)
title = slide.shapes.title
title.text = f"Source: {file_name}"
prs.save(pptx_path)
class FileMergerGUI(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('文件合并工具')
self.setGeometry(300, 300, 500, 300)
layout = QVBoxLayout()
title_label = QLabel('文件合并工具')
title_label.setAlignment(Qt.AlignCenter)
title_label.setFont(QFont('Arial', 16, QFont.Bold))
layout.addWidget(title_label)
description = QLabel('这个工具可以合并多种类型的文件,包括PDF、图片、Word、Excel和PowerPoint。')
description.setWordWrap(True)
layout.addWidget(description)
input_layout = QHBoxLayout()
self.input_edit = QLineEdit()
self.input_edit.setReadOnly(True)
input_layout.addWidget(self.input_edit)
browse_button = QPushButton('浏览')
browse_button.clicked.connect(self.browse_folder)
input_layout.addWidget(browse_button)
layout.addLayout(input_layout)
self.merge_button = QPushButton('开始合并')
self.merge_button.clicked.connect(self.start_merge)
layout.addWidget(self.merge_button)
self.progress_bar = QProgressBar()
layout.addWidget(self.progress_bar)
self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
layout.addWidget(self.log_text)
self.setLayout(layout)
def browse_folder(self):
folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
if folder:
self.input_edit.setText(folder)
def start_merge(self):
source_dir = self.input_edit.text()
if not source_dir:
self.log_text.append("请先选择源文件夹")
return
output_dir = QFileDialog.getExistingDirectory(self, "选择输出文件夹")
if not output_dir:
return
self.merger = FileMerger(source_dir, output_dir)
self.merger.progress.connect(self.update_progress)
self.merger.log.connect(self.update_log)
self.merger.finished.connect(self.merge_finished)
self.merger.start()
self.merge_button.setEnabled(False)
def update_progress(self, value):
self.progress_bar.setValue(value)
def update_log(self, message):
self.log_text.append(message)
def merge_finished(self):
self.merge_button.setEnabled(True)
self.log_text.append("合并完成!")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = FileMergerGUI()
ex.show()
sys.exit(app.exec_())
虽然 UI 比较将就,但勉勉强强还是可以用的。
在这个过程中,如果你意识到自己对代码结构不太理解,或者想同时学习 Python 代码,或者想优化他的代码,那你可以直接对里面的知识点进行提问;或者你可以直接跟他说:
我是一个编程新手,想要学习 Python,希望你可以仔细讲解这段代码,每段代码什么意思,使用了什么,应该注意什么,可以加上案例协助讲解说明。
这次经历让我深刻认识到,编程学习是一个循环往复的过程,需要不断实践、遇到问题、解决问题,并在这个过程中逐步提升自己的技能。Ai 编程学习则需要你学会提问,需要把你的需求用干练的语言精准描述出来。自己可以多试试。如果大家对 Ai 编程感兴趣可以看我其他文章,并且后续会持续分享,可以码住我哦~
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- haog.cn 版权所有 赣ICP备2024042798号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务