Ejemplo n.º 1
0
class MainForm(QDialog):
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)
        self.running = False
        self.scene = QGraphicsScene(self)  # 场景
        self.scene.setSceneRect(0, 0, SCENESIZE, SCENESIZE)
        self.scene.setItemIndexMethod(QGraphicsScene.NoIndex)
        self.view = QGraphicsView()  # 创建视图
        self.view.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        self.view.setScene(self.scene)  # 设置视图的场景
        self.view.setFocusPolicy(Qt.NoFocus)
        zoomSlider = QSlider(Qt.Horizontal)  # 水平滑块,用于视图缩放
        zoomSlider.setRange(5, 200)
        zoomSlider.setValue(100)
        self.pauseButton = QPushButton("Pa&use")  # 暂停继续按钮
        quitButton = QPushButton("&Quit")  # 退出按钮
        quitButton.setFocusPolicy(Qt.NoFocus)
        layout = QVBoxLayout()
        layout.addWidget(self.view)
        bottomLayout = QHBoxLayout()
        bottomLayout.addWidget(self.pauseButton)
        bottomLayout.addWidget(zoomSlider)
        bottomLayout.addWidget(quitButton)
        layout.addLayout(bottomLayout)
        self.setLayout(layout)
        self.pauseButton.clicked.connect(self.pauseOrResume)
        zoomSlider.valueChanged[int].connect(self.zoom)
        quitButton.clicked.connect(self.accept)
        self.populate()
        self.startTimer(5)  # 每5ms 产生一次计时器事件
        self.setWindowTitle("Multipedes")

    def zoom(self, value):
        factor = value / 100.0
        matrix = self.view.transform()  # 视图的变换矩阵
        matrix.reset()  # 重置
        matrix.scale(factor, factor)  # 设置x,y缩放比例
        self.view.setTransform(matrix)  # 重新设定视图的变换矩阵

    def pauseOrResume(self):
        self.running = not self.running  # 改变运行状态
        self.pauseButton.setText(
            "Pa&use" if self.running else "Res&ume")  # 改变按钮文本

    def populate(self):  # 生成千足虫
        red, green, blue = 0, 150, 0
        for i in range(random.randint(6, 10)):  # 随机产生6~10条千足虫
            angle = random.randint(0, 360)  # 随机角度
            offset = random.randint(0, SCENESIZE // 2)
            half = SCENESIZE / 2
            x = half + (offset * math.sin(math.radians(angle)))
            y = half + (offset * math.cos(math.radians(angle)))
            color = QColor(red, green, blue)
            head = Head(color, angle, QPointF(x, y))  # 每条虫有1个头

            color = QColor(random.randint(20, 255), random.randint(20, 255),
                           random.randint(20, 255))  # 随机色
            offset = 25
            segment = Segment(color, offset, head)  # 第一节身体段属于头部的子图形项
            for j in range(random.randint(
                    5, 8)):  # 每条虫有6~9个身体节段(1 + (5~8)),randint包含两端
                offset += 25  # 每节 长25
                segment = Segment(color, offset,
                                  segment)  # 第n+1节身体段属于第 n节的子图形项
            head.setRotation(random.randint(0, 360))
            self.scene.addItem(head)  # 向场景中添加头部(也会递归地添加个身体段)
        self.running = True

    def timerEvent(self, event):  # 计时器事件的槽
        if not self.running:
            return
        dead = set()  # 死亡集
        items = self.scene.items()  # 场景的所有图形项
        if len(items) == 0:  # 没有虫了就重开
            self.populate()
            return
        heads = set()
        for item in items:
            if isinstance(item, Head):  # 是 头部
                heads.add(item)
                if item.color.red() == 255:  # 头部颜色红色分量达最大时放入死亡集
                    dead.add(item)
        if len(heads) == 1:  # 只剩一只虫时也加入死亡集,以待删除
            dead = heads
        del heads
        # 依次删除死亡集中的头部
        while dead:
            item = dead.pop()  # 从死亡集弹出
            self.scene.removeItem(item)  # 从场景移除头部(会递归地移除子项(各身体段))
            del item
        self.scene.advance()  # 调用各个图形项的 advance()方法
Ejemplo n.º 2
0
class MenuController(QDialog):
    def __init__(self, parent=None):
        super(MenuController, self).__init__(parent)

        self.c = 0
        self.backgroundColor = QColor(38, 44, 105)
        self.cellColor = QColor(209, 220, 237)
        self.woundColor = QColor(252, 236, 0)
        self.woundPen = QPen(self.woundColor)
        self.woundPen.setWidth(3)
        self.foregroundBrush = QBrush()
        self.woundGraphics = None

        self.sceneState = SceneState()
        self.originalPalette = QApplication.palette()

        settingsComboBox = QComboBox()
        settingsComboBox.addItems(QStyleFactory.keys())

        mainLabel = QLabel("Simulation Controls")
        mainLabel.setBuddy(settingsComboBox)

        self.createWoundControlBox()
        self.createSimulationControlBox()
        self.createGraphicsDisplay()

        mainLayout = QGridLayout()
        mainLayout.addWidget(self.graphicsDisplayBox, 0, 0, 2, 10)
        mainLayout.addWidget(self.woundControlBox, 0, 11)
        mainLayout.addWidget(self.simulationControlBox, 1, 11)
        self.setLayout(mainLayout)

        self.setWindowTitle("WoundSimulation")
        self.simRunning = False

    def setWoundTrue(self):
        self.sceneState.setWound(True)
        self.drawWound()

    def setWoundFalse(self):
        if (self.woundGraphics):
            self.graphicsScene.removeItem(self.woundGraphics)
            self.woundGraphics = None

            for c in self.sceneState.visible_cells:
                self.sceneState.removeCellGraphics(c, self.graphicsScene)

            self.foregroundBrush.setColor(self.cellColor)
            self.foregroundBrush.setStyle(Qt.SolidPattern)

            for c in self.sceneState.cells:
                self.sceneState.addCellGraphics(c, self.graphicsScene)
            self.sceneState.update_cell_info()
        self.sceneState.setWound(False)

    def toggleArea(self):
        self.sceneState.setArea(float(self.woundArea.text()))

    def toggleAngles(self):
        self.sceneState.setAngles(int(self.angleEditor.value()))

    def setLine(self):
        self.sceneState.setGeometry("line")

    def setPoly(self):
        self.sceneState.setGeometry("poly")

    def setCircle(self):
        self.sceneState.setGeometry("circle")

    def removeCells(self, cells):

        for c in cells:
            i = self.cellGraphicsItems.index(c)
            del self.sceneState.cells[i]

            self.removeCellGraphics(c)

    def cleanupCells(self):
        for cell in self.sceneState.cells:
            if cell.collidesWithItem(self.woundGraphics):
                self.sceneState.removeCellGraphics(cell, self.graphicsScene)

    def clearSceneOfGraphics(self):
        for cell in self.sceneState.visible_cells:
            self.sceneState.removeCellGraphics(cell, self.graphicsScene)

        self.graphicsScene.removeItem(self.woundGraphics)
        self.woundGraphics = None

    def setPenToCells(self):
        self.foregroundBrush.setColor(self.cellColor)
        self.foregroundBrush.setStyle(Qt.SolidPattern)

    def drawWound(self):
        print("DRAWING WOUND\n")
        if (self.woundGraphics):
            self.graphicsScene.removeItem(self.woundGraphics)
            self.woundGraphics = None

        for cell in self.sceneState.visible_cells:
            self.sceneState.removeCellGraphics(cell, self.graphicsScene)

        self.setPenToCells()

        for cell in self.sceneState.cells:
            self.sceneState.addCellGraphics(cell, self.graphicsScene)

        geo = self.sceneState.getGeometry()
        area = self.sceneState.getArea()
        angles = self.sceneState.getAngles()
        self.foregroundBrush.setColor(self.woundColor)

        if (geo == "line"):
            self.woundGraphics = self.graphicsScene.addRect(
                0, 256 - area / 2, 512, area, self.woundPen)
        elif (geo == "poly"):
            r = math.sqrt(area / (math.pi))
            poly = QPolygonF()
            for i in range(1, angles + 1):
                px, py = (r * math.cos(2 * math.pi * i / angles),
                          r * math.sin(2 * math.pi * i / angles))
                p = QPoint(px + 256, py + 256)
                poly.append(p)
            self.woundGraphics = self.graphicsScene.addPolygon(
                poly, self.woundPen)
        elif (geo == "circle"):
            r = math.sqrt(area / (math.pi))
            self.woundGraphics = self.graphicsScene.addEllipse(
                256 - (r / 2), 256 - (r / 2), r, r, self.woundPen)

        self.cleanupCells()

    def createWoundControlBox(self):
        self.woundControlBox = QGroupBox("Wound")

        checkBox = QCheckBox("Add Wound")
        checkBox.setTristate(False)
        checkBox.setChecked(False)

        layout = QVBoxLayout()

        self.woundArea = QLineEdit()

        horizbox = QHBoxLayout()

        geometryLine = QRadioButton("Line")
        geometryPolygon = QRadioButton("Polygon")
        geometryCircle = QRadioButton("Circle")
        geometryLine.setChecked(True)
        geometryLine.clicked.connect(self.setLine)
        geometryPolygon.clicked.connect(self.setPoly)
        geometryCircle.clicked.connect(self.setCircle)
        horizbox.addWidget(geometryLine, 0)
        horizbox.addWidget(geometryPolygon, 1)
        horizbox.addWidget(geometryCircle, 2)

        areaValidator = QDoubleValidator()
        areaValidator.setBottom(50.0)
        areaValidator.setTop(100000.0)
        self.woundArea.setValidator(areaValidator)
        self.woundArea.editingFinished.connect(self.toggleArea)

        # create wound angle editor widget
        self.angleEditor = QSlider(Qt.Horizontal)
        self.angleEditor.setMinimum(3)
        self.angleEditor.setMaximum(50)
        self.angleEditor.setSingleStep(1)
        self.angleEditor.setValue(1)
        self.angleEditor.setTickInterval(10)
        self.angleEditor.setTickPosition(QSlider.TicksBelow)
        self.angleEditor.sliderReleased.connect(self.toggleAngles)

        addWound = QPushButton('Add Wound')
        removeWound = QPushButton('Remove Wound')
        addWound.clicked.connect(self.setWoundTrue)
        removeWound.clicked.connect(self.setWoundFalse)

        self.cellGraphicsItems = list()

        layout.addWidget(self.woundArea, 0)
        layout.addLayout(horizbox)
        layout.addWidget(self.angleEditor, 1)
        layout.addWidget(addWound)
        layout.addWidget(removeWound)
        self.woundControlBox.setLayout(layout)

    def toggleRadioOne(self):
        self.sceneState.setTimeFactor(0.5)

    def toggleRadioTwo(self):
        self.sceneState.setTimeFactor(1.0)

    def toggleRadioThree(self):
        self.sceneState.setTimeFactor(1.5)

    def toggleRadioFour(self):
        self.sceneState.setTimeFactor(2.0)

    def toggleRadioFive(self):
        self.sceneState.setTimeFactor(5.0)

    def toggleRadioSix(self):
        self.sceneState.setTimeFactor(10.0)

    def startSim(self):
        self.simRunning = True
        self.runSimulation()

    def pauseSim(self):
        print("Pausing sim\n")
        self.timer.stop()

    def changeR1Coeff(self):
        self.sceneState.setR1(float(self.r1coeffedit.text()))

    def changeR2Coeff(self):
        self.sceneState.setR2(float(self.r2coeffedit.text()))

    def changeR3Coeff(self):
        self.sceneState.setR3(float(self.r3coeffedit.text()))

    def createSimulationControlBox(self):
        self.simulationControlBox = QGroupBox("Simulation")

        # create editors for rule coefficients
        r1coefflabel = QLabel("Rule 1 Coefficient")
        self.r1coeffedit = QLineEdit()

        r2coefflabel = QLabel("Rule 2 Coefficient")
        self.r2coeffedit = QLineEdit()

        r3coefflabel = QLabel("Rule 3 Coefficient")
        self.r3coeffedit = QLineEdit()

        self.r1coeffedit.setPlaceholderText("0.01")
        self.r2coeffedit.setPlaceholderText("0.01")
        self.r3coeffedit.setPlaceholderText("0.01")

        # create validators for rule coefficient editors
        r1Validator = QDoubleValidator()
        r1Validator.setBottom(-1.1)
        r1Validator.setTop(1.1)

        r2Validator = QDoubleValidator()
        r2Validator.setBottom(-1.1)
        r2Validator.setTop(1.1)

        r3Validator = QDoubleValidator()
        r3Validator.setBottom(-1.1)
        r3Validator.setTop(1.1)

        self.r1coeffedit.setValidator(r1Validator)
        self.r2coeffedit.setValidator(r2Validator)
        self.r3coeffedit.setValidator(r3Validator)

        self.r1coeffedit.editingFinished.connect(self.changeR1Coeff)
        self.r2coeffedit.editingFinished.connect(self.changeR2Coeff)
        self.r3coeffedit.editingFinished.connect(self.changeR3Coeff)

        #self.woundArea.editingFinished.connect(self.toggleArea)

        #self.simulationControlBox = QGroupBox("Simulation")

        timeLabel = QLabel('Simulation Speed')
        radioButton1 = QRadioButton("0.5x Speed")
        radioButton2 = QRadioButton("1.0x Speed")
        radioButton3 = QRadioButton("1.5x Speed")
        radioButton4 = QRadioButton("2.0x Speed")
        radioButton5 = QRadioButton("5.0x Speed")
        radioButton6 = QRadioButton("10.0x Speed")
        radioButton2.setChecked(True)

        radioButton1.clicked.connect(self.toggleRadioOne)
        radioButton2.clicked.connect(self.toggleRadioTwo)
        radioButton3.clicked.connect(self.toggleRadioThree)
        radioButton4.clicked.connect(self.toggleRadioFour)
        radioButton5.clicked.connect(self.toggleRadioFive)
        radioButton6.clicked.connect(self.toggleRadioSix)

        startButton = QPushButton("Start Simulation")
        startButton.clicked.connect(self.startSim)

        stopButton = QPushButton("Stop Simulation")
        stopButton.clicked.connect(self.pauseSim)

        resetButton = QPushButton("Reset Simulation")

        layout = QVBoxLayout()
        layout.addWidget(self.r1coeffedit)
        layout.addWidget(self.r2coeffedit)
        layout.addWidget(self.r3coeffedit)
        layout.addWidget(radioButton1)
        layout.addWidget(radioButton2)
        layout.addWidget(radioButton3)
        layout.addWidget(radioButton4)
        layout.addWidget(radioButton5)
        layout.addWidget(radioButton6)
        layout.addWidget(startButton)
        layout.addWidget(stopButton)
        layout.addWidget(resetButton)
        self.simulationControlBox.setLayout(layout)

    def add_cells(self, num):
        self.foregroundBrush.setColor(self.cellColor)
        self.foregroundBrush.setStyle(Qt.SolidPattern)

        for _ in range(num):
            cpx, cpy = np.random.randint(0, 256, 2)

            cell = self.sceneState.addCell(cpx, cpy)
            self.graphicsScene.addItem(cell)
        self.sceneState.updateCellCoords()

    def createGraphicsDisplay(self):
        self.graphicsDisplayBox = QGroupBox('Simulation')
        self.graphicsScene = QGraphicsScene()
        self.graphicsScene.setSceneRect(0, 0, 512, 512)
        self.add_cells(2500)
        self.graphicsScene.setBackgroundBrush(self.backgroundColor)
        self.graphicsView = QGraphicsView(self.graphicsScene)
        self.graphicsView.setSceneRect(0, 0, 512, 512)
        layout = QVBoxLayout()
        layout.addWidget(self.graphicsView)
        self.graphicsDisplayBox.setLayout(layout)
        self.sceneState.graphicsScene = self.graphicsScene

    def cellAnimState(self, x, y):
        a = QPoint(x, y)
        b = QPoint(x + 8, y + 8)
        c = QPoint(x + 16, y)

        tri = QPolygonF()
        tri.append(a)
        tri.append(b)
        tri.append(c)

        return tri

    def simulationStep(self):
        self.c += 1
        print("running sim step {0}!\n".format(self.c))
        self.sceneState.update_cell_info(self.graphicsScene)
        self.graphicsScene.advance()

    def runSimulation(self):
        print("Running simulation\n")
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.simulationStep)
        self.timer.start(1000 / 33)