在settings.py上添加注释

This commit is contained in:
louyunyun 2025-10-19 21:51:11 +08:00
parent c298526f90
commit e82a220222

View file

@ -13,53 +13,70 @@
# You should have received a copy of the GNU General Public License
# along with BeeRef. If not, see <https://www.gnu.org/licenses/>.
import argparse
import logging
import os
import os.path
# 导入必要的库
import argparse # 用于解析命令行参数
import logging # 用于日志记录
import os # 用于处理操作系统相关功能
import os.path # 用于处理文件路径
from PyQt6 import QtCore, QtGui
from PyQt6 import QtCore, QtGui # 导入PyQt6核心和GUI模块
from beeref import constants
from beeref import constants # 导入应用程序常量
# 配置日志记录器
logger = logging.getLogger(__name__)
# 创建命令行参数解析器
parser = argparse.ArgumentParser(
description=f'{constants.APPNAME_FULL} {constants.VERSION}')
description=f'{constants.APPNAME_FULL} {constants.VERSION}') # 程序描述,包含全名和版本
# 添加文件名参数可指定要打开的bee文件或图像文件
parser.add_argument(
'filenames',
nargs='*',
nargs='*', # 接受0个或多个参数
default=None,
help=('Bee file or images to open. '
'If the first file is a bee file, it will be opened and all '
'further files will be ignored. If the first argument isn\'t a '
'bee file, all files will be treated as images and inserted as '
'if opened with "Insert -> Images".'))
'if opened with "Insert -> Images".')) # 参数帮助说明
# 添加设置目录参数:指定非默认的设置目录
parser.add_argument(
'--settings-dir',
help='settings directory to use instead of default location')
# 添加日志级别参数:指定控制台输出的日志级别
parser.add_argument(
'-l', '--loglevel',
default='INFO',
choices=list(logging._nameToLevel.keys()),
default='INFO', # 默认日志级别为INFO
choices=list(logging._nameToLevel.keys()), # 允许的日志级别选项
help='log level for console output')
# 添加调试边界矩形参数:用于调试,绘制项目的边界矩形
parser.add_argument(
'--debug-boundingrects',
default=False,
action='store_true',
action='store_true', # 无需参数出现即设为True
help='draw item\'s bounding rects for debugging')
# 添加调试形状参数:用于调试,绘制项目的鼠标事件形状
parser.add_argument(
'--debug-shapes',
default=False,
action='store_true',
help='draw item\'s mouse event shapes for debugging')
# 添加调试手柄参数:用于调试,绘制项目的变换手柄区域
parser.add_argument(
'--debug-handles',
default=False,
action='store_true',
help='draw item\'s transform handle areas for debugging')
# 添加调试错误参数:立即退出并显示指定错误消息
parser.add_argument(
'--debug-raise-error',
default='',
@ -67,31 +84,46 @@ parser.add_argument(
class CommandlineArgs:
"""Wrapper around argument parsing.
"""命令行参数解析的包装类。
Checking for unknown arguments is configurable so that it can be
deliberately enabled from the main() function while ignored for
other imports so that unit tests won't fail.
检查未知参数是可配置的以便在main()函数中有选择地启用
而在其他导入中忽略这样单元测试就不会失败
This is a singleton so that arguments are only parsed once, unless
``with_check`` is ``True``.
这是一个单例类因此参数只会被解析一次除非with_check为True
"""
_instance = None
_instance = None # 单例实例
def __new__(cls, *args, **kwargs):
"""创建单例实例确保只存在一个CommandlineArgs对象"""
if not cls._instance or kwargs.get('with_check'):
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, with_check=False):
"""初始化方法,解析命令行参数
参数:
with_check: 是否严格检查未知参数True则严格检查False则忽略未知参数
"""
# 确保只初始化一次
if not hasattr(self, '_args'):
if with_check:
# 严格解析所有参数,未知参数会抛出错误
self._args = parser.parse_args()
else:
# 解析已知参数,忽略未知参数
self._args = parser.parse_known_args()[0]
def __getattribute__(self, name):
"""自定义属性获取方法,将属性访问转发到解析后的参数对象
参数:
name: 要获取的属性名
返回:
参数对象中对应属性的值
"""
if name == '_args':
return super().__getattribute__(name)
else:
@ -99,134 +131,196 @@ class CommandlineArgs:
class BeeSettingsEvents(QtCore.QObject):
"""设置相关事件的信号类,用于全局发送设置变更信号"""
# 恢复默认设置信号
restore_defaults = QtCore.pyqtSignal()
# 恢复键盘默认设置信号
restore_keyboard_defaults = QtCore.pyqtSignal()
# We want to send and receive settings events globally, not per
# BeeSettings instance. Since we can't instantiate BeeSettings
# globally on module level (because the Qt app doesn't exist yet), we
# use this events proxy
# 全局设置事件代理实例,用于发送和接收全局设置事件
# 不能在模块级别实例化BeeSettings因为Qt应用程序可能尚未存在
settings_events = BeeSettingsEvents()
class BeeSettings(QtCore.QSettings):
"""应用程序设置类继承自QSettings提供自定义设置管理功能"""
# 定义所有可配置的设置字段及其元数据
FIELDS = {
'Save/confirm_close_unsaved': {
'default': True,
'cast': bool,
'default': True, # 默认值:关闭未保存文件时确认
'cast': bool, # 类型转换:转换为布尔值
},
'Items/image_storage_format': {
'default': 'best',
'validate': lambda x: x in ('png', 'jpg', 'best'),
'default': 'best', # 默认值:最佳存储格式
'validate': lambda x: x in ('png', 'jpg', 'best'), # 验证:必须是这三个值之一
},
'Items/arrange_gap': {
'default': 0,
'cast': int,
'validate': lambda x: 0 <= x <= 200,
'default': 0, # 默认值排列间隙为0
'cast': int, # 类型转换:转换为整数
'validate': lambda x: 0 <= x <= 200, # 验证必须在0-200之间
},
'Items/arrange_default': {
'default': 'optimal',
'validate': lambda x: x in (
'default': 'optimal', # 默认值:最佳排列方式
'validate': lambda x: x in ( # 验证:必须是这四个值之一
'optimal', 'horizontal', 'vertical', 'square'),
},
'Items/image_allocation_limit': {
'default': 256,
'cast': int,
'validate': lambda x: x >= 0,
'default': 256, # 默认值图像分配限制为256MB
'cast': int, # 类型转换:转换为整数
'validate': lambda x: x >= 0, # 验证必须大于等于0
# 保存后回调设置QImageReader的分配限制
'post_save_callback': QtGui.QImageReader.setAllocationLimit,
}
}
def __init__(self):
settings_format = QtCore.QSettings.Format.IniFormat
settings_scope = QtCore.QSettings.Scope.UserScope
settings_dir = self.get_settings_dir()
"""初始化设置对象,配置设置存储路径和格式"""
settings_format = QtCore.QSettings.Format.IniFormat # 使用INI格式存储
settings_scope = QtCore.QSettings.Scope.UserScope # 存储在用户范围
settings_dir = self.get_settings_dir() # 获取设置目录
# 如果指定了自定义设置目录,则设置路径
if settings_dir:
QtCore.QSettings.setPath(
settings_format, settings_scope, settings_dir)
# 调用父类构造函数,初始化设置
super().__init__(
settings_format,
settings_scope,
constants.APPNAME,
constants.APPNAME)
constants.APPNAME, # 组织名称
constants.APPNAME) # 应用程序名称
def on_startup(self):
"""Settings to be applied on application startup."""
"""应用程序启动时需要应用的设置"""
# 检查环境变量中是否设置了图像分配限制
if os.environ.get('QT_IMAGEIO_MAXALLOC'):
alloc = int(os.environ['QT_IMAGEIO_MAXALLOC'])
else:
# 从设置中获取图像分配限制
alloc = self.valueOrDefault('Items/image_allocation_limit')
# 设置QImageReader的分配限制
QtGui.QImageReader.setAllocationLimit(alloc)
def setValue(self, key, value):
super().setValue(key, value)
"""重写setValue方法添加保存后回调功能
参数:
key: 设置的键名
value: 要设置的值
"""
super().setValue(key, value) # 调用父类方法设置值
# 如果该设置有保存后回调函数,则执行
if key in self.FIELDS and 'post_save_callback' in self.FIELDS[key]:
self.FIELDS[key]['post_save_callback'](value)
def remove(self, key):
super().remove(key)
"""重写remove方法移除设置后恢复默认值并执行回调
参数:
key: 要移除的设置键名
"""
super().remove(key) # 调用父类方法移除设置
# 如果该设置有保存后回调函数,则使用默认值执行回调
if key in self.FIELDS and 'post_save_callback' in self.FIELDS[key]:
value = self.valueOrDefault(key)
self.FIELDS[key]['post_save_callback'](value)
def valueOrDefault(self, key):
"""Get the value for key, or the default value specified in FIELDS.
This is the method to be used for configurable settings (as
opposed to settings that BeeRef stores on its own.)
This will validate and type cast the given value if 'cast' and
'validate' are specified in the FIELDS entry for the given
key. The default value will be returned if validation or type
casting fails.
"""获取指定键的值,如果不存在或无效则返回默认值
这是用于可配置设置的方法与BeeRef自行存储的设置相对
如果FIELDS中为给定键指定了'cast''validate'将对值进行
类型转换和验证如果类型转换或验证失败将返回默认值
参数:
key: 要获取的设置键名
返回:
有效的设置值或默认值
"""
val = self.value(key)
conf = self.FIELDS[key]
val = self.value(key) # 从设置中获取值
conf = self.FIELDS[key] # 获取该键的配置信息
# 如果值不存在,使用默认值
if val is None:
val = conf['default']
# 如果需要类型转换,尝试转换
if 'cast' in conf:
try:
val = conf['cast'](val)
except (ValueError, TypeError):
# 转换失败,使用默认值
val = conf['default']
# 如果需要验证,检查值是否有效
if 'validate' in conf:
if not conf['validate'](val):
# 验证失败,使用默认值
val = conf['default']
return val
def value_changed(self, key):
"""Whether the value for given key has changed from its default."""
"""检查指定键的值是否与默认值不同
参数:
key: 要检查的设置键名
返回:
如果值与默认值不同则为True否则为False
"""
return self.valueOrDefault(key) != self.FIELDS[key]['default']
def restore_defaults(self):
"""Restore all the values specified in FILEDS to their default values
by removing them from the settings file.
"""
logger.debug('Restoring settings to defaults')
"""将所有在FIELDS中指定的值恢复为默认值通过从设置文件中移除它们实现"""
logger.debug('Restoring settings to defaults') # 记录调试日志
# 遍历所有设置键并移除
for key in self.FIELDS.keys():
self.remove(key)
# 发送恢复默认设置信号
settings_events.restore_defaults.emit()
def fileName(self):
"""获取设置文件的规范化路径
返回:
规范化的设置文件路径字符串
"""
return os.path.normpath(super().fileName())
def get_settings_dir(self): # pragma: no cover
"""获取设置目录,从命令行参数中获取
返回:
命令行中指定的设置目录或None
"""
args = CommandlineArgs()
return args.settings_dir
def update_recent_files(self, filename):
filename = os.path.abspath(filename)
values = self.get_recent_files()
"""更新最近文件列表将指定文件添加到列表开头最多保留10个
参数:
filename: 要添加到最近文件列表的文件路径
"""
filename = os.path.abspath(filename) # 获取绝对路径
values = self.get_recent_files() # 获取当前最近文件列表
# 如果文件已在列表中,先移除
if filename in values:
values.remove(filename)
# 将文件添加到列表开头
values.insert(0, filename)
# 写入最近文件列表到设置最多保留前10个
self.beginWriteArray('RecentFiles')
for i, filename in enumerate(values[:10]):
self.setArrayIndex(i)
@ -234,13 +328,24 @@ class BeeSettings(QtCore.QSettings):
self.endArray()
def get_recent_files(self, existing_only=False):
"""获取最近文件列表
参数:
existing_only: 是否只返回存在的文件
返回:
最近文件路径列表
"""
values = []
# 读取RecentFiles数组
size = self.beginReadArray('RecentFiles')
for i in range(size):
self.setArrayIndex(i)
values.append(self.value('path'))
self.endArray()
# 如果需要,过滤掉不存在的文件
if existing_only:
values = [f for f in values if os.path.exists(f)]
return values
return values