如何使用 intersphinx 正确链接到 PyQt5 文档?

Posted

技术标签:

【中文标题】如何使用 intersphinx 正确链接到 PyQt5 文档?【英文标题】:How to properly link to PyQt5 documentation using intersphinx? 【发布时间】:2017-11-03 18:06:23 【问题描述】:

我在尝试使用 intersphinx 链接到 PyQt5 docs 时遇到了一些麻烦。

尝试交叉引用任何QtCore 类(例如QThread)不会像我预期的那样工作。我已经使用python -m sphinx.ext.intersphinx objects.inv 解析了objects.inv 可用的here,这导致了gist 中显示的输出。

不幸的是,在 python 命名空间下没有类,只有几个函数。与PyQt5 相关的所有内容都在sip:class 命名空间中。尝试使用标准 :py:class: 语法在文档中引用它不会链接到任何东西(因为 sphinx 没有看到该引用连接到任何东西),并且使用 :sip:class: 会导致 Unknown interpreted text role "sip:class" 的警告,这是有道理的,因为不是已知的参考代码。

那么,我们如何通过 intersphinx 访问 PyQt 的文档(如果可以的话)?

【问题讨论】:

我认为这是不可能的。看看riverbankcomputing.com/pipermail/pyqt/2013-March/032528.html,他们提到了可以引用的类的有限包含。 好吧,没有什么是不可能的。这是向项目提交 PR 的问题。在***.com/a/45700546/2214933 上查看我对类似问题的回答 【参考方案1】:

编辑: 我用这个解决方案创建 python 包:https://pypi.org/project/sphinx-qt-documentation/

原始答案:

我使用另一种方法来解决这个问题。我创建了自定义 sphinx 插件来翻译动态库存文件以使用 sip 域。它允许选择应该指向哪些文档(请参阅顶部的文档字符串)。它适用于我的项目,但我不确定它是否支持所有情况。

这个扩展需要sphinx.ext.intersphinx扩展在sphinx扩展中配置,PyQt在映射中配置

intersphinx_mapping = ...,
                       "PyQt": ("https://www.riverbankcomputing.com/static/Docs/PyQt5", None)
"""
This module contains sphinx extension supporting for build PartSeg documentation.

this extensio provides one configuration option:

`qt_documentation` with possibe values:

 * PyQt - linking to PyQt documentation on https://www.riverbankcomputing.com/static/Docs/PyQt5/api/ (incomplete)
 * Qt - linking to Qt documentation on "https://doc.qt.io/qt-5/" (default)
 * PySide - linking to PySide documentation on  "https://doc.qt.io/qtforpython/PySide2/"
"""
import re
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
from docutils.nodes import Element, TextElement
from docutils import nodes
from typing import List, Optional, Dict, Any
from sphinx.locale import _

from sphinx.ext.intersphinx import InventoryAdapter

try:
    from qtpy import QT_VERSION
except ImportError:
    QT_VERSION = None

# TODO add response to
#  https://***.com/questions/47102004/how-to-properly-link-to-pyqt5-documentation-using-intersphinx

signal_slot_uri = 
    "Qt": "https://doc.qt.io/qt-5/signalsandslots.html",
    "PySide": "https://doc.qt.io/qtforpython/overviews/signalsandslots.html",
    "PyQt": "https://www.riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html"


signal_name = 
    "Qt": "Signal",
    "PySide": "Signal",
    "PyQt": "pyqtSignal"


slot_name = 
    "Qt": "Slot",
    "PySide": "Slot",
    "PyQt": "pyqtSlot"


signal_pattern = re.compile(r'((\w+\d?\.QtCore\.)|(QtCore\.)|(\.)())?(pyqt)?Signal')
slot_pattern = re.compile(r'((\w+\d?\.QtCore\.)|(QtCore\.)|(\.)())?(pyqt)?Slot')

def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnode: TextElement
                      ) -> Optional[nodes.reference]:
    """Linking to Qt documentation."""
    target: str = node['reftarget']
    inventories = InventoryAdapter(env)
    objtypes = None  # type: Optional[List[str]]
    if node['reftype'] == 'any':
        # we search anything!
        objtypes = ['%s:%s' % (domain.name, objtype)
                    for domain in env.domains.values()
                    for objtype in domain.object_types]
        domain = None
    else:
        domain = node.get('refdomain')
        if not domain:
            # only objects in domains are in the inventory
            return None
        objtypes = env.get_domain(domain).objtypes_for_role(node['reftype'])
        if not objtypes:
            return None
        objtypes = ['%s:%s' % (domain, objtype) for objtype in objtypes]
    if target.startswith("PySide2"):
        head, tail = target.split(".", 1)
        target = "PyQt5." + tail
    obj_type_name = "sip:".format(node.get("reftype"))
    if obj_type_name not in inventories.named_inventory["PyQt"]:
        return None
    target_list = [target, "PyQt5." + target]
    target_list += [name + "." + target for name in inventories.named_inventory["PyQt"]["sip:module"].keys()]
    if signal_pattern.match(target):
        uri = signal_slot_uri[app.config.qt_documentation]
        dispname = signal_name[app.config.qt_documentation]
        version = QT_VERSION
    elif slot_pattern.match(target):
        uri = signal_slot_uri[app.config.qt_documentation]
        dispname = slot_name[app.config.qt_documentation]
        version = QT_VERSION
    else:
        for target_name in target_list:
            if target_name in inventories.main_inventory[obj_type_name]:
                proj, version, uri, dispname = inventories.named_inventory["PyQt"][obj_type_name][target_name]
                print(node)  # print nodes with unresolved references
                break
        else:
            return None
        if app.config.qt_documentation == "Qt":
            html_name = uri.split("/")[-1]
            uri = "https://doc.qt.io/qt-5/" + html_name
        elif app.config.qt_documentation == "PySide":
            html_name = "/".join(target.split(".")[1:]) + ".html"
            uri = "https://doc.qt.io/qtforpython/PySide2/" + html_name

    # remove this line if you would like straight to pyqt documentation
    if version:
        reftitle = _('(in %s v%s)') % (app.config.qt_documentation, version)
    else:
        reftitle = _('(in %s)') % (app.config.qt_documentation,)
    newnode = nodes.reference('', '', internal=False, refuri=uri, reftitle=reftitle)
    if node.get('refexplicit'):
        # use whatever title was given
        newnode.append(contnode)
    else:
        # else use the given display name (used for :ref:)
        newnode.append(contnode.__class__(dispname, dispname))
    return newnode


def setup(app: Sphinx) -> Dict[str, Any]:
    app.connect('missing-reference', missing_reference)
    app.add_config_value('qt_documentation', "Qt", True)
    return 
        'version': "0.9",
        'env_version': 1,
        'parallel_read_safe': True
    

【讨论】:

这作为一个独立的 sphinx 扩展非常棒。您是否考虑过将其与您使用它的项目分开发布? 欢迎您pypi.org/project/sphinx-qt-documentation 太棒了。非常感谢!【参考方案2】:

为了让intersphinx 映射适用于我使用 PyQt5 的项目,我执行了以下操作:

    下载原objects.inv文件 将:sip: 域更改为:py: 重定向大多数 PyQt 对象的 URL 以指向 Qt 网站,这意味着当有人单击我的文档中的 QWidget 时,他们不会被定向到 PyQt-QWidget,而是被定向到 Qt-QWidget 添加别名以便:class:`QWidget`:class:`QtWidgets.QWidget`:class:`PyQt5.QtWidgets.QWidget` 都链接到Qt-QWidget

如果你想在你自己的项目中使用我修改过的objects.inv文件,你可以download它,将它保存到与conf.py文件相同的目录,然后在你的intersphinx_mapping字典中编辑@987654338 @成为

    intersphinx_mapping = 
        # 'PyQt5': ('http://pyqt.sourceforge.net/Docs/PyQt5/', None),
        'PyQt5': ('', 'pyqt5-modified-objects.inv'),
    

如果我的'pyqt5-modified-objects.inv'文件不符合你项目的要求(例如我没有为所有Qt模块添加别名,只有QtWidgetsQtCoreQtCoreQtGui)那么你可以修改@ 987654326@ 自动执行上述步骤 1 - 4。

源代码也可以用来为PyQt4创建修改后的objects.inv文件;但是,PyQt4 的原始 objects.inv 文件不包含所有 Qt 模块和类的完整列表,因此使用 PyQt4 的 intersphinx 映射不是很有用。

注意:SourceForge 团队目前正在解决一些issues,因此执行源代码将引发ConnectionError,直到他们的问题得到解决。

【讨论】:

以上是关于如何使用 intersphinx 正确链接到 PyQt5 文档?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确使用.kv文件和.py? - 从 .kv 调用函数如何构建 .py

如何在 Python 中正确导入模块

我如何确认我正确使用了纱线链接?

如何正确使用测试工具.py与cpp文件?

如何在 discord.py 机器人中正确使用任务/事件循环?

如何在 WordPress header.php 中正确链接到 CSS?