diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 648f6f6..f210511 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -19,21 +19,40 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} + + - name: Install wkhtmltopdf + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + # apt install important_linux_software + sudo apt-get install xvfb libfontconfig wkhtmltopdf + elif [ "$RUNNER_OS" == "Windows" ]; then + choco install wkhtmltopdf + elif [ "$RUNNER_OS" == "macOS" ]; then + brew cask install wkhtmltopdf + else + echo "$RUNNER_OS not supported" + exit 1 + fi + shell: bash + - name: Install dependencies run: | python -m pip install --upgrade pip pip install flake8 pytest pip install -r requirements.txt + - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest run: | pytest diff --git a/ipyplot/_html_helpers.py b/ipyplot/_html_helpers.py index adc8803..31b558c 100644 --- a/ipyplot/_html_helpers.py +++ b/ipyplot/_html_helpers.py @@ -3,13 +3,18 @@ required for displaying images, grid/tab layout and general styling. """ +import os from typing import Sequence +import imgkit import numpy as np import shortuuid from numpy import str_ from ._img_helpers import _img_to_base64 +from ._utils import ( + _find_and_replace_html_for_imgkit, + _to_imgkit_path) try: from IPython.display import display, HTML @@ -17,6 +22,34 @@ raise Exception('IPython not detected. Plotting without IPython is not possible') # NOQA E501 +def _html_to_image(html, out_img): + if os.path.dirname(out_img) != '': + os.makedirs(os.path.dirname(out_img), exist_ok=True) + + html = _find_and_replace_html_for_imgkit(html) + options = { + # 'xvfb': '', + 'enable-local-file-access': '', + } + saving = True + while saving: + print("Saving output as image under: ", out_img) + + try: + # path_wkthmltoimage = r'C:/Program Files/wkhtmltopdf/bin/wkhtmltoimage.exe' + # config = imgkit.config(wkhtmltoimage=path_wkthmltoimage) + imgkit.from_string( + html, out_img, + # config=config, + options=options) + except Exception as e: + if "You need to install xvfb" in str(e): + options['xvfb'] = '' + continue + raise + saving = False + + def _create_tabs( images: Sequence[object], labels: Sequence[str or int], @@ -25,7 +58,7 @@ def _create_tabs( img_width: int = 150, zoom_scale: float = 2.5, force_b64: bool = False, - tabs_order: Sequence[str or int] = None): + tabs_order: Sequence[str or int] = None) -> str: """ Generates HTML code required to display images in interactive tabs grouped by labels. For tabs ordering and filtering check out `tabs_order` param. @@ -63,6 +96,11 @@ def _create_tabs( By default, tabs will be sorted alphabetically based on provided labels. This param can be also used as a filtering mechanism - only labels provided in `tabs_order` param will be displayed as tabs. Defaults to None. + + Returns + ------- + str + HTML code for class tabs viewer control. """ # NOQA E501 tab_layout_id = shortuuid.uuid() @@ -144,7 +182,7 @@ def _create_tabs( def _create_html_viewer( - html: str): + html: str) -> str: """Creates HTML code for HTML previewer. Parameters @@ -235,7 +273,7 @@ def _create_img( width: int, grid_style_uuid: str, custom_text: str = None, - force_b64: bool = False): + force_b64: bool = False) -> str: """Helper function to generate HTML code for displaying images along with corresponding texts. Parameters @@ -272,11 +310,14 @@ def _create_img( use_b64 = True # if image is a string (URL) display its URL if type(image) is str or type(image) is str_: - img_html += '

%s

' % (image) # NOQA E501 + matches = ['http:', 'https:', 'ftp:', 'www.'] + if not any(image.lower().startswith(x) for x in matches): + image = os.path.relpath(image) + img_html += '

%s

\n' % (image) # NOQA E501 if not force_b64: use_b64 = False img_html += '' % image - elif "http" in image: + elif any(image.lower().startswith(x) for x in matches): print("WARNING: Current implementation doesn't allow to use 'force_b64=True' with images as remote URLs. Ignoring 'force_b64' flag") # NOQA E501 use_b64 = False @@ -309,7 +350,7 @@ def _create_imgs_grid( max_images: int = 30, img_width: int = 150, zoom_scale: float = 2.5, - force_b64: bool = False): + force_b64: bool = False) -> str: """ Creates HTML code for displaying images provided in `images` param in grid-like layout. Check optional params for max number of images to plot, labels and custom texts to add to each image, image width and other options. @@ -355,7 +396,7 @@ def _create_imgs_grid( # create code with style definitions html, grid_style_uuid = _get_default_style(img_width, zoom_scale) - html += '
' % grid_style_uuid + html += '
' % grid_style_uuid html += ''.join([ _create_img( x, width=img_width, label=y, @@ -370,7 +411,7 @@ def _create_imgs_grid( return html -def _get_default_style(img_width: int, zoom_scale: float): +def _get_default_style(img_width: int, zoom_scale: float) -> str: """Creates HTML code with default style definitions required for elements to be properly displayed Parameters @@ -389,13 +430,11 @@ def _get_default_style(img_width: int, zoom_scale: float): style_uuid = shortuuid.uuid() html = """