rxts 发表于 2025-8-15 11:07:37

如何让ScreenConfig在不继承pyside6的任何类的情况下,报错AttributeError

class ScreenConfig:
    """一次性计算所有屏幕参数"""

    def __init__(self):
      screen = QGuiApplication.primaryScreen().geometry()
      self.screen_width = screen.width()
      self.screen_height = screen.height()

      # 计算常用参数(按需扩展)
      self.font_size = self.screen_height * 0.015
      self.header_height = self.screen_height * 0.1
      self.window_margin = self.screen_width * 0.02


# 全局单例(确保QApplication已初始化后创建)
screen_config = ScreenConfig()
以上代码报错:
    screen = QGuiApplication.primaryScreen().geometry()
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'geometry'
如何让ScreenConfig在不继承pyside6的任何类的情况下,报错AttributeError,并可以正确计算各项参数

FishC 发表于 2025-8-15 11:10:15

要使 `ScreenConfig` 在不继承 PySide6 类的情况下正确运行,需要解决 `QGuiApplication` 未初始化的问题。以下是修改方案:

### 修改后的代码(惰性初始化 + 延迟计算):

class ScreenConfig:
    """一次性计算所有屏幕参数(惰性初始化)"""
   
    def __init__(self):
      self._initialized = False# 初始化状态标记
   
    def _initialize(self):
      """实际执行参数计算的私有方法"""
      screen = QGuiApplication.primaryScreen().geometry()
      self.screen_width = screen.width()
      self.screen_height = screen.height()
      self.font_size = self.screen_height * 0.015
      self.header_height = self.screen_height * 0.1
      self.window_margin = self.screen_width * 0.02
      self._initialized = True
   
    def __getattr__(self, name):
      """在访问属性时按需初始化"""
      if not self._initialized:
            self._initialize()
      return self.__dict__# 返回已计算的属性值

# 全局单例(此时不立即计算参数)
screen_config = ScreenConfig()


### 关键修改说明:
1. **惰性初始化**:
   - 在 `__init__` 中只设置初始化标记 self._initialized=False
   - 首次访问属性时通过 __getattr__ 触发实际计算

2. **错误处理逻辑**:
   - 若在创建 QApplication 前访问属性:
   # 错误示例(QApplication未初始化)
   print(screen_config.screen_width)
   **触发原版报错**:
   AttributeError: 'NoneType' object has no attribute 'geometry'

3. **正确使用方式**:
   from PySide6.QtWidgets import QApplication
   import sys
   
   app = QApplication(sys.argv)# 先初始化QApplication
   
   # 此时访问属性会正常计算
   print(screen_config.screen_width)# 输出屏幕宽度

### 注意事项:
1. **错误触发条件**:必须确保在访问属性前已创建 QApplication 实例
2. **单例优势**:全局对象 screen_config 只需初始化一次
3. **线程安全**:该方案适用于主线程,多线程场景需额外同步

> 通过此设计,既保留了原错误触发机制(确保调试时能发现问题),又能在正确初始化后自动计算参数。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

rxts 发表于 2025-8-15 11:24:17

我每次在其他py文件中访问self.font_size时__getattr__方法都会调用一次对吗?

FishC 发表于 2025-8-15 11:25:57

rxts 发表于 2025-08-15 11:24
我每次在其他py文件中访问self.font_size时__getattr_ ...

您的问题很好理解。在您提供的 `ScreenConfig` 类中,使用 `__getattr__` 方法时,**不会每次访问 `self.font_size` 都触发 `__getattr__` 方法**。具体行为如下:

### 属性访问机制:
1. **首次访问任何属性时**:
   - 会触发 `__getattr__` 方法
   - 执行 `_initialize()` 完成参数计算
   - 设置 `self._initialized = True`

2. **后续访问已存在的属性时**:
   - 如 `self.font_size`、`self.screen_width` 等
   - **不会触发** `__getattr__` 方法
   - 直接从实例字典中获取值

### 验证示例:
class ScreenConfig:
    def __init__(self):
      self._initialized = False
   
    def _initialize(self):
      print("执行初始化计算!")# 添加打印以便观察
      # 实际计算代码...
      self.font_size = 16# 示例值
      self._initialized = True
   
    def __getattr__(self, name):
      if not self._initialized:
            self._initialize()
      return getattr(self, name)# 返回已存在的属性

# 测试
config = ScreenConfig()
print("第一次访问:", config.font_size)# 触发初始化
print("第二次访问:", config.font_size)# 直接返回值
print("第三次访问:", config.font_size)# 直接返回值

### 输出结果:
执行初始化计算!
第一次访问: 16
第二次访问: 16
第三次访问: 16

### 关键结论:
1. `__getattr__` 仅在**属性不存在时**被调用
2. 初始化完成后,所有属性已存在于实例中
3. 后续访问直接从 `self.__dict__` 读取属性值
4. 访问已存在的属性与普通类属性访问效率相同

> 因此您无需担心性能问题,后续访问 `screen_config.font_size` 的效率与访问普通类属性完全一致,不会反复触发初始化逻辑。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: 如何让ScreenConfig在不继承pyside6的任何类的情况下,报错AttributeError