Python 如何将QRAPHICSTEM的位置保存并加载到qraphicscene或(正确的drow项)

Python 如何将QRAPHICSTEM的位置保存并加载到qraphicscene或(正确的drow项),python,pyqt,pyqt5,pyside2,Python,Pyqt,Pyqt5,Pyside2,原则上,这不是一项困难的任务,但事实证明一切都不像看上去那么容易。很难理解坐标。因为它们有三对:模型、项目和视图。我保留x和y位置以及类的其他属性。然后我尝试使用当前位置将它们添加回。这就是魔法的开始。元素采取的立场显然是以前不存在的。这显然也与move items功能的实现有关。但是,它不是我的,我不知道如何正确设置坐标,无论是在开始还是结束时。还有一个功能,放大和缩小的场景,这是可能的,它也会影响后续加载,但我已经没有想法 装货前 后装 我的MRE工具 import sys from P

原则上,这不是一项困难的任务,但事实证明一切都不像看上去那么容易。很难理解坐标。因为它们有三对:模型、项目和视图。我保留x和y位置以及类的其他属性。然后我尝试使用当前位置将它们添加回。这就是魔法的开始。元素采取的立场显然是以前不存在的。这显然也与move items功能的实现有关。但是,它不是我的,我不知道如何正确设置坐标,无论是在开始还是结束时。还有一个功能,放大和缩小的场景,这是可能的,它也会影响后续加载,但我已经没有想法

装货前

后装

我的MRE工具

import sys
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from  math import pi
import os
from pickle import load,dump

class MyQGraphicsView(QGraphicsView):

    def __init__ (self, parent=None):
        super(MyQGraphicsView, self).__init__ (parent)


    def wheelEvent(self, event):

        if QApplication.keyboardModifiers() == Qt.ControlModifier:
            print('Control+Click')
        # Zoom Factor
            zoomInFactor = 1.25
            zoomOutFactor = 1 / zoomInFactor

            # Set Anchors
            self.setTransformationAnchor(QGraphicsView.NoAnchor)
            self.setResizeAnchor(QGraphicsView.NoAnchor)

            # Save the scene pos
            oldPos = self.mapToScene(event.pos())

            # Zoom
            if event.delta() > 0:
                zoomFactor = zoomInFactor
            else:
                zoomFactor = zoomOutFactor
            self.scale(zoomFactor, zoomFactor)

            # Get the new position
            newPos = self.mapToScene(event.pos())

            # Move scene to old position
            delta = newPos - oldPos
            self.translate(delta.x(), delta.y())

class Communicate(QObject):
    closeApp = Signal()
    add_delete_row = Signal(QModelIndex, str)

class Drow_equipent(QGraphicsItem):
    def __init__(self, x, y, w, h,name,brush=Qt.blue,type='эллипс'):
        super().__init__()
        self.setPos(x, y)
        self.penWidth = 1
        self.name=name
        self.x,self.y,self.h,self.w=x,y,h,w
        self.setAcceptHoverEvents(True)
        self.signal=Communicate()
        self.setFlags(QGraphicsItem.ItemSendsGeometryChanges|QGraphicsItem.ItemIsSelectable)
        self.rotation_=False
        self._brush=QBrush(brush)
        self.pen_color=Qt.black
        self.type_obj=type
        self.types = {'эллипс': lambda x: x.drawEllipse(self.x, self.y, self.w, self.h),'прямоугольник':lambda x: x.drawRect(self.x, self.y, self.w, self.h)}
                                            # x.drawLine(QPoint(self.x+self.x*2,self.y+self.y*2),QPoint(self.w_eq-self.w_eq/8,self.h_eq-self.h_eq/8)),
                                            # x.drawLine(QPoint(self.x+self.x*8,self.y+self.y*8),QPoint(self.x+self.x*4,self.y+self.y*4+self.h_eq/8)),
                                            # x.drawLine(QPoint(self.x, self.y), QPoint(5, self.y)),
                                            # x.drawLine(QPoint(self.w_eq,self.h_eq), QPoint(self.w_eq, self.h_eq-5)),
                                            # x.drawLine(QPoint(self.w_eq, self.h_eq), QPoint(self.w_eq-5, self.h_eq)),





    def get_file_settings(self):

        c=self.scenePos()


        return {'x':c.x(),'y':c.y(),'h':self.h,'w':self.w,'name':self.name,'rotation':self.rotation(),
                'brush_color':self._brush.color().toTuple(),'type_obj':self.type_obj}


    def createDefaultContextMenu(self):
        menu = QMenu()
        menu.addAction('Повернуть').triggered.connect(lambda: self.setMode('scale'))
        return menu

    def hoverEnterEvent(self, event):
        QApplication.instance().setOverrideCursor(Qt.OpenHandCursor)

    def hoverLeaveEvent(self, event):
        QApplication.instance().restoreOverrideCursor()

    def mouseMoveEvent(self, event):

        if self.rotation_:
            self.my_rotation(self.rotation()+1)
            return True


        orig_cursor_position = event.lastScenePos()
        updated_cursor_position = event.scenePos()
        orig_position = self.scenePos()
        updated_cursor_x = updated_cursor_position.x() - orig_cursor_position.x() + orig_position.x()
        updated_cursor_y = updated_cursor_position.y() - orig_cursor_position.y() + orig_position.y()



        self.setPos(QPointF(updated_cursor_x, updated_cursor_y))




    def createDefaultContextMenu(self):
        menu = QMenu()

        menu.addAction('Повернуть').triggered.connect(lambda: self.setMode(True))

        return menu



    def contextMenuEvent(self, event):
            menu = self.createDefaultContextMenu()

            menu.exec_(event.screenPos())

    def setMode(self, mode):
        self.rotation_ = mode

    def mouseReleaseEvent(self, event):
        print('x: {0}, y: {1}'.format(self.pos().x(), self.pos().y()))
        if self.rotation_:
            self.rotation_=False

    def my_rotation(self,angle):
        # self.prepareGeometryChange()
        c=self.mapToScene(self.boundingRect().center())
        self.setRotation(angle)
        cNew = self.mapToScene(self.boundingRect().center())
        offset = c - cNew
        self.moveBy(offset.x(), offset.y())


    def mousePressEvent(self, event):

        print(event.pos())


    def boundingRect(self):
        # return QRectF(-10 - penWidth / 2, -10 - penWidth / 2,
        #               20 + penWidth, 20 + penWidth)

        return QRectF(self.x,self.y,self.w,self.h)
        # return QRectF(self.x,self.y,self.w_eq,self.h_eq)

    def paint(self, painter, option, widget, PySide2_QtWidgets_QWidget=None, *args, **kwargs):
        # painter.drawRoundedRect(-10, -10, 20, 20, 5, 5)
        # painter.setBrush()

        painter.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing,True)

        painter.setPen(self.pen_color)

        painter.setBrush(self._brush)


        self.types[self.type_obj](painter)
        self._brush = painter.brush()



    def setBrush(self,brush):
        self._brush.setColor(brush)

    def get_square(self):
        type={'эллипс': round ((((pi*self.w*self.h)/4)/10000),4),
              'прямоугольник':(self.h*self.w)/10000}
        return  type[self.type_obj]

class MainWindow(QMainWindow):

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

        self.centralwidget = QWidget(self)
        self.centralwidget.setGeometry(0, 0, 600, 700)
        self.centralwidget.setObjectName(u"centralwidget")
        self.gridLayout = QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName(u"gridLayout")
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setObjectName(u"verticalLayout")
        self.graphicsView = MyQGraphicsView(self.centralwidget)
        self.graphicsView.setObjectName(u"graphicsView")
        self.scene = QGraphicsScene(self.graphicsView)
        self.scene.setObjectName('scene')
        self.scene.setSceneRect(0, 0, 500, 500)
        self.graphicsView.setScene(self.scene)
        self.graphicsView.setMouseTracking(True)
        self.verticalLayout.addWidget(self.graphicsView)

        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName(u"horizontalLayout")
        self.add_item = QPushButton(self.centralwidget)
        self.add_item.setObjectName(u"add_item")

        self.horizontalLayout.addWidget(self.add_item)

        self.save_items = QPushButton(self.centralwidget)
        self.save_items.setObjectName(u"save_items")

        self.horizontalLayout.addWidget(self.save_items)

        self.load_items = QPushButton(self.centralwidget)
        self.load_items.setObjectName(u"load_items")

        self.horizontalLayout.addWidget(self.load_items)

        self.verticalLayout.addLayout(self.horizontalLayout)

        self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)

        self.setGeometry(0, 0, 600, 700)

        self.add_item.pressed.connect(self.add_equipment)
        self.save_items.pressed.connect(self.save_items_)
        self.load_items.pressed.connect(self.load_items_)

        self.retranslateUi()


    # setupUi


    def retranslateUi(self):

        self.add_item.setText(QCoreApplication.translate("MainWindow", u"add item", None))
        self.save_items.setText(QCoreApplication.translate("MainWindow", u"save items", None))
        self.load_items.setText(QCoreApplication.translate("MainWindow", u"load items", None))
    # retranslateUi


    def add_equipment(self):
        self.eq_obj = Drow_equipent(30, 40, 20,
                                    20, 'block', Qt.blue,'прямоугольник')

        self.scene.addItem(self.eq_obj)
        self.last_selected_item = self.eq_obj


    def load_items_(self):
        with open(os.path.join(os.getcwd(), 'config.ini'), 'rb') as f:
            settings = load(f)
        print(settings)

        items_keys = [key for key in settings.keys() if key != 'grid']
        print(items_keys)
        for item in items_keys:
            self.eq_obj = Drow_equipent(settings[item]['x'], settings[item]['y'],
                                        settings[item]['w'], settings[item]['h'],
                                        settings[item]['name'],
                                        QBrush(QColor().fromRgb(*settings[item]['brush_color'])),
                                        settings[item]['type_obj'])
            
            self.scene.addItem(self.eq_obj)


    def save_items_(self):
        settings = {}
        number_item = 0
        for item in self.scene.items():
            settings[f'item_№:{number_item}'] = item.get_file_settings()
            number_item += 1
        with open(os.path.join(os.getcwd(), 'config.ini'), 'wb') as f:
            dump(settings, f)


if __name__ == '__main__':

        app = QApplication(sys.argv)
        translator = QTranslator()
        if len(sys.argv) > 1:
            locale = sys.argv[1]
        else:
            locale = QLocale.system().name()
        translator.load('qt_%s' % locale,
                        QLibraryInfo.location(QLibraryInfo.TranslationsPath))
        app.installTranslator(translator)

        window = MainWindow()
        window.show()
        app.exec_()

一个非常重要的事情总是要考虑的是QLogiStIm的位置不一定要符合它的内容。 考虑两个矩形项:

rect1 = self.scene.addRect(5, 10, 20, 10)
rect2 = self.scene.addRect(0, 0, 20, 10)
rect2.setPos(5, 10)
虽然它们都显示在同一位置,但它们不是相同的。这些参数实际上将成为这些项的
boundingRect
,但第一个参数的
pos
仍在(0,0),第二个参数已被移动,这是因为图形项始终的起始位置在坐标(0,0)处,而boundingRect始终在项坐标中

创建新项目时,通常最好将其边界矩形设置为(0,0),除非需要特殊要求(例如,项目始终以其位置为中心显示)

在展示如何更正代码之前,还有一些其他问题需要解决

  • 如果需要允许移动项目,通常不需要自己实现移动,因为QGraphicsItem标志
    ItemIsMovable
    就足够了;如果需要对鼠标事件进行特殊控制,只需确保在需要移动时调用默认实现
  • 这也意味着必须调用
    super()
    
  • x()
    e
    y()
    是所有QGraphics站点的现有函数,尽管您仍然可以通过
    self.pos().x()
    self.pos().y()
    访问它们,但覆盖这些属性确实没有任何好处
  • 鼠标事件不应返回
    bool

还要注意的是,虽然不是运行所必需的,但始终建议使用良好的代码固定;逗号后面应该有空格,等号周围应该有空格,否则阅读会更不舒服,因为能够立即区分物体是非常重要的:看到
self.x,self.y=x,y
(这是不好的)与
self.x,self.y=x,y
(这是好的). 阅读有关官方的更多信息。

请在等待MRE时提供一段时间,从我所看到的情况来看,显然存在一个问题:给定的
x
y
坐标用于位置,但也用于绘图;结果是,如果X是5,Y是10,绘图将发生在10x20,因为绘图总是相对于项目的位置,所以我将从这个问题开始。然后,
x
y
都是QGraphicsItem的属性,因此它们永远不应该被其他数据覆盖。@musicamante感谢您的关注和评论@musicamante仍不清楚错误在哪里is@Valvea如第一条评论所述,您需要向我们提供MRE。请仔细阅读该链接并遵循其建议。您必须提供最少的代码,以便我们重现您的问题(这意味着我们应该能够轻松地复制、粘贴和运行您的代码),并且应该删除与此问题无关的任何代码部分。这个答案非常好!非常感谢您的时间和解释。没有你的帮助,我就不能再行动了。我真的很怀念代码的整洁。由于您的回答,进一步的实施将基于正确的概念。
class Drow_equipent(QGraphicsItem):
    def __init__(self, x, y, w, h, name, brush=Qt.blue, type='эллипс'):
        super().__init__()
        self.setPos(x, y)
        self.penWidth = 1
        self.name = name
        self.h, self.w = h, w
        self.setAcceptHoverEvents(True)
        self.signal = Communicate()
        self.setFlags(
            QGraphicsItem.ItemSendsGeometryChanges|
            QGraphicsItem.ItemIsSelectable|
            QGraphicsItem.ItemIsMovable)
        # ...
        self.types = {
            'эллипс': lambda x: x.drawEllipse(0, 0, self.w, self.h),
            'прямоугольник':lambda x: x.drawRect(0, 0, self.w, self.h)
        }

    # ...
    def mouseMoveEvent(self, event):
        if self.rotation_:
            self.my_rotation(self.rotation() + 1)
        else:
            super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        if self.rotation_:
            self.rotation_ = False
        super().mouseReleaseEvent(event)

    def mousePressEvent(self, event):
        print(event.pos())
        super().mousePressEvent(event)

    def boundingRect(self):
        return QRectF(0, 0, self.w, self.h)
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.centralwidget = QWidget(self)
        self.setCentralWidget(self.centralwidget)