Python 如何根据QVBoxLayout在QScrolla中的子内容自动调整其大小?

Python 如何根据QVBoxLayout在QScrolla中的子内容自动调整其大小?,python,pdf,pyqt,pyqt5,pymupdf,Python,Pdf,Pyqt,Pyqt5,Pymupdf,最近,我尝试使用PyQT5制作PDF查看器。我修改了本文中提供的代码()。我创建了一个包含QVBoxLayout的QScrollArea,以便在滚动区域中动态添加多个QLABLE。然后,我将把PDF页面作为QImage(pixmap)加载到每个QLabel中。我已成功加载并在QLabels中显示PDF页面。然而,我遇到了一个问题。带有PDF页面图像的垂直布局中的QLabel无法展开以显示整个页面(根据QImage pixmap的大小)。因此,使用这种方式的结果将只显示页面的一小部分。我也无法向下

最近,我尝试使用PyQT5制作PDF查看器。我修改了本文中提供的代码()。我创建了一个包含QVBoxLayout的QScrollArea,以便在滚动区域中动态添加多个QLABLE。然后,我将把PDF页面作为QImage(pixmap)加载到每个QLabel中。我已成功加载并在QLabels中显示PDF页面。然而,我遇到了一个问题。带有PDF页面图像的垂直布局中的QLabel无法展开以显示整个页面(根据QImage pixmap的大小)。因此,使用这种方式的结果将只显示页面的一小部分。我也无法向下滚动所有页面。 我希望PDF页面可以加载到Qlabel中,并根据内容进行良好的扩展。然后,QLabel可以在布局中垂直分组。布局可以根据QLable自动展开和调整大小。最后,我可以向下滚动滚动区域来阅读所有PDF页面。就像其他PDF阅读器一样

此外,如何捕获每个QLabel中的鼠标位置?最后,我想让用户点击页面上的特定位置,在该位置添加文本。在我从QLabel和特定页码获得坐标后,我将把信息传递给PyMuPDF,将文本写入textBox并导出PDF文件

以下是我目前的代码:

import fitz
import cv2
import numpy as np
from PyQt5.QtCore import QDir, Qt, QPoint
from PyQt5.QtGui import QImage, QPainter, QPalette, QPixmap, QColor, QFont
from PyQt5.QtWidgets import (QAction, QApplication, QFileDialog, QLabel,
        QMainWindow, QMenu, QMessageBox, QScrollArea, QSizePolicy)
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter


"""
class MyLabel(QLabel):
    def __init__(self):
        super(MyLabel, self).__init__()

    def paintEvent(self, event):
        super(MyLabel, self).paintEvent(event)
        if txt_cache:
            for c in txt_cache:
                print(c)
                pos, txt = c
                painter = QPainter(self)
                painter.setPen(QColor(255, 0, 0))
                painter.drawText(pos, txt)
"""


class ImageViewer(QMainWindow):
    def __init__(self):
        super(ImageViewer, self).__init__()

        self.original_pdf_img_cv = []
        self.qImg_pdf = []
        self.qLabels = []
        self.pageCount = 0

        self.printer = QPrinter()
        self.scaleFactor = 0.0

        self.imageLabel = QLabel()
        self.imageLabel.setBackgroundRole(QPalette.Base)
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)

        self.content_widget = QtWidgets.QWidget()
        self.content_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.scrollArea = QScrollArea(widgetResizable=True)
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scroll_layout = QtWidgets.QVBoxLayout(self.content_widget)
        self.scrollArea.setWidget(self.content_widget)
        self.setCentralWidget(self.scrollArea)

        self.createActions()
        self.createMenus()

        self.setWindowTitle("PDF Viewer")
        self.resize(500, 400)

    def open(self):
        fileName, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.currentPath())
        if fileName:
            doc = fitz.open(fileName)
            self.pageCount = doc.pageCount
            print(self.pageCount)
            for page in doc:
                pix = page.getPixmap()
                im = self.pix2np(pix)
                self.original_pdf_img_cv.append(im)
                self.qImg_pdf.append(self.convert_cv(im))
            pp_num = 1
            for qimg in self.qImg_pdf:
                label = QLabel()
                label.setBackgroundRole(QPalette.Base)
                label.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
                label.setScaledContents(True)

                #self.scrollArea.setWidget(label)
                label.setPixmap(QPixmap.fromImage(qimg))

                self.scroll_layout.addWidget(label)

                label.setObjectName(str(pp_num))
                print(label.objectName())
                self.qLabels.append(label)
                pp_num += 1

            """
            image = QImage(fileName)
            if image.isNull():
                QMessageBox.information(self, "Image Viewer", "Cannot load %s." % fileName)
                return
            """

            #self.imageLabel.setPixmap(QPixmap.fromImage(image))
            self.scaleFactor = 1.0

            self.printAct.setEnabled(True)
            self.fitToWindowAct.setEnabled(True)
            self.updateActions()

            if not self.fitToWindowAct.isChecked():
                for qlabel in self.qLabels:
                    qlabel.adjustSize()
                #self.imageLabel.adjustSize()

    def print_(self):
        dialog = QPrintDialog(self.printer, self)
        if dialog.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.imageLabel.pixmap().size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
            painter.setWindow(self.imageLabel.pixmap().rect())
            painter.drawPixmap(0, 0, self.imageLabel.pixmap())

    def zoomIn(self):
        self.scaleImage(1.25)

    def zoomOut(self):
        self.scaleImage(0.8)

    def normalSize(self):
        for qlabel in self.qLabels:
            qlabel.adjustSize()
        #self.imageLabel.adjustSize()
        self.scaleFactor = 1.0

    def fitToWindow(self):
        fitToWindow = self.fitToWindowAct.isChecked()
        self.scrollArea.setWidgetResizable(fitToWindow)
        if not fitToWindow:
            self.normalSize()

        self.updateActions()

    def about(self):
        QMessageBox.about(self, "About Image Viewer",
                "<p>The <b>Image Viewer</b> example shows how to combine "
                "QLabel and QScrollArea to display an image. QLabel is "
                "typically used for displaying text, but it can also display "
                "an image. QScrollArea provides a scrolling view around "
                "another widget. If the child widget exceeds the size of the "
                "frame, QScrollArea automatically provides scroll bars.</p>"
                "<p>The example demonstrates how QLabel's ability to scale "
                "its contents (QLabel.scaledContents), and QScrollArea's "
                "ability to automatically resize its contents "
                "(QScrollArea.widgetResizable), can be used to implement "
                "zooming and scaling features.</p>"
                "<p>In addition the example shows how to use QPainter to "
                "print an image.</p>")

    def createActions(self):
        self.openAct = QAction("&Open...", self, shortcut="Ctrl+O",
                triggered=self.open)

        self.printAct = QAction("&Print...", self, shortcut="Ctrl+P",
                enabled=False, triggered=self.print_)

        self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q",
                triggered=self.close)

        self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++",
                enabled=False, triggered=self.zoomIn)

        self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-",
                enabled=False, triggered=self.zoomOut)

        self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S",
                enabled=False, triggered=self.normalSize)

        self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False,
                checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow)

        self.aboutAct = QAction("&About", self, triggered=self.about)

        self.aboutQtAct = QAction("About &Qt", self,
                triggered=QApplication.instance().aboutQt)

    def createMenus(self):
        self.fileMenu = QMenu("&File", self)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.printAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.viewMenu = QMenu("&View", self)
        self.viewMenu.addAction(self.zoomInAct)
        self.viewMenu.addAction(self.zoomOutAct)
        self.viewMenu.addAction(self.normalSizeAct)
        self.viewMenu.addSeparator()
        self.viewMenu.addAction(self.fitToWindowAct)

        self.helpMenu = QMenu("&Help", self)
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

        self.menuBar().addMenu(self.fileMenu)
        self.menuBar().addMenu(self.viewMenu)
        self.menuBar().addMenu(self.helpMenu)

    def updateActions(self):
        self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked())
        self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked())

    def scaleImage(self, factor):
        self.scaleFactor *= factor
        for qlabel in self.qLabels:
            qlabel.resize(self.scaleFactor * qlabel.pixmap().size())
        #self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size())

        self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor)
        self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor)

        self.zoomInAct.setEnabled(self.scaleFactor < 3.0)
        self.zoomOutAct.setEnabled(self.scaleFactor > 0.333)

    def adjustScrollBar(self, scrollBar, factor):
        scrollBar.setValue(int(factor * scrollBar.value()
                                + ((factor - 1) * scrollBar.pageStep()/2)))

    def mousePressEvent(self, event):
        self.originQPoint = self.imageLabel.mapFromGlobal(self.mapToGlobal(event.pos()))
        self.currentQRubberBand = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self.imageLabel)
        self.currentQRubberBand.setGeometry(QtCore.QRect(self.originQPoint, QtCore.QSize()))
        self.currentQRubberBand.show()

    def mouseMoveEvent(self, event):
        p = self.imageLabel.mapFromGlobal(self.mapToGlobal(event.pos()))
        QtWidgets.QToolTip.showText(event.pos(), "X: {} Y: {}".format(p.x(), p.y()), self)
        if self.currentQRubberBand.isVisible() and self.imageLabel.pixmap() is not None:
            self.currentQRubberBand.setGeometry(
                QtCore.QRect(self.originQPoint, p).normalized() & self.imageLabel.rect())

    def mouseReleaseEvent(self, event):
        self.currentQRubberBand.hide()
        currentQRect = self.currentQRubberBand.geometry()
        self.currentQRubberBand.deleteLater()
        if self.imageLabel.pixmap() is not None:
            tr = QtGui.QTransform()
            if self.fitToWindowAct.isChecked():
                tr.scale(self.imageLabel.pixmap().width() / self.scrollArea.width(),
                         self.imageLabel.pixmap().height() / self.scrollArea.height())
            else:
                tr.scale(1 / self.scaleFactor, 1 / self.scaleFactor)
            r = tr.mapRect(currentQRect)



            txt_cache.append((QPoint(r.x(), r.y()), 'Test!!!!!!'))
            self.imageLabel.update()

            cropQPixmap = self.imageLabel.pixmap().copy(r)
            cropQPixmap.save('output.png')

    def pix2np(self, pix):
        im = np.frombuffer(pix.samples, dtype=np.uint8).reshape(pix.h, pix.w, pix.n)
        im = np.ascontiguousarray(im[..., [2, 1, 0]])  # rgb to bgr
        return im

    def convert_cv(self, cvImg):
        height, width, channel = cvImg.shape
        bytesPerLine = 3 * width
        qImg = QImage(cvImg.data, width, height, bytesPerLine, QImage.Format_RGB888)
        return qImg


if __name__ == '__main__':
    import sys
    from PyQt5 import QtGui, QtCore, QtWidgets

    app = QApplication(sys.argv)
    imageViewer = ImageViewer()
    imageViewer.show()
    sys.exit(app.exec_())
导入fitz
进口cv2
将numpy作为np导入
从PyQt5.QtCore导入QDir、Qt、QPoint
从PyQt5.QtGui导入QImage、QPainter、qpalete、QPixmap、QColor、QFont
从PyQt5.QtWidgets导入(QAction、QApplication、QFileDialog、QLabel、,
QMainWindow、QMenu、QMessageBox、qScrollara、QSizePolicy)
从PyQt5.QtPrintSupport导入QPrintDialog,QPrinter
"""
类MyLabel(QLabel):
定义初始化(自):
超级(MyLabel,self)。\uuuu init
def paintEvent(自身,事件):
超级(MyLabel,self).paintEvent(事件)
如果txt_缓存:
对于txt_缓存中的c:
印刷品(c)
pos,txt=c
油漆工=油漆工(自身)
画师设置笔(QColor(255,0,0))
painter.drawText(pos,txt)
"""
类ImageViewer(QMainWindow):
定义初始化(自):
超级(ImageViewer,self)。\uuuu init\uuuuu()
self.original\u pdf\u img\u cv=[]
self.qImg_pdf=[]
self.qLabels=[]
self.pageCount=0
self.printer=QPrinter()
self.scaleFactor=0.0
self.imageLabel=QLabel()
self.imageLabel.setBackgroundRole(qpalete.Base)
self.imageLabel.setSizePolicy(QSizePolicy.Ignored,QSizePolicy.Ignored)
self.imageLabel.setScaledContents(True)
self.content\u widget=qtwidts.QWidget()
self.content_widget.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)
self.scrollArea=QScrollArea(WidgetResizeable=True)
self.scrollArea.setBackgroundRole(QPalette.Dark)
self.scroll\u layout=qtwidts.QVBoxLayout(self.content\u小部件)
self.scrollArea.setWidget(self.content\u小部件)
self.setCentralWidget(self.scrollArea)
self.createActions()
self.createMenus()
self.setWindowTitle(“PDF查看器”)
自我调整大小(500400)
def打开(自):
文件名,QFileDialog.getOpenFileName(self,“打开文件”,QDir.currentPath())
如果文件名为:
doc=fitz.open(文件名)
self.pageCount=doc.pageCount
打印(self.pageCount)
对于文档中的页面:
pix=page.getPixmap()
im=self.pix2np(pix)
self.original\u pdf\u img\u cv.append(im)
self.qImg_pdf.append(self.convert_cv(im))
pp_num=1
对于self.qimg_pdf中的qimg:
label=QLabel()
label.setBackgroundRole(QPalette.Base)
label.setSizePolicy(QSizePolicy.Ignored,QSizePolicy.Ignored)
label.setScaledContents(True)
#self.scrollArea.setWidget(标签)
label.setPixmap(QPixmap.fromImage(qimg))
self.scroll\u layout.addWidget(标签)
label.setObjectName(str(pp_num))
打印(label.objectName())
self.qLabels.append(标签)
pp_num+=1
"""
image=QImage(文件名)
如果image.isNull():
QMessageBox.information(self,“图像查看器”,“无法加载%s.%fileName)
返回
"""
#self.imageLabel.setPixmap(QPixmap.fromImage(图像))
self.scaleFactor=1.0
self.printAct.setEnabled(True)
self.fitToWindowAct.setEnabled(真)
self.updateActions()
如果不是self.fittowAct.isChecked():
对于self.qLabels中的qlabel:
qlabel.adjustSize()
#self.imageLabel.adjustSize()文件
def打印(自我):
dialog=QPrintDialog(self.printer,self)
if dialog.exec_389;()
painter=QPainter(自动打印机)
rect=painter.viewport()
size=self.imageLabel.pixmap().size()
size.scale(rect.size(),Qt.KeepAspectRatio)
painter.setViewport(rect.x(),rect.y(),size.width(),size.height())
painter.setWindow(self.imageLabel.pixmap().rect())
painter.drawPixmap(0,0,self.imageLabel.pixmap())
def缩放(自身):
自缩放图像(1.25)
def zoomOut(自身):
自缩放图像(0.8)
def正常大小(自身):
对于self.qLabels中的qlabel:
qlabel.adjustSize()
#self.imageLabel.adjustSize()文件
self.scaleFactor=1.0
def fitToWindow(自身):
fitToWindow=self.fitToWindowAct.isChecked()
self.scrollArea.setWidgetResizeable(fitToWindow)
如果不适合Windows:
self.normalSize()
self.updateActions()
关于(自我)的定义:
QMessageBox。