diff options
Diffstat (limited to 'doc/source/ext/snapshotqt_directive.py')
-rw-r--r-- | doc/source/ext/snapshotqt_directive.py | 257 |
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) |