diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 88913f03..4b968844 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -254,16 +254,16 @@ jobs: submodules: 'recursive' fetch-depth: 0 - - name: Set up Python 3.9 + - name: Set up Python 3.13 uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.13' cache: 'pip' - name: Install Python dependencies run: | python -m pip install --upgrade -r requirements.txt - python -m pip install pillow + python -m pip install --upgrade pillow numpy comtypes pywin32 cairocffi PyMuPDF - name: Setup MSVC uses: ilammy/msvc-dev-cmd@v1 diff --git a/.gitignore b/.gitignore index 14e46c46..e2acb59c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,10 @@ .waf-* .waf3-* .lock-waf* +.DS_Store wingdbstub.py* mydbstub.py* +venv*/ .idea .cache @@ -27,6 +29,7 @@ mydbstub.py* /bin/waf-* /bin/waf3-* /bin/doxygen-* +/bin/libclang* /demo/version.py diff --git a/build.py b/build.py index da09d1b8..de85259f 100755 --- a/build.py +++ b/build.py @@ -21,6 +21,11 @@ import os import re import shutil import subprocess +import requests +from requests.exceptions import HTTPError +import traceback +from io import BytesIO +import bz2 import tarfile import tempfile import datetime @@ -83,11 +88,11 @@ wxICON = 'packaging/docset/mondrian.png' wafCurrentVersion = '2.1.5' wafMD5 = '2e7b2166c030dbac3e21891048df10aa' -doxygenCurrentVersion = '1.8.8' +doxygenCurrentVersion = '1.9.1' doxygenMD5 = { - 'darwin' : '71c590e6cab47100f23919a2696cc7fd', - 'win32' : 'a3dcff227458e423c132f16f57e26510', - 'linux' : '083b3d8f614b538696041c7364e0f334', + 'darwin' : '6912d41cef5971fb07573190849a8a84', + 'win32' : '90439896025dc8ddcd5d767ab2c2c489', + 'linux' : 'ed2c35099165fce0d07d9a1809935928', } # And the location where they can be downloaded from @@ -525,7 +530,6 @@ def deleteIfExists(deldir, verbose=True): shutil.rmtree(deldir) except Exception: if verbose: - import traceback msg("Error: %s" % traceback.format_exc(1)) else: if verbose: @@ -538,6 +542,49 @@ def delFiles(fileList, verbose=True): print("Removing file: %s" % afile) os.remove(afile) +def errorMsg(txt, cmdname, envvar): + msg('ERROR: ' + txt) + msg(' Set %s in the environment to use a local build of %s instead' % (envvar, cmdname)) + +def downloadTool(cmd, cmdname, envvar): + msg('Not found. Attempting to download...') + url = f"{toolsURL}/{os.path.basename(cmd)}.bz2" + + try: + resp = requests.get(url) + resp.raise_for_status() + except HTTPError: + # The files could be packed as a tarball + url = url.replace(".bz2", ".tar.bz2") + try: + resp = requests.get(url) + resp.raise_for_status() + except HTTPError: + errorMsg('Unable to download ' + url, cmdname, envvar) + traceback.print_exc() + sys.exit(1) + + msg('Connection successful...') + data = resp.content + msg('Data downloaded...') + + if ".tar." in url: + with tarfile.open('r:bz2', fileobj=BytesIO(data)) as tar: + # Extraction filters are only available since Python 3.12 + tar.extraction_filter = getattr(tarfile, 'data_filter', + (lambda member, path: member)) + filelist = [] + for file in tar.getmembers(): + if file.name.startswith("doxygen") or \ + file.name.startswith("libclang"): + filelist.append(file) + + tar.extractall(members=filelist, path=os.path.dirname(cmd)) + else: + data = bz2.decompress(data) + Path(cmd).write_bytes(data) + + os.chmod(cmd, 0o755) def getTool(cmdName, version, MD5, envVar, platformBinary, linuxBits=False): # Check in the bin dir for the specified version of the tool command. If @@ -546,88 +593,67 @@ def getTool(cmdName, version, MD5, envVar, platformBinary, linuxBits=False): if os.environ.get(envVar): # Setting a value in the environment overrides other options return os.environ.get(envVar) + + # setup + if platformBinary: + platform = getToolsPlatformName(linuxBits) + ext = '' + if platform == 'win32': + ext = '.exe' + cmd = opj(phoenixDir(), 'bin', '%s-%s-%s%s' % (cmdName, version, platform, ext)) + md5 = MD5[platform] else: - # setup - if platformBinary: - platform = getToolsPlatformName(linuxBits) - ext = '' - if platform == 'win32': - ext = '.exe' - cmd = opj(phoenixDir(), 'bin', '%s-%s-%s%s' % (cmdName, version, platform, ext)) - md5 = MD5[platform] - else: - cmd = opj(phoenixDir(), 'bin', '%s-%s' % (cmdName, version)) - md5 = MD5 + cmd = opj(phoenixDir(), 'bin', '%s-%s' % (cmdName, version)) + md5 = MD5 + msg('Checking for %s...' % cmd) + if os.path.exists(cmd): + # if the file exists run some verification checks on it - def _error_msg(txt): - msg('ERROR: ' + txt) - msg(' Set %s in the environment to use a local build of %s instead' % (envVar, cmdName)) - - - msg('Checking for %s...' % cmd) - if os.path.exists(cmd): - # if the file exists run some verification checks on it - - # first make sure it is a normal file - if not os.path.isfile(cmd): - _error_msg('%s exists but is not a regular file.' % cmd) - sys.exit(1) - - # now check the MD5 if not in dev mode and it's set to None - if not (devMode and md5 is None): - m = hashlib.md5() - m.update(Path(cmd).read_bytes()) - if m.hexdigest() != md5: - _error_msg('MD5 mismatch, got "%s"\n ' - 'expected "%s"' % (m.hexdigest(), md5)) - sys.exit(1) - - # If the cmd is a script run by some interpreter, or similar, - # then we don't need to check anything else - if not platformBinary: - return cmd - - # Ensure that commands that are platform binaries are executable - if not os.access(cmd, os.R_OK | os.X_OK): - _error_msg('Cannot execute %s due to permissions error' % cmd) - sys.exit(1) - - try: - p = subprocess.Popen([cmd, '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=os.environ) - p.communicate() - except OSError as e: - _error_msg('Could not execute %s, got "%s"' % (cmd, e)) - sys.exit(1) - - # if we get this far then all is well, the cmd is good to go - return cmd - - - msg('Not found. Attempting to download...') - url = '%s/%s.bz2' % (toolsURL, os.path.basename(cmd)) - try: - import requests - resp = requests.get(url) - resp.raise_for_status() - msg('Connection successful...') - data = resp.content - msg('Data downloaded...') - except Exception: - _error_msg('Unable to download ' + url) - import traceback - traceback.print_exc() + # first make sure it is a normal file + if not os.path.isfile(cmd): + errorMsg('%s exists but is not a regular file.' % cmd, + cmdName, envVar) sys.exit(1) - import bz2 - data = bz2.decompress(data) - Path(cmd).write_bytes(data) - os.chmod(cmd, 0o755) + # now check the MD5 if not in dev mode and it's set to None + if not (devMode and md5 is None): + m = hashlib.md5() + m.update(Path(cmd).read_bytes()) + if m.hexdigest() != md5: + errorMsg('MD5 mismatch, got "%s"\n ' + 'expected "%s"' % (m.hexdigest(), md5), + cmdName, envVar) + sys.exit(1) - # Recursive call so the MD5 value will be double-checked on what was - # just downloaded - return getTool(cmdName, version, MD5, envVar, platformBinary, linuxBits) + # If the cmd is a script run by some interpreter, or similar, + # then we don't need to check anything else + if not platformBinary: + return cmd + # Ensure that commands that are platform binaries are executable + if not os.access(cmd, os.R_OK | os.X_OK): + errorMsg('Cannot execute %s due to permissions error' % cmd, + cmdName, envVar) + sys.exit(1) + + try: + p = subprocess.Popen([cmd, '--help'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE, env=os.environ) + p.communicate() + except OSError as e: + errorMsg('Could not execute %s, got "%s"' % (cmd, e), + cmdName, envVar) + sys.exit(1) + + # if we get this far then all is well, the cmd is good to go + return cmd + + downloadTool(cmd, cmdName, envVar) + + # Recursive call so the MD5 value will be double-checked on what was + # just downloaded + return getTool(cmdName, version, MD5, envVar, platformBinary, linuxBits) # The download and MD5 check only needs to happen once per run, cache the sip @@ -658,14 +684,12 @@ def getMSWebView2(): msg('Downloading microsoft.web.webview2 {}...'.format(MS_edge_version)) try: - import requests resp = requests.get(MS_edge_url) resp.raise_for_status() msg('Connection successful...') data = resp.content msg('Data downloaded...') except Exception: - import traceback traceback.print_exc() sys.exit(1) @@ -1018,8 +1042,8 @@ def cmd_docset_py(options, args): sys.exit(1) # clear out any old docset build - name = 'wxPython-{}'.format(cfg.VERSION) - docset = posixjoin('dist', '{}.docset'.format(name)) + name = 'wxPython-{}.docset'.format(cfg.VERSION) + docset = posixjoin('dist', name) if os.path.isdir(docset): shutil.rmtree(docset) @@ -1036,7 +1060,7 @@ def cmd_docset_py(options, args): # Remove the sidebar from the pages in the docset msg('Removing sidebar from docset pages...') - _removeSidebar(opj('dist', name+'.docset', 'Contents', 'Resources', 'Documents')) + _removeSidebar(opj('dist', name, 'Contents', 'Resources', 'Documents')) # build the tarball msg('Archiving Phoenix docset...') @@ -1045,7 +1069,7 @@ def cmd_docset_py(options, args): if os.path.exists(tarfilename): os.remove(tarfilename) with tarfile.open(name=tarfilename, mode="w:gz") as tarball: - tarball.add(opj('dist', name+'.docset'), name+'.docset', filter=_setTarItemPerms) + tarball.add(opj('dist', name), name, filter=_setTarItemPerms) if options.upload: uploadPackage(tarfilename, options, keep=5, @@ -1121,6 +1145,7 @@ def cmd_etg(options, args): def cmd_sphinx(options, args): from sphinxtools.postprocess import genIndexes, makeHeadings, postProcess, genGallery + from sphinxtools.stc_doc_postprocess import stc_categorise_methods cmdTimer = CommandTimer('sphinx') pwd = pushDir(phoenixDir()) @@ -1134,6 +1159,9 @@ def cmd_sphinx(options, args): if not textFiles: raise Exception('No documentation files found. Please run "build.py touch etg" first') + # Sort all wx.stc.StyledTextCtrl methods into categories + stc_categorise_methods() + # Copy the rst files into txt files restDir = os.path.join(sphinxDir, 'rest_substitutions', 'overviews') rstFiles = sorted(glob.glob(restDir + '/*.rst')) @@ -1156,7 +1184,10 @@ def cmd_sphinx(options, args): pwd2 = pushDir(sphinxDir) buildDir = os.path.join(sphinxDir, 'build') htmlDir = os.path.join(phoenixDir(), 'docs', 'html') - runcmd('{} -m sphinx -b html -d {}/doctrees . {}'.format(PYTHON, buildDir, htmlDir)) + sphinx_log = os.path.join(htmlDir, 'warnings', 'sphinx_warnings.log') + + runcmd('"{}" -m sphinx --builder html --color --warning-file {} \ + --doctree-dir {}/doctrees . {}'.format(PYTHON, sphinx_log, buildDir, htmlDir)) del pwd2 msg('Postprocessing sphinx output...') @@ -1566,7 +1597,6 @@ def cmd_build_wx(options, args): except Exception: print("ERROR: failed building wxWidgets") - import traceback traceback.print_exc() sys.exit(1) diff --git a/docs/sphinx/_static/css/phoenix.css b/docs/sphinx/_static/css/phoenix.css index 152b4014..da43c268 100644 --- a/docs/sphinx/_static/css/phoenix.css +++ b/docs/sphinx/_static/css/phoenix.css @@ -107,13 +107,18 @@ div.headerimage-noshow { } div.document { - background-color: rgb(230,230,230); - position: relative; - margin-left: 240px; + background-color: rgb(230,230,230); + position: relative; + margin-left: 240px; + z-index: 500; } div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; + min-height: 98%; + display: flex; + flex-direction: column; + justify-content: space-between; } div.document-no-sidebar { @@ -136,17 +141,17 @@ div.sphinxsidebar { left: 0; top: 30px; bottom: 0; + z-index: 600; } -div#searchbox { - margin-top: 30px; +#searchbox { + margin-top: 1.8em; + flex-grow: 1; } -div#sourcelink { - position: absolute; - bottom: 0; - font-style: italic; - font-size: small; +#sourcelink { + margin-top: 1.8em; + flex-grow: 0; } div.related { @@ -156,9 +161,9 @@ div.related { font-size: 90%; line-height: normal; height: auto; - left: 0; - right: 0; + position: sticky; top: 0; + z-index: 999; } div.related ul { @@ -255,6 +260,36 @@ div.footer { color: rgb(0,102,204); } +.control-appearance-figures { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: end; + column-gap: 1.0em; + justify-content: space-around; +} + +.appearance-figure { + margin: 0; + padding: 0; + max-width: 500px; + flex-shrink: 1; +} + +.appearance-figure img { + max-height: 320px; +} + +figcaption { + text-align: center; + text-align-last: center; +} + +figcaption a.headerlink { + margin: 0; + padding: 0; +} + .floatleft { position: relative; float:left; @@ -551,9 +586,10 @@ cite, code, tt { letter-spacing: 0.01em; } -code.descname, code.descclassname { +code.descname, code.descclassname, span.sig-name, span.sig-prename { font-weight: bold; } + a.headerlink { color: #c60f0f; font-size: 0.8em; @@ -755,6 +791,14 @@ table.contentstable p.mybiglink { line-height: 150%; } +hr { + color: #ccc; + background-color: #ccc; + height: 1px; + border: none; + margin-bottom: 0.7rem; +} + hr.overloadsep { color: #0000FF; background-color: #0000FF; @@ -762,4 +806,4 @@ hr.overloadsep { border: none; width: 50%; float: left; -} \ No newline at end of file +} diff --git a/docs/sphinx/_templates/searchbox.html b/docs/sphinx/_templates/searchbox.html index e9c86019..30459536 100644 --- a/docs/sphinx/_templates/searchbox.html +++ b/docs/sphinx/_templates/searchbox.html @@ -8,14 +8,14 @@ :license: BSD, see LICENSE for details. #} {%- if pagename != "search" and builder != "singlehtml" %} - + + {%- endif %} diff --git a/docs/sphinx/_templates/sourcelink.html b/docs/sphinx/_templates/sourcelink.html index 2692973e..25ce54a9 100644 --- a/docs/sphinx/_templates/sourcelink.html +++ b/docs/sphinx/_templates/sourcelink.html @@ -8,8 +8,10 @@ :license: BSD, see LICENSE for details. #} {%- if show_source and has_source and sourcename %} - + {%- endif %} diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py index 8184a084..2158acc8 100644 --- a/docs/sphinx/conf.py +++ b/docs/sphinx/conf.py @@ -14,6 +14,7 @@ import sys, os from datetime import datetime +from buildtools.config import Config # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -25,7 +26,8 @@ sys.path.append(os.path.abspath('..')) # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.todo', +extensions = ["sphinxcontrib.jquery", + 'sphinx.ext.todo', 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.coverage', @@ -36,7 +38,7 @@ extensions = ['sphinx.ext.todo', templates_path = ['_templates'] # The suffix(es) of source filenames. -source_suffix = ['.txt'] +source_suffix = {'.txt': 'restructuredtext'} todo_include_todos = True todo_all_todos = True @@ -49,6 +51,7 @@ availability_all_availabilities = True # The master toctree document. master_doc = 'index' +toc_object_entries = False # General information about the project. project = u'wxPython Phoenix' @@ -58,7 +61,6 @@ copyright = u'2012-{}, The wxPython Team'.format(datetime.now().year) # |version| and |release|, also used in various other places throughout the # built documents. -from buildtools.config import Config cfg = Config(noWxConfig=True) # The short X.Y version. @@ -66,7 +68,6 @@ version = '%s.%s' % (cfg.VER_MAJOR, cfg.VER_MINOR) # The full version, including alpha/beta/rc tags. release = cfg.VERSION - # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None @@ -101,11 +102,10 @@ show_authors = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +keep_warnings = False +show_warning_types = True +supress_warnings = [] # -- Options for HTML output --------------------------------------------------- @@ -165,10 +165,12 @@ html_sidebars = { # Additional templates that should be rendered to pages, maps page names to # template names. -html_additional_pages = {'gallery': 'gallery.html', 'main': 'main.html'} +html_additional_pages = {'gallery': 'gallery.html', + 'main': 'main.html'} -# If false, no module index is generated. -html_use_modindex = True +html_domain_indices = {'py-modindex'} +# A list of ignored prefixes for module index sorting. +modindex_common_prefix = ['wx.', 'wx.lib.', 'wx.py.'] # If false, no index is generated. html_use_index = True @@ -280,3 +282,4 @@ pdf_verbosity = 2 # Enable experimental feature to split table cells. Use it # if you get "DelayedTable too big" errors pdf_splittables = True + diff --git a/docs/sphinx/phoenix_theme/theme.conf b/docs/sphinx/phoenix_theme/theme.conf deleted file mode 100644 index 6ae2b098..00000000 --- a/docs/sphinx/phoenix_theme/theme.conf +++ /dev/null @@ -1,38 +0,0 @@ -[theme] - -inherit = basic -stylesheet = phoenix.css -pygments_style = sphinx - -[options] - -disqus_comments = false -google_analytics = false - -rightsidebar = false -collapsiblesidebar = true -headerheight = 30px - -externalrefs = false - -footerbgcolor = #11303d -footertextcolor = #ffffff -sidebarbgcolor = #1c4e63 -sidebarbtncolor = #3c6e83 -sidebartextcolor = #ffffff -sidebarlinkcolor = #98dbcc -relbarbgcolor = #133f52 -relbartextcolor = #ffffff -relbarlinkcolor = #ffffff -bgcolor = #ffffff -textcolor = #000000 -headbgcolor = #f2f2f2 -headtextcolor = #20435c -headlinkcolor = #c60f0f -linkcolor = #355f7c -visitedlinkcolor = #355f7c -codebgcolor = #eeffcc -codetextcolor = #333333 - -bodyfont = sans-serif -headfont = 'Trebuchet MS', Verdana, Arial, Helvetica, sans-serif \ No newline at end of file diff --git a/docs/sphinx/phoenix_theme/theme.toml b/docs/sphinx/phoenix_theme/theme.toml new file mode 100644 index 00000000..71ee4a4a --- /dev/null +++ b/docs/sphinx/phoenix_theme/theme.toml @@ -0,0 +1,35 @@ +[theme] +inherit = "basic" +stylesheets = [ + "phoenix.css", +] +pygments_style = { default = "sphinx" } + +[options] +disqus_comments = "false" +google_analytics = "false" +rightsidebar = "false" +collapsiblesidebar = "true" +headerheight = "30px" +externalrefs = "false" +footerbgcolor = "#11303d" +footertextcolor = "#ffffff" +sidebarbgcolor = "#1c4e63" +sidebarbtncolor = "#3c6e83" +sidebartextcolor = "#ffffff" +sidebarlinkcolor = "#98dbcc" +relbarbgcolor = "#133f52" +relbartextcolor = "#ffffff" +relbarlinkcolor = "#ffffff" +bgcolor = "#ffffff" +textcolor = "#000000" +headbgcolor = "#f2f2f2" +headtextcolor = "#20435c" +headlinkcolor = "#c60f0f" +linkcolor = "#355f7c" +visitedlinkcolor = "#355f7c" +codebgcolor = "#eeffcc" +codetextcolor = "#333333" +bodyfont = "sans-serif" +headfont = "'Trebuchet MS', Verdana, Arial, Helvetica, sans-serif" + diff --git a/docs/sphinx/rest_substitutions/overviews/high_dpi_overview.rst b/docs/sphinx/rest_substitutions/overviews/high_dpi_overview.rst index 210a0bb7..4dc0f451 100644 --- a/docs/sphinx/rest_substitutions/overviews/high_dpi_overview.rst +++ b/docs/sphinx/rest_substitutions/overviews/high_dpi_overview.rst @@ -1,7 +1,7 @@ .. include:: headings.inc -.. _html overview: +.. _high dpi overview: ====================================== |phoenix_title| **High DPI Overview** diff --git a/docs/sphinx/rest_substitutions/overviews/index.rst b/docs/sphinx/rest_substitutions/overviews/index.rst index 190e616f..5439305b 100644 --- a/docs/sphinx/rest_substitutions/overviews/index.rst +++ b/docs/sphinx/rest_substitutions/overviews/index.rst @@ -23,7 +23,7 @@ and launch the wxPython demo for you. .. note:: If you wish to help in the documentation effort, the main docstrings guidelines are outlined in the - `Docstring Guidelines `_ + `Docstring Guidelines `_ document. diff --git a/etgtools/sphinx_generator.py b/etgtools/sphinx_generator.py index 27019da6..b41768b3 100644 --- a/etgtools/sphinx_generator.py +++ b/etgtools/sphinx_generator.py @@ -1163,7 +1163,9 @@ class Image(Node): rel_path = os.path.normpath(static_path[rel_path_index:]) docstrings = '\n\n' - docstrings += '.. figure:: %s\n' % rel_path + # Sphinx (on windows) can't parse windows style paths when reading + # .rst files. Therefore paths are written unix style. + docstrings += '.. figure:: %s\n' % rel_path.replace('\\', '/') docstrings += ' :align: center\n\n\n' docstrings += '|\n\n' @@ -1592,7 +1594,7 @@ class XRef(Node): text = '' elif not isNumeric(text): - text = '``%s``'%text + text = '``%s``' % text.strip() elif 'funcmacro' in values: if '(' in stripped: @@ -2893,7 +2895,7 @@ class XMLDocString(object): pickleItem(desc, self.current_module, self.class_name, 'class') if self.overloads: - docstrings += '\n\n%s|overload| Overloaded Implementations:\n\n'%spacer + docstrings += '\n\n%s|overload| **Overloaded Implementations:**\n\n'%spacer docstrings += '%s:html:`

`\n\n'%spacer for index, over in enumerate(self.overloads): diff --git a/requirements/devel.txt b/requirements/devel.txt index 8164e828..d341a4b3 100644 --- a/requirements/devel.txt +++ b/requirements/devel.txt @@ -12,17 +12,8 @@ pytest-xdist pytest-forked pytest-timeout -sphinx==2.2.0 ; python_version >= '3.0' -sphinx==1.8.5 ; python_version < '3.0' -alabaster<0.7.14 -sphinxcontrib-applehelp<1.0.8 -sphinxcontrib-devhelp<1.0.6 -sphinxcontrib-htmlhelp<2.0.5 -sphinxcontrib-jsmath<1.0.2 -sphinxcontrib-qthelp<1.0.7 -sphinxcontrib-serializinghtml<1.1.10 -Jinja2==2.10 -markupsafe==1.1.1 -doc2dash==2.3.0 +sphinx +sphinxcontrib-jquery +doc2dash beautifulsoup4 typing-extensions; python_version < '3.11' diff --git a/sphinxtools/constants.py b/sphinxtools/constants.py index 7ee45bfe..67bb8fc8 100644 --- a/sphinxtools/constants.py +++ b/sphinxtools/constants.py @@ -169,15 +169,17 @@ RE_KEEP_SPACES = re.compile(r'(\s+)') # A list of things used in the post-processing of the HTML files generated by Sphinx # This list is used only to insert a HTML horizontal line (
) after each method/function # description -HTML_REPLACE = ['module', 'function', 'method', 'class', 'classmethod', 'staticmethod', 'attribute'] +HTML_REPLACE = ['module', 'function', 'py function', 'class', 'py class', + 'method', 'py method', 'classmethod', 'staticmethod', + 'attribute', 'py attribute'] # Today's date representation for the Sphinx HTML docs TODAY = datetime.date.today().strftime('%d %B %Y') # Inheritance diagram external hyperlinks -PYTHON_DOCS = 'http://docs.python.org/library/' -NUMPY_DOCS = 'http://docs.scipy.org/doc/numpy/reference/generated/' +PYTHON_DOCS = 'https://docs.python.org/3/library/' +NUMPY_DOCS = 'https://numpy.org/doc/stable/reference/generated/' EXTERN_INHERITANCE = {'UserDict.' : PYTHON_DOCS, 'ctypes.' : PYTHON_DOCS, diff --git a/sphinxtools/inheritance.py b/sphinxtools/inheritance.py index 18704ed3..f2fb37b1 100644 --- a/sphinxtools/inheritance.py +++ b/sphinxtools/inheritance.py @@ -10,22 +10,19 @@ #--------------------------------------------------------------------------- # Standard library imports - import os import sys import errno from subprocess import Popen, PIPE # Phoenix-specific imports - -from .utilities import wx2Sphinx, formatExternalLink +from .utilities import formatExternalLink from .constants import INHERITANCEROOT ENOENT = getattr(errno, 'ENOENT', 0) EPIPE = getattr(errno, 'EPIPE', 0) - class InheritanceDiagram: """ Given a list of classes, determines the set of classes that they inherit @@ -108,8 +105,7 @@ class InheritanceDiagram: 'shape': 'box', 'fontsize': 10, 'height': 0.3, - 'fontname': '"Vera Sans, DejaVu Sans, Liberation Sans, ' - 'Arial, Helvetica, sans"', + 'fontname': '"Liberation Sans, Arial, sans-serif"', 'style': '"setlinewidth(0.5)"', } default_edge_attrs = { @@ -123,7 +119,8 @@ class InheritanceDiagram: def _format_graph_attrs(self, attrs): return ''.join(['%s=%s;\n' % x for x in list(attrs.items())]) - def generate_dot(self, class_summary, name="dummy", graph_attrs={}, node_attrs={}, edge_attrs={}): + def generate_dot(self, class_summary, name="dummy", + graph_attrs={}, node_attrs={}, edge_attrs={}): """Generate a graphviz dot graph from the classes that were passed in to __init__. @@ -133,18 +130,22 @@ class InheritanceDiagram: key/value pairs to pass on as graphviz properties. """ - inheritance_graph_attrs = dict(fontsize=9, ratio='auto', size='""', rankdir="TB") - inheritance_node_attrs = {"align": "center", 'shape': 'box', - 'fontsize': 10, 'height': 0.3, - 'fontname': '"Vera Sans, DejaVu Sans, Liberation Sans, ' - 'Arial, Helvetica, sans"', 'style': '"setlinewidth(0.5)"', - 'labelloc': 'c', 'fontcolor': 'grey45'} + inheritance_graph_attrs = {"fontsize": 9, "ratio": 'auto', + "size": '""', "rankdir": "TB"} - inheritance_edge_attrs = {'arrowsize': 0.5, - 'style': '"setlinewidth(0.5)"', - 'color': '"#23238E"', + inheritance_node_attrs = {"align": "center", 'shape': 'box', + 'fontsize': 12, 'height': 0.3, + 'margin': '"0.15, 0.05"', + 'fontname': '"Liberation Sans, Arial, sans-serif"', + 'style': '"setlinewidth(0.8), rounded"', + 'labelloc': 'c', 'fontcolor': 'grey45', + "color": "dodgerblue4"} + + inheritance_edge_attrs = {'arrowsize': 0.6, + 'style': '"setlinewidth(0.8)"', + 'color': 'dodgerblue4', 'dir': 'back', - 'arrowtail': 'open', + 'arrowtail': 'normal', } g_attrs = self.default_graph_attrs.copy() @@ -163,9 +164,9 @@ class InheritanceDiagram: this_node_attrs = n_attrs.copy() if fullname in self.specials: - this_node_attrs['fontcolor'] = 'black' - this_node_attrs['color'] = 'blue' - this_node_attrs['style'] = 'bold' + this_node_attrs['fontcolor'] = 'dodgerblue4' + this_node_attrs['color'] = 'dodgerblue2' + this_node_attrs['style'] = '"bold, rounded"' if class_summary is None: # Phoenix base classes, assume there is always a link @@ -185,7 +186,7 @@ class InheritanceDiagram: for base_name in bases: this_edge_attrs = e_attrs.copy() if fullname in self.specials: - this_edge_attrs['color'] = 'red' + this_edge_attrs['color'] = 'darkorange1' res.append(' "%s" -> "%s" [%s];\n' % (base_name, fullname, @@ -198,7 +199,7 @@ class InheritanceDiagram: def makeInheritanceDiagram(self, class_summary=None): """ - Actually generates the inheritance diagram as a PNG file plus the corresponding + Actually generates the inheritance diagram as a SVG file plus the corresponding MAP file for mouse navigation over the inheritance boxes. These two files are saved into the ``INHERITANCEROOT`` folder (see `sphinxtools/constants.py` @@ -209,7 +210,7 @@ class InheritanceDiagram: :rtype: `tuple` - :returns: a tuple containing the PNG file name and a string representing the content + :returns: a tuple containing the SVG file name and a string representing the content of the MAP file (with newlines stripped away). .. note:: The MAP file is deleted as soon as its content has been read. @@ -224,13 +225,13 @@ class InheritanceDiagram: else: filename = self.specials[0] - outfn = os.path.join(static_root, filename + '_inheritance.png') + outfn = os.path.join(static_root, filename + '_inheritance.svg') mapfile = outfn + '.map' if os.path.isfile(outfn) and os.path.isfile(mapfile): - with open(mapfile, 'rt') as fid: - map = fid.read() - return os.path.split(outfn)[1], map.replace('\n', ' ') + with open(mapfile, 'rt', encoding="utf-8") as fid: + mp = fid.read() + return os.path.split(outfn)[1], mp.replace('\n', ' ') code = self.generate_dot(class_summary) @@ -245,7 +246,7 @@ class InheritanceDiagram: if os.path.isfile(mapfile): os.remove(mapfile) - dot_args.extend(['-Tpng', '-o' + outfn]) + dot_args.extend(['-Tsvg', '-o' + outfn]) dot_args.extend(['-Tcmapx', '-o' + mapfile]) popen_args = { @@ -283,7 +284,7 @@ class InheritanceDiagram: if p.returncode != 0: print(('\nERROR: Graphviz `dot` command exited with error:\n[stderr]\n%s\n[stdout]\n%s\n\n' % (stderr, stdout))) - with open(mapfile, 'rt') as fid: - map = fid.read() + with open(mapfile, 'rt', encoding="utf-8") as fid: + mp = fid.read() - return os.path.split(outfn)[1], map.replace('\n', ' ') + return os.path.split(outfn)[1], mp.replace('\n', ' ') diff --git a/sphinxtools/modulehunter.py b/sphinxtools/modulehunter.py index 7626614a..4a6acfaa 100644 --- a/sphinxtools/modulehunter.py +++ b/sphinxtools/modulehunter.py @@ -13,7 +13,7 @@ import pkgutil from buildtools.config import phoenixDir -from inspect import getargspec, ismodule, getdoc, getmodule, getcomments, isfunction +from inspect import getfullargspec, ismodule, getdoc, getmodule, getcomments, isfunction from inspect import ismethoddescriptor, getsource, ismemberdescriptor, isgetsetdescriptor from inspect import isbuiltin, isclass, getfile, ismethod @@ -117,23 +117,21 @@ def analyze_params(obj, signature): return signature, param_tuple try: - arginfo = getargspec(obj) - # TODO: Switch to getfullargspec + arginfo = getfullargspec(obj) except (TypeError, ValueError): arginfo = None pevals = {} if arginfo: - args = arginfo[0] - argsvar = arginfo[1] + args = arginfo.args - if arginfo[3]: + if arginfo.defaults: - dl = len(arginfo[3]) + dl = len(arginfo.defaults) al = len(args) defargs = args[al-dl:al] - info = arginfo[3] + info = arginfo.defaults for d, i in zip(defargs, info): pevals[d] = i diff --git a/sphinxtools/postprocess.py b/sphinxtools/postprocess.py index 0c66051d..e61ac73f 100644 --- a/sphinxtools/postprocess.py +++ b/sphinxtools/postprocess.py @@ -339,7 +339,7 @@ def removeUnreferenced(input, class_summary, enum_base, unreferenced_classes, te def addSpacesToLinks(text): - regex = re.findall('\w:ref:`(.*?)`', text) + regex = re.findall(r'\w:ref:`(.*?)`', text) for reg in regex: text = text.replace(':ref:`%s`'%reg, ' :ref:`%s`'%reg) @@ -657,22 +657,24 @@ def postProcess(folder, options): for indx, enum in enumerate(enum_base): html_file = os.path.split(enum_files[indx])[1] base = enum.split('.')[-1] - new = '(%s)'%(html_file, base, base) - enum_dict['(%s)'%enum] = new + new = '(%s)' % (html_file, base, base) + enum_dict['(%s)' % enum] = new for filename in fileNames: if "genindex" in filename or "modindex" in filename: continue - methods_done = properties_done = False + methods_done = attr_done = fn_done = False with textfile_open(filename, "rt") as fid: orig_text = text = fid.read() - text = text.replace('Overloaded Implementations:', 'Overloaded Implementations:') for item in HTML_REPLACE: - if item != 'class': - text = text.replace('
'%item, '\n

\n
'%item) + if item in ('class', 'py class'): + continue + + text = text.replace('
' % item, + '

' % item) newtext = '' splitted_text = text.splitlines() @@ -684,19 +686,32 @@ def postProcess(folder, options): line = line.replace('–

', '– ') line = line.replace('

', '') - if line.strip() == '

' or line.strip() == '


': - next_line = splitted_text[index+1] - stripline = next_line.strip() + if not fn_done and \ + line.strip() in ('

', + '

'): - # replace the
with a new headline for the first method or first property - if (stripline == '
' or stripline == '
' \ - or stripline == '
') and not methods_done: - line = '\n

Methods

\n' - methods_done = True + line = line[8:] + fn_done = True - elif stripline == '
' and not properties_done: - line = '\n

Properties

\n' - properties_done = True + if not methods_done and \ + line.strip() in ('

', + '

', + '

', + '

'): + + line = '\n
\n

Methods

\n' + line[8:] + methods_done = True + + if not attr_done and \ + line.strip() in ('

', + '

'): + + line = '\n
\n

Properties

\n' + line[8:] + attr_done = True newtext += line + '\n' diff --git a/sphinxtools/stc_doc_postprocess.py b/sphinxtools/stc_doc_postprocess.py new file mode 100644 index 00000000..7abfd342 --- /dev/null +++ b/sphinxtools/stc_doc_postprocess.py @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- +#--------------------------------------------------------------------------- +# Name: sphinxtools/stc_doc_postprocess.py +# Author: Matthias (neofelis2X) +# +# Created: 27-Jan-2025 +# Copyright: (c) 2010-2025 +# License: wxWindows License +#--------------------------------------------------------------------------- +""" +This script postprocesses the ReST documentation file of the +wx.stc.StyledTextCtrl class. This class has more than 760 methods, +so the 'Method Summary' is sorted into categories. +""" + +import xml.etree.ElementTree as ET +from pathlib import Path + +from buildtools.config import msg +from sphinxtools.constants import XMLSRC, SPHINXROOT + +STC_CLASS_XML = Path(XMLSRC, "classwx_styled_text_ctrl.xml") +STC_RST = Path(SPHINXROOT, "wx.stc.StyledTextCtrl.txt") +TBL_SEP = "======================================================" + \ + "========================== ===========================" + \ + "=====================================================\n" + +def _parse_method_categories(xml_file): + """ + Parses values from an xml file containing categories, method + names and occasional additional category descriptions. + """ + + tree = ET.parse(xml_file) + root = tree.getroot() + + method_mapping = {} + current_category = "empty" + current_pretty = "Empty" + description = '' + new_header = False + + for elem in root.iter(): + if elem.tag == 'listofallmembers': + break + + if elem.tag == "header" and isinstance(elem.text, str): + if "Raw variants" in elem.text: + current_category = "Raw Variants" + current_pretty = "Raw Variants" + else: + current_category = elem.text + current_pretty = _clean_category_title(current_category) + + description = '' + new_header = True + + elif elem.tag == "description" and new_header: + para = elem.findall("para") + description = _parse_description(para) + + new_header = False + + elif elem.tag == "name": + method_mapping[elem.text] = (current_pretty, description) + + return method_mapping + +def _parse_description(para): + """ + Goes through some extra steps to parse method descriptions from references. + """ + description = '' + + if len(para) > 1: + txt = para[1].itertext() + sect = para[1].findall("simplesect") + + if len(sect): + description = "".join(txt).split('(', maxsplit=1)[0].strip() + description = f".. seealso:: :meth:`~wx.stc.StyledTextCtrl.{description}`" + else: + description = "".join(txt).strip() + + return description + +def _parse_stcdoc_segments(file): + """ + Read the reStructuredText file and split relevant parts into textblocks. + """ + + m_count = 0 # Count the collected methods + pretext = '' # All text before the 'Method Summary' + parse_pretext = True + links = [] # + index_links_done = False + methods = {} + current_method = '' + parse_methods = False + posttext = '' # All text after the 'Method Summary' + parse_posttext = False + + with open(file, 'r', encoding="utf-8") as f: + for line in f: + + if parse_posttext: + posttext += line + + elif not index_links_done and line.startswith("- `"): + # Rewrite index links + new_link = line.split('<')[0].strip("-_ `") + new_link = _clean_category_title(new_link) + pretext += f"- `{new_link}`_\n" + links.append(new_link) + + if "Constructors" in line: + awm = "Additional wxPython Methods" + pretext += f"- `{awm}`_\n" + links.append(awm) + + elif "Text area methods" in line: + index_links_done = True + + elif line.startswith(":meth:`~wx.stc.StyledTextCtrl."): + # Collect all methods from 'Method Summary' + m_count += 1 + parse_pretext = False + parse_methods = True + + current_method = line[30:].split('`')[0].strip() + + if not current_method: + print("stc_doc_postprocess:: WARNING: Invalid method name") + else: + methods[current_method] = line + + elif parse_methods and line.strip() == '|': + parse_methods = False + parse_posttext = True + posttext = '\n' + line + + elif parse_pretext: + if not TBL_SEP.strip() in line: + pretext += line + + # print(f"Debug: Read {m_count} methods from file.") + return (pretext, methods, posttext, links) + +def _methods_to_categories(methods, mapping): + """ + Find the right category for each method. Around 20 methods are + unique in wxPython and will be put into their own group. + """ + grouped_methods = {} + + for name, text in methods.items(): + + if name in ("__init__", "Create"): + category = "Constructors and Related Methods" + description = '' + text = text.replace("Ctor.", '') + + elif name in mapping: + category = mapping[name][0] + description = mapping[name][1] + + else: + category = "Additional wxPython Methods" + description = "In addition to the standard Scintilla " + \ + "functions, wxPython includes the following " + \ + "methods to better integrate better with other features." + + if category in grouped_methods: + grouped_methods[category][0].append(text) + grouped_methods[category][1] = description + else: + grouped_methods[category] = [[text, ], ''] + + if description: + grouped_methods[category][1] = description + + return grouped_methods + +def _clean_category_title(raw_title): + """ + Applies proper title case to category titles. + """ + category_title = raw_title.strip().title().replace("And", "and") + category_title = category_title.replace("Wxstyledtextctrl", "wxStyledTextCtrl") + category_title = category_title.replace("Wxpython", "wxPython") + + return category_title + +def _assemble_method_category(category, cat_methods, cc): + """ + Assembles all method ReST directives that go into one category. + """ + group = '' + + dashline = '-' * len(category) + group = f"\n{category}\n{dashline}\n\n" + + if cat_methods[1]: + group += '\n' + cat_methods[1] + '\n\n' + + group += TBL_SEP + + for method in cat_methods[0]: + cc += 1 + group += method + + group += TBL_SEP + '\n' + + return group, cc + +def _output_reordered_doc(pretext, posttext, grouped_methods): + """ + Writes the formatted ReST to a file. Overwrites the original file. + """ + m_count = 0 # count the written methods + doc = pretext + + for category, methods in grouped_methods.items(): + cat, m_count = _assemble_method_category(category, methods, m_count) + doc += cat + + doc += posttext + + STC_RST.write_text(doc, encoding="utf-8") + + # print(f"Debug: Wrote {m_count} methods to new file.") + +def stc_categorise_methods(): + """ + Loads the ReST file, categorises the method summary and saves a new file. + Thr original file is overwritten. + """ + if not STC_CLASS_XML.is_file(): + return msg(f"Warning: StyledTextCtrl post-processing failed: {STC_CLASS_XML.name} not available.") + + mapping = _parse_method_categories(STC_CLASS_XML) + + if not STC_RST.is_file(): + return msg(f"Warning: StyledTextCtrl post-processing failed: {STC_RST.name} not available.") + + pre, methods, post, links = _parse_stcdoc_segments(STC_RST) + grouped_methods = _methods_to_categories(methods, mapping) + sorted_methods = {key: grouped_methods[key] for key in links} + _output_reordered_doc(pre, post, sorted_methods) + + return msg("StyledTextCtrl post-processing completed successfully.") + + +if __name__ == "__main__": + stc_categorise_methods() + diff --git a/sphinxtools/templates.py b/sphinxtools/templates.py index 03fbeee8..97832dd6 100644 --- a/sphinxtools/templates.py +++ b/sphinxtools/templates.py @@ -60,26 +60,27 @@ TEMPLATE_APPEARANCE = ''' | -.. figure:: _static/images/widgets/fullsize/wxmsw/%s - :alt: wxMSW - :figclass: floatleft +.. container:: control-appearance-figures - **wxMSW** + .. figure:: _static/images/widgets/fullsize/wxmsw/%s + :alt: wxMSW + :figclass: appearance-figure + + **wxMSW** -.. figure:: _static/images/widgets/fullsize/wxmac/%s - :alt: wxMAC - :figclass: floatright + .. figure:: _static/images/widgets/fullsize/wxmac/%s + :alt: wxMAC + :figclass: appearance-figure - **wxMAC** + **wxMAC** -.. figure:: _static/images/widgets/fullsize/wxgtk/%s - :alt: wxGTK - :figclass: floatcenter - - **wxGTK** + .. figure:: _static/images/widgets/fullsize/wxgtk/%s + :alt: wxGTK + :figclass: appearance-figure + **wxGTK** | diff --git a/wx/lib/agw/scrolledthumbnail.py b/wx/lib/agw/scrolledthumbnail.py index 22155816..ce8f187e 100644 --- a/wx/lib/agw/scrolledthumbnail.py +++ b/wx/lib/agw/scrolledthumbnail.py @@ -71,7 +71,7 @@ wxWidgets implementation of the :class:`ThumbnailCtrl` control. Andrea Gavana notes that :class:`ThumbnailCtrl` wouldn't have been so fast and complete without the suggestions and hints from Peter Damoc. -Usage: +Usage ===== Usage example:: diff --git a/wx/lib/masked/maskededit.py b/wx/lib/masked/maskededit.py index 87335d42..b99d0543 100644 --- a/wx/lib/masked/maskededit.py +++ b/wx/lib/masked/maskededit.py @@ -117,7 +117,7 @@ mask X Allow string.letters, string.punctuation, string.digits & Allow string.punctuation only (doesn't include all unicode symbols) \* Allow any visible character - | explicit field boundary (takes no space in the control; allows mix + \| Explicit field boundary (takes no space in the control; allows mix of adjacent mask characters to be treated as separate fields, eg: '&|###' means "field 0 = '&', field 1 = '###'", but there's no fixed characters in between. @@ -189,59 +189,67 @@ defaultEncoding formatcodes These other properties can be passed to the class when instantiating it: Formatcodes are specified as a string of single character formatting - codes that modify behavior of the control:: + codes that modify behavior of the control: - _ Allow spaces - ! Force upper - ^ Force lower - R Right-align field(s) - r Right-insert in field(s) (implies R) - < Stay in field until explicit navigation out of it + ======= ========================================================== + _ Allow spaces + ! Force upper + ^ Force lower + R Right-align field(s) + r Right-insert in field(s) (implies R) + < Stay in field until explicit navigation out of it - > Allow insert/delete within partially filled fields (as - opposed to the default "overwrite" mode for fixed-width - masked edit controls.) This allows single-field controls - or each field within a multi-field control to optionally - behave more like standard text controls. - (See EMAIL or phone number autoformat examples.) + > Allow insert/delete within partially filled fields (as + opposed to the default "overwrite" mode for fixed-width + masked edit controls.) This allows single-field controls + or each field within a multi-field control to optionally + behave more like standard text controls. + (See EMAIL or phone number autoformat examples.) - *Note: This also governs whether backspace/delete operations - shift contents of field to right of cursor, or just blank the - erased section. + .. note:: - Also, when combined with 'r', this indicates that the field - or control allows right insert anywhere within the current - non-empty value in the field. (Otherwise right-insert behavior - is only performed to when the entire right-insertable field is - selected or the cursor is at the right edge of the field.* + This also governs whether backspace/delete operations + shift contents of field to right of cursor, or just blank + the erased section. + + Also, when combined with 'r', this indicates that the field + or control allows right insert anywhere within the current + non-empty value in the field. (Otherwise right-insert behavior + is only performed to when the entire right-insertable field is + selected or the cursor is at the right edge of the field. - , Allow grouping character in integer fields of numeric controls - and auto-group/regroup digits (if the result fits) when leaving - such a field. (If specified, .SetValue() will attempt to - auto-group as well.) - ',' is also the default grouping character. To change the - grouping character and/or decimal character, use the groupChar - and decimalChar parameters, respectively. - Note: typing the "decimal point" character in such fields will - clip the value to that left of the cursor for integer - fields of controls with "integer" or "floating point" masks. - If the ',' format code is specified, this will also cause the - resulting digits to be regrouped properly, using the current - grouping character. - - Prepend and reserve leading space for sign to mask and allow - signed values (negative #s shown in red by default.) Can be - used with argument useParensForNegatives (see below.) - 0 integer fields get leading zeros - D Date[/time] field - T Time field - F Auto-Fit: the control calculates its size from - the length of the template mask - V validate entered chars against validRegex before allowing them - to be entered vs. being allowed by basic mask and then having - the resulting value just colored as invalid. - (See USSTATE autoformat demo for how this can be used.) - S select entire field when navigating to new field + , Allow grouping character in integer fields of numeric controls + and auto-group/regroup digits (if the result fits) when leaving + such a field. (If specified, .SetValue() will attempt to + auto-group as well.) + ',' is also the default grouping character. To change the + grouping character and/or decimal character, use the groupChar + and decimalChar parameters, respectively. + + .. note:: + + Typing the "decimal point" character in such fields will + clip the value to that left of the cursor for integer + fields of controls with "integer" or "floating point" masks. + + If the ',' format code is specified, this will also cause the + resulting digits to be regrouped properly, using the current + grouping character. + \- Prepend and reserve leading space for sign to mask and allow + signed values (negative #s shown in red by default.) Can be + used with argument useParensForNegatives (see below.) + 0 Integer fields get leading zeros + D Date[/time] field + T Time field + F Auto-Fit: the control calculates its size from + the length of the template mask + V Validate entered chars against validRegex before allowing them + to be entered vs. being allowed by basic mask and then having + the resulting value just colored as invalid. + (See USSTATE autoformat demo for how this can be used.) + S Select entire field when navigating to new field + ======= ========================================================== fillChar diff --git a/wx/lib/masked/numctrl.py b/wx/lib/masked/numctrl.py index d0177d19..284f7a5a 100644 --- a/wx/lib/masked/numctrl.py +++ b/wx/lib/masked/numctrl.py @@ -89,7 +89,7 @@ masked.NumCtrl: emptyBackgroundColour = "White", validBackgroundColour = "White", invalidBackgroundColour = "Yellow", - autoSize = True + autoSize = True, ) diff --git a/wx/lib/plot/examples/demo.py b/wx/lib/plot/examples/demo.py index ce1767b1..4bb63437 100644 --- a/wx/lib/plot/examples/demo.py +++ b/wx/lib/plot/examples/demo.py @@ -7,10 +7,14 @@ demo.py ======= -This is a demo showing some of the capabilities of the :mod:`wx.lib.plot` -package. It is intended to be run as a standalone script via:: +.. highlight:: shell - user@host:.../site-packages/wx/lib/plot$ python examples/demo.py +This is a demo showing some of the capabilities of the :mod:`wx.lib.plot` +package. + +It is intended to be run as a standalone script via:: + + user@host:.../site-packages/wx/lib/plot$ python examples/demo.py """ __docformat__ = "restructuredtext en" diff --git a/wx/lib/plot/examples/simple_example.py b/wx/lib/plot/examples/simple_example.py index e8f539c8..b120bfe0 100644 --- a/wx/lib/plot/examples/simple_example.py +++ b/wx/lib/plot/examples/simple_example.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- """ +.. highlight:: shell + A simple example showing how to use lib.plot from wxPython. It is intended to be run as a standalone script via:: - user@host:.../site-packages/wx/lib/plot$ python examples/simple_example.py + user@host:.../site-packages/wx/lib/plot$ python examples/simple_example.py """ __docformat__ = "restructuredtext en" diff --git a/wx/lib/plot/polyobjects.py b/wx/lib/plot/polyobjects.py index be9ab831..81f5d27b 100644 --- a/wx/lib/plot/polyobjects.py +++ b/wx/lib/plot/polyobjects.py @@ -120,7 +120,7 @@ class PolyPoints(object): :type: tuple of bool, length 2 :raises ValueError: when setting an invalid value - .. notes:: + .. note:: This is a simplified example of how SymLog works:: @@ -163,7 +163,7 @@ class PolyPoints(object): :type: tuple of float, length 2 :raises ValueError: when setting an invalid value - .. notes:: + .. note:: This is a simplified example of how SymLog works:: @@ -221,7 +221,7 @@ class PolyPoints(object): :setter: Sets the value of points. :type: list of `(x, y)` pairs - .. Note:: + .. note:: Only set unscaled points - do not perform the log, abs, or symlog adjustments yourself. @@ -1010,17 +1010,10 @@ class PolyBoxPlot(PolyPoints): outliers are outside of 1.5 * IQR - Parameters - ---------- - data : array-like - The data to plot - - Returns - ------- - bpdata : collections.namedtuple - Descriptive statistics for data: + :param array-like data: The data to plot + :return bpdata: Descriptive statistics for data: (min_data, low_whisker, q25, median, q75, high_whisker, max_data) - + :rtype: collections.namedtuple """ data = self._clean_data(data) @@ -1066,20 +1059,20 @@ class PolyBoxPlot(PolyPoints): """ Draws a box plot on the DC. - Notes - ----- - The following draw order is required: + .. note:: - 1. First the whisker line - 2. Then the IQR box - 3. Lasly the median line. + The following draw order is required: - This is because + 1. First the whisker line + 2. Then the IQR box + 3. Lasly the median line. - + The whiskers are drawn as single line rather than two lines - + The median line must be visible over the box if the box has a fill. + This is because - Other than that, the draw order can be changed. + + The whiskers are drawn as single line rather than two lines + + The median line must be visible over the box if the box has a fill. + + Other than that, the draw order can be changed. """ self._draw_whisker(dc, printerScale) self._draw_iqr_box(dc, printerScale)