summaryrefslogtreecommitdiff
path: root/doc/source/ext/snapshotqt_directive.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/source/ext/snapshotqt_directive.py')
-rw-r--r--doc/source/ext/snapshotqt_directive.py257
1 files changed, 0 insertions, 257 deletions
diff --git a/doc/source/ext/snapshotqt_directive.py b/doc/source/ext/snapshotqt_directive.py
deleted file mode 100644
index 1cd8dbe..0000000
--- a/doc/source/ext/snapshotqt_directive.py
+++ /dev/null
@@ -1,257 +0,0 @@
-"""RST directive to include snapshot of a Qt application in Sphinx doc.
-
-Configuration variable in conf.py:
-
-- snapshotqt_image_type: image file extension (default 'png').
-- snapshotqt_script_dir: relative path of the root directory for scripts from
- the documentation source directory (i.e., the directory of conf.py)
- (default: '..').
-"""
-
-from __future__ import absolute_import
-
-import logging
-import os
-import subprocess
-import sys
-
-from docutils.parsers.rst.directives.images import Figure
-
-
-logging.basicConfig()
-_logger = logging.getLogger(__name__)
-
-# TODO:
-# - Create image in build directory
-# - Check if it is needed to patch block_text?
-
-# RST directive ###############################################################
-
-class SnapshotQtDirective(Figure):
- """Figure of a Qt application snapshot.
-
- Directive Type: "snapshotqt"
- Doctree Elements: As for figure
- Directive Arguments: One or more, required (script URI + script arguments).
- Directive Options: Possible.
- Directive Content: Interpreted as the figure caption and optional legend.
-
- A "snapshotqt" is a rst `figure
- <http://docutils.sourceforge.net/docs/ref/rst/directives.html#figure>`_
- that is generated from a Python script that uses Qt.
-
- The path of the script to take a snapshot is relative to
- the path given in conf.py 'snapshotqt_script_dir' value.
-
- ::
-
- .. snapshotqt: ../examples/demo.py
- :align: center
- :height: 5cm
-
- Caption of the figure.
- """
-
- # TODO this should be configured in conf.py
- IMAGE_RELATIVE_DIR = os.path.join('images')
- """The path where to store images relative to doc directory."""
-
- def run(self):
- # Run script stored in arguments and replace by snapshot filename
-
- env = self.state.document.settings.env
-
- # Create an image filename from arguments
- image_ext = env.config.snapshotqt_image_type.lower()
- image_name = '_'.join(self.arguments) + '.' + image_ext
- image_name = image_name.replace('./\\', '_')
- image_name = ''.join([c for c in image_name
- if c.isalnum() or c in '_-.'])
- image_name = os.path.join(self.IMAGE_RELATIVE_DIR, image_name)
-
- # Change path to absolute path to run the script
- script_dir = os.path.join(env.srcdir, env.config.snapshotqt_script_dir)
- script_cmd = self.arguments[:]
- script_cmd[0] = os.path.join(script_dir, script_cmd[0])
-
- # Run snapshot
- snapshot_tool = os.path.abspath(__file__)
- _logger.info('Running script: %s', script_cmd)
- _logger.info('Saving snapshot to: %s', image_name)
- abs_image_name = os.path.join(env.srcdir, image_name)
- cmd = [sys.executable, snapshot_tool, '--output', abs_image_name]
- cmd += script_cmd
- subprocess.check_call(cmd)
-
- # Use created image as in Figure
- self.arguments = [image_name]
-
- return super(SnapshotQtDirective, self).run()
-
-
-def setup(app):
- app.add_config_value('snapshotqt_image_type', 'png', 'env')
- app.add_config_value('snapshotqt_script_dir', '..', 'env')
-
- app.add_directive('snapshotqt', SnapshotQtDirective)
-
- return {'version': '0.1'}
-
-
-# Qt monkey-patch #############################################################
-
-def monkeyPatchQApplication(filename, qt=None):
- """Monkey-patch QApplication to take a snapshot and close the application.
-
- :param str filename: The image filename where to save the snapshot.
- :param str qt: The Qt binding to patch.
- This MUST be the same as the one used by the script
- for which to take a snapshot.
- In: 'PyQt4', 'PyQt5', 'PySide' or None (the default).
- If None, it will try to use PyQt4, then PySide and
- finally PyQt4.
- """
-
- # Probe Qt binding
- if qt is None:
- try:
- import PyQt4.QtCore # noqa
- qt = 'PyQt4'
- except ImportError:
- try:
- import PySide.QtCore # noqa
- qt = 'PySide'
- except ImportError:
- try:
- import PyQt5.QtCore # noqa
- qt = 'PyQt5'
- except ImportError:
- raise RuntimeError('Cannot find any supported Qt binding.')
-
- if qt == 'PyQt4':
- from PyQt4.QtGui import QApplication, QPixmap
- from PyQt4.QtCore import QTimer
- import PyQt4.QtGui as _QApplicationPackage
-
- def grabWindow(winID):
- return QPixmap.grabWindow(winID)
-
- elif qt == 'PyQt5':
- from PyQt5.QtGui import QPixmap
- from PyQt5.QtWidgets import QApplication
- from PyQt5.QtCore import QTimer
- import PyQt5.QtWidgets as _QApplicationPackage
-
- def grabWindow(winID):
- screen = QApplication.primaryScreen()
- return screen.grabWindow(winID)
-
- elif qt == 'PySide':
- from PySide.QtGui import QApplication, QPixmap
- from PySide.QtCore import QTimer
- import PySide.QtGui as _QApplicationPackage
-
- def grabWindow(winID):
- return QPixmap.grabWindow(winID)
-
- else:
- raise ValueError('Unsupported Qt binding: %s' % qt)
-
- _logger.info('Using Qt bindings: %s', qt)
-
- class _QApplication(QApplication):
-
- _TIMEOUT = 1000.
- _FILENAME = filename
-
- def _grabActiveWindowAndClose(self):
- activeWindow = QApplication.activeWindow()
- if activeWindow is not None:
- if activeWindow.isVisible():
- pixmap = grabWindow(activeWindow.winId())
- saveOK = pixmap.save(self._FILENAME)
- if not saveOK:
- _logger.error(
- 'Cannot save snapshot to %s', self._FILENAME)
- else:
- _logger.error('activeWindow is not visible.')
- self.quit()
- else:
- self._count -= 1
- if self._count > 0:
- # Only restart a timer if everything is OK
- QTimer.singleShot(self._TIMEOUT,
- self._grabActiveWindowAndClose)
- else:
- raise RuntimeError(
- 'Aborted: It took too long to have an active window.')
-
- def exec_(self):
- self._count = 10
- QTimer.singleShot(self._TIMEOUT, self._grabActiveWindowAndClose)
-
- return super(_QApplication, self).exec_()
-
- _QApplicationPackage.QApplication = _QApplication
-
-
-# main ########################################################################
-
-if __name__ == '__main__':
- import argparse
- import runpy
-
- # Parse argv
-
- parser = argparse.ArgumentParser(
- description=__doc__,
- epilog="""Arguments provided after the script or module name are passed
- to the script or module.""")
- parser.add_argument(
- '-o', '--output', nargs=1, type=str,
- default='snapshot.png',
- help='Image filename of the snapshot (default: snapshot.png).')
- parser.add_argument(
- '--bindings', nargs='?',
- choices=('PySide', 'PyQt4', 'PyQt5', 'auto'),
- default='auto',
- help="""Qt bindings used by the script/module.
- If 'auto' (the default), it is probed from available python modules.
- """)
- parser.add_argument(
- '-m', '--module', action='store_true',
- help='Run the corresponding module as a script.')
- parser.add_argument(
- 'script_or_module', nargs=1, type=str,
- help='Python script to run for the snapshot.')
- args, unknown = parser.parse_known_args()
-
- script_or_module = args.script_or_module[0]
-
- # arguments provided after the script or module
- extra_args = sys.argv[sys.argv.index(script_or_module) + 1:]
-
- if unknown != extra_args:
- parser.print_usage()
- _logger.error(
- '%s: incorrect arguments', os.path.basename(sys.argv[0]))
- sys.exit(1)
-
- # Monkey-patch Qt
- monkeyPatchQApplication(args.output[0],
- args.bindings if args.bindings != 'auto' else None)
-
- # Update sys.argv and sys.path
- sys.argv = [script_or_module] + extra_args
- sys.path.insert(0, os.path.abspath(os.path.dirname(script_or_module)))
-
- if args.module:
- _logger.info('Running module: %s', ' '.join(sys.argv))
- runpy.run_module(script_or_module, run_name='__main__')
-
- else:
- with open(script_or_module) as f:
- code = f.read()
-
- _logger.info('Running script: %s', ' '.join(sys.argv))
- exec(code)