找回密码
 立即注册
首页 业界区 安全 一个PDF合并器

一个PDF合并器

跟尴 2025-5-31 23:30:23
效果

1.jpeg

直接上代码

[code]from PySide6.QtWidgets import (    QGridLayout,    QGroupBox,    QHBoxLayout,    QVBoxLayout,    QApplication,    QWidget,    QLabel,    QScrollArea,)from PySide6.QtCore import Signalfrom PySide6.QtCore import Qt, QSize, Signal,  QMimeData, QPoint, QRect, QUrlfrom PySide6.QtGui import QPixmap, QPainter, QImage, QDragimport subprocessimport pathlibimport fitz #pip install PyMuPdfimport weakrefimport qdarkthemeclass PDFInfo:    def __init__(self, path: pathlib.Path):        self.path: pathlib.Path = path        self.pixmap: QPixmap = None  # type: ignore        self.page_count: int = 0    def load_pixmap(self):        if not self.pixmap:            doc = fitz.open(self.path)            self.page_count = len(doc)            page = doc.load_page(0)            pm = page.get_pixmap()  # type: ignore            imgfmt = (                QImage.Format.Format_RGBA8888                if pm.alpha                else QImage.Format.Format_RGB888            )            self.pixmap = QPixmap.fromImage(                QImage(pm.samples, pm.width, pm.height, pm.stride, imgfmt)            )        return self.pixmap    def word(self, ind: int):        return f"{ind}: 共{self.page_count}页"class card(QGroupBox):    doubleClicked = Signal(int)    cardDropped = Signal(int, int)  # 参数1:源card索引, 参数2:目标card索引    def __init__(self, parent):        super().__init__(parent)        self.pp = weakref.ref(parent)        self.pdf_info: PDFInfo = None  # type: ignore        self.setAcceptDrops(True)    def supportedDropActions(self):        return Qt.DropAction.CopyAction | Qt.DropAction.MoveAction    def setui(self):        self.lb = QLabel()        self.lb.setScaledContents(True)        hbox = QHBoxLayout()        self.setLayout(hbox)        hbox.addWidget(self.lb)        self.setFixedSize(QSize(200, 160))        return self    def setdata(self, pdf_info: PDFInfo):        self.pdf_info = pdf_info        if self.pdf_info.page_count > 1:            self.setProperty("muti", True)        else:            self.setProperty("muti", False)        # self.setTitle(f"{pdf_info.path.stem}: 共{pdf_info.page_count}页")    def mouseDoubleClickEvent(self, event):        if self.pdf_info:            self.doubleClicked.emit(self.pdf_info.path)        super().mouseDoubleClickEvent(event)    def mousePressEvent(self, event):        self.dragStartPosition = event.position().toPoint()        super().mousePressEvent(event)    def mime_encode(self):        mime = QMimeData()        # 设置自定义格式的拖动数据(传入当前card在box中的索引)        main_window = self.pp()        if isinstance(main_window, box):            card_index = main_window.cards.index(self)            mime.setData("application/x-item", str(card_index).encode("utf-8"))        return mime    def mime_decode(self, mime: QMimeData):        if mime.hasFormat("application/x-item"):            return int(mime.data("application/x-item").data())        else:            return None    def mouseMoveEvent(self, event):        if not (event.buttons() & Qt.MouseButton.LeftButton):            return        offset = (event.position().toPoint() - self.dragStartPosition).manhattanLength()        if offset < QApplication.startDragDistance():            # 记录当前card的索引            mime = self.mime_encode()            drag = QDrag(self)            drag.setMimeData(mime)            drag.exec(Qt.DropAction.MoveAction)            # 设置拖动预览图像            pixmap = QPixmap(self.size())            self.render(pixmap)            drag.setPixmap(pixmap)            drag.setHotSpot(event.position().toPoint())            drag.exec()    def dragEnterEvent(self, event):        mime = event.mimeData()        if mime.hasFormat("application/x-item"):            event.acceptProposedAction()    def dropEvent(self, event):        s = self.mime_decode(event.mimeData())        if s is not None:            parent = self.pp()            if parent and isinstance(parent, box):                target_index = parent.cards.index(self)                if s != target_index and s != target_index - 1:                    self.cardDropped.emit(s, target_index)        event.accept()    def paintEvent(self, event):        super().paintEvent(event)        if not self.pdf_info:            return        painter = QPainter(self)        painter.setRenderHint(QPainter.RenderHint.Antialiasing)        # 设置字体        font = painter.font()        font.setPointSize(8)        painter.setFont(font)        # 设置文本颜色        cl = painter.background().color()        painter.setPen(cl)        painter.setBrush(cl)        # 获取文本内容        text = self.pdf_info.path.name        # 计算文本绘制位置        text_rect = painter.fontMetrics().boundingRect(text)        if text_rect.width() > self.width() - 20:            text = text[:18] + "..."            text_rect = painter.fontMetrics().boundingRect(text)        x = (self.width() - text_rect.width()) // 2        y = self.height() - 5        d = 3        r2 = QRect(            x - d, y - 10 - d, text_rect.width() + 2 * d, text_rect.height() + 2 * d        )        painter.drawRect(r2)        painter.setPen(Qt.GlobalColor.black)        painter.setBrush(Qt.BrushStyle.NoBrush)        # 绘制文本        painter.drawText(x, y, text)class EndLabel(QLabel):    cardDropped = Signal(int)    def __init__(self, text, parent=None):        super().__init__(text, parent)        self.setAlignment(Qt.AlignmentFlag.AlignCenter)        self.setStyleSheet("background-color: lightgray; border: 1px solid gray;")        self.setFixedSize(200, 150)        self.setAcceptDrops(True)    def dragEnterEvent(self, event):        mime = event.mimeData()        if mime.hasFormat("application/x-item"):            event.acceptProposedAction()    def dropEvent(self, event):        mime = event.mimeData()        if mime.hasFormat("application/x-item"):            source_index = int(mime.data("application/x-item").data())            self.cardDropped.emit(source_index)        event.accept()class c1(QLabel):    sg_dropped = Signal(list)    def __init__(self, text, parent=None):        super().__init__(text, parent)        self.setFixedSize(200, 100)        self.setProperty("level", "funcCard1")        self.setAcceptDrops(True)        self.setAlignment(Qt.AlignmentFlag.AlignCenter)    def dragEnterEvent(self, event):        if event.mimeData().hasUrls():            event.acceptProposedAction()    def dropEvent(self, event):        urls = event.mimeData().urls()        if urls:            ret = []            for file in urls:                file_path = file.toLocalFile()                if file_path.lower().endswith(".pdf"):                    ret.append(pathlib.Path(file_path).absolute())            if ret:                self.sg_dropped.emit(ret)        event.accept()class c2(QLabel):    sg_dropped = Signal(int)    def __init__(self, text, parent=None):        super().__init__(text, parent)        self.setFixedSize(200, 100)        self.setProperty("level", "funcCard2")        self.setAcceptDrops(True)        self.setAlignment(Qt.AlignmentFlag.AlignCenter)    def dragEnterEvent(self, event):        mime = event.mimeData()        if mime.hasFormat("application/x-item"):            event.acceptProposedAction()    def dropEvent(self, event):        mime = event.mimeData()        if mime.hasFormat("application/x-item"):            source_index = int(mime.data("application/x-item").data())            self.sg_dropped.emit(source_index)        event.accept()class c3(QLabel):    sg_dropped = Signal(int)    def __init__(self, text, parent=None):        super().__init__(text, parent)        self.setFixedSize(200, 100)        self.setProperty("level", "funcCard3")        self.setAcceptDrops(True)        self.setAlignment(Qt.AlignmentFlag.AlignCenter)        self.temp_pdf = None        self.dragStartPosition = QPoint()    def mousePressEvent(self, event):        if event.button() == Qt.MouseButton.LeftButton:            self.dragStartPosition = event.position().toPoint()        super().mousePressEvent(event)    def mouseMoveEvent(self, event):        if not (event.buttons() & Qt.MouseButton.LeftButton):            return        # 检查拖动距离是否足够        if (            event.position().toPoint() - self.dragStartPosition        ).manhattanLength() < QApplication.startDragDistance():            return        try:            # 合并所有PDF文件            output_path = pathlib.Path("temp.pdf").absolute()            doc = fitz.open()            parent = self.parent()            if isinstance(parent, mainw) and parent.w1.cards:                for card in parent.w1.cards:                    if hasattr(card, "pdf_info"):                        try:                            src_doc = fitz.open(card.pdf_info.path)                            doc.insert_pdf(src_doc)                            src_doc.close()                        except Exception as e:                            print(f"Error processing {card.pdf_info.path}: {e}")                if len(doc) > 0:                    doc.save(output_path)                    print(f"DF合并完成,保存到: {output_path}")                    doc.close()                    self.temp_pdf = output_path                    # 创建拖拽操作                    drag = QDrag(self)                    mime = QMimeData()                    urls = [QUrl.fromLocalFile(str(output_path))]                    print(urls)                    mime.setUrls(urls)                    drag.setMimeData(mime)                    # 设置拖拽预览图像                    pixmap = QPixmap(self.size())                    self.render(pixmap)                    drag.setPixmap(pixmap)                    drag.setHotSpot(event.position().toPoint())                    # 执行拖拽操作                    result = drag.exec(                        Qt.DropAction.CopyAction | Qt.DropAction.MoveAction                    )                    print(f"拖拽操作结果: {result}")                else:                    print("没有可合并的PDF文件")                    doc.close()        except Exception as e:            print(f"拖拽操作出错: {e}")class box(QScrollArea):    def __init__(self, parent=None):        super().__init__(parent)        # self.setAcceptDrops(True)        self.cards: list[card] = []        # 创建容器widget和布局        self.container = QWidget()        self.grid = QGridLayout(self.container)        self.container.setLayout(self.grid)        # 设置滚动区域属性        self.setWidgetResizable(True)        self.setWidget(self.container)        self.setMinimumSize(450, 350)  # 设置最小窗口尺寸        # 添加始终显示在末尾的特殊EndLabel        self.end_label = EndLabel("拖到此处移动到最后", self.container)        self.end_label.cardDropped.connect(self.move_card_to_end)    def setui(self):        # self.load_pdfs(r"C:\Users\Administrator\Desktop\ZJ\5.6")        return self    def load_pdfs(self, folder_path):        pp = pathlib.Path(folder_path).absolute()        for i, pdf_file in enumerate(pp.glob("*.pdf")):            self.add_pdf_item(pdf_file, i)    def add_pdf_item(self, pdf_path, pos):        pdf_info = PDFInfo(pdf_path)        pdf_info.load_pixmap()        cd = card(self).setui()        cd.setdata(pdf_info)        cd.lb.setPixmap(pdf_info.load_pixmap())        cd.doubleClicked.connect(self.on_item_double_clicked)        cd.cardDropped.connect(self.move_card)        row = pos // (self.width() // 200)        col = pos % (self.width() // 200)        self.grid.addWidget(cd, row, col)        self.cards.append(cd)    def resizeEvent(self, event):        self._resize()        super().resizeEvent(event)    def _resize(self):        self.rearrange_items()        # 计算并设置高度: card高度*(行数+2)        cols = max(1, self.width() // 200)        rows = (len(self.cards) + cols - 1) // cols + 2  # 行数+2        self.container.setFixedHeight(150 * rows)    def rearrange_items(self):        cols = max(1, self.width() // 200)        # 清除布局        while self.grid.count():            item = self.grid.takeAt(0)            if item.widget():                item.widget().setParent(None)        # 添加所有普通card        cd: card        for i, cd in enumerate(self.cards):            row = i // cols            col = i % cols            self.grid.addWidget(cd, row, col)            cd.setTitle(cd.pdf_info.word(i + 1))        # 添加end_label到最后一行第一列        total_items = len(self.cards)        rows = (total_items + cols - 1) // cols        self.grid.addWidget(self.end_label, rows, 0)    def makepixmap(self, p: pathlib.Path):        pone = fitz.open(p).load_page(0)        pm = pone.get_pixmap()  # type: ignore        imgfmt = (            QImage.Format.Format_RGBA8888 if pm.alpha else QImage.Format.Format_RGB888        )        pimg = QImage(pm.samples, pm.width, pm.height, pm.stride, imgfmt)        return QPixmap.fromImage(pimg)    def move_card_to_end(self, source_index):        if source_index != -1:            source_card = self.cards.pop(source_index)            self.cards.append(source_card)            self.rearrange_items()    def move_card(self, source_index, target_index):        if source_index == target_index:            return        # 从原位置移除源card        source_card = self.cards.pop(source_index)        # 在目标位置插入源card        if source_index < target_index:            target_index -= 1        self.cards.insert(target_index, source_card)        # 重新排列布局        self.rearrange_items()    def on_item_double_clicked(self, index):        print(f"Item {index} double clicked")        pinfoDFInfo=self.cards[index].pdf_info        cmd=['explorer',str(pinfo.path.absolute())]        subprocess.Popen(cmd)class mainw(QWidget):    def __init__(self):        super().__init__()        self.setWindowTitle("ZLPDF-PDF合并器")        ly = QVBoxLayout()        self.w1 = box().setui()        ly.addWidget(self.w1)        hb = QHBoxLayout()        self.c1 = c1("把要合并的PDF拖到我这里", self)        self.c2 = c2("把要去掉的PDF拖动到我这里", self)        self.c3 = c3("把合并后的文件从我这里拖走", self)        for i in [self.c1, self.c2, self.c3]:            hb.addWidget(i)        ly.addLayout(hb)        self.setLayout(ly)        self.c1.sg_dropped.connect(self.addpdfs)        self.c2.sg_dropped.connect(self.remove_pdf)    def addpdfs(self, ll: list[pathlib.Path]):        for pdf in ll:            self.w1.add_pdf_item(pdf, len(self.w1.cards))        self.w1._resize()    def remove_pdf(self, index: int):        """移除指定索引的PDF卡片"""        if 0
您需要登录后才可以回帖 登录 | 立即注册