diff --git a/atest/resources/common.robot b/atest/resources/common.robot index d172cb7..3d28e6a 100644 --- a/atest/resources/common.robot +++ b/atest/resources/common.robot @@ -1,5 +1,6 @@ *** Settings *** Library ScreenCapLibrary +Library ScreenCapLibrary screenshot_module=PyGTK WITH NAME ScreenCapLibraryGtk Library OperatingSystem Library Collections diff --git a/atest/screenshot_directory.robot b/atest/screenshot_directory.robot index 5d7be13..1c85fa5 100644 --- a/atest/screenshot_directory.robot +++ b/atest/screenshot_directory.robot @@ -11,9 +11,9 @@ ${FIRST_SCREENSHOT} = ${BASENAME}_1.png *** Test Cases *** Set Screenshot Directory - ${old} = Set Screenshot Directory ${SCREENSHOT DIR} + ${old} = ScreenCapLibrary.Set Screenshot Directory ${SCREENSHOT DIR} Paths Should Be Equal ${OUTPUT DIR} ${old} - Take Screenshot + ScreenCapLibrary.Take Screenshot Screenshot Should Exist ${FIRST SCREENSHOT} *** Keywords *** diff --git a/atest/take_screenshot.robot b/atest/take_screenshot.robot index 43ebcad..6a5a3f2 100644 --- a/atest/take_screenshot.robot +++ b/atest/take_screenshot.robot @@ -12,6 +12,8 @@ ${FIRST_CUSTOM_SCREENSHOT} ${OUTPUTDIR}${/}foo_1.png ${SECOND_CUSTOM_SCREENSHOT} ${OUTPUTDIR}${/}foo_2.png ${PNG_CUSTOM_SCREENSHOT} ${OUTPUTDIR}${/}foo.png ${JPG_CUSTOM_SCREENSHOT} ${OUTPUTDIR}${/}foo.jpg +${GTK_PNG_SCREENSHOT} ${OUTPUTDIR}${/}pygtk_png.png +${GTK_JPEG_SCREENSHOT} ${OUTPUTDIR}${/}pygtk_jpeg.jpeg *** Test Cases *** Screenshot Is Taken @@ -23,24 +25,24 @@ Each Screenshot Gets Separate Index Take Screenshot and Verify ${FIRST_SCREENSHOT} ${SECOND_SCREENSHOT} Basename May Be Defined - Repeat Keyword 2 Take Screenshot foo + Repeat Keyword 2 ScreenCapLibrary.Take Screenshot foo Screenshots Should Exist ${OUTPUTDIR} ${FIRST_CUSTOM_SCREENSHOT} ${SECOND_CUSTOM_SCREENSHOT} Basename With Extension Turns Off Index Generation - Repeat Keyword 3 Take Screenshot xxx.jpg jpg - Repeat Keyword 2 Take Screenshot yyy.jpeg jpeg + Repeat Keyword 3 ScreenCapLibrary.Take Screenshot xxx.jpg jpg + Repeat Keyword 2 ScreenCapLibrary.Take Screenshot yyy.jpeg jpeg Screenshots Should Exist ${OUTPUTDIR} ${OUTPUTDIR}${/}xxx.jpg ${OUTPUTDIR}${/}yyy.jpeg Screenshot Width Can Be Given - Take Screenshot width=300px + ScreenCapLibrary.Take Screenshot width=300px Screenshots Should Exist ${OUTPUTDIR} ${FIRST_SCREENSHOT} Basename With Non-existing Directories Fails [Documentation] FAIL Directory '${OUTPUTDIR}${/}non-existing' where to save the screenshot does not exist - Take Screenshot ${OUTPUTDIR}${/}non-existing${/}foo + ScreenCapLibrary.Take Screenshot ${OUTPUTDIR}${/}non-existing${/}foo Without Embedding - Take Screenshot Without Embedding no_embed.png + ScreenCapLibrary.Take Screenshot Without Embedding no_embed.png Png Screenshot Quality Compare Size ${PNG_CUSTOM_SCREENSHOT} png @@ -48,18 +50,22 @@ Png Screenshot Quality Jpg Screenshot Quality Compare Size ${JPG_CUSTOM_SCREENSHOT} jpg +Png Screenshot Gtk + ScreenCapLibraryGtk.Take Screenshot ${GTK_PNG_SCREENSHOT} png + ScreenCapLibraryGtk.Take Screenshot ${GTK_JPEG_SCREENSHOT} jpeg + *** Keywords *** Take Screenshot And Verify [Arguments] @{expected files} - ${path}= Take Screenshot format=png + ${path}= ScreenCapLibrary.Take Screenshot format=png Screenshots Should Exist ${OUTPUTDIR} @{expected files} [Return] ${path} Compare Size [Arguments] ${screenshot_name} ${screenshot_format} - Take Screenshot ${screenshot_name} ${screenshot_format} quality=100 + ScreenCapLibrary.Take Screenshot ${screenshot_name} ${screenshot_format} quality=100 ${high_quality_size}= Get File Size ${screenshot_name} - Take Screenshot ${screenshot_name} ${screenshot_format} quality=0 + ScreenCapLibrary.Take Screenshot ${screenshot_name} ${screenshot_format} quality=0 ${low_quality_size}= Get File Size ${screenshot_name} ${decrease}= Evaluate ${high_quality_size} - ${low_quality_size} ${percentage_size_decrease}= Evaluate float(${decrease}) / float(${high_quality_size}) * 100 diff --git a/src/ScreenCapLibrary/ScreenCapLibrary.py b/src/ScreenCapLibrary/ScreenCapLibrary.py index 74d91e2..eeb15c1 100644 --- a/src/ScreenCapLibrary/ScreenCapLibrary.py +++ b/src/ScreenCapLibrary/ScreenCapLibrary.py @@ -20,6 +20,7 @@ from robot.utils import get_link_path, abspath from robot.libraries.BuiltIn import BuiltIn from .version import VERSION +from .pygtk import _take_gtk_screenshot __version__ = VERSION @@ -43,6 +44,8 @@ class ScreenCapLibrary: - [https://pillow.readthedocs.io | Pillow] used on top of ``mss`` in order to save the screenshots in JPG/JPEG format. + - [http://pygtk.org/ | PyGTK] is an alternative to ``mss`` for taking screenshots when using VNC. + = Where screenshots are saved = By default screenshots are saved into the same directory where the Robot @@ -57,11 +60,14 @@ class ScreenCapLibrary: ROBOT_LIBRARY_VERSION = __version__ - def __init__(self, screenshot_directory=None, format='png', quality=50): - """Configure where screenshots are saved. + def __init__(self, screenshot_module=None, screenshot_directory=None, format='png', quality=50): + """ + ``screenshot_module`` specifies the module or tool to use when taking screenshots using this library. + If no tool or module is specified, ``mss`` will be used by default. For running + on Linux with VNC, use ``PyGTK``. - If ``screenshot_directory`` is not given, screenshots are saved into - same directory as the log file. The directory can also be set using + To configure where screenshots are saved use ``screenshot_directory``. If no value is given, + screenshots are saved into same directory as the log file. The directory can also be set using `Set Screenshot Directory` keyword. ``format`` specifies the format in which the screenshots will be saved. @@ -80,6 +86,7 @@ def __init__(self, screenshot_directory=None, format='png', quality=50): | Library | Screenshot | format=jpg | | Library | Screenshot | quality=0 | """ + self._screenshot_module = screenshot_module self._given_screenshot_dir = self._norm_path(screenshot_directory) self._format = format self._quality = quality @@ -184,6 +191,23 @@ def _take_jpg_screenshot(self, name, format, quality): img.save(path, quality=self._pil_quality_conversion(quality)) return path + def _take_screenshot(self, name, format, quality): + format = (format or self._format).lower() + quality = quality or self._quality + if self._screenshot_module and self._screenshot_module.lower() == 'pygtk': + format = 'jpeg' if format == 'jpg' else format + if format == 'png': + quality = self._compression_value_conversion(quality) + path = self._save_screenshot_path(name, format) + return _take_gtk_screenshot(path, format, quality) + else: + if format == 'png': + return self._take_png_screenshot(name, format, quality) + elif format in ['jpg', 'jpeg']: + return self._take_jpg_screenshot(name, format, quality) + else: + raise RuntimeError("Invalid screenshot format.") + def take_screenshot(self, name='screenshot', format=None, quality=None, width='800px'): """Takes a screenshot in the specified format at library import and embeds it into the log file (PNG by default). @@ -223,17 +247,6 @@ def take_screenshot(self, name='screenshot', format=None, quality=None, width='8 self._embed_screenshot(path, width) return path - def _take_screenshot(self, name, format, quality): - format = (format or self._format).lower() - quality = quality or self._quality - if format == 'png': - path = self._take_png_screenshot(name, format, quality) - elif format in ['jpg', 'jpeg']: - path = self._take_jpg_screenshot(name, format, quality) - else: - raise RuntimeError("Invalid screenshot format.") - return path - def _embed_screenshot(self, path, width): link = get_link_path(path, self._log_dir) logger.info('' % (link, link, width), html=True) diff --git a/src/ScreenCapLibrary/pygtk.py b/src/ScreenCapLibrary/pygtk.py new file mode 100644 index 0000000..514349b --- /dev/null +++ b/src/ScreenCapLibrary/pygtk.py @@ -0,0 +1,71 @@ +# Copyright 2008-2015 Nokia Networks +# Copyright 2016- Robot Framework Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +try: + from gtk import gdk +except ImportError: + gdk = None + +try: + from gi.repository import Gdk +except ImportError: + Gdk = None + + +def _gtk_quality(format, quality): + quality_setting = {} + if format == 'png': + quality_setting['compression'] = str(quality) + else: + quality_setting['quality'] = str(quality) + return quality_setting + + +def _take_gtk_screenshot(path, format, quality): + if not gdk and not Gdk: + raise RuntimeError('PyGTK not installed/supported on this platform.') + if gdk: + _take_gtk_screenshot_py2(path, format, quality) + elif Gdk: + _take_gtk_screenshot_py3(path, format, quality) + + +def _take_gtk_screenshot_py2(path, format, quality): + window = gdk.get_default_root_window() + if not window: + raise RuntimeError('Taking screenshot failed.') + width, height = window.get_size() + pb = gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8, width, height) + pb = pb.get_from_drawable(window, window.get_colormap(), + 0, 0, 0, 0, width, height) + if not pb: + raise RuntimeError('Taking screenshot failed.') + quality_setting = _gtk_quality(format, quality) + pb.save(path, format, quality_setting) + return path + + +def _take_gtk_screenshot_py3(path, format, quality): + window = Gdk.get_default_root_window() + if not window: + raise RuntimeError('Taking screenshot failed.') + width = window.get_width() + height = window.get_height() + pb = Gdk.pixbuf_get_from_window(window, 0, 0, width, height) + if not pb: + raise RuntimeError('Taking screenshot failed.') + quality_setting = _gtk_quality(format, quality) + pb.savev(path, format, [list(quality_setting.keys())[0]], [list(quality_setting.values())[0]]) + return path