複数のPDFを統合したいときに作ったスクリプト

## はじめに

仕事や学業で複数のPDFファイルを1つに統合する必要が出てくることはよくあります。この記事では、PyQt5を使って作成した使いやすいGUIアプリケーションを紹介します。このアプリケーションを使用すれば、複数のPDFファイルを簡単に選択し、ドラッグ＆ドロップで順序を変更し、ワンクリックで統合することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34295/ba109593-4d08-6d2c-db09-d8107aa42d80.png)


## GUIアプリケーションの概要

このアプリケーションには以下の機能があります：

1. PDFファイルの追加（複数選択可能）
2. 選択したPDFファイルの削除
3. ドラッグ＆ドロップによるPDFファイルの順序変更
4. ワンクリックでのPDF結合と保存
5. プログレスバーによる結合進捗の表示

## 必要なライブラリのインストール

まず、必要なライブラリをインストールしましょう。

```bash
pip install PyQt5 PyPDF2
```

## GUIアプリケーションのコード

以下が完全なアプリケーションのコードです：

```python
import sys
import os
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, 
                             QPushButton, QListWidget, QFileDialog, QMessageBox, 
                             QProgressBar)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyPDF2 import PdfMerger

class PDFMergerThread(QThread):
    progress = pyqtSignal(int)
    finished = pyqtSignal()

    def __init__(self, pdf_files, output_file):
        super().__init__()
        self.pdf_files = pdf_files
        self.output_file = output_file

    def run(self):
        merger = PdfMerger()
        for i, pdf in enumerate(self.pdf_files):
            merger.append(pdf)
            self.progress.emit(int((i + 1) / len(self.pdf_files) * 100))
        merger.write(self.output_file)
        merger.close()
        self.finished.emit()

class PDFMergerApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.pdf_files = []

    def initUI(self):
        self.setWindowTitle('PDF結合アプリ')
        self.setGeometry(100, 100, 500, 400)

        layout = QVBoxLayout()

        self.list_widget = QListWidget()
        self.list_widget.setDragDropMode(QListWidget.InternalMove)
        layout.addWidget(self.list_widget)

        btn_layout = QHBoxLayout()

        add_btn = QPushButton('PDFを追加')
        add_btn.clicked.connect(self.add_pdf)
        btn_layout.addWidget(add_btn)

        remove_btn = QPushButton('選択したPDFを削除')
        remove_btn.clicked.connect(self.remove_pdf)
        btn_layout.addWidget(remove_btn)

        merge_btn = QPushButton('PDFを結合')
        merge_btn.clicked.connect(self.merge_pdfs)
        btn_layout.addWidget(merge_btn)

        layout.addLayout(btn_layout)

        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        layout.addWidget(self.progress_bar)

        self.setLayout(layout)

    def add_pdf(self):
        files, _ = QFileDialog.getOpenFileNames(self, "PDFファイルを選択", "", "PDF Files (*.pdf)")
        for file in files:
            if file not in self.pdf_files:
                self.pdf_files.append(file)
                self.list_widget.addItem(os.path.basename(file))

    def remove_pdf(self):
        for item in self.list_widget.selectedItems():
            self.pdf_files.remove(self.pdf_files[self.list_widget.row(item)])
            self.list_widget.takeItem(self.list_widget.row(item))

    def merge_pdfs(self):
        if len(self.pdf_files) < 2:
            QMessageBox.warning(self, "警告", "結合するには少なくとも2つのPDFファイルが必要です。")
            return

        output_file, _ = QFileDialog.getSaveFileName(self, "結合したPDFを保存", "", "PDF Files (*.pdf)")
        if output_file:
            self.progress_bar.setVisible(True)
            self.merger_thread = PDFMergerThread(self.pdf_files, output_file)
            self.merger_thread.progress.connect(self.update_progress)
            self.merger_thread.finished.connect(self.merge_finished)
            self.merger_thread.start()

    def update_progress(self, value):
        self.progress_bar.setValue(value)

    def merge_finished(self):
        self.progress_bar.setVisible(False)
        QMessageBox.information(self, "成功", "PDFが正常に結合されました。")

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

## アプリケーションの特徴と使い方

### 1. PDFファイルの追加

「PDFを追加」ボタンをクリックすると、ファイル選択ダイアログが開きます。複数のPDFファイルを一度に選択できます。選択したファイルはリストに追加されます。

### 2. PDFファイルの削除

リストから削除したいPDFファイルを選択し、「選択したPDFを削除」ボタンをクリックします。

### 3. PDFファイルの順序変更

リスト内のPDFファイルをドラッグ＆ドロップすることで、結合後のPDFにおけるページの順序を変更できます。

### 4. PDFの結合

「PDFを結合」ボタンをクリックすると、保存ダイアログが開きます。結合後のPDFファイルの保存先と名前を指定します。

### 5. 進捗表示

PDFの結合処理中は、プログレスバーが表示され、処理の進行状況を確認できます。

## コードの解説

### PDFMergerThread クラス

PDFの結合処理を別スレッドで実行し、メインのGUIがフリーズしないようにします。

```python
class PDFMergerThread(QThread):
    progress = pyqtSignal(int)
    finished = pyqtSignal()

    def __init__(self, pdf_files, output_file):
        super().__init__()
        self.pdf_files = pdf_files
        self.output_file = output_file

    def run(self):
        merger = PdfMerger()
        for i, pdf in enumerate(self.pdf_files):
            merger.append(pdf)
            self.progress.emit(int((i + 1) / len(self.pdf_files) * 100))
        merger.write(self.output_file)
        merger.close()
        self.finished.emit()
```

### PDFMergerApp クラス

メインのアプリケーションウィンドウを定義します。

```python
class PDFMergerApp(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.pdf_files = []

    def initUI(self):
        # UIの初期化（省略）

    def add_pdf(self):
        # PDFファイルの追加処理

    def remove_pdf(self):
        # 選択したPDFファイルの削除処理

    def merge_pdfs(self):
        # PDFファイルの結合処理

    def update_progress(self, value):
        # プログレスバーの更新

    def merge_finished(self):
        # 結合処理完了時の処理
```

## まとめ

このGUIアプリケーションを使用することで、複数のPDFファイルを簡単に結合することができます。ドラッグ＆ドロップによる順序変更やプログレスバーによる進捗表示など、ユーザーフレンドリーな機能を備えています。

さらなる改善点としては、以下のような機能を追加することが考えられます：

1. PDFのプレビュー機能
2. 特定のページ範囲のみを結合する機能
3. PDFファイルのメタデータ編集機能

このアプリケーションを基に、自分のニーズに合わせてカスタマイズしてみてください。PDFファイルの管理と結合が、より効率的になることを願っています。