python - 如何在 pyqt5 中复制粘贴 Qgraphicsitem?

标签 python pyqt pyqt5 qgraphicsitem

我在场景中复制粘贴 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 不是一项简单的任务,在这种情况下,可以做的是保存最重要的特征并使用该信息重新创建另一个对象。在这种情况下,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_())

关于python - 如何在 pyqt5 中复制粘贴 Qgraphicsitem?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57353390/

相关文章:

python - 修改其他类的列表框

python - 一般来说,(Python)项目是如何构建的?

python - 无法将字典更新序列元素 #0 转换为序列

python - 是否可以在 NumPy 中创建没有行或列的虚拟稀疏矩阵?

python - 如何使用 pyqt 编写诺基亚应用程序?

python - PyQt:退出时没有错误消息(回溯)

python:使用 PyCharm 和 PyQt5 时,进程已完成,退出代码为 1

python - 将两个变量添加到 QComboBox

python - Keras ImageDataGenerator flow_from_dataframe 返回 KeyError

python - 为什么使用Python请求GET请求gz数据时出现ConnectionError