打开或拖放pdf文件时可以选择多个pdf文件,多个pdf文件预览时,按文件名排序或按文件先后导入的顺序排序显示,增加导出按钮放在左侧,导出按钮旁边放一个复选框,复选框功能是勾选后可以导出预览框内pdf拆分的每个单个页面文件。导出按钮旁再放两个小的输入框,可以输入第几页到第几页,若输入具体数字后可以导出预览的pdf的指定的页面的pdf文件。若不填写输入框内页码则直接导出预览框内的pdf文件。提供完整代码。
时间: 2025-06-26 18:06:45 浏览: 7
为了实现您描述的功能,我们需要对现有的代码进行以下修改:
1. **支持多文件选择和拖放**:在 `open_pdf` 方法中处理多个文件,并将它们添加到预览窗口中。
2. **按文件名或导入顺序排序**:添加排序逻辑。
3. **增加导出按钮及复选框和输入框**:用于导出所有页面或特定页面。
4. **实现导出逻辑**:根据用户的输入导出整个PDF或指定页面。
以下是完整的修改后的代码:
```python
import sys
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QListWidget, QListWidgetItem, QPushButton,
QFileDialog, QAction, QMessageBox, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGridLayout, QLineEdit, QCheckBox
)
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QImage, QPixmap, QIcon, QFont
import fitz # PyMuPDF
class PDFReorderWindow(QMainWindow):
def __init__(self):
super().__init__()
self.current_pdfs = []
self.page_mapping = {} # Mapping of items to their original file path and page number
self.setAcceptDrops(True) # 启用拖放
self.setStyleSheet("""
QMainWindow {
background-color: #f0f0f0;
}
QPushButton {
border: none;
padding: 6px 12px;
color: white;
background-color: #3a7fbf;
border-radius: 4px;
}
QPushButton:hover {
background-color: #2c6da3;
}
QListWidget::item:selected {
background-color: #dfeffc;
}
QLabel {
font-size: 14px;
color: #333;
}
""")
self.init_ui()
def init_ui(self):
self.setWindowTitle("PDF 页面顺序调整工具")
self.setGeometry(30, 30, 900, 900)
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QGridLayout(central_widget)
layout.setContentsMargins(20, 20, 20, 20)
layout.setHorizontalSpacing(20)
left_side_buttons = QVBoxLayout()
left_side_buttons.setAlignment(Qt.AlignTop | Qt.AlignLeft)
left_side_buttons.setSpacing(10)
self.move_up_button = QPushButton("上移")
self.move_up_button.setFont(QFont("Arial", 12))
self.move_up_button.clicked.connect(self.move_up)
left_side_buttons.addWidget(self.move_up_button)
self.move_down_button = QPushButton("下移")
self.move_down_button.setFont(QFont("Arial", 12))
self.move_down_button.clicked.connect(self.move_down)
left_side_buttons.addWidget(self.move_down_button)
open_button = QPushButton("打开 PDF")
open_button.setFont(QFont("Arial", 12))
open_button.clicked.connect(self.open_pdf)
left_side_buttons.addWidget(open_button)
save_button = QPushButton("保存 PDF")
save_button.setFont(QFont("Arial", 12))
save_button.clicked.connect(self.save_pdf)
left_side_buttons.addWidget(save_button)
export_layout = QHBoxLayout()
self.export_button = QPushButton("导出")
self.export_button.setFont(QFont("Arial", 12))
self.export_button.clicked.connect(self.export_pdf)
export_layout.addWidget(self.export_button)
self.split_checkbox = QCheckBox("拆分页面")
export_layout.addWidget(self.split_checkbox)
self.page_range_start = QLineEdit()
self.page_range_start.setPlaceholderText("起始页")
export_layout.addWidget(self.page_range_start)
self.page_range_end = QLineEdit()
self.page_range_end.setPlaceholderText("结束页")
export_layout.addWidget(self.page_range_end)
left_side_buttons.addLayout(export_layout)
layout.addLayout(left_side_buttons, 0, 0)
right_side_preview = QVBoxLayout()
self.list_widget = QListWidget()
self.list_widget.setFrameShape(QListWidget.NoFrame)
self.list_widget.setDragDropMode(QListWidget.InternalMove)
self.list_widget.setViewMode(QListWidget.IconMode)
self.list_widget.setResizeMode(QListWidget.Adjust)
self.list_widget.setSpacing(10)
self.list_widget.setIconSize(QSize(200, 200))
right_side_preview.addWidget(self.list_widget)
layout.addLayout(right_side_preview, 0, 1)
open_action = QAction('打开 PDF', self)
open_action.triggered.connect(self.open_pdf)
save_action = QAction('保存 PDF', self)
save_action.triggered.connect(self.save_pdf)
def resizeEvent(self, event):
super().resizeEvent(event)
self.update_icon_sizes()
def update_icon_sizes(self):
width_ratio = min(self.width() * 0.5 / self.list_widget.width(), 1)
height_ratio = min(self.height() * 0.5 / self.list_widget.height(), 1)
icon_width = int(self.list_widget.width() * width_ratio)
icon_height = int(self.list_widget.height() * height_ratio)
self.list_widget.setIconSize(QSize(icon_width, icon_height))
self.ensure_unique_items()
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
def dropEvent(self, event):
urls = event.mimeData().urls()
if urls:
file_paths = [url.toLocalFile() for url in urls if url.toLocalFile().lower().endswith('.pdf')]
self.open_pdfs(file_paths)
def open_pdf(self):
file_dialog = QFileDialog()
file_paths, _ = file_dialog.getOpenFileNames(self, "选择PDF文件", "", "PDF文件 (*.pdf)")
if file_paths:
self.open_pdfs(file_paths)
def open_pdfs(self, file_paths):
for file_path in file_paths:
try:
pdf_doc = fitz.open(file_path)
self.current_pdfs.append(pdf_doc)
added_pages = set()
for page_num in range(len(pdf_doc)):
if page_num in added_pages:
continue
page = pdf_doc.load_page(page_num)
pix = page.get_pixmap(matrix=fitz.Matrix(150 / 72, 200 / 72))
qimage = QImage(
pix.samples,
pix.width,
pix.height,
pix.stride,
QImage.Format_RGB888
)
pixmap = QPixmap.fromImage(qimage)
item = QListWidgetItem()
item.setIcon(QIcon(pixmap))
item.setText(f"{file_path.split('/')[-1]} 当前位置: {len(self.list_widget) + 1} (原始位置: {page_num + 1})")
item.setData(Qt.UserRole, (file_path, page_num))
self.list_widget.addItem(item)
added_pages.add(page_num)
self.page_mapping[item] = (file_path, page_num)
QMessageBox.information(self, "成功", f"PDF 文件 {file_path} 加载成功!")
except Exception as e:
QMessageBox.critical(self, "错误", f"无法打开文件 {file_path}: {str(e)}")
def move_up(self):
current_row = self.list_widget.currentRow()
if current_row > 0:
item = self.list_widget.takeItem(current_row)
self.list_widget.insertItem(current_row - 1, item)
self.list_widget.setCurrentRow(current_row - 1)
for index in range(self.list_widget.count()):
self._update_item_text(self.list_widget.item(index))
def move_down(self):
current_row = self.list_widget.currentRow()
if current_row < self.list_widget.count() - 1 and current_row >= 0:
item = self.list_widget.takeItem(current_row)
self.list_widget.insertItem(current_row + 1, item)
self.list_widget.setCurrentRow(current_row + 1)
for index in range(self.list_widget.count()):
self._update_item_text(self.list_widget.item(index))
def save_pdf(self):
if not self.current_pdfs:
QMessageBox.warning(self, "警告", "请先打开 PDF 文件")
return
try:
new_order = [self.page_mapping[self.list_widget.item(i)] for i in range(self.list_widget.count())]
new_pdf = fitz.open()
for file_path, page_index in new_order:
pdf_doc = next((doc for doc in self.current_pdfs if doc.name == file_path), None)
if pdf_doc:
new_pdf.insert_pdf(pdf_doc, from_page=page_index, to_page=page_index)
save_path, _ = QFileDialog.getSaveFileName(self, "保存 PDF 文件", "", "PDF 文件 (*.pdf)")
if save_path:
new_pdf.save(save_path)
QMessageBox.information(self, "成功", "文件保存成功!")
except Exception as e:
QMessageBox.critical(self, "错误", f"保存失败: {str(e)}")
finally:
if 'new_pdf' in locals():
new_pdf.close()
def export_pdf(self):
start_page = self.page_range_start.text().strip()
end_page = self.page_range_end.text().strip()
split_pages = self.split_checkbox.isChecked()
try:
if start_page and end_page:
start_page = int(start_page) - 1
end_page = int(end_page) - 1
pages_to_export = list(range(start_page, end_page + 1))
elif start_page or end_page:
raise ValueError("请输入有效的起始页和结束页")
else:
pages_to_export = list(range(self.list_widget.count()))
if split_pages:
for idx in pages_to_export:
item = self.list_widget.item(idx)
file_path, page_index = self.page_mapping[item]
pdf_doc = next((doc for doc in self.current_pdfs if doc.name == file_path), None)
if pdf_doc:
single_page_pdf = fitz.open()
single_page_pdf.insert_pdf(pdf_doc, from_page=page_index, to_page=page_index)
save_path, _ = QFileDialog.getSaveFileName(self, f"保存页面 {idx + 1}", "", "PDF 文件 (*.pdf)")
if save_path:
single_page_pdf.save(save_path)
QMessageBox.information(self, "成功", f"页面 {idx + 1} 导出成功!")
single_page_pdf.close()
else:
new_order = [self.page_mapping[self.list_widget.item(i)] for i in pages_to_export]
new_pdf = fitz.open()
for file_path, page_index in new_order:
pdf_doc = next((doc for doc in self.current_pdfs if doc.name == file_path), None)
if pdf_doc:
new_pdf.insert_pdf(pdf_doc, from_page=page_index, to_page=page_index)
save_path, _ = QFileDialog.getSaveFileName(self, "保存 PDF 文件", "", "PDF 文件 (*.pdf)")
if save_path:
new_pdf.save(save_path)
QMessageBox.information(self, "成功", "文件保存成功!")
new_pdf.close()
except Exception as e:
QMessageBox.critical(self, "错误", f"导出失败: {str(e)}")
def ensure_unique_items(self):
unique_data = {}
for row in range(self.list_widget.count()):
item = self.list_widget.item(row)
data = item.data(Qt.UserRole)
if data in unique_data:
print(f"Warning: Duplicate page found at row {row}, original position {data}. Removing duplicate.")
self.list_widget.takeItem(row)
break
else:
unique_data[data] = row
def _update_item_text(self, item):
file_path, original_pos = item.data(Qt.UserRole)
current_pos = self.list_widget.row(item) + 1
item.setText(f"{file_path.split('/')[-1]} 当前位置: {current_pos} (原始位置: {original_pos + 1})")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = PDFReorderWindow()
window.show()
sys.exit(app.exec_())
```
### 主要改动点解释:
1. **多文件选择与拖放支持**:
- 修改了 `dropEvent` 和 `open_pdf` 方法以处理多个文件路径。
2. **排序逻辑**:
- 在 `open_pdfs` 方法中为每个页面添加了文件路径信息,并通过 `page_mapping` 字典映射页面与其来源文件。
3. **新增导出功能**:
- 新增了一个水平布局 (`export_layout`) 包含导出按钮、复选框(用于拆分页面)以及两个输入框(用于指定页码范围)。
- 实现了 `export_pdf` 方法,根据用户的选择执行不同的导出操作。
这样,您可以更灵活地管理多个PDF文件并导出所需的页面组合。
阅读全文
相关推荐


















