# ----------------------------------------------------------------------------
# This file is part of qarbon (http://qarbon.rtfd.org/)
#
# Copyright (c) 2013 European Synchrotron Radiation Facility, Grenoble, France
#
# Distributed under the terms of the GNU Lesser General Public License,
# either version 3 of the License, or (at your option) any later version.
# See LICENSE.txt for more info.
# ----------------------------------------------------------------------------
"""Helper functions to deal with Qt GUI related stuff"""
__all__ = ["isWidget", "isWidgetClass", "getWidgetClasses",
"grabWidget"]
import time
import inspect
import threading
from qarbon import log
from qarbon.util import import_module
from qarbon.external.qt import QtCore, QtGui
[docs]def isWidget(obj):
"""Determines if the given object is a Qt Widget.
:param obj: object
:return: True if the given object is a Qt Widget or False otherwise
:rtype: bool
"""
return isinstance(obj, QtCore.QObject) and obj.isWidgetType()
[docs]def isWidgetClass(obj):
"""Determines if the given object is a Qt Widget class.
:param obj: object
:return: True if the given object is a Qt Widget class or False otherwise
:rtype: bool
"""
return inspect.isclass(obj) and issubclass(obj, QtGui.QWidget)
[docs]def getWidgetClasses(module_name):
"""Returns the widget classes defined in a given module.
Returns:
{widget full name(str): {"klass": widget class(class),
"name": widget name(str), "full_name": widget full name(str)}}
:param module_name: name of the module in the format "a.b.c"
:type module_name: str
:return: a map with the widgets for the given module
:rtype: dict
"""
widgets = {}
module = import_module(module_name)
for name, value in inspect.getmembers(module, isWidgetClass):
if inspect.getmodule(value) != module:
continue
full_name = module_name + "." + name
widgets[full_name] = dict(klass=value, name=name, full_name=full_name)
return widgets
class __GrabberThread(threading.Thread):
"""Helper to trigger grabbing a widget periodically"""
def __init__(self, widget, fileName, period):
threading.Thread.__init__(self, name="Grabber")
self.daemon = True
if period <= 0:
raise ValueError("period MUST be greater than 0")
self.__period = period
self.__continue = True
self.__grabber = __Grabber(widget, fileName)
def run(self):
period = self.__period
while self.__continue:
self.__grabber.grabTrigger()
time.sleep(period)
def stop(self):
self.__continue = False
class __Grabber(QtCore.QObject):
grab = QtCore.Signal()
def __init__(self, widget, fileName):
QtCore.QObject.__init__(self)
self.__widget = widget
self.__fileName = fileName
self.grab.connect(self.__onGrab)
def grabTrigger(self):
self.grab.emit()
def __onGrab(self):
grabWidget(self._widget, self._fileName)
[docs]def grabWidget(widget, fileName, period=None):
"""Grabs the given widget into the given image filename. If period is
None (default) it grabs immediately once and returns.
If period is given and >0 means grab the image every period (in seconds).
.. warning::
this method **MUST** be called from the same thread which created
the widget
:param widget: the Qt widget to be grabbed
:type widget: QtWidget
:param fileName: the name of the image file
:type fileName: str
:param period: period (seconds)
:type period: float
"""
if period is None:
widgetName = widget.objectName()
widgetTitle = widget.windowTitle()
log.debug("Grabbing widget '%s' to '%s':", widgetName, fileName)
try:
pixmap = QtGui.QPixmap.grabWidget(widget)
if fileName.endswith('.svg'):
import qarbon.external.qt.QtSvg
generator = qarbon.external.qt.QtSvg.QSvgGenerator()
generator.setFileName(fileName)
generator.setSize(pixmap.size())
if hasattr(generator, 'setViewBox'):
viewBox = QtCore.QRect(QtCore.QPoint(0, 0), pixmap.size())
generator.setViewBox(viewBox)
title = "Qarbon widget"
if widgetTitle:
title += " - " + widgetTitle
elif widgetName:
title += " - " + widgetName
desc = "An SVG created by the qarbon widget grabber"
generator.setTitle(title)
generator.setDescription(desc)
painter = QtGui.QPainter()
painter.begin(generator)
try:
painter.drawPixmap(0, 0, -1, -1, pixmap)
finally:
painter.end()
else:
pixmap.save(fileName, quality=100)
except Exception:
log.warning("Could not save file into '%s':", fileName)
log.debug("Details:", exc_info=1)
ret = __GrabberThread(widget, fileName, period)
ret.start()
return ret