如何访问可移动 QGraphicsItem 的位置偏移量?

How to access the position offsets of movable QGraphicsItems?

我最近开始使用 Python Qt,我正在尝试制作一个点击并拖动地图编辑器,它可以读取和写入 .json 文件。到目前为止,我的进展是让 QGraphicsView 准确地动态显示整个房间的矩形布局。但是现在我不知道如何访问每个矩形的 x 和 y 偏移量,因为它们都可以使用 setFlag(QGraphicsItem.ItemIsMovable) 属性.

单独移动

这是我的代码的简短版本:

import json
import sys
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *

TILEWIDTH = 25
TILEHEIGHT = 15
OUTLINE = 3

class Room:
    def __init__(self, name, width, height, offset_x, offset_z):
        self.name = name
        self.width = width
        self.height = height
        self.offset_x = offset_x
        self.offset_z = offset_z

class Main(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        #Setting up viewport
        self.scene = QGraphicsScene(self)
        self.view = QGraphicsView(self.scene, self)
        
        self.view.scale(1, -1)
        self.view.setStyleSheet("background:transparent; border: 0px")
        self.setCentralWidget(self.view)
        
        self.setGeometry(360, 190, 1200, 700)
        self.showMaximized()
        
        #Reading json and converting entries to rooms
        self.room_list = []
        with open("Data\Content\PB_DT_RoomMaster.json", "r") as file_reader:
            self.content = json.load(file_reader)
        for i in self.content:
            self.room_list.append(self.convert_json_to_room(i))
        self.draw_map()
    
    def convert_json_to_room(self, json):
        name = json["Key"]
        width = json["Value"]["AreaWidthSize"] * TILEWIDTH
        height = json["Value"]["AreaHeightSize"] * TILEHEIGHT
        offset_x = round(json["Value"]["OffsetX"]/12.6) * TILEWIDTH
        offset_z = round(json["Value"]["OffsetZ"]/7.2) * TILEHEIGHT 
        
        room = Room(name, width, height, offset_x, offset_z)
        return room
  
    def draw_map(self):
        for i in self.room_list:
            fill = QColor("#000000")
            outline = QPen("#ffffff")
            outline.setWidth(OUTLINE)
            outline.setJoinStyle(Qt.MiterJoin)
            #Drawing rooms
            rect = self.scene.addRect(i.offset_x, i.offset_z, i.width, i.height, outline, fill)
            rect.setFlag(QGraphicsItem.ItemIsMovable)

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

if __name__ == '__main__':
    main()

这是json:

[
  {
    "Key": "m01SIP_000",
    "Value": {
      "LevelName": "m01SIP_000",
      "EnemyPatternSuffix": "",
      "AreaID": "EAreaID::m01SIP",
      "SameRoom": "None",
      "AdjacentRoomName": [
        "m01SIP_001",
        "m01SIP_024",
        "m01SIP_023"
      ],
      "OutOfMap": false,
      "EventFlagNameForShowEventIfNotSeen": "None",
      "EventFlagNameForMarkEventAsSeen": "None",
      "WarpPositionX": 0.0,
      "WarpPositionY": 0.0,
      "WarpPositionZ": 0.0,
      "RoomType": "ERoomType::Normal",
      "RoomPath": "ERoomPath::Both",
      "ConsiderLeft": true,
      "ConsiderRight": true,
      "ConsiderTop": true,
      "ConsiderBottom": true,
      "AreaWidthSize": 2,
      "AreaHeightSize": 1,
      "OffsetX": 25.2,
      "OffsetZ": 0.0,
      "DoorFlag": [
        2,
        32
      ],
      "HiddenFlag": [],
      "RoomCollisionFromSplineOnly": false,
      "RoomCollisionFromGimmick": false,
      "NoRoomOutBlinder": false,
      "Collision2DProjectionDistance": -1.0,
      "FlyMaterialDistance": 10.0,
      "NoTraverse": [],
      "MagCameraFovScale": 0.0,
      "MagCameraVolumeScale": 1.5,
      "DemagCameraFovScale": 0.77,
      "DemagCameraVolumeScale": 0.0,
      "BgmID": "BGM_m01SIP",
      "BgmType": "ERoomBgmType::PlayNormal",
      "Amb1": "AMB_01SIP_Ship_Roll01",
      "AmbVol1": 70,
      "Amb2": "",
      "AmbVol2": 0,
      "Amb3": "",
      "AmbVol3": 0,
      "Amb4": "",
      "AmbVol4": 0,
      "Decay_Near": 1260.0,
      "Decay_Far": 2520.0,
      "Decay_Far_Volume": 0.5,
      "UseLava": false,
      "FrameType": "EFramePlateType::FPT_Full",
      "PerfLevel": 1
    }
  },
  {
    "Key": "m01SIP_001",
    "Value": {
      "LevelName": "m01SIP_001",
      "EnemyPatternSuffix": "",
      "AreaID": "EAreaID::m01SIP",
      "SameRoom": "None",
      "AdjacentRoomName": [
        "m01SIP_000",
        "m01SIP_023",
        "m01SIP_002"
      ],
      "OutOfMap": false,
      "EventFlagNameForShowEventIfNotSeen": "None",
      "EventFlagNameForMarkEventAsSeen": "None",
      "WarpPositionX": 0.0,
      "WarpPositionY": 0.0,
      "WarpPositionZ": 0.0,
      "RoomType": "ERoomType::Normal",
      "RoomPath": "ERoomPath::Both",
      "ConsiderLeft": true,
      "ConsiderRight": true,
      "ConsiderTop": true,
      "ConsiderBottom": true,
      "AreaWidthSize": 3,
      "AreaHeightSize": 1,
      "OffsetX": 50.4,
      "OffsetZ": 0.0,
      "DoorFlag": [
        1,
        24,
        3,
        8
      ],
      "HiddenFlag": [],
      "RoomCollisionFromSplineOnly": false,
      "RoomCollisionFromGimmick": false,
      "NoRoomOutBlinder": false,
      "Collision2DProjectionDistance": -1.0,
      "FlyMaterialDistance": 10.0,
      "NoTraverse": [],
      "MagCameraFovScale": 0.0,
      "MagCameraVolumeScale": 1.5,
      "DemagCameraFovScale": 0.77,
      "DemagCameraVolumeScale": 0.0,
      "BgmID": "BGM_m01SIP",
      "BgmType": "ERoomBgmType::PlayNormal",
      "Amb1": "AMB_01SIP_Ship_Roll01",
      "AmbVol1": 70,
      "Amb2": "AMB_01SIP_Wind02_LP",
      "AmbVol2": 70,
      "Amb3": "",
      "AmbVol3": 0,
      "Amb4": "",
      "AmbVol4": 0,
      "Decay_Near": 1260.0,
      "Decay_Far": 2520.0,
      "Decay_Far_Volume": 0.5,
      "UseLava": false,
      "FrameType": "EFramePlateType::FPT_Full",
      "PerfLevel": 1
    }
  },
...

基本上我需要更新 json 中每个房间在地图编辑器上移动后的偏移量。处理此问题的最佳方法是什么?

您没有将偏移设置为矩形的参数,因为稍后您将不得不进行转换,因为它位于项目的本地坐标中,而是使用场景坐标中的方法 pos() :

rect = self.scene.addRect(0, 0, i.width, i.height, outline, fill)
rect.setPos(i.offset_x, i.offset_z)
rect.setFlag(QGraphicsItem.ItemIsMovable)

那么做完之后就可以得到位置了:

i.offset_x = rect.pos().x()
i.offset_z = rect.pos().y()

我认为没有必要将信息分开,因为您可以创建一个项目来存储信息以便以后检索。

import json
import sys
from functools import cached_property

from PySide6.QtCore import Qt
from PySide6.QtGui import QColor, QPen
from PySide6.QtWidgets import (
    QApplication,
    QGraphicsRectItem,
    QGraphicsScene,
    QGraphicsView,
    QMainWindow,
    QGraphicsItem,
)

TILEWIDTH = 25
TILEHEIGHT = 15
OUTLINE = 3

KEY_METADATA = 1


class RoomItem(QGraphicsRectItem):
    def __init__(self, x, y, width, height, metadata=None, parent=None):
        super().__init__(0, 0, width, height, parent)
        self.setPos(x, y)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setData(KEY_METADATA, metadata)

        fill = QColor("#000000")
        outline = QPen("#ffffff")
        outline.setWidth(OUTLINE)
        outline.setJoinStyle(Qt.MiterJoin)
        self.setPen(outline)
        self.setBrush(fill)

    @classmethod
    def from_json(cls, d):
        x = d["Value"]["OffsetX"] / 12.6 * TILEWIDTH
        y = d["Value"]["OffsetZ"] / 7.2 * TILEHEIGHT
        width = d["Value"]["AreaWidthSize"] * TILEWIDTH
        height = d["Value"]["AreaHeightSize"] * TILEHEIGHT
        return cls(x, y, width, height, d)

    def to_json(self):
        d = self.data(KEY_METADATA) or dict()
        d["Value"] = dict(
            OffsetX=self.pos().x() * 12.6 / TILEWIDTH,
            OffsetZ=self.pos().y() * 7.2 / TILEHEIGHT,
            AreaWidthSize=self.rect().width() / TILEWIDTH,
            AreaHeightSize=self.rect().height() / TILEHEIGHT,
        )
        return d


class Main(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    @cached_property
    def items(self):
        return list()

    def initUI(self):
        # Setting up viewport
        self.scene = QGraphicsScene(self)
        self.view = QGraphicsView(self.scene, self)

        self.view.scale(1, -1)
        self.view.setStyleSheet("background:transparent; border: 0px")
        self.setCentralWidget(self.view)

        self.setGeometry(360, 190, 1200, 700)
        self.showMaximized()

    def load_from_json(self, filename):
        with open(filename, "r") as f:
            for e in json.load(f):
                item = RoomItem.from_json(e)
                self.scene.addItem(item)
                self.items.append(item)

    def save_to_json(self, filename):
        with open(filename, "w") as f:
            l = []
            for item in self.items:
                l.append(item.to_json())
            json.dump(l, f)


def main():
    app = QApplication(sys.argv)
    main = Main()
    main.show()
    main.load_from_json("data.json")
    ret = app.exec_()

    main.save_to_json("data.json")

    sys.exit(ret)


if __name__ == "__main__":
    main()