Issue
I have a problem with copy-pasting a QGraphicsitem in a scene. I have tried the following code but it is not working properly. If I tried to Paste the item, the first instance it is pasting correctly. For the second instance, it is removing the first instance item and pasting the second instance.
As of now, I have tried to get the path of the item in copy action and adding it to the scene in paste action.
@pos2 is my grid position
I just want to paste the copied item for n number of time until the new item is copied. Correct me if I am doing copy-paste in the wrong way.
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_())
Solution
Copying a QGraphicsItem is not a trivial task, in this case what can be done is to save the most important features and recreate another object using that information. In this case, a QDataStream can be used to serialize said information that can be easily transported to another application that knows how to decode it.
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_())
Update:
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_())
Answered By - eyllanesc
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.