ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TagStudio 代码风格指南:PEP 8、Ruff 规则与 Qt MVC 目录结构约定

TagStudio 代码风格指南:PEP 8、Ruff 规则与 Qt MVC 目录结构约定 TagStudio 代码风格指南PEP 8、Ruff 规则与 Qt MVC 目录结构约定【免费下载链接】TagStudioA User-Focused Photo File Management System项目地址: https://gitcode.com/GitHub_Trending/ta/TagStudio本篇技术指南系统梳理 TagStudio一个用户友好的照片与文件管理系统官方的代码风格规范 STYLE.md涵盖 Ruff/EditorConfig/Prettier 三套格式化体系的配置细节、PEP 8 在项目中的具体约定与例外、core/与qt/两层项目布局的职责边界以及 Qt 前端 MVC 模式的落地方式与真实示例。读完本文你可以准确知道在 TagStudio 中提交代码前应遵循的命名、目录、翻译与文档规范并能用仓库中的实际源码逐条验证这些约定是如何执行的。规范文档的仓库位置风格指南原文位于 STYLE.md该文件是从 docs/style.md 软链接到仓库根目录的与CHANGELOG.md、CONTRIBUTING.md相同的方式。文档开头声明它假设读者已经阅读过 开发指南 与 贡献指南因此本文聚焦于风格本身一般原则、格式化、语法约定、模块与系统级约定、项目布局、文档与翻译规范。一般原则风格指南给出的总纲只有三条但它们是后续所有细节的出发点编写清晰、简洁、模块化的代码如果一段代码的用途不明显应加简短注释解释其余贡献要求见 贡献指南。代码格式化Ruff、EditorConfig 与 Prettier 三套体系TagStudio 的格式化策略是按文件类型分工的Python 文件交给 Ruff其余文本文件Markdown、JSON、YAML、HTML、CSS 等交给 EditorConfig Prettier。Ruff 配置Python 文件Python 代码的 linting 基本由 Ruff 完成规则声明在 pyproject.toml 中仓库固定使用ruff0.15.17pyproject.toml#L83。关键配置如下[tool.ruff] exclude [home_ui.py, resources.py, resources_rc.py] line-length 100 [tool.ruff.lint] select [B, D, E, F, FBT003, I, N, SIM, T20, UP] ignore [D100, D101, D102, D103, D104, D105, D106, D107] [tool.ruff.lint.per-file-ignores] tests/** [D, E402] src/tagstudio/previews/vendored/** [B, E, N, UP, SIM115] [tool.ruff.lint.pydocstyle] convention google来源pyproject.toml#L119-L132其中几点值得注意line-length 100就是风格指南里提到的“最显著的例外”——PEP 8 默认 79 字符而 TagStudio 放宽到100并由 Ruff 强制select中的D规则组即 docstring 检查配合pydocstyle.convention google强制 Google 风格 docstringignore排除了 D100–D107缺少模块/类/方法级 docstring 的要求即不强制每个对象都有 docstring但写了就必须符合 Google 风格per-file-ignores对测试目录tests/**豁免 D 与 E402对 vendored 第三方代码previews/vendored/**整体豁免 B/E/N/UP/SIM115——这与 pyproject.toml#L96-L101 中 Pyright 的ignore列表保持一致的思路vendored 与 legacy 代码不执行风格检查。EditorConfig所有文本文件仓库根目录的 .editorconfig 声明了全局基线[*] charset utf-8 end_of_line lf indent_size 4 indent_style space insert_final_newline true max_line_length 100 trim_trailing_whitespace true [*.{nix,yaml,yml}] indent_size 2可以看到 100 字符行宽同时出现在 Ruff 与 EditorConfig 两处保证两类工具口径一致.nix文件与 YAML 使用 2 空格缩进是全局 4 空格缩进的唯一例外。PrettierMarkdown、JSON、YAML、HTML、CSS 等非 Python 文件的格式由 .prettierrc.toml 约束bracketSameLine false bracketSpacing true objectWrap preserve quoteProps as-needed singleQuote false [[overrides]] files [.github/**/*.yml, .github/**/*.yaml] [overrides.options] singleQuote true即默认使用双引号、保留对象换行结构只有.github/下的 YAML 使用单引号。风格指南特别提醒部分文件含有!-- prettier-ignore --标记如果你不用 Prettier 编辑这些文件务必保留这些标记——它们保护了 MkDocs 站点渲染依赖的特定排版例如本文所依据的文档中各 admonition 代码块前都有这样的标记。语法约定PEP 8 与 100 字符行宽Python 文件必须遵循 PEP 8除文档明说并允许的情况外不例外。两个最常触发的例外行宽 100 字符由 Ruff 强制见上文配置内部 Qt 方法使用camelCase。Qt 的信号槽与事件方法名本身就是驼峰的因此对它们的重写在代码库中随处可见。可以在真实代码中验证这一点SuggestBox 控制器 重写了 Qt 的显示事件override def showEvent(self, event: QShowEvent) - None: self._update_items() self._on_shift_held(heldFalse) ... return super().showEvent(event)同类情况还有keyPressEventsuggest_box.py#L305-L311。私有命名单下划线与双重下划线视为“私有”的类、方法、属性以单个下划线前缀命名如_internal_method()。仓库中大量使用例如控制器的内部状态_selection_index、_connect_callbackssuggest_box.py#L61、suggest_box.py#L89仅当功能上确有必要触发 name mangling 时才使用双下划线前缀如__mangled_method()。Google 风格 docstring类与方法应包含 Google 风格 docstring且该要求由 Ruff 的D规则组 convention google强制执行。一个实际例子是SuggestBox._update_items的单行 docstringsuggest_box.py#L214-L215def _update_items(self, query: str | None None) - None: Update the item list given a search query.排序约定列表和 JSON 键应按**自然排序natural sort order**排列除非另有说明部分文件的属性整体保持有序修改文件时应尊重这些既有排序模式。Python 模块约定风格指南对 Python 模块级写法给出三条硬性约定使用Pathlib代替os.path使用platform.system()代替os.name或sys.platform避免嵌套 f-string。TagStudio 系统级约定以下约定来自 STYLE.md 的“TagStudio Systems”一节均可以在源码中找到对应实现。翻译键的两种访问方式翻译键可以通过方括号语法访问例如Translations[translation_key]当占位符需要传值时使用Translations.format()方法。真实用法见 suggest_box.py#L69self._keep_box_open_action QAction(Translations[settings.keep_suggest_boxes_open], self)底层实现在 src/tagstudio/i18n/translations.py__getitem__按“当前语言 → 默认语言 →[{key}]兜底”的顺序取字符串translations.py#L103-L104format()则在取到字符串后执行str.format并在格式化失败时记录日志、用{unknown_key}占位而非抛异常translations.py#L86-L101。对应的行为测试位于 tests/i18n/test_translations.py。少传 QtDriver多传具体组件尽可能避免到处传递QtDriver类而是只传递需要的组件例如Library与AppSettings实例。可以推断这条约定直接来源于 MVC 拆分后的控制器签名——SuggestBox 构造函数 就是典型形态class SuggestBoxT: def __init__(self, library: Library, settings: AppSettings, placeholder_text: str ) - None: super().__init__() self._lib library self._settings settings它依赖的是核心的Library与 Qt 层的AppSettingssrc/tagstudio/qt/app_settings.py而不是驱动整个应用的QtDriversrc/tagstudio/qt/qt_driver.py。文本格式化HTML 风格标签指南建议在字符串中优先使用 HTML 风格的标签来格式化文本而不是显式 stylesheet并指出 stylesheet 相关类提供了Style类与format方法用于格式化文本头部。需要说明的是从当前仓库src/tagstudio/qt/views/styles/目录的结构看样式能力集中在 stylesheets.py提供如autofill_line_edit_style()等函数见 suggest_box_view.py#L12-L15具体工具类名以仓库当前代码为准。项目布局core/ 后端与 TagStudio 核心功能相关且不依赖 UI的代码放在core/目录下只要目的是服务任意 UI 而非特定于 Qt如文件预览渲染相关代码也可以放这里。指南给出的目录示例与实际仓库结构一致core/ ├── library/ # TagStudio 库系统 │ ├── alchemy/ # 当前 SQLite 后端SQLAlchemy ORM │ ├── json/ # 只读 legacy JSON 库系统仅保留用于迁移 │ ├── query_lang/ # 查询语言解析器 │ # 不涉及 SQLAlchemy ORM 的库文件放在这里 │ ├── refresh.py │ └── ... └── utils/ # core 的工具类与函数对应实际目录 src/tagstudio/core/library/alchemy/下是当前 ORM 实现db.py、models.py、migrations.py等json/是只读 legacy 系统query_lang/是查询解析器parser.py、tokenizer.py、ast.py。指南对 legacy 代码有一条强约束不要修改src/core/library/json/下的只读 legacy 库代码。仓库中还有旁证——Pyright 配置直接忽略了该目录pyproject.toml#L97-L101意味着静态检查也不对其生效。qt/ 前端与 MVC 模式应用 UI 代码全部在qt/目录Qt widget 采用MVC 模式构建各角色的职责与命名规则如下角色职责命名约定Model通常就是 core 库 中的对象只有 controller 与之交互view 不直接交互复用库模型View继承 Qt 布局类只负责一个或多个 widget 的布局与样式不单独使用而是作为 controller 的 layout允许少量模块化布局的逻辑类名加View后缀文件名加_view后缀Controller完整的 widget 或完整 widget 的基类负责连接回调与业务逻辑文件名直接用最终 widget 名不加 Controller 后缀与直接扩展 Qt widget 的普通 widget 命名保持对等View 的两个特例可复用布局如果某个布局类不是作为 view、而是要在其他布局内独立复用的放在views/layouts/文件名加_layout后缀如 flow_layout.py样式类纯粹作为可复用样式来源的类放在views/styles/如 stylesheets.py、palette.py。指南给出的前端目录示例与实际仓库一致qt/ ├── controllers/ # 实现 view 的 widget或扩展其他 widget ├── mixed/ # 尚未按 MVC 重构的文件 ├── views/ │ ├── layouts/ # 可在其他布局中独立复用的布局 │ ├── styles/ # 专用于样式的类 │ └── *_view.py # 由 controller 实现的 view布局 ├── resource_manager.py # 与 widget 无关的前端管理类 ├── cache_manager.py ├── qt_driver.py └── ...一条硬性禁令不要向qt/mixed/添加新文件。该目录是遗留区其中文件尚未按 MVC 规范重构待迁移完成后该目录会被移除——从当前仓库看src/tagstudio/qt/mixed/ 仍存放着约 30 个待重构文件。一个可验证的 MVC 真实示例指南用一个MyCoolWidget示例说明“view 继承 Qt 布局类、controller 直接继承 QWidget、view 中被 controller 操作的控件是公开的、controller 的方法大体私有”这几个要点。仓库里可以直接对照真实实现SuggestBoxcontrollerSuggestBoxViewview。View 侧src/tagstudio/qt/views/suggest_box_view.py只负责布局与外观控件以公开属性暴露class SuggestBoxView(QVBoxLayout): def __init__(self, placeholder_text: str ) - None: super().__init__() ... # Autocomplete ScrollArea self.scroll_area HorizontalScrollArea() ... # Search Field self.search_field AutofillLineEdit(scroll_area_container) ... # Finalize Layout self.addWidget(scroll_area_container) self.addWidget(self.search_field)Controller 侧src/tagstudio/qt/controllers/suggest_box.py继承QWidget在构造函数里挂上 view 并连接回调方法全部私有class SuggestBoxT: def __init__(self, library: Library, settings: AppSettings, placeholder_text: str ) - None: super().__init__() ... self.setLayout(SuggestBoxView(placeholder_text)) self._connect_callbacks() def _connect_callbacks(self) - None: self.layout().search_field.textChanged.connect(self._on_search_query_changed) self.layout().search_field.return_pressed.connect( lambda: self._on_search_query_submitted(self.layout().search_field.text()) ) ...这与指南示例一一对应view 继承布局类QVBoxLayout、controller 继承QWidget并以最终 widget 名命名、self.setLayout(...)装配、_connect_callbacks中通过self.layout()访问公开控件。另外controller 还重写了layout()返回类型suggest_box.py#L301-L303让self.layout().search_field这类访问获得精确类型——这是该模式下常见的收尾手段。最后指南给出了一条逻辑放置经验法则如果一个 widget 创建之后出现了条件逻辑这些逻辑应该放在controller里而不是 view 里。文档规范文档贡献包括docs/文件夹内的所有文件以及README.md。docs/内的文档构建并发布到静态文档站点。几个约定文件与文件夹名使用dash-case / kebab-case——仓库中的library-changes.md、preview-support.md、macros.md等均符合遵循既有文件夹结构模式不要添加文件尺寸过大的图片或其他媒体嵌入媒体必须提供 alt 文本标题使用Title Case大写规范。翻译规范翻译在 TagStudio 的 Weblate 项目上进行仓库内对应实现见 src/tagstudio/i18n/translations.py。三条规则不要改动占位符内部的文本不要改动翻译中的样式标签术语定义以官方术语表glossary为准。小结提交前自检清单结合本文各节可以在提交 TagStudio 代码前按以下清单快速自查格式化Python 通过ruff0.15.17行宽 100、Google docstringMarkdown/YAML/CSS 等符合 .editorconfig 与 .prettierrc.toml且未破坏prettier-ignore标记命名私有成员单下划线仅必要处使用双下划线 name manglingQt 重写方法保持驼峰如showEvent布局核心逻辑放core/UI 放qt/并遵守 MVC 命名*_view.py/views/layouts/*_layout/views/styles/不触碰qt/mixed/与core/library/json/依赖注入传Library、AppSettings等具体组件不传QtDriver国际化字符串走Translations[...]或Translations.format()占位符与样式标签保持不变文档kebab-case 文件名、Title Case 标题、媒体带 alt 文本且体积受控。【免费下载链接】TagStudioA User-Focused Photo File Management System项目地址: https://gitcode.com/GitHub_Trending/ta/TagStudio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表