PyQt5 画家组对象
PyQt5 Painter Group Object
我想知道是否有一种方法可以将一堆形状绘制为自己的对象,以便当用户想要缩放、更改颜色或旋转时,所有形状都会相应地调整?
然后我希望将那组形状放置在 QGraphicsView
上预定的 x,y 坐标上,并进行指定的旋转。目前,我收到一些错误消息,显示 QPainter::scale: Painter not active
和 NotImplementedError: QGraphicsItem.paint() is abstract and must be overridden
.
我对 PyQt 中的图形和绘画还很陌生,不确定这是否是实现我想要的目标的正确方法。非常感谢任何帮助
代码
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class LogObject(QObject):
hovered = pyqtSignal()
notHovered = pyqtSignal()
class ExamplePaintObject(QPainter):
def __init__(self):
super(ExamplePaintObject,self).__init__()
self.color = QColor(0,0,0)
def paintEvent(self,event):
self.drawEllipse(0,0,0.0268,0.0268)
self.drawEllipse(0,0,0.0357,0.0357)
self.drawLine(0.0357,0.0045,0.0357+0.0821,0.0045)
self.drawLine(0.0357,-0.0045,0.0357+0.0821,-0.0045)
self.drawRect(0.0357+0.0821,-0.0045,0.1450,0.1450)
class Point(QGraphicsItem):
def __init__(self, x, y,r, name):
super(Point,self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.name = name
self.ex= ExamplePaintObject()
self.ex.scale(40,40)
self.x = x
self.y = y
self.rotate = r
self.setRotation(self.rotate)
self.setAcceptHoverEvents(True)
self.log = LogObject()
self.setPos(x, y)
self.isSelected = False
def itemChange(self, change, value):
if change == self.ItemSelectedChange:
self.setBrush(QBrush(Qt.green) if value else QBrush(Qt.black))
return QGraphicsItem.itemChange(self, change, value)
def hoverEnterEvent(self, event):
self.setBrush(QColor("red"))
self.log.hovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def hoverLeaveEvent(self, event):
if self.isSelected == True:
self.setBrush(QBrush(Qt.green))
else:
self.setBrush(QColor("black"))
self.log.notHovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def mousePressEvent(self, event):
print(self.name)
QGraphicsItem.mousePressEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self._zoom = 0
self._empty = True
self.setScene(QGraphicsScene(self))
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
self.viewport().setCursor(Qt.ArrowCursor)
QTimer.singleShot(0, self.reset_fit)
self.selectedItems = []
def setItems(self):
self.data = {
"x": [
-2414943.8686,
-2417160.6592,
-2417160.6592,
-2417856.1783,
-2417054.7618,
-2416009.9966,
-2416012.5232,
-2418160.8952,
-2418160.8952,
-2416012.5232,
-2417094.7694,
-2417094.7694,
],
"y": [
10454269.7008,
10454147.2672,
10454147.2672,
10453285.2456,
10452556.8132,
10453240.2808,
10455255.8752,
10455183.1912,
10455183.1912,
10455255.8752,
10456212.5959,
10456212.5959,
],
"rotation":[
313.9962,
43.9962,
223.9962,
227.7070,
227.7070,
313.9962,
43.9962,
43.9962,
223.9962,
223.9962,
43.9962,
223.9962,
]
}
maxX = max(self.data["x"])
minX = min(self.data["x"])
maxY = max(self.data["y"])
minY = min(self.data["y"])
distance = sqrt((maxX - minX) ** 2 + (maxY - minY) ** 2)
self.area = QRectF(minX, minY, distance, distance)
for i, (x, y,r) in enumerate(zip(self.data["x"], self.data["y"],self.data["rotation"])):
p = Point(x, y,r, "Point__" + str(i))
p.log.hovered.connect(self.hoverChange)
p.log.notHovered.connect(self.notHoverChange)
self.scene().addItem(p)
def setPoints(self):
self.setItems()
self.setDragMode(self.ScrollHandDrag)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.reset_fit()
else:
self._zoom = 0
def hoverChange(self):
self.viewport().setCursor(Qt.PointingHandCursor)
def notHoverChange(self):
self.viewport().setCursor(Qt.ArrowCursor)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
singleItem = self.itemAt(event.pos().x(), event.pos().y())
if singleItem != None:
if QApplication.keyboardModifiers() == Qt.ShiftModifier: # This will determine if the shift key is depressed
if singleItem.isSelected == True:
singleItem.setSelected(False)
singleItem.isSelected = False
self.selectedItems.remove(singleItem)
elif singleItem.isSelected == False:
singleItem.setSelected(True)
singleItem.isSelected = True
self.selectedItems.append(singleItem)
else:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(
QEvent.MouseButtonPress,
QPointF(event.pos()),
Qt.LeftButton,
event.buttons(),
Qt.KeyboardModifiers(),
)
QGraphicsView.mousePressEvent(self, handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
point = event.pos()
# print(self.mapToScene(point))
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
if self.rubberBand.isVisible():
self.rubberBand.hide()
rect = self.rubberBand.geometry()
rect_scene = self.mapToScene(rect).boundingRect()
selected = self.scene().items(rect_scene)
if selected:
for selectedPoints in selected:
if QApplication.keyboardModifiers() == Qt.ShiftModifier: # This will determine if the shift key is depressed
if selectedPoints.isSelected == True:
selectedPoints.setSelected(False)
selectedPoints.isSelected = False
self.selectedItems.remove(selectedPoints)
elif selectedPoints.isSelected == False: # if the shif key is not depressed and its not selected, then select it
selectedPoints.setSelected(True)
selectedPoints.isSelected = True
self.selectedItems.append(selectedPoints)
print( "".join("Item: %s\n" % child.name for child in self.selectedItems))
else:
print(" Nothing\n")
for selected in self.selectedItems:
selected.setSelected(False)
selected.isSelected = False
self.selectedItems.clear()
QGraphicsView.mouseReleaseEvent(self, event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ArrowCursor)
handmade_event = QMouseEvent(
QEvent.MouseButtonRelease,
QPointF(event.pos()),
Qt.LeftButton,
event.buttons(),
Qt.KeyboardModifiers(),
)
QGraphicsView.mouseReleaseEvent(self, handmade_event)
# super(Viewer, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(
QRect(self.origin, event.pos()).normalized()
)
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self, event)
super(Viewer, self).mouseMoveEvent(event)
def reset_fit(self):
r = self.scene().itemsBoundingRect()
self.resetTransform()
self.setSceneRect(r)
self.fitInView(r, Qt.KeepAspectRatio)
self._zoom = 0
self.scale(1, -1)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText("Fit Into View")
self.btnLoad.clicked.connect(self.fitPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
def fitPoints(self):
self.viewer.reset_fit()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
一个可能的解决方案是使用 QPainterPath:
def create_path():
path = QPainterPath()
path.addEllipse(0, 0, 0.0268, 0.0268)
path.addEllipse(0, 0, 0.0357, 0.0357)
path.moveTo(0.0357, 0.0045)
path.lineTo(0.0357 + 0.0821, 0.0045)
path.moveTo(0.0357, -0.0045)
path.lineTo(0.0357 + 0.0821, -0.0045)
path.addRect(0.0357 + 0.0821, -0.0045, 0.1450, 0.1450)
tr = QTransform()
tr.scale(100, 100)
path = tr.map(path)
return path
class Point(QGraphicsPathItem):
def __init__(self, x, y, r, name):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.name = name
self.setPath(create_path())
self.setScale(10)
# ...
为了更好地观察,您必须对 QGraphicView 使用 setRenderHints (QPainter.Antialiasing):
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self.setRenderHints(QPainter.Antialiasing) # <---
# ....
我想知道是否有一种方法可以将一堆形状绘制为自己的对象,以便当用户想要缩放、更改颜色或旋转时,所有形状都会相应地调整?
然后我希望将那组形状放置在 QGraphicsView
上预定的 x,y 坐标上,并进行指定的旋转。目前,我收到一些错误消息,显示 QPainter::scale: Painter not active
和 NotImplementedError: QGraphicsItem.paint() is abstract and must be overridden
.
我对 PyQt 中的图形和绘画还很陌生,不确定这是否是实现我想要的目标的正确方法。非常感谢任何帮助
代码
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class LogObject(QObject):
hovered = pyqtSignal()
notHovered = pyqtSignal()
class ExamplePaintObject(QPainter):
def __init__(self):
super(ExamplePaintObject,self).__init__()
self.color = QColor(0,0,0)
def paintEvent(self,event):
self.drawEllipse(0,0,0.0268,0.0268)
self.drawEllipse(0,0,0.0357,0.0357)
self.drawLine(0.0357,0.0045,0.0357+0.0821,0.0045)
self.drawLine(0.0357,-0.0045,0.0357+0.0821,-0.0045)
self.drawRect(0.0357+0.0821,-0.0045,0.1450,0.1450)
class Point(QGraphicsItem):
def __init__(self, x, y,r, name):
super(Point,self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.name = name
self.ex= ExamplePaintObject()
self.ex.scale(40,40)
self.x = x
self.y = y
self.rotate = r
self.setRotation(self.rotate)
self.setAcceptHoverEvents(True)
self.log = LogObject()
self.setPos(x, y)
self.isSelected = False
def itemChange(self, change, value):
if change == self.ItemSelectedChange:
self.setBrush(QBrush(Qt.green) if value else QBrush(Qt.black))
return QGraphicsItem.itemChange(self, change, value)
def hoverEnterEvent(self, event):
self.setBrush(QColor("red"))
self.log.hovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def hoverLeaveEvent(self, event):
if self.isSelected == True:
self.setBrush(QBrush(Qt.green))
else:
self.setBrush(QColor("black"))
self.log.notHovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def mousePressEvent(self, event):
print(self.name)
QGraphicsItem.mousePressEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self._zoom = 0
self._empty = True
self.setScene(QGraphicsScene(self))
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
self.viewport().setCursor(Qt.ArrowCursor)
QTimer.singleShot(0, self.reset_fit)
self.selectedItems = []
def setItems(self):
self.data = {
"x": [
-2414943.8686,
-2417160.6592,
-2417160.6592,
-2417856.1783,
-2417054.7618,
-2416009.9966,
-2416012.5232,
-2418160.8952,
-2418160.8952,
-2416012.5232,
-2417094.7694,
-2417094.7694,
],
"y": [
10454269.7008,
10454147.2672,
10454147.2672,
10453285.2456,
10452556.8132,
10453240.2808,
10455255.8752,
10455183.1912,
10455183.1912,
10455255.8752,
10456212.5959,
10456212.5959,
],
"rotation":[
313.9962,
43.9962,
223.9962,
227.7070,
227.7070,
313.9962,
43.9962,
43.9962,
223.9962,
223.9962,
43.9962,
223.9962,
]
}
maxX = max(self.data["x"])
minX = min(self.data["x"])
maxY = max(self.data["y"])
minY = min(self.data["y"])
distance = sqrt((maxX - minX) ** 2 + (maxY - minY) ** 2)
self.area = QRectF(minX, minY, distance, distance)
for i, (x, y,r) in enumerate(zip(self.data["x"], self.data["y"],self.data["rotation"])):
p = Point(x, y,r, "Point__" + str(i))
p.log.hovered.connect(self.hoverChange)
p.log.notHovered.connect(self.notHoverChange)
self.scene().addItem(p)
def setPoints(self):
self.setItems()
self.setDragMode(self.ScrollHandDrag)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.reset_fit()
else:
self._zoom = 0
def hoverChange(self):
self.viewport().setCursor(Qt.PointingHandCursor)
def notHoverChange(self):
self.viewport().setCursor(Qt.ArrowCursor)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
singleItem = self.itemAt(event.pos().x(), event.pos().y())
if singleItem != None:
if QApplication.keyboardModifiers() == Qt.ShiftModifier: # This will determine if the shift key is depressed
if singleItem.isSelected == True:
singleItem.setSelected(False)
singleItem.isSelected = False
self.selectedItems.remove(singleItem)
elif singleItem.isSelected == False:
singleItem.setSelected(True)
singleItem.isSelected = True
self.selectedItems.append(singleItem)
else:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(
QEvent.MouseButtonPress,
QPointF(event.pos()),
Qt.LeftButton,
event.buttons(),
Qt.KeyboardModifiers(),
)
QGraphicsView.mousePressEvent(self, handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
point = event.pos()
# print(self.mapToScene(point))
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
if self.rubberBand.isVisible():
self.rubberBand.hide()
rect = self.rubberBand.geometry()
rect_scene = self.mapToScene(rect).boundingRect()
selected = self.scene().items(rect_scene)
if selected:
for selectedPoints in selected:
if QApplication.keyboardModifiers() == Qt.ShiftModifier: # This will determine if the shift key is depressed
if selectedPoints.isSelected == True:
selectedPoints.setSelected(False)
selectedPoints.isSelected = False
self.selectedItems.remove(selectedPoints)
elif selectedPoints.isSelected == False: # if the shif key is not depressed and its not selected, then select it
selectedPoints.setSelected(True)
selectedPoints.isSelected = True
self.selectedItems.append(selectedPoints)
print( "".join("Item: %s\n" % child.name for child in self.selectedItems))
else:
print(" Nothing\n")
for selected in self.selectedItems:
selected.setSelected(False)
selected.isSelected = False
self.selectedItems.clear()
QGraphicsView.mouseReleaseEvent(self, event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ArrowCursor)
handmade_event = QMouseEvent(
QEvent.MouseButtonRelease,
QPointF(event.pos()),
Qt.LeftButton,
event.buttons(),
Qt.KeyboardModifiers(),
)
QGraphicsView.mouseReleaseEvent(self, handmade_event)
# super(Viewer, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(
QRect(self.origin, event.pos()).normalized()
)
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self, event)
super(Viewer, self).mouseMoveEvent(event)
def reset_fit(self):
r = self.scene().itemsBoundingRect()
self.resetTransform()
self.setSceneRect(r)
self.fitInView(r, Qt.KeepAspectRatio)
self._zoom = 0
self.scale(1, -1)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText("Fit Into View")
self.btnLoad.clicked.connect(self.fitPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
def fitPoints(self):
self.viewer.reset_fit()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
一个可能的解决方案是使用 QPainterPath:
def create_path():
path = QPainterPath()
path.addEllipse(0, 0, 0.0268, 0.0268)
path.addEllipse(0, 0, 0.0357, 0.0357)
path.moveTo(0.0357, 0.0045)
path.lineTo(0.0357 + 0.0821, 0.0045)
path.moveTo(0.0357, -0.0045)
path.lineTo(0.0357 + 0.0821, -0.0045)
path.addRect(0.0357 + 0.0821, -0.0045, 0.1450, 0.1450)
tr = QTransform()
tr.scale(100, 100)
path = tr.map(path)
return path
class Point(QGraphicsPathItem):
def __init__(self, x, y, r, name):
super(Point, self).__init__()
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.name = name
self.setPath(create_path())
self.setScale(10)
# ...
为了更好地观察,您必须对 QGraphicView 使用 setRenderHints (QPainter.Antialiasing):
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self.setRenderHints(QPainter.Antialiasing) # <---
# ....