创建鼠标事件后保留QGraphicsItem多选移动

Retain QGraphicsItem multi-selection movement after creating mouse events

我正在使用 Python Qt 使用动态创建的 QGraphicsRectItems 开发点击并拖动地图编辑器。我需要在 QGraphicsRectItem class 中添加 3 个鼠标事件函数,以便在移动项目后释放鼠标时这些矩形自动捕捉到 25x15 网格。

问题是,在添加这些功能后,我不再能够在 selected 时一次移动多个矩形。我仍然可以 select 通过将鼠标拖到它们上面来同时 select 几个矩形,但是尝试移动整个 selection 只会移动其中一个项目。

这是我的代码示例:

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, 0, width, height, 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, outline, fill)
            self.scene.addItem(rect)

def main():
    app = QApplication(sys.argv)
    main = Main()
    main.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

如何恢复默认设置的多 select 移动系统,同时保留允许每个对象捕捉到网格的鼠标功能?

如果您希望项目位置是某些整数值的倍数,那么您必须在释放鼠标后更正它,没有必要覆盖 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, 0, width, height, 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, width, height, outline, fill)
            self.scene.addItem(rect)


def main():
    app = QApplication(sys.argv)
    main = Main()
    main.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()