问题描述
我正在使用动态创建的 QGraphicsRectItems 使用 Python Qt 开发一个单击并拖动地图编辑器。我需要在 QGraphicsRectItem 类中添加 3 个鼠标事件函数,以便这些矩形在移动项目后释放鼠标时自动捕捉到 25x15 网格,这很好用。
问题是,添加这些功能后,我不再能够在选择时一次移动多个矩形。我仍然可以通过将鼠标拖到多个矩形上来同时选择多个矩形,但尝试移动整个选择只会移动其中一个项目。
这是我的代码示例:
import sys
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
TILEWIDTH = 25
TILEHEIGHT = 15
OUTLINE = 3
KEY_METADATA = 1
class RoomItem(QGraphicsRectItem):
def __init__(self,offset_x,offset_y,width,height,outline,fill,metadata=None,parent=None):
super().__init__(0,parent)
self.setPos(offset_x,offset_y)
self.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable)
self.setData(KEY_METADATA,metadata)
self.setPen(outline)
self.setBrush(fill)
#Mouse functions to snap rectItem to grid
#Get mouse and rect positions on the initial click
def mousePressEvent(self,event):
self.click_x = event.scenePos().x()
self.click_y = event.scenePos().y()
self.initial_x = self.pos().x()
self.initial_y = self.pos().y()
#Move rectangle relative to the mouse
def mouseMoveEvent(self,event):
x = event.scenePos().x() - (self.click_x - self.initial_x)
y = event.scenePos().y() - (self.click_y - self.initial_y)
pos = QPointF(x,y)
self.setPos(pos)
#Snap rectangle to 25x15 grid when mouse is released
def mouseReleaseEvent(self,event):
x = round((event.scenePos().x() - (self.click_x - self.initial_x))/TILEWIDTH)*TILEWIDTH
y = round((event.scenePos().y() - (self.click_y - self.initial_y))/TILEHEIGHT)*TILEHEIGHT
pos = QPointF(x,y)
self.setPos(pos)
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.scene = QGraphicsScene(self)
self.view = QGraphicsView(self.scene,self)
self.view.setDragMode(QGraphicsView.RubberBandDrag)
self.view.scale(1,-1)
self.view.setStyleSheet("background:transparent; border: 0px")
self.setCentralWidget(self.view)
def draw_map(self):
#Drawing from an existing list
for i in self.room_list:
fill = QColor("#000000")
outline = QPen("#ffffff")
outline.setWidth(OUTLINE)
outline.setJoinStyle(Qt.MiterJoin)
#Creating the RoomItem
rect = RoomItem(i.offset_x,i.offset_z,i.width,i.height,fill)
self.scene.addItem(rect)
def main():
app = QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
如何恢复默认设置的多选和移动系统,同时保留允许每个对象对齐网格的鼠标功能?
解决方法
如果您希望项目位置是某些整数值的倍数,那么您必须在释放鼠标后更正它,没有必要覆盖 mousePressEvent 和 mouseReleaseEvent 方法,因为在您的实现中您正在修改默认功能,在此案例是一次移动多个项目。
import random
import sys
from PySide6.QtCore import Qt
from PySide6.QtGui import QColor,QPen
from PySide6.QtWidgets import (
QApplication,QGraphicsItem,QGraphicsRectItem,QGraphicsScene,QGraphicsView,QMainWindow,)
TILEWIDTH = 25
TILEHEIGHT = 15
OUTLINE = 3
KEY_METADATA = 1
def round_by_factor(value,factor):
return round(value / factor) * factor
class RoomItem(QGraphicsRectItem):
def __init__(
self,offset_x,offset_y,width,height,outline,fill,metadata=None,parent=None,):
super().__init__(0,parent)
self.setPos(offset_x,offset_y)
self.setFlags(
QGraphicsItem.ItemIsSelectable
| QGraphicsItem.ItemIsFocusable
| QGraphicsItem.ItemIsMovable
)
self.setData(KEY_METADATA,metadata)
self.setPen(outline)
self.setBrush(fill)
def mouseReleaseEvent(self,event):
super().mouseReleaseEvent(event)
for item in self.scene().selectedItems():
self.apply_round(item)
def apply_round(self,item):
x = round_by_factor(item.pos().x(),TILEWIDTH)
y = round_by_factor(item.pos().y(),TILEHEIGHT)
item.setPos(x,y)
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.scene = QGraphicsScene(self)
self.view = QGraphicsView(self.scene,self)
self.view.setDragMode(QGraphicsView.RubberBandDrag)
self.view.scale(1,-1)
self.view.setStyleSheet("background:transparent; border: 0px")
self.setCentralWidget(self.view)
self.draw_map()
def draw_map(self):
for _ in range(10):
fill = QColor("#000000")
outline = QPen("#ffffff")
outline.setWidth(OUTLINE)
outline.setJoinStyle(Qt.MiterJoin)
offset_x = TILEWIDTH * random.randint(-10,10)
offset_z = TILEHEIGHT * random.randint(-10,10)
width = TILEWIDTH * random.randint(2,4)
height = TILEHEIGHT * random.randint(2,4)
rect = RoomItem(offset_x,offset_z,fill)
self.scene.addItem(rect)
def main():
app = QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()