【问题标题】:How to copy paste an Qgraphicsitem in pyqt5?如何在pyqt5中复制粘贴Qgraphicsitem?
【发布时间】:2019-12-12 16:04:57
【问题描述】:

我在场景中复制粘贴 QGraphicsitem 时遇到问题。 我已经尝试了以下代码,但它无法正常工作。 如果我尝试粘贴该项目,则它是正确粘贴的第一个实例。对于第二个实例,它正在删除第一个实例项并粘贴第二个实例。

到目前为止,我已经尝试在复制操作中获取项目的路径,并在粘贴操作中将其添加到场景中。

@pos2 是我的网格位置

我只想将复制的项目粘贴 n 次,直到新项目被复制。如果我以错误的方式进行复制粘贴,请纠正我。

from PyQt5.QtCore import (QByteArray, QDataStream, QIODevice, QMimeData, QPointF, QPoint, Qt, QRect,QTimer,QLineF, QEvent,QRectF)
from PyQt5.QtGui import QColor, QDrag, QPainter, QPixmap,QFont,QFontMetrics,QBrush, QLinearGradient, QIcon, QPen, QPainterPath, QTransform,QCursor,QMouseEvent,QClipboard
from PyQt5.QtWidgets import QApplication,QGraphicsTextItem,QGraphicsItemGroup, QSizePolicy, QScrollArea, QPushButton,QLineEdit, QMainWindow,QInputDialog, QGraphicsPathItem,QDialog, QVBoxLayout,QGraphicsItem,QStatusBar,QTextEdit, QAction,QMenu, qApp,QSplitter, QButtonGroup, QToolButton, QFrame, QHBoxLayout, QGraphicsView, QGraphicsItem, QGraphicsPixmapItem, QLabel, QGraphicsScene, QWidget
class GraphicsSceneClass(QGraphicsScene):
    global selectedObjType
    def __init__(self, parent=None):
        super(GraphicsSceneClass, self).__init__(parent)

        self.setSceneRect(0, 0, 1920, 1080)
        self.setItemIndexMethod(QGraphicsScene.NoIndex)
        self.setBackgroundBrush(QBrush(Qt.black))

    def mousePressEvent(self, event):
            sampleTransform = QTransform()
            objectAtMouse = self.itemAt(event.scenePos(), sampleTransform)

            if objectAtMouse and event.button()== Qt.LeftButton:
                objectAtMouse.setSelected(True)

            elif objectAtMouse==None and event.button()==Qt.RightButton:
                self.grid = self.TargPosForLine(event.scenePos(), "ForLine")
    def TargPosForLine(self, position, mode):

        clicked_column = int((position.y() // 16)) * 16
        clicked_row = int((position.x() // 16)) * 16
        if clicked_column < 0:
            clicked_column = 0
        if clicked_row < 0:
            clicked_row = 0
        if(mode == "ForRect"):
            return QRect(clicked_row, clicked_column,16,16)
        elif(mode == "ForLine"):
            return QPointF(clicked_row,clicked_column)
class MainWindow(QMainWindow):
    global selectedObjType
    # global item
    def __init__(self,):
        super(MainWindow, self).__init__()
        self.scene = GraphicsSceneClass()
        MainWindow.obj = self.scene
        self.view = QGraphicsView(self.scene)
        self.view.setMouseTracking(True)
        self.view.setRenderHint(QPainter.HighQualityAntialiasing)
        self.widg = QWidget()
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.addWidget(self.view)
        self.widg.setMouseTracking(True)
        self.widget = QWidget()
        self.widget.setLayout(self.horizontalLayout)
        self.setCentralWidget(self.widget)
        self.obj=None

    def contextMenuEvent(self, event):
        contextMenu = QMenu(self)

        Cutaction = contextMenu.addAction("Cut")
        Coaction = contextMenu.addAction("Copy")
        Paaction = contextMenu.addAction("Paste")
        Propaction = contextMenu.addAction("draw")
        quitAct = contextMenu.addAction("quit")
        action = contextMenu.exec_(self.mapToGlobal(event.pos()))
        if action == quitAct:
            self.close()
        elif action == Propaction:
            painterPath = QPainterPath()

            painterPath.moveTo(10, 50.0)
            painterPath.lineTo(50,50)
            painterPath.lineTo(50,55)
            painterPath.lineTo(10,55)
            gradient = QLinearGradient(1, 1, 1, 5)
            gradient.setColorAt(0, QColor(Qt.gray))
            gradient.setColorAt(0.5, QColor(192, 192, 192, 255))
            gradient.setColorAt(1, QColor(Qt.darkGray))
            painterPath.closeSubpath()

            objectDrop = QGraphicsPathItem()
            objectDrop.setPath(painterPath)
            objectDrop.setBrush(QBrush(gradient))
            self.scene.addItem(objectDrop)
            objectDrop.setFlag(QGraphicsItem.ItemIsSelectable)
            objectDrop._type1="line"
            # self.scene.addPath(painterPath)


        elif action == Coaction:
            self.copy()
        elif action == Paaction:
            self.paste()

    def copy(self):
        item = self.selectedItem()
        self.dragObject = item
        x = str(self.dragObject._type1)
        if item is None:
            return
        if 'text' in x:
            self.object = QGraphicsPixmapItem(item.pixmap())
        else:

            self.object = QGraphicsPathItem(item.path())
            gradient = QLinearGradient(1, 1, 1, 5)
            gradient.setColorAt(0, QColor(Qt.gray))
            gradient.setColorAt(0.5, QColor(192, 192, 192, 255))
            gradient.setColorAt(1, QColor(Qt.darkGray))
            self.object.setBrush(QBrush(gradient))
            self.object.setPen(QPen(Qt.NoPen))
            self.object._type1 = item._type1


    def paste(self):
        self.scene.addItem(self.object)
        self.object.setPos(self.scene.grid.x(), self.scene.grid.y())


    def selectedItem(self):
        items = self.scene.selectedItems()

        if len(items) == 1:
            return items[0]
        return None
if __name__=="__main__":
    import sys
    app=QApplication(sys.argv)
    mainWindow = MainWindow()

    mainWindow.show()

    sys.exit(app.exec_())

【问题讨论】:

  • 复制 QGraphicsItem 的任务并不简单,也不能一概而论,所以这取决于您要复制的小部件类型,因此您需要提供minimal reproducible example
  • 我必须在场景中复制并粘贴一个 Qpainterpath 项
  • 你的代码非常少见,因为你使用了dragObject,然后检查if 'text' in x:等。你可以提供一个MRE
  • 我还有一个问题:假设你要复制的item的位置在(x,y)位置,那么复制的item会粘贴到哪里?
  • 对不起..这是我的错误..我的代码中的第二种方法应该是粘贴(def paste(self):)。我把它误认为是副本。

标签: python pyqt pyqt5 qgraphicsitem


【解决方案1】:

复制 QGraphicsItem 不是一项简单的任务,在这种情况下,可以做的是保存最重要的特征并使用该信息重新创建另一个对象。在这种情况下,可以使用 QDataStream 序列化所述信息,这些信息可以轻松传输到知道如何解码它的另一个应用程序。

import importlib
import random
from PyQt5 import QtCore, QtGui, QtWidgets

custom_mimeType = "application/x-qgraphicsitems"


def item_to_ds(it, ds):
    if not isinstance(it, QtWidgets.QGraphicsItem):
        return
    ds.writeQString(it.__class__.__module__)
    ds.writeQString(it.__class__.__name__)
    ds.writeInt(it.flags())
    ds << it.pos()
    ds.writeFloat(it.opacity())
    ds.writeFloat(it.rotation())
    ds.writeFloat(it.scale())
    if isinstance(it, QtWidgets.QAbstractGraphicsShapeItem):
        ds << it.brush() << it.pen()
    if isinstance(it, QtWidgets.QGraphicsPathItem):
        ds << it.path()


def ds_to_item(ds):
    module_name = ds.readQString()
    class_name = ds.readQString()
    mod = importlib.import_module(module_name)
    it = getattr(mod, class_name)()
    flags = QtWidgets.QGraphicsItem.GraphicsItemFlag(ds.readInt())
    pos = QtCore.QPointF()
    ds >> pos
    it.setFlags(flags)
    it.setPos(pos)
    it.setOpacity(ds.readFloat())
    it.setRotation(ds.readFloat())
    it.setScale(ds.readFloat())

    if isinstance(it, QtWidgets.QAbstractGraphicsShapeItem):
        pen, brush = QtGui.QPen(), QtGui.QBrush()
        ds >> brush
        ds >> pen
        it.setPen(pen)
        it.setBrush(brush)
    if isinstance(it, QtWidgets.QGraphicsPathItem):
        path = QtGui.QPainterPath()
        ds >> path
        it.setPath(path)
    return it


class GraphicsView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
        self.setScene(
            QtWidgets.QGraphicsScene(QtCore.QRectF(-200, -200, 400, 400), self)
        )

        for _ in range(4):
            path = QtGui.QPainterPath()
            poly = QtGui.QPolygonF(
                [
                    QtCore.QPointF(0, -40),
                    QtCore.QPointF(-38, -12),
                    QtCore.QPointF(-24, 32),
                    QtCore.QPointF(24, 32),
                    QtCore.QPointF(38, -12),
                    QtCore.QPointF(0, -40),
                ]
            )
            path.addPolygon(poly)
            it = QtWidgets.QGraphicsPathItem(path)
            it.setBrush(QtGui.QColor(*random.sample(range(255), 3)))
            it.setPen(QtGui.QColor(*random.sample(range(255), 3)))
            self.scene().addItem(it)
            it.setPos(QtCore.QPointF(*random.sample(range(-100, 100), 2)))
            it.setFlags(
                it.flags()
                | QtWidgets.QGraphicsItem.ItemIsSelectable
                | QtWidgets.QGraphicsItem.ItemIsMovable
            )

        QtWidgets.QShortcut(
            QtGui.QKeySequence(QtGui.QKeySequence.Copy), self, activated=self.copy_items
        )
        QtWidgets.QShortcut(
            QtGui.QKeySequence(QtGui.QKeySequence.Paste),
            self,
            activated=self.paste_items,
        )

    @QtCore.pyqtSlot()
    def copy_items(self):
        mimedata = QtCore.QMimeData()
        ba = QtCore.QByteArray()
        ds = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
        for it in self.scene().selectedItems():
            item_to_ds(it, ds)
        mimedata.setData(custom_mimeType, ba)
        clipboard = QtGui.QGuiApplication.clipboard()
        clipboard.setMimeData(mimedata)

    @QtCore.pyqtSlot()
    def paste_items(self):
        pos2 = QtCore.QPointF(40, 40)

        clipboard = QtGui.QGuiApplication.clipboard()
        mimedata = clipboard.mimeData()
        if mimedata.hasFormat(custom_mimeType):
            ba = mimedata.data(custom_mimeType)
            ds = QtCore.QDataStream(ba)
            while not ds.atEnd():
                it = ds_to_item(ds)
                self.scene().addItem(it)
                it.setPos(pos2)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = GraphicsView()
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())

更新:

import importlib
from PyQt5 import QtCore, QtGui, QtWidgets


custom_mimeType = "application/x-qgraphicsitems"


def item_to_ds(it, ds):
    if not isinstance(it, QtWidgets.QGraphicsItem):
        return
    ds.writeQString(it.__class__.__module__)
    ds.writeQString(it.__class__.__name__)
    ds.writeInt(it.flags())
    ds << it.pos()
    ds.writeFloat(it.opacity())
    ds.writeFloat(it.rotation())
    ds.writeFloat(it.scale())
    if isinstance(it, QtWidgets.QAbstractGraphicsShapeItem):
        ds << it.brush() << it.pen()
    if isinstance(it, QtWidgets.QGraphicsPathItem):
        ds << it.path()


def ds_to_item(ds):
    module_name = ds.readQString()
    class_name = ds.readQString()
    mod = importlib.import_module(module_name)
    it = getattr(mod, class_name)()
    flags = QtWidgets.QGraphicsItem.GraphicsItemFlag(ds.readInt())
    pos = QtCore.QPointF()
    ds >> pos
    it.setFlags(flags)
    it.setPos(pos)
    it.setOpacity(ds.readFloat())
    it.setRotation(ds.readFloat())
    it.setScale(ds.readFloat())

    if isinstance(it, QtWidgets.QAbstractGraphicsShapeItem):
        pen, brush = QtGui.QPen(), QtGui.QBrush()
        ds >> brush
        ds >> pen
        it.setPen(pen)
        it.setBrush(brush)
    if isinstance(it, QtWidgets.QGraphicsPathItem):
        path = QtGui.QPainterPath()
        ds >> path
        it.setPath(path)
    return it


class GraphicsScene(QtWidgets.QGraphicsScene):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setSceneRect(0, 0, 1920, 1080)
        self.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)
        self.setBackgroundBrush(QtGui.QBrush(QtCore.Qt.black))

    def mousePressEvent(self, event):
        if event.button == QtCore.Qt.LeftButton:
            it = self.itemAt(event.scenePos())
            if it:
                it.setSelected(True)


class GraphicsView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setScene(GraphicsScene(self))
        self.setRenderHint(QtGui.QPainter.HighQualityAntialiasing)

    def contextMenuEvent(self, event):
        menu = QtWidgets.QMenu(self)

        cut_action = menu.addAction("Cut")
        copy_action = menu.addAction("Copy")
        paste_action = menu.addAction("Paste")
        draw_action = menu.addAction("draw")
        quit_action = menu.addAction("quit")
        action = menu.exec_(self.mapToGlobal(event.pos()))
        if action == quit_action:
            self.window().close()
        elif action == draw_action:
            path = QtGui.QPainterPath()
            path.moveTo(-20, -2.5)
            path.lineTo(20, -2.5)
            path.lineTo(20, 2.5)
            path.lineTo(-20, 2.5)
            path.closeSubpath()

            gradient = QtGui.QLinearGradient(1, 1, 1, 5)
            gradient.setColorAt(0, QtGui.QColor(QtCore.Qt.gray))
            gradient.setColorAt(0.5, QtGui.QColor(192, 192, 192, 255))
            gradient.setColorAt(1, QtGui.QColor(QtCore.Qt.darkGray))

            item = QtWidgets.QGraphicsPathItem(path)
            item.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable)
            item.setBrush(QtGui.QBrush(gradient))
            self.scene().addItem(item)
            item.setPos(self.mapToScene(event.pos()))

        elif action == copy_action:
            self.copy_items()

        elif action == paste_action:
            self.paste_items(self.mapToScene(event.pos()))

    def copy_items(self):
        mimedata = QtCore.QMimeData()
        ba = QtCore.QByteArray()
        ds = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
        for it in self.scene().selectedItems():
            item_to_ds(it, ds)
        mimedata.setData(custom_mimeType, ba)
        clipboard = QtGui.QGuiApplication.clipboard()
        clipboard.setMimeData(mimedata)

    def paste_items(self, pos):
        clipboard = QtGui.QGuiApplication.clipboard()
        mimedata = clipboard.mimeData()
        if mimedata.hasFormat(custom_mimeType):
            ba = mimedata.data(custom_mimeType)
            ds = QtCore.QDataStream(ba)
            while not ds.atEnd():
                it = ds_to_item(ds)
                self.scene().addItem(it)
                it.setPos(pos)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._view = GraphicsView()
        self.setCentralWidget(self._view)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

【讨论】:

  • 如果我使用单独的模块来绘制 QGraphicsitem。粘贴操作不起作用。单独的模块有一个 Qgraphicspathitem 类,我在其中编写了painterpath 代码来绘制 Qgraphicsitem。
  • @Python 我只能根据您提供的代码来回答您,现在我已经根据您在问题中提供的代码添加了代码,但是如果您有基于另一个代码的错误不提供出于显而易见的原因我不可能帮助您。
猜你喜欢
  • 1970-01-01
  • 2020-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-16
相关资源
最近更新 更多