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

PyQt5自定义Planet控件为何单独弹窗?如何在SolarSystem窗口显示

问题分析与解决方案

错误原因

  1. Planet默认作为独立窗口:你的Planet类继承了QWidget,创建实例时未指定父控件,导致每个Planet都是独立的顶级窗口,调用show()后会单独弹出。
  2. 绘制逻辑不符合布局需求:即使给Planet设置父控件,当前paintEvent是在Planet自身控件范围内绘制,位置计算基于控件局部坐标,无法实现太阳系整体布局的控制需求。

推荐解决方案:统一绘制方案

把Planet改成纯数据类,由SolarSystem统一负责所有绘制逻辑,完全手动控制位置,不需要布局,后续扩展轨道运动也更灵活。

1. 重构Planet类(数据类)

from dataclasses import dataclass
from PyQt5.QtCore import Qt, QPoint

@dataclass
class Planet:
    name: str
    object_diameter: int
    orbit_radius: float
    speed_degrees_earth_day: float
    color: Qt.GlobalColor
    current_angle: float = 0.0  # 用于轨道运动的角度参数

    def get_position(self, center_x: int, center_y: int) -> QPoint:
        # 基础位置计算,若要实现轨道运动,可替换为三角函数计算
        # 示例轨道运动代码:
        # from math import cos, sin, radians
        # x = center_x + self.orbit_radius * cos(radians(self.current_angle))
        # y = center_y + self.orbit_radius * sin(radians(self.current_angle))
        # return QPoint(int(x), int(y))
        return QPoint(center_x + self.orbit_radius, center_y + self.orbit_radius)

2. 修改SolarSystem类(统一绘制)

from PyQt5 import QtWidgets
from PyQt5.QtGui import QPainter, QPen, QBrush
from PyQt5.QtCore import Qt, QPoint
from Planet import Planet

class SolarSystem(QtWidgets.QWidget):
    def __init__(self, space_objects: list[list], *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.space_objects = []
        for planet_data in space_objects:
            self.space_objects.append(Planet(*planet_data))
        self.initUi()
        self.show()
    
    def initUi(self):
        self.setGeometry(300, 300, 800, 700)
        self.setWindowTitle("太阳系")
    
    def paintEvent(self, e):
        super().paintEvent(e)
        painter = QPainter(self)
        # 以窗口中心作为太阳系的中心点
        center_x = self.width() // 2
        center_y = self.height() // 2

        # 遍历所有行星,逐个绘制
        for planet in self.space_objects:
            pos = planet.get_position(center_x, center_y)
            painter.setPen(QPen(planet.color, 8, Qt.SolidLine))
            painter.setBrush(QBrush(planet.color, Qt.SolidPattern))
            painter.drawEllipse(pos, planet.object_diameter, planet.object_diameter)
        
        painter.end()

3. MainWindow类保持不变

确保导入的是重构后的Planet类,原继承QWidget的Planet类可删除或重命名。

可选方案:子控件手动定位

如果坚持保留Planet作为QWidget子类,需修改以下内容:

  • 创建Planet时指定父控件为SolarSystem
  • 去掉Planet的show()调用(父控件显示时子控件会自动显示)
  • 手动设置Planet控件位置,调整paintEvent绘制到控件中心

修改SolarSystem的__init__:

new_space_objects = Planet(*planet, parent=self)  # 指定父控件

修改SolarSystem的initUi:

def initUi(self):
    self.setGeometry(300, 300, 800, 700)
    center_x = self.width() // 2
    center_y = self.height() // 2
    for planet in self.space_objects:
        # 手动设置控件位置,让行星中心对应轨道位置
        widget_x = center_x + planet.orbit_radius - planet.object_diameter
        widget_y = center_y + planet.orbit_radius - planet.object_diameter
        planet.setGeometry(widget_x, widget_y, 
                           planet.object_diameter*2, planet.object_diameter*2)

修改Planet的paintEvent:

def paintEvent(self, e):
    super().paintEvent(e)
    painter = QPainter(self)
    center = QPoint(self.width()//2, self.height()//2)
    painter.setPen(QPen(self.color, 8, Qt.SolidLine))
    painter.setBrush(QBrush(self.color, Qt.SolidPattern))
    painter.drawEllipse(center, self.object_diameter, self.object_diameter)
    painter.end()

此方案灵活性较差,后续实现轨道运动时需要频繁调整控件位置,不如统一绘制方案高效。

内容的提问来源于stack exchange,提问作者Polskay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 11:07:10