首页 > 解决方案 > 无法理解如何在方法中使用图形场景对象

问题描述

这是我可视化检查器的代码的主要部分。问题出在方法moving上。如何self.scene在此函数中获取对象并将其传输(self.scene)以在将来的函数中使用它?因为,我必须scene在我的其他模块中使用,我的程序的逻辑部分在哪里移动我的项目。

我想像我在移动函数中的注释行(场景 = self,scene,但是没有 self 对象怎么办!!??)

另外,我认为我可以使用 app 对象(myapp在 RUN MODULE 中)来做到这一点,但是当我开始这样做时myapp.scene.removeitem()..,我的进程完全停止了。

运行模块

import sys
from visual import *
from PyQt5 import QtCore,QtGui,QtWidgets

    class MyWin(QtWidgets.QMainWindow):
        def __init__(self,parent=None):
            QtWidgets.QWidget.__init__(self,parent)

            self.ui=MainScreen()
            self.ui.setupMS(self)

    #if __name__ == '__main__':
    app=QtWidgets.QApplication(sys.argv)
    myapp=MyWin()
    #myapp.show()
    sys.exit(app.exec_()) 

模块视觉

import sys
import PyQt5.QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

transform_dict_to_pix = { (x,y):( (x+1)*60 , (y+1)*60 ) for x in range(8) for y in range(8) }
transform_dict_to_08 =  { (x,y):( int((x/60-1)) , int((y/60-1)) ) for x in range(60,481,60) for y in range(60,481,60)}
class MyEllipse(QGraphicsEllipseItem):
    last_click=()
    def mousePressEvent(self,event):
        MainScreen.click1=True
        MainScreen.click2=False
        self.setBrush(Qt.yellow)
        event.accept()

    def mouseReleaseEvent(self,event):
        coord=event.buttonDownScenePos(Qt.LeftButton)
        x=coord.x()
        y=coord.y()
        MyEllipse.last_click=(x,y,self)




class MyRect(QGraphicsRectItem):

    def mousePressEvent(self, event):
        event.accept() if MainScreen.click1 else event.ignore()


    def mouseReleaseEvent(self, event):
        coord = event.buttonDownScenePos(Qt.LeftButton)
        obj=MyEllipse.last_click[2]
        x1=MyEllipse.last_click[0]
        y1=MyEllipse.last_click[1]
        x2 = coord.x()
        y2 = coord.y()
        obj.setBrush(obj.color)

        MainScreen.moving(obj,x1,x2,y1,y2)


class MainScreen():
    click1 = False
    click2 = False

    @staticmethod
    def moving(obj,x1,x2,y1,y2):
        '''how can i do like that : self.scene.removeitem() without self object ??? How can i use my scene in methods similar to it !!!???'''

        '''scene=self.scene''' #!!!!!!!!!!!!!!PROBLEM !!!!!!!!!!!!!!!!!!!


        x1=x1//60*60
        x2=x2//60*60
        y1=y1//60*60
        y2=y2//60*60

        print(f'x1 : {x1} ; y1 : {y1} \n x2 : {x2} ; y2 : {y2} ')
        x08_from,y08_from=transform_dict_to_08[(x1,y1)]
        x08_to,y08_to=transform_dict_to_08[(x2,y2)]

        move_x=x2-x1
        move_y=y2-y1


        obj.moveBy(move_x,move_y)



    def setupMS(self,MainWindow):

        self.scene=QGraphicsScene()
        self.scene.setSceneRect(0,0,600,600)
        self.create_icons()
        self.create_checkers()
        self.labels_create()

        checkers = self.object_checkers()
        self.white_checkers = checkers[:13]
        self.black_checkers = checkers[13:]

        self.view = QGraphicsView(self.scene)

        self.view.centerOn(0,0)

        self.view.show()



    def object_checkers(self):
        '''get all objects ()'''
        list_of_checkers=[]
        flagx = 65
        flagx2 = 125

        for i in range(65,205,60):
            for j in range(flagx2,545,120):
                #self.scene.addEllipse(j,i,50,50)

                item=self.scene.items(QRectF(j-1,i-1,52,52),Qt.ContainsItemBoundingRect)
                list_of_checkers.append(item[0])
            flagx,flagx2=flagx2,flagx

        for i in range(365,545,60):
            for j in range(flagx2,545,120):
                item = self.scene.items(QRectF(j - 1, i - 1, 52, 52), Qt.ContainsItemBoundingRect)
                list_of_checkers.append(item[0])
            flagx,flagx2=flagx2,flagx

        #print(len(list_of_checkers))
        #print (list_of_checkers)
        return list_of_checkers

    def labels_create(self):
        self.label1 = QLabel()
        self.label1.setGeometry(QRect(180,15, 240, 40))
        self.label1.setText("Player_1")
        self.scene.addWidget(self.label1)

        self.label2 = QLabel()
        self.label2.setGeometry(QRect(180, 550, 240, 40))
        self.label2.setText("Player_2")
        self.scene.addWidget(self.label2)


    def create_checkers(self):
        '''create object of figures on the deck'''

        flagx=65
        flagx2=125

        brush1=QBrush(Qt.blue)
        brush2=QBrush(Qt.darkMagenta)

        for i in range(65,205,60):

            for j in range(flagx2,545,120):
                #self.scene.addEllipse(j,i,50,50,brush=brush1)
                item=MyEllipse(j,i,50,50)
                item.setBrush(brush1)
                item.color=brush1
                item.setAcceptedMouseButtons(Qt.LeftButton)
                self.scene.addItem(item)

            flagx,flagx2=flagx2,flagx

        for i in range(365,545,60):

            for j in range(flagx2,545,120):
                #self.scene.addEllipse(j,i,50,50,brush=brush2)
                item = MyEllipse(j, i, 50, 50)
                item.setBrush(brush2)
                item.color=brush2
                item.setAcceptedMouseButtons(Qt.LeftButton)
                self.scene.addItem(item)

            flagx,flagx2=flagx2,flagx





    def create_icons(self):
        '''create icons of the deck'''
        x = 60;y = 60;w = 60;h = 60

        brush1 = QBrush(Qt.darkGray)
        brush2 = QBrush(Qt.black)

        flag = 0
        for _ in range(8):
            x = 60

            for __ in range(8):
                rect=MyRect(x, y, w, h)
                if flag :
                    rect.setBrush(brush1 )
                self.scene.addItem(rect)
                #self.scene.addRect(x, y, w, h, brush=brush1) if flag else self.scene.addRect(x, y, w, h)
                x += 60
                flag = 1 - flag
            flag = 1 - flag
            y += 60

标签: pythonpython-3.xpyqtpyqt5

解决方案


您的实现存在一些问题,其中大部分与实现的逻辑有关。

首先,只有在需要对类进行写/读访问时才应该设置类属性而不是它的实例。
与此相关,另一个问题是您正在尝试使用类方法(好吧,它是 a staticmethod,但在这种情况下并不重要,因为它仍然是类的方法),这显然不会给出您对其任何实例的任何属性的任何访问:在您的情况下,您正在尝试访问scene,但这是一个实例属性,该类完全不知道。

为了能够跟踪对象,您需要更好的父/子逻辑,可能避免直接和绝对地从他们的孩子引用父母(并且几乎从不引用父母的班级!),通过让父母被其“通知”孩子,让父母在需要的时候处理必要的事情。这非常重要,因为您可能有更多的“兄弟姐妹”孩子可能需要他们之间进行一些互动,而这个责任应该真正留给父母,主要是因为有时孩子不可能(或太复杂)知道所有的兄弟姐妹及其状态/属性/能力,因为它们通常由父级本身管理。


最后,还有几个其他问题。
您正在使用碰撞/形状检测来跟踪检查器对象,这没有多大意义,并且使调试或将来的修改变得非常困难;只需在创建它们后立即添加它们:如果您不使用几何图形的通用变量和全局变量,并且有一天,您需要更改这些值,则需要在使用它们的每个地方都更改它们,从而增加错误的可能性,通常是因为您对其他变量使用了相同的值,这种情况发生的频率比人们想象的要多。我已经编辑了示例代码,以展示一个更一致的解决方案来“统一”大小,同时保持整个棋盘格创建始终一致。
最后,您是 QMainWindow 的子类,但在初始化时您使用了 QWidget,这肯定会产生问题。


我创建了一个模仿你的小而非常有限的例子。请注意,它不是“准备就绪”,没有转弯控制,并且,就像在您的示例中一样,您可以在网格的每个(自由)空间中移动每个检查器;而已。
这背后的概念是我们知道只有“灰色”(如有效)网格槽可用于检查器,因此我将每个检查器添加为可用网格槽矩形项的子项。每当我想移动一个检查器时,我单击它并通过单击另一个空的网格槽来移动它,这使得新的网格项成为检查器的父项。

from PyQt5 import QtCore, QtGui, QtWidgets

gridSlotSize = 60
margin = 60
checkerSize = 50
checkerMargin = (gridSlotSize - checkerSize) / 2
gridCount = 8
gridSize = margin + gridSlotSize * gridCount + margin

class CheckerItem(QtWidgets.QGraphicsEllipseItem):
    def __init__(self, player, parent):
        QtWidgets.QGraphicsEllipseItem.__init__(self, parent)
        self.setRect(checkerMargin, checkerMargin, checkerSize, checkerSize)
        if player == 1:
            self.color = QtGui.QColor(QtCore.Qt.darkMagenta)
        else:
            self.color = QtGui.QColor(QtCore.Qt.blue)
        self.activeColor = self.color.lighter()
        self.setBrush(self.color)

    def setActive(self, active):
        if active:
            self.setBrush(self.activeColor)
        else:
            self.setBrush(self.color)


class CheckerScene(QtWidgets.QGraphicsScene):
    def __init__(self):
        QtWidgets.QGraphicsScene.__init__(self)
        self.setSceneRect(margin, margin, gridCount * gridSlotSize, gridCount * gridSlotSize)

        self.addRect(self.sceneRect())

        self.current = None

        self.grid = []
        self.white_checkers = []
        self.black_checkers = []
        for row in range(8):
            for column in range(8):
                # this is a "trick" to make the grid creation easier: it creates
                # a grid square only if the row is odd and the column is even, 
                # and viceversa.
                if (not row & 1 and column & 1) or (row & 1 and not column & 1):
                    # create a gridItem with a rectangle that uses 0-based 
                    # coordinates, *then* we set its position
                    gridItem = self.addRect(0, 0, gridSlotSize, gridSlotSize)
                    gridItem.setPos(margin + column * gridSlotSize, margin + row * gridSlotSize)
                    gridItem.setBrush(QtGui.QColor(QtCore.Qt.lightGray))
                    self.grid.append(gridItem)
                    if 3 <= row <= 4:
                        # don't add checkers in the middle
                        continue
                    # create checkers being careful to assign them the gridItem 
                    # as a *parent*; their coordinate will *always* be relative
                    # to the parent, so that if we change it, they will always 
                    # be centered
                    if row < 3:
                        self.white_checkers.append(CheckerItem(0, gridItem))
                    else:
                        self.black_checkers.append(CheckerItem(1, gridItem))

    def mousePressEvent(self, event):
        # right button to deleselect
        if event.button() == QtCore.Qt.RightButton:
            self.setCurrent()
            return
        # find items at the event position, in descending order, top to bottom
        items = self.items(event.scenePos())
        if not items:
            self.setCurrent()
            return
        if isinstance(items[0], QtWidgets.QGraphicsEllipseItem):
            # we found a checker!
            self.setCurrent(items[0])
        elif items[0] in self.grid:
            # we found a grid item!
            gridItem = items[0]
            if gridItem.childItems():
                # if it has a checker in it, select it!
                self.setCurrent(gridItem.childItems()[0])
            elif self.current:
                # no checker here, but if we have a previous one selected
                # we "move" it here, by changing its parent to the new grid item
                self.current.setParentItem(gridItem)

    def setCurrent(self, new=None):
        # if a previous checker is selected, deselect it
        if self.current:
            self.current.setActive(False)
        self.current = new
        if new is not None:
            # set the current checker!
            self.current.setActive(True)


class Checkers(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        layout = QtWidgets.QGridLayout()
        self.setLayout(layout)

        self.player1Label = QtWidgets.QLabel('Player 1')
        layout.addWidget(self.player1Label)
        self.player1Label.setAlignment(QtCore.Qt.AlignCenter)

        self.checkerView = QtWidgets.QGraphicsView()
        layout.addWidget(self.checkerView)
        self.checkerScene = CheckerScene()
        self.checkerView.setScene(self.checkerScene)
        self.checkerView.setFixedSize(gridSize, gridSize)
        # set the Antialiasing render hints to paint "smoother" shapes
        self.checkerView.setRenderHints(QtGui.QPainter.Antialiasing)

        self.player2Label = QtWidgets.QLabel('Player 2')
        layout.addWidget(self.player2Label)
        self.player2Label.setAlignment(QtCore.Qt.AlignCenter)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    checkers = Checkers()
    checkers.show()
    sys.exit(app.exec_())

推荐阅读