简体   繁体   中英

How to properly link to PyQt5 documentation using intersphinx?

I'm running into some trouble trying to link to the PyQt5 docs using intersphinx .

Trying to cross reference any of the QtCore classes (such as QThread ) does not work as I'd expect. I have parsed the objects.inv available here using python -m sphinx.ext.intersphinx objects.inv , which results in an output shown in thisgist .

Unfortunately, under the python namespace there are no classes and only a few functions. Everything PyQt5 -related is in the sip:class namespace. Trying to reference this in documentation using the standard :py:class: syntax does not link to anything (since sphinx doesn't see that reference connected to anything), and using :sip:class: causes a warning of Unknown interpreted text role "sip:class" , which makes sense because that is not a known reference code.

So, how do we access the documentation of PyQt through intersphinx (if we can at all)?

EDIT: I create python package with this solution: https://pypi.org/project/sphinx-qt-documentation/

ORIGINAL ANSWER:

I use another approach to this problem. I create custom sphinx plugin to translate on fly inventory file to use sip domain. It allows to chose which documentation should be pointed (pleas see docstring on top). It works on my project, but I'm not sure if it support all cases.

This extension need sphinx.ext.intersphinx extension to be configured in sphinx extension and PyQt configure in mapping

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://stackoverflow.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
    }

In order to get intersphinx mapping to work for my project that uses PyQt5 I did the following:

  1. Downloaded the original objects.inv file
  2. Changed the :sip: domain to be :py:
  3. Redirected the URL for most PyQt objects to point to the Qt website, which means that instead of being directed to PyQt-QWidget when someone clicks on a QWidget in my documentation they are directed to Qt-QWidget
  4. Added aliases so that :class:`QWidget` , :class:`QtWidgets.QWidget` and :class:`PyQt5.QtWidgets.QWidget` are all linked to Qt-QWidget

If you would like to use my modified objects.inv file in your own project you can download it, save it to the same directory as your conf.py file and then edit your intersphinx_mapping dictionary in your conf.py to be

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

If my 'pyqt5-modified-objects.inv' file does not meet the requirements for your project (for example, I did not add aliases for all Qt modules, only QtWidgets , QtCore and QtGui ) then you can modify the source code that automatically performs steps 1 - 4 above.

The source code can also be used to create a modified objects.inv file for PyQt4; however, the original objects.inv file for PyQt4 does not contain a complete listing of all Qt modules and classes and therefore using intersphinx mapping with PyQt4 isn't very useful.

Note: The SourceForge team is currently solving some issues and so executing the source code will raise a ConnectionError until their issues are resolved.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM