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

问题描述

我正在使用动态创建的 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()

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...