【问题标题】:Resizable In-Line Image on QTextEditQTextEdit 上可调整大小的内嵌图像
【发布时间】:2020-12-03 23:19:30
【问题描述】:

我正在为 QTextEdit 开发一个内联小部件,以通过 QSizeGrip 调整大小并通过上下文菜单进行 90 度旋转。我已经能够在里面渲染一个 QLabel,但它失去了它的常规功能。问题是如何使用 QTextObjectInterface 将 QWidget 正确嵌入到 QTextEdit 中?

https://blog.rfox.eu/en/Programming/Python/Active_widget_in_PyQT5_-_QTextEdit.html

C++ 的相关问题 How to resize an image in a QTextEdit?

用本地文件替换“picture.jpg”以启动示例。

from PyQt5.QtCore import QObject, QSizeF, QRectF, Qt
from PyQt5.QtGui import QTextObjectInterface, QTextFormat, QTextCharFormat, QTextDocument, QPainter, QPixmap
from PyQt5.QtWidgets import QTextEdit, QWidget, QVBoxLayout, QPushButton, QLabel, QSizeGrip


class InLineWidget(QLabel):

    def __init__(self, parent=None):
        super(InLineWidget, self).__init__(parent)
        w, h = self.width(), self.height()
        p = QPixmap(r"picture.jpg")
        self.setPixmap(p)

        self.setMinimumSize(1, 1)
        # self.setScaledContents(False)

        self.setWindowFlags(Qt.SubWindow)
        sizeGrip = QSizeGrip(self)
        # self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        # self.setScaledContents(True)
        # self.setFrameStyle(3)
        # self.setFixedSize(500, 600)
        # self.setMinimumSize(10, 10)
        # self.setStyleSheet("background-color: rgba(0, 0, 0, 40%)")
        # self.setAlignment(Qt.AlignCenter)


class ImageObject(QObject, QTextObjectInterface):
    IMAGE_TYPE = QTextFormat.UserObject + 2

    def __init__(self, parent=None):
        super(ImageObject, self).__init__(parent)

    def drawObject(self, painter: QPainter, rect: QRectF, doc: QTextDocument, posInDocument: int,
                   format: QTextFormat) -> None:
        w = format.property(2)
        w.render(painter, rect.topLeft().toPoint())

    def intrinsicSize(self, doc: QTextDocument, posInDocument: int, format: QTextFormat) -> QSizeF:
        w = format.property(2)
        return QSizeF(w.size())


class TextEdit(QTextEdit):

    def __init__(self):
        super(TextEdit, self).__init__()
        self.img_interface = ImageObject()
        self.img_interface.setParent(self)

        layout = self.document().documentLayout()
        layout.registerHandler(ImageObject.IMAGE_TYPE, self.img_interface)


class TestWidget(QWidget):

    def __init__(self):
        super(TestWidget, self).__init__()
        layout = QVBoxLayout(self)
        self.te = TextEdit()
        layout.addWidget(self.te)
        self.btn_img = QPushButton('add picture', clicked=self.addPic)
        layout.addWidget(self.btn_img)

    def addPic(self):
        image = InLineWidget()
        image.setParent(self.te)

        imgCharFormat = QTextCharFormat()
        imgCharFormat.setObjectType(ImageObject.IMAGE_TYPE)
        imgCharFormat.setProperty(2, image)

        cursor = self.te.textCursor()
        cursor.insertText(chr(0xfffc), imgCharFormat)


if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication
    import sys

    app = QApplication(sys.argv)
    widget = TestWidget()

    widget.show()

    sys.exit(app.exec_())

【问题讨论】:

  • 您的问题不清楚。您想使用 QSizeGrip 来调整/旋转 QLabel 的大小吗?如果是这样,你不能,或者至少不是这样:小部件不是“实际上”在那里:它只是 drawn 在那里。如果你想操作图像对象,你需要与鼠标事件交互,或者更好的是,QTextEdit 子类中的viewportEvent()
  • 注意:我发现在处理 sizeGripper 时会遇到最大递归的情况。当我处理 QTextEdit 小部件而不是 QLabel 时。作为最后一种方式,我们将 imageHandler 伪装成小部件本身通过paint方法,并且大小在mouseReleaseEvent处更新。只需一次更换。

标签: python-3.x pyqt pyqt5 qtextedit qpixmap


【解决方案1】:

更新

因为这些问题,我已经更改了我之前的代码。

1.我们在mouseMoveEvent 中插入和重新插入inlineWidget,它会导致溢出错误和最大递归错误,具体取决于inlineWidget 是什么。在我的情况下,我嵌入QTextEdit(我自己的应用程序的子类)并实现相同行动,但这些错误已经发生。 所以我判断插入应该是一次。 我将插入新小部件的代码移至mouseReleaseEvent

2.对于实施 1 有一个问题。我们只在mouseReleaseEvent处插入了一次inlinewidget,在调整大小的过程中我们无法注意图片大小的图片。所以,我要求ImageHandlerpaint func 采取行动。

请将示例代码替换为此。

class ImageHandler(QGraphicsRectItem):
    def __init__(self, rect = QRectF(), view=None):
        super(ImageHandler, self).__init__(rect)
        self.view = view
        self._color = QColor(200, 100, 200, 100)
    def paint(self, painter, option, widget):
        if hasattr(self, "inlinewidget"):
            #pixmap = QPixmap(r"self.inlinewidget.pixmap()") is more abstractive.
            pixmap = QPixmap(r"picture.jpg")
            pixmap =  pixmap.scaled(self.rect().size().toSize())
            painter.drawPixmap(self.rect().toRect(), pixmap)
        painter.drawRect(self.rect())
    def boundingRect(self):
        return self.rect()
class ImageSizeGrip(ImageHandler):   
    def __init__(self, rect = QRectF(), view=None):
        super(ImageSizeGrip, self).__init__(rect, view)
        self.start_pos = QPointF()
        self.setAcceptedMouseButtons(Qt.LeftButton)
        self.setAcceptHoverEvents(True)
        self.color = QColor(120, 243, 80, 200)
        self.setFlags(QGraphicsRectItem.ItemIsSelectable|QGraphicsRectItem.ItemIsMovable)
        self.grab_textobject = False
        self.find_cursor = QTextCursor()
        self.find_position = 0
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.start_pos = event.scenePos()
        cur = self.parentItem().inlinewidget.parent().cursorForPosition(self.parentItem().inlinewidget.geometry().bottomRight())
        doc = self.parentItem().inlinewidget.parent().document()
        
        self.find_cursor = doc.find(chr(0xfffc), cur.position(), doc.FindBackward)
        self.find_position = self.find_cursor.position()
        self.find_cursor.clearSelection()             
        self.find_cursor.setPosition(self.find_position, self.find_cursor.MoveAnchor)
        self.find_cursor.movePosition(self.find_cursor.Left, self.find_cursor.KeepAnchor)
        return QGraphicsRectItem.mousePressEvent(self, event)
    def mouseMoveEvent(self, event):
        parentItem = self.parentItem()
        parentItem.prepareGeometryChange()
        rect = QRectF()
        rect.setCoords(parentItem.rect().topLeft().x(), parentItem.rect().topLeft().y(), event.scenePos().x(), event.scenePos().y())
        parentItem.setRect(rect)
        parentItem.update() 
        self.find_cursor.clearSelection()
        self.find_cursor.setPosition(self.find_position, self.find_cursor.MoveAnchor)
        return QGraphicsRectItem.mouseMoveEvent(self, event)
    def mouseReleaseEvent(self, event):
        parentItem = self.parentItem()
        self.find_cursor.movePosition(self.find_cursor.Left, self.find_cursor.KeepAnchor)
        if self.find_cursor.selectedText() == chr(0xfffc) and  len(self.find_cursor.selectedText()) == 1:
            self.find_cursor.setKeepPositionOnInsert(True)           
            self.find_cursor.deleteChar()
            inLineWidget = InLineWidget()
            inLineWidget.setGeometry(parentItem.rect().toRect())
            parentItem.inlinewidget.parent().insert_text_object(self.find_cursor, inLineWidget)        
            self.find_cursor.setKeepPositionOnInsert(False)
            parentItem.inlinewidget.parent()._trigger_obj_char_rescan()
        self.view.scene().removeItem(self.parentItem())
        self.view.scene().removeItem(self)
        self.view.scene().update()
        return QGraphicsRectItem.mouseReleaseEvent(self, event)
    def hoverEnterEvent(self, event):
        self.setCursor(Qt.PointingHandCursor)
        return QGraphicsRectItem.hoverEnterEvent(self, event)
    def hoverLeaveEvent(self, event):        
        return QGraphicsRectItem.hoverLeaveEvent(self, event)

老答案

我已经判断问题的更新是最后一个,所以我想用假设来写我的答案。

至少,如果你想在小部件上嵌入相同大小的图片,你必须将它与父小部件成比例地缩放。

为此,您必须添加缩放代码。

w.setPixmap(w.pixmap().scaled(w.size()))

然后,您想通过QSizeGrip 调整小部件的大小。 您还想调整嵌入图像的大小。

QTextObjectInterface 可以绘制QTextObject 但它不是交互式的。 根据您问题的网址,您也必须渲染 QSizeGrip 小部件。

你忘了在def addPic(self):写这段代码

image.show()

但仅此而已,绘制了两个维度的图像。一个是drawObject绘制的,另一个是'InlineWidget`嵌入的像素图对象。(一个= A,另一个= B)

现在,您绘制一个小部件,但这是一种特殊情况。本来,QTextObjectInterface 是准备画一个非交互对象,就好像它是一个角色一样。当然,你可以画什么,它就可以画。但通常,我们不会用它来绘制一个交互小部件。 你是从特例开始的。这让我们很难认清问题出在哪里。

由于无意义的句子,您将设法确认重复图片下发生的事情。 'A' 由drawObject 绘制。 'B' 只显示,但有QTextEdit 的父级,可以说它是唯一的小部件。我们的文本处理对'B' 没有影响。

如果添加一些文本,“A”会被文本逐渐滑动,因为它确实是TextObject。 但是'B'没有滑动,因为它不是TextObject而是QWidget

利用自动计算QTextObjectInterface 的优势,我们必须将“B”的位置与“A”的位置重合。我们必须将此代码添加到drawObject

w.move(rect.topLeft().toPoint())

通过这些步骤,您可以将图片嵌入到与小部件一样多的大小,显示交互式小部件,将InLineLabel 移动到图片和图片本身。

当您显示小部件时,我认为drawObject() 中的inlined_widget.widget.render(painter, rect.topLeft().toPoint()) 已经不需要,因为小部件代表它工作。


从现在开始,我会大幅更改您的代码。

即使添加了上面提到的修复代码,如果要调整图像大小,仍然存在很多问题。

  1. QSizeGrip 在这种情况下很难处理。我强烈建议由QGraphicsViewQGraphicsSceneQGraphicsItem 处理。(我不知道如何使用QSizeGrip,就我看你的代码而言,这是在左上角。我认为这应该是放置在右下角。我很快就无法捕捉到坐标系。我认为这是因为InLineWidget 的几何形状尚未确定。插入时已确定。但 QSizeGrip 已经是它的孩子。 ) 我尝试实现QSizeGripEnabled(True)QDialog,即使我将它设置为False,我也可以调整对话框的大小...

  2. 如果我们调整小部件的大小,嵌入的像素图将被调整大小。但是jpg文件如果在同一个内存中,由于粗略的、近似的重新计算的积累,很容易崩溃。我们需要重新加载新的同名像素图并缩放大小,然后交换。

  3. 即使我们通过删除、复制、剪切等方式删除TextObject'A',小部件'B'仍然保留。我们需要实现同时删除小部件和基本对象的方式.

  4. 要从“B”调整大小,当然,您可以调整它的大小,但“A”没有调整大小。因此,调整'B'的大小对行距没有影响。我们可以从这两种方法调整大小,但我采用'B'的方式。我们需要使'B'的变化对'A'产生影响。为此,我们必须在第二个 URL 之后交换和更新 'A'。我们需要同时调整 'B' 的大小,删除 'A' 并重新插入 'A'。

为了解决这些问题,我们至少需要实现 URL 的过程。 多亏了小部件,接受鼠标处理要容易得多。它充当几何图形的代理和吸气剂。

这里是混合代码与你介绍的网址,你的和我的。

from PyQt5.QtCore import QObject, QSizeF, QRectF, Qt, QPointF
from PyQt5.QtGui import QTextObjectInterface, QTextFormat, QTextCharFormat, QTextDocument, QPainter, QPixmap, QPageSize, QBrush, QColor, QTextCursor
from PyQt5.QtWidgets import QTextEdit, QWidget, QVBoxLayout, QPushButton, QLabel, QGraphicsView, QGraphicsScene, QGraphicsRectItem, QSizeGrip


class View(QGraphicsView):
    def __init__(self, parent=None):
        super(View, self).__init__(parent)       
        self.imageHandler = ImageHandler(QRectF(), self)
        self.sizeGripHandler = ImageSizeGrip(QRectF(), self)
        self.sizeGripHandler.setParentItem(self.imageHandler)
    def keyPressEvent(self, event):
        self.scene().removeItem(self.imageHandler)
        self.scene().removeItem(self.sizeGripHandler)
        return QGraphicsView.keyPressEvent(self, event)
class Scene(QGraphicsScene):
    def __init__(self, parent=None):
        super(Scene, self).__init__(parent)
        self.setBackgroundBrush(QBrush(Qt.gray, Qt.SolidPattern))
        
    def addItem(self, item):
        if item not in self.items():
            super(Scene, self).addItem(item)
    def removeItem(self, item):
        if item  in self.items():
            super(Scene, self).removeItem(item)
class ImageHandler(QGraphicsRectItem):
    def __init__(self, rect = QRectF(), view=None):
        super(ImageHandler, self).__init__(rect)
        self.view = view
        self._color = QColor(200, 100, 200, 100)
    def paint(self, painter, option, widget):
        painter.setBrush(QBrush(self._color, Qt.Dense1Pattern))
        painter.drawRect(self.rect())
    def boundingRect(self):
        return self.rect()
    
class ImageSizeGrip(ImageHandler):
   
    def __init__(self, rect = QRectF(), view=None):
        super(ImageSizeGrip, self).__init__(rect, view)
        self.start_pos = QPointF()
        self.setAcceptedMouseButtons(Qt.LeftButton)
        self.setAcceptHoverEvents(True)
        self.color = QColor(120, 243, 80, 200)
        self.setFlags(QGraphicsRectItem.ItemIsSelectable|QGraphicsRectItem.ItemIsMovable)
        self.grab_textobject = False
        self.find_cursor = QTextCursor()
        self.find_position = 0
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.start_pos = event.scenePos()
        cur = self.parentItem().inlinewidget.parent().cursorForPosition(self.parentItem().inlinewidget.geometry().bottomRight())
        doc = self.parentItem().inlinewidget.parent().document()
        #chr(0xfffc) is probably located in the previous positions because ImageSizeGrip is positioned at the rightbottom of the picture.
        self.find_cursor = doc.find(chr(0xfffc), cur.position(), doc.FindBackward)
        self.find_position = self.find_cursor.position()
        self.find_cursor.clearSelection()             
        self.find_cursor.setPosition(self.find_position, self.find_cursor.MoveAnchor)
        movable = self.find_cursor.movePosition(self.find_cursor.Left, self.find_cursor.KeepAnchor)
        return QGraphicsRectItem.mousePressEvent(self, event)
    def mouseMoveEvent(self, event):
        parentItem = self.parentItem()
        parentItem.prepareGeometryChange()
        rect = QRectF()
        rect.setCoords(parentItem.rect().topLeft().x(), parentItem.rect().topLeft().y(), event.scenePos().x(), event.scenePos().y())
        parentItem.setRect(rect)
        parentItem.inlinewidget.resize(rect.size().toSize())
        parentItem.update()
        #I think it is better you load initial pixmap item. Especially jpg is subject to be broken by size change.
        pixmap = QPixmap(r"picture.jpg")
        pixmap =  pixmap.scaled(rect.size().toSize())
        parentItem.inlinewidget.pixmap().swap(pixmap)
        self.find_cursor.clearSelection()
        self.find_cursor.setPosition(self.find_position, self.find_cursor.MoveAnchor)
        movable = self.find_cursor.movePosition(self.find_cursor.Left, self.find_cursor.KeepAnchor)
        if self.find_cursor.selectedText() == chr(0xfffc) and  len(self.find_cursor.selectedText()) == 1:
            self.find_cursor.setKeepPositionOnInsert(True)           
            self.find_cursor.deleteChar()
            inLineWidget = InLineWidget()
            inLineWidget.setGeometry(parentItem.rect().toRect())
            parentItem.inlinewidget.parent().insert_text_object(self.find_cursor, inLineWidget)        
            self.find_cursor.setKeepPositionOnInsert(False)
            parentItem.inlinewidget.parent()._trigger_obj_char_rescan()
        return QGraphicsRectItem.mouseMoveEvent(self, event)
    def mouseReleaseEvent(self, event):
        self.view.scene().removeItem(self.parentItem())
        self.view.scene().removeItem(self)
        self.view.scene().update()
        return QGraphicsRectItem.mouseReleaseEvent(self, event)
    def hoverEnterEvent(self, event):
        self.setCursor(Qt.PointingHandCursor)
        return QGraphicsRectItem.hoverEnterEvent(self, event)
    def hoverLeaveEvent(self, event):        
        return QGraphicsRectItem.hoverLeaveEvent(self, event)
class InLineWidget(QLabel):
    def __init__(self, parent=None):
        super(InLineWidget, self).__init__(parent)
        self.view = None
        #Now, you are using only one picture. but if you set it as variable, you can set any pixmap item.
        p = QPixmap(r"picture.jpg")
        self.setPixmap(p)
        self.setMinimumSize(1, 1)
        # self.setScaledContents(False)
        # self.setWindowFlags(Qt.SubWindow)
        sizeGrip = QSizeGrip(self)
        sizeGrip.setWindowFlags(Qt.SubWindow)
        # self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        # self.setScaledContents(True)
        self.setFrameStyle(3)
        # self.setFixedSize(500, 600)
        # self.setMinimumSize(10, 10)
        # self.setStyleSheet("background-color: rgba(0, 0, 0, 40%)")
        # self.setAlignment(Qt.AlignCenter)
    def mousePressEvent(self, event):
        self.view.imageHandler.setRect(QRectF(self.geometry()))        
        self.view.sizeGripHandler.setRect(QRectF(self.view.imageHandler.rect().bottomRight(), QSizeF(10, 10)))
        self.view.sizeGripHandler.setPos(0, 0)
        self.view.scene().addItem(self.view.imageHandler)
        self.view.scene().addItem(self.view.sizeGripHandler)        
        self.view.imageHandler.inlinewidget = self
        return QLabel.mousePressEvent(self, event)    

class InlinedWidgetInfo:
    #From your pasting URL
    object_replacement_character = chr(0xfffc)
    _instance_counter = 0
    def __init__(self, widget):
        self.widget = widget
        self.text_format_id = QTextFormat.UserObject + InlinedWidgetInfo._instance_counter
        self.char = self.object_replacement_character
        InlinedWidgetInfo._instance_counter += 1

class TextEdit(QTextEdit):
    
    def __init__(self):
        super(TextEdit, self).__init__()
        #From your pasting URL, but some valiables belong to QTextEdit.
        self.last_text_lenght = 0
        self.text_format_id_to_inlined_widget_map = {}
        self.currentCharFormatChanged.connect(self.on_character_format_change)
        self.selectionChanged.connect(self._trigger_obj_char_rescan)
        self.textChanged.connect(self.on_text_changed)
    
    def wrap_with_text_object(self, inlined_widget):       
        class ImageObject(QObject, QTextObjectInterface):       
            def __init__(self, parent=None):
                super(ImageObject, self).__init__(parent)        
            def drawObject(self, painter: QPainter, rect: QRectF, doc: QTextDocument, posInDocument: int,
                           format: QTextFormat) -> None:
                
                # inlined_widget.widget.render(painter, rect.topLeft().toPoint())
                inlined_widget.widget.setGeometry(rect.toRect())
            def intrinsicSize(self, doc: QTextDocument, posInDocument: int, format: QTextFormat) -> QSizeF:               
                return QSizeF(inlined_widget.widget.size())
        document_layout = self.document().documentLayout()
        document_layout.registerHandler(inlined_widget.text_format_id, ImageObject(self))
        self.text_format_id_to_inlined_widget_map[inlined_widget.text_format_id] = inlined_widget
        inlined_widget.widget.setPixmap(inlined_widget.widget.pixmap().scaled(inlined_widget.widget.size()))

    def insert_text_object(self, cursor, inlined_widget):
        inlined_widget = InlinedWidgetInfo(inlined_widget)        
        self.wrap_with_text_object(inlined_widget)
        inlined_widget.widget.view = self.vi
        inlined_widget.widget.setParent(self)

        char_format = QTextCharFormat()
        char_format.setObjectType(inlined_widget.text_format_id)
        cursor.insertText(inlined_widget.char, char_format)
        inlined_widget.widget.show()

    def on_character_format_change(self, qtextcharformat):
        text_format_id = qtextcharformat.objectType()
        
        # id 0 is used when the object is deselected - I don't really want the id
        # itself, I just want to know that there was some change AFTER it was done
        if text_format_id == 0:
            self._trigger_obj_char_rescan()

    def on_text_changed(self):
        current_text_lenght = len(self.toPlainText())
        if self.last_text_lenght > current_text_lenght:
            self._trigger_obj_char_rescan()

        self.last_text_lenght = current_text_lenght

    def _trigger_obj_char_rescan(self):
        text = self.toPlainText()
        character_indexes = [
            cnt for cnt, char in enumerate(text)
            if char == InlinedWidgetInfo.object_replacement_character
        ]

        # get text_format_id for all OBJECT REPLACEMENT CHARACTERs
        present_text_format_ids = set()
        for index in character_indexes:
            cursor = QTextCursor(self.document())

            # I have to create text selection in order to detect correct character
            cursor.setPosition(index)
            if index < len(text):
                cursor.setPosition(index + 1, QTextCursor.KeepAnchor)

            text_format_id = cursor.charFormat().objectType()

            present_text_format_ids.add(text_format_id)

        # diff for characters that are there and that should be there
        expected_text_format_ids = set(self.text_format_id_to_inlined_widget_map.keys())
        removed_text_ids = expected_text_format_ids - present_text_format_ids

        # hide widgets for characters that were removed
        for text_format_id in removed_text_ids:
            inlined_widget = self.text_format_id_to_inlined_widget_map[text_format_id]
            inlined_widget.widget.hide()
            del self.text_format_id_to_inlined_widget_map[text_format_id]
class TestWidget(QWidget):

    def __init__(self):
        super(TestWidget, self).__init__()
        layout = QVBoxLayout(self)
        self.vi = View()
        self.sc = Scene()
        self.te = TextEdit()
        self.te.vi = self.vi
        self.te.resize(int(QPageSize.size(QPageSize.A0, QPageSize.Point).width()), int(QPageSize.size(QPageSize.A0, QPageSize.Point).height()))
        self.sc.addWidget(self.te)
        self.vi.setScene(self.sc)
        self.vi.setSceneRect(QRectF(0, 0, int(QPageSize.size(QPageSize.A0, QPageSize.Point).width()), int(QPageSize.size(QPageSize.A0, QPageSize.Point).height())))
        layout.addWidget(self.vi)
        self.btn_img = QPushButton('add picture', clicked=lambda:self.te.insert_text_object(self.te.textCursor(), InLineWidget()))
        layout.addWidget(self.btn_img)
        self.vi.centerOn(0 ,0)
    def addPic(self):
        # is not used
        pass

if __name__ == '__main__':
    from PyQt5.QtWidgets import QApplication
    import sys

    app = QApplication(sys.argv)
    widget = TestWidget()

    widget.show()

    sys.exit(app.exec_())

变化点

我想你大概会明白哪里改了。

  1. QGraphicsView是新导入的,用于查看QGraphicsItem
  2. QTextEdit 适合 QGraphicsViewQGraphicsScene
  3. 实现 URLs 代码以干净地删除小部件。
  4. 实现第二个url交换QTextObject的方式,对齐QTextLine的动态底部。

如果您有任何问题,请发表评论。

感谢您的 URL 易于理解和可读的编码,停靠代码非常容易。

如果嵌入多张图片,每张图片的底线会被强制对齐在当前QTextLine的最大底线。所以你在调整大小时会看起来很奇怪。但很难解决只要图片是QTextInlineObject

【讨论】:

  • 感谢您的回答,这是一个完美的解决方案,将有助于许多尝试扩展 QTextEdit。
  • 将 transformMode = Qt.SmoothTransformation 添加到 pixmap().scaled 可以提供更好的视觉效果,我正在寻找限制纵横比的方法..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-15
  • 2018-03-16
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
相关资源
最近更新 更多