You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt5气体分子模拟程序故障:点击添加按钮无分子显示求助

问题描述

我开发了一款基于PyQt5的气体分子模拟程序,预期点击UI中的Addmolecule按钮时,界面会随机显示分子,但实际无任何分子显示,终端(Visual Studio)也未报错,请求技术协助。

问题分析与修复方案
  • QMainWindow布局错误:QMainWindow不能直接设置布局,必须通过centralWidget承载布局。原代码直接给窗口实例设置布局,导致QGraphicsView没有正确显示在窗口中。
  • 坐标范围错误:分子生成时使用窗口整体尺寸self.width()/self.height(),但实际应该使用QGraphicsView视口的尺寸,否则分子可能生成在视图可见区域之外。
  • 场景元素索引不匹配:self.scene.items()[i]无法保证与self.molecules列表的顺序一致,应该将图形项直接关联到对应的分子字典中,避免索引错误。
  • 定时器停止方法错误:使用startTimer()创建的定时器,需要用killTimer()停止,而非timer.stop()。
修复后的完整代码
import sys
import random
import math
from PyQt5.QtWidgets import QApplication, QMainWindow, QSlider, QLabel, QVBoxLayout, QWidget, QPushButton, QGraphicsEllipseItem, QGraphicsScene, QGraphicsView
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtCore import Qt
from PyQt5 import uic

form_ui = uic.loadUiType("qqq.ui")[0]

class GasMoleculeWindow(QMainWindow, form_ui):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
         
        self.setWindowTitle("Gas Molecule Simulation")
        self.setGeometry(100, 100, 800, 600)

        self.molecules = []
        self.temperature = 1.0

        self.addMoleculeButton.clicked.connect(self.addMolecule)
        self.stopSimulation.clicked.connect(self.stopProgram)
        self.massSlider.setRange(1,10)
        self.massSlider.valueChanged.connect(self.updateMass)
        self.radiusSlider.valueChanged.connect(self.updateRadius)
        self.temperatureSlider.valueChanged.connect(self.updateTemperature)

        self.massLabel.setAlignment(Qt.AlignCenter)
        self.radiusLabel.setAlignment(Qt.AlignCenter)
        self.temperatureLabel.setAlignment(Qt.AlignCenter)

        self.scene = QGraphicsScene(self)
        self.view = QGraphicsView(self.scene)

        # 修复QMainWindow布局问题:使用centralWidget承载布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        layout.addWidget(self.view)

        self.timer_id = self.startTimer(10)  # 重命名变量避免混淆

    def addMolecule(self):
        # 获取视图视口的实际尺寸,作为分子生成的范围
        view_width = self.view.viewport().width()
        view_height = self.view.viewport().height()

        for _ in range(10):
            molecule = {
                'x': random.uniform(0, view_width),
                'y': random.uniform(0, view_height),
                'vx': random.uniform(-1, 1) * math.sqrt(3 * self.temperature / self.massSlider.value()),
                'vy': random.uniform(-1, 1) * math.sqrt(3 * self.temperature / self.massSlider.value()),
                'mass': self.massSlider.value(),
                'radius': self.radiusSlider.value(),
                'item': None  # 预留存储图形项的位置
            }
            self.molecules.append(molecule)

            molecule_item = QGraphicsEllipseItem(
                molecule['x'] - molecule['radius'],
                molecule['y'] - molecule['radius'],
                2 * molecule['radius'],
                2 * molecule['radius']
            )
            mass_color = self.getColorByMass(molecule['mass'])
            molecule_item.setBrush(QColor(*mass_color))

            self.scene.addItem(molecule_item)
            molecule['item'] = molecule_item  # 将图形项关联到分子字典

    def getColorByMass(self, mass):
        min_mass = 1
        max_mass = 10

        min_color = QColor(0, 0, 255)  # Blue
        max_color = QColor(255, 0, 0)  # Red

        ratio = (mass - min_mass) / (max_mass - min_mass)

        r = int(min_color.red() + ratio * (max_color.red() - min_color.red()))
        g = int(min_color.green() + ratio * (max_color.green() - min_color.green()))
        b = int(min_color.blue() + ratio * (max_color.blue() - min_color.blue()))

        return r, g, b

    def updateMass(self):
        massValue = self.massSlider.value()
        self.massLabel.setText(f"Mass: {massValue}")

    def updateRadius(self):
        radiusValue = self.radiusSlider.value()
        self.radiusLabel.setText(f"Radius: {radiusValue}")

    def updateTemperature(self):
        temperatureValue = self.temperatureSlider.value() / 10.0
        self.temperature = temperatureValue
        self.temperatureLabel.setText(f"Temperature: {temperatureValue}")

        for molecule in self.molecules:
            molecule['vx'] = random.uniform(-1, 1) * math.sqrt(3 * temperatureValue / molecule['mass'])
            molecule['vy'] = random.uniform(-1, 1) * math.sqrt(3 * temperatureValue / molecule['mass'])

    def timerEvent(self, event):
        if event.timerId() != self.timer_id:
            super().timerEvent(event)
            return

        view_width = self.view.viewport().width()
        view_height = self.view.viewport().height()

        for i in range(len(self.molecules)):
            molecule = self.molecules[i]
            molecule_item = molecule['item']  # 直接从字典获取关联的图形项

            molecule['x'] += molecule['vx']
            molecule['y'] += molecule['vy']

            molecule_item.setRect(
                molecule['x'] - molecule['radius'],
                molecule['y'] - molecule['radius'],
                2 * molecule['radius'],
                2 * molecule['radius']
            )

            # 边界碰撞检测使用视图视口尺寸
            if molecule['x'] < molecule['radius'] or molecule['x'] > view_width - molecule['radius']:
                molecule['vx'] *= -1
            if molecule['y'] < molecule['radius'] or molecule['y'] > view_height - molecule['radius']:
                molecule['vy'] *= -1

            for j in range(i + 1, len(self.molecules)):
                other_molecule = self.molecules[j]
                dx = other_molecule['x'] - molecule['x']
                dy = other_molecule['y'] - molecule['y']
                distance = math.sqrt(dx ** 2 + dy ** 2)

                if distance < molecule['radius'] + other_molecule['radius']:
                    mass_sum = molecule['mass'] + other_molecule['mass']
                    dx_normalized = dx / distance
                    dy_normalized = dy / distance

                    v1x = molecule['vx']
                    v1y = molecule['vy']

                    v2x = other_molecule['vx']
                    v2y = other_molecule['vy']

                    u1 = v1x * dx_normalized + v1y * dy_normalized
                    u2 = v2x * dx_normalized + v2y * dy_normalized

                    v1 = ((molecule['mass'] - other_molecule['mass']) * u1 + 2 * other_molecule['mass'] * u2) / mass_sum
                    v2 = ((other_molecule['mass'] - molecule['mass']) * u2 + 2 * molecule['mass'] * u1) / mass_sum

                    molecule['vx'] += (v1 - u1) * dx_normalized
                    molecule['vy'] += (v1 - u1) * dy_normalized
                    other_molecule['vx'] += (v2 - u2) * dx_normalized
                    other_molecule['vy'] += (v2 - u2) * dy_normalized

            molecule['x'] += random.uniform(-0.5, 0.5)
            molecule['y'] += random.uniform(-0.5, 0.5)

        self.view.viewport().update()

    def stopProgram(self):
        self.killTimer(self.timer_id)  # 使用killTimer停止定时器

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = GasMoleculeWindow()
    window.show()
    app.exec_()

内容的提问来源于stack exchange,提问作者1207권태형

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 02:37:01