<commit_before>"""Distribute jobs across multiple processes."""
# =============================================================================
# CONTENTS
# -----------------------------------------------------------------------------
# phlmp_cyclingpool
#
# Public Classes:
#   MultiprocessingWorkerFinishError
#   CyclingPool
#    .cycle_results
#    .finish_results
#    .num_active_jobs
#
# -----------------------------------------------------------------------------
# (this contents block is generated, edits will be lost)
# =============================================================================

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import multiprocessing
import multiprocessing.queues
import os

import phlsys_timer

class MultiprocessingWorkerFinishError(Exception):
    # Occasionally we see errors of this form after starting workers:
    #
    #   File "/.../python2.7/multiprocessing/process.py", line 130, in start
    #     self._popen = Popen(self)
    #   File "/.../python2.7/multiprocessing/forking.py", line 127, in __init__
    #     sys.stderr.flush()
    #
    # AttributeError: 'Devnull' object has no attribute 'flush'
    #
    # This is when running version '2.7.2'. The only instance of a 'Devnull'
    # object seems to be in the standard library module 'smtpd' module, this
    # doesn't seem to be something we're using even indirectly so it's a bit
    # surprising. This doesn't seem to be related to 'os.devnull', which is
    # simply a string path to the system's '/dev/null' equivalent.
    #
    # There's no mechanism for propagating exceptions back to the original
    # process at the moment, the least harmful thing seems to be to emit a
    # custom exception in the worker process which describes the situation.
    #
    # The error occurs after all the work has been done, so unless there are
    # side-effects that haven't yet been noticed, this doesn't appear to be
    # harmful.
    #
    pass

class CyclingPool(object):

    """Pooling for jobs that are repeated in a loop, supports overruning jobs.

    This is useful if you have a lot of polling jobs which usually don't
    need to do much work. When they occasionally do need to do a lot of
    work, you don't want to stop cycling the other jobs while they're
    processed.

    Jobs are processed in separate worker processes, this means that changes to
    the jobs or any global state will not be reflected in the calling process.

    Jobs started during each 'cycle_results' are guaranteed to be handled in
    new forks of the current process. This means that the current version of
    the jobs in the supplied job list will be used.

    """

    def __init__(self, job_list, max_workers, max_overrunnable):
        """Create a CyclingPool to cycle over 'job_list'.

        :job_list: a list of callables to execute in worker processes
        :max_workers: the maximum number of worker processes to make
        :max_overrunnable: the maximum number of workers to leave behind

        """
        super(CyclingPool, self).__init__()

        if max_workers < 1:
            raise ValueError(
                'invalid value for max_workers: {}'.format(max_workers))

        if max_overrunnable < 0:
            raise ValueError(
                'invalid value for max_overrunnable: {}'.format(
                    max_overrunnable))

        if max_overrunnable >= max_workers:
            raise ValueError(
                'invalid value for max_overrunnable: {}, should be less '
                'than max_workers: {}'.format(
                    max_overrunnable, max_workers))

        self._job_list = job_list
        self._max_workers = max_workers
        self._overunnable_workers = _calc_overrunnable_workers(
            max_workers=max_workers,
            max_overrunnable=max_overrunnable,
            num_jobs=len(job_list))
        self._pool_list = _PoolList()
        self._active_job_index_set = set()

    def cycle_results(self, overrun_secs):
        """Yield the results from a run of all the jobs.

        If overrun_secs elapse and max_overrunnable is nonzero then jobs may be
        left to run in the background.

        The results from these 'overrun' jobs will be yielded in subsequent
        calls to 'cycle_results' or 'finish_results'.

        Jobs which are currently overrunning will not be started again until
        the overrun job has finished.

        :overrun_secs: seconds to wait before considering leaving jobs behind
        :yields: an (index, result) tuple

        """

        # make a timer out of the overrun_secs and pass to _cycle_results
        timer = phlsys_timer.Timer()
        timer.start()

        def overrun_condition():
            return timer.duration >= overrun_secs

        for index, result in self._cycle_results(overrun_condition):
            yield index, result

    def finish_results(self):
        """Yield the results from any outstanding jobs, block until done."""
        while not self._pool_list.is_yield_finished():
            for index, result in self._overrun_cycle_results():
                yield index, result

    @property
    def num_active_jobs(self):
        """Return the number of jobs not yet yielded."""
        return len(self._active_job_index_set)

    def _overrun_cycle_results(self):
        for index, result in self._pool_list.yield_available_results():
            self._active_job_index_set.remove(index)
            yield index, result

    def _cycle_results(self, overrun_condition):

        # clear up any dead pools and yield results
        for i, res in self._overrun_cycle_results():
            yield i, res

        self._start_new_cycle()

        # wait for results, overrun if half our workers are available
        should_break = False
        while not should_break:

            should_break = _calc_should_overrun(
                num_active=self._pool_list.count_active_workers(),
                num_overrunnable=self._overunnable_workers,
                condition=overrun_condition,
                is_finished=self._pool_list.is_yield_finished())

            for index, result in self._overrun_cycle_results():
                yield index, result

    def _start_new_cycle(self):

        active_workers = self._pool_list.count_active_workers()
        max_new_workers = self._max_workers - active_workers

        pool = _Pool(self._job_list, max_new_workers)

        # schedule currently inactive jobs in the new pool
        all_job_index_set = set(xrange(len(self._job_list)))
        inactive_job_index_set = all_job_index_set - self._active_job_index_set
        for i in inactive_job_index_set:
            pool.add_job_index(i)
            self._active_job_index_set.add(i)
        pool.finish()

        self._pool_list.add_pool(pool)

def _calc_overrunnable_workers(max_workers, max_overrunnable, num_jobs):
    return min(
        max_workers - 1,
        num_jobs - 1,
        max_overrunnable)

def _calc_should_overrun(num_active, num_overrunnable, condition, is_finished):
    too_busy = num_active > num_overrunnable
    can_overrun = not too_busy and condition()
    return is_finished or can_overrun

class _PoolList(object):

    def __init__(self):
        self._pool_list = []

    def add_pool(self, pool):
        self._pool_list.append(pool)

    def count_active_workers(self):
        active_workers = 0
        for pool in self._pool_list:
            active_workers += pool.count_active_workers()
        return active_workers

    def yield_available_results(self):

        # yield results
        for pool in self._pool_list:
            pool.join_finished_workers()
            for index, result in pool.yield_available_results():
                yield index, result

        # clean up dead pools
        finished_pools = []
        for pool in self._pool_list:
            if pool.is_finished():
                finished_pools.append(pool)
        for pool in finished_pools:
            self._pool_list.remove(pool)

    def is_yield_finished(self):
        return not self._pool_list

class _Pool(object):

    def __init__(self, job_list, max_workers):
        super(_Pool, self).__init__()

        if max_workers < 1:
            raise ValueError(
                'invalid value for max_workers: {}'.format(max_workers))

        # pychecker makes us do this, it won't recognise that
        # multiprocessing.queues is a thing.
        mp = multiprocessing
        self._job_index_queue = mp.queues.SimpleQueue()
        self._results_queue = mp.queues.SimpleQueue()

        # create the workers
        self._worker_list = []
        num_workers = min(max_workers, len(job_list))
        for _ in xrange(num_workers):
            worker = _start_worker_process(
                job_list, self._job_index_queue, self._results_queue)
            self._worker_list.append(worker)

    def add_job_index(self, job_index):
        self._job_index_queue.put(job_index)

    def finish(self):
        # the worker processes will stop when they process 'None'
        for _ in xrange(len(self._worker_list)):
            self._job_index_queue.put(None)

    def join_finished_workers(self):

        # join all finished workers and remove from list
        finished_workers = []
        for worker in self._worker_list:
            if not worker.is_alive():
                worker.join()
                finished_workers.append(worker)
        for worker in finished_workers:
            self._worker_list.remove(worker)

    def yield_available_results(self):
        while not self._results_queue.empty():
            yield self._results_queue.get()

    def count_active_workers(self):
        return len(self._worker_list)

    def is_finished(self):
        return not self._worker_list

def _start_worker_process(job_list, work_queue, results_queue):

    worker = multiprocessing.Process(
        target=_worker_process,
        args=(job_list, work_queue, results_queue))

    pid = os.getpid()

    try:
        worker.start()
    except AttributeError:
        if pid != os.getpid():
            # We've encountered an unexpected error.
            # We are in the worker process.
            # This matches the strange 'Devnull' error, see comments in the
            # custom exception class for more details
            raise MultiprocessingWorkerFinishError(
                'Worker with pid {} oddly failed to finish.'.format(pid))

    return worker

def _worker_process(job_list, work_queue, results_queue):
    while True:
        job_index = work_queue.get()
        if job_index is None:
            break
        job = job_list[job_index]
        results = job()
        results_queue.put((job_index, results))

# -----------------------------------------------------------------------------
# Copyright (C) 2014 Bloomberg Finance L.P.
#
# 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.
# ------------------------------ END-OF-FILE ----------------------------------
<commit_msg>fixup! phlmp_cyclingpool:MultiprocessingWorkerFinishError<commit_after>"""Distribute jobs across multiple processes."""
# =============================================================================
# CONTENTS
# -----------------------------------------------------------------------------
# phlmp_cyclingpool
#
# Public Classes:
#   MultiprocessingWorkerFinishError
#   CyclingPool
#    .cycle_results
#    .finish_results
#    .num_active_jobs
#
# -----------------------------------------------------------------------------
# (this contents block is generated, edits will be lost)
# =============================================================================

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import multiprocessing
import multiprocessing.queues
import os

import phlsys_timer

class MultiprocessingWorkerFinishError(Exception):
    # Occasionally we see errors of this form after starting workers:
    #
    #   File "/.../python2.7/multiprocessing/process.py", line 130, in start
    #     self._popen = Popen(self)
    #   File "/.../python2.7/multiprocessing/forking.py", line 127, in __init__
    #     sys.stderr.flush()
    #
    # AttributeError: 'Devnull' object has no attribute 'flush'
    #
    # This is when running version '2.7.2'. The only instance of a 'Devnull'
    # object seems to be in the standard library module 'smtpd' module, this
    # doesn't seem to be something we're using even indirectly so it's a bit
    # surprising. This doesn't seem to be related to 'os.devnull', which is
    # simply a string path to the system's '/dev/null' equivalent.
    #
    # There's no mechanism for propagating exceptions back to the original
    # process at the moment, the least harmful thing seems to be to emit a
    # custom exception in the worker process which describes the situation.
    #
    # The error occurs after all the work has been done, so unless there are
    # side-effects that haven't yet been noticed, this doesn't appear to be
    # harmful.
    #
    pass

class CyclingPool(object):

    """Pooling for jobs that are repeated in a loop, supports overruning jobs.

    This is useful if you have a lot of polling jobs which usually don't
    need to do much work. When they occasionally do need to do a lot of
    work, you don't want to stop cycling the other jobs while they're
    processed.

    Jobs are processed in separate worker processes, this means that changes to
    the jobs or any global state will not be reflected in the calling process.

    Jobs started during each 'cycle_results' are guaranteed to be handled in
    new forks of the current process. This means that the current version of
    the jobs in the supplied job list will be used.

    """

    def __init__(self, job_list, max_workers, max_overrunnable):
        """Create a CyclingPool to cycle over 'job_list'.

        :job_list: a list of callables to execute in worker processes
        :max_workers: the maximum number of worker processes to make
        :max_overrunnable: the maximum number of workers to leave behind

        """
        super(CyclingPool, self).__init__()

        if max_workers < 1:
            raise ValueError(
                'invalid value for max_workers: {}'.format(max_workers))

        if max_overrunnable < 0:
            raise ValueError(
                'invalid value for max_overrunnable: {}'.format(
                    max_overrunnable))

        if max_overrunnable >= max_workers:
            raise ValueError(
                'invalid value for max_overrunnable: {}, should be less '
                'than max_workers: {}'.format(
                    max_overrunnable, max_workers))

        self._job_list = job_list
        self._max_workers = max_workers
        self._overunnable_workers = _calc_overrunnable_workers(
            max_workers=max_workers,
            max_overrunnable=max_overrunnable,
            num_jobs=len(job_list))
        self._pool_list = _PoolList()
        self._active_job_index_set = set()

    def cycle_results(self, overrun_secs):
        """Yield the results from a run of all the jobs.

        If overrun_secs elapse and max_overrunnable is nonzero then jobs may be
        left to run in the background.

        The results from these 'overrun' jobs will be yielded in subsequent
        calls to 'cycle_results' or 'finish_results'.

        Jobs which are currently overrunning will not be started again until
        the overrun job has finished.

        :overrun_secs: seconds to wait before considering leaving jobs behind
        :yields: an (index, result) tuple

        """

        # make a timer out of the overrun_secs and pass to _cycle_results
        timer = phlsys_timer.Timer()
        timer.start()

        def overrun_condition():
            return timer.duration >= overrun_secs

        for index, result in self._cycle_results(overrun_condition):
            yield index, result

    def finish_results(self):
        """Yield the results from any outstanding jobs, block until done."""
        while not self._pool_list.is_yield_finished():
            for index, result in self._overrun_cycle_results():
                yield index, result

    @property
    def num_active_jobs(self):
        """Return the number of jobs not yet yielded."""
        return len(self._active_job_index_set)

    def _overrun_cycle_results(self):
        for index, result in self._pool_list.yield_available_results():
            self._active_job_index_set.remove(index)
            yield index, result

    def _cycle_results(self, overrun_condition):

        # clear up any dead pools and yield results
        for i, res in self._overrun_cycle_results():
            yield i, res

        self._start_new_cycle()

        # wait for results, overrun if half our workers are available
        should_break = False
        while not should_break:

            should_break = _calc_should_overrun(
                num_active=self._pool_list.count_active_workers(),
                num_overrunnable=self._overunnable_workers,
                condition=overrun_condition,
                is_finished=self._pool_list.is_yield_finished())

            for index, result in self._overrun_cycle_results():
                yield index, result

    def _start_new_cycle(self):

        active_workers = self._pool_list.count_active_workers()
        max_new_workers = self._max_workers - active_workers

        pool = _Pool(self._job_list, max_new_workers)

        # schedule currently inactive jobs in the new pool
        all_job_index_set = set(xrange(len(self._job_list)))
        inactive_job_index_set = all_job_index_set - self._active_job_index_set
        for i in inactive_job_index_set:
            pool.add_job_index(i)
            self._active_job_index_set.add(i)
        pool.finish()

        self._pool_list.add_pool(pool)

def _calc_overrunnable_workers(max_workers, max_overrunnable, num_jobs):
    return min(
        max_workers - 1,
        num_jobs - 1,
        max_overrunnable)

def _calc_should_overrun(num_active, num_overrunnable, condition, is_finished):
    too_busy = num_active > num_overrunnable
    can_overrun = not too_busy and condition()
    return is_finished or can_overrun

class _PoolList(object):

    def __init__(self):
        self._pool_list = []

    def add_pool(self, pool):
        self._pool_list.append(pool)

    def count_active_workers(self):
        active_workers = 0
        for pool in self._pool_list:
            active_workers += pool.count_active_workers()
        return active_workers

    def yield_available_results(self):

        # yield results
        for pool in self._pool_list:
            pool.join_finished_workers()
            for index, result in pool.yield_available_results():
                yield index, result

        # clean up dead pools
        finished_pools = []
        for pool in self._pool_list:
            if pool.is_finished():
                finished_pools.append(pool)
        for pool in finished_pools:
            self._pool_list.remove(pool)

    def is_yield_finished(self):
        return not self._pool_list

class _Pool(object):

    def __init__(self, job_list, max_workers):
        super(_Pool, self).__init__()

        if max_workers < 1:
            raise ValueError(
                'invalid value for max_workers: {}'.format(max_workers))

        # pychecker makes us do this, it won't recognise that
        # multiprocessing.queues is a thing.
        mp = multiprocessing
        self._job_index_queue = mp.queues.SimpleQueue()
        self._results_queue = mp.queues.SimpleQueue()

        # create the workers
        self._worker_list = []
        num_workers = min(max_workers, len(job_list))
        for _ in xrange(num_workers):
            worker = _start_worker_process(
                job_list, self._job_index_queue, self._results_queue)
            self._worker_list.append(worker)

    def add_job_index(self, job_index):
        self._job_index_queue.put(job_index)

    def finish(self):
        # the worker processes will stop when they process 'None'
        for _ in xrange(len(self._worker_list)):
            self._job_index_queue.put(None)

    def join_finished_workers(self):

        # join all finished workers and remove from list
        finished_workers = []
        for worker in self._worker_list:
            if not worker.is_alive():
                worker.join()
                finished_workers.append(worker)
        for worker in finished_workers:
            self._worker_list.remove(worker)

    def yield_available_results(self):
        while not self._results_queue.empty():
            yield self._results_queue.get()

    def count_active_workers(self):
        return len(self._worker_list)

    def is_finished(self):
        return not self._worker_list

def _start_worker_process(job_list, work_queue, results_queue):

    worker = multiprocessing.Process(
        target=_worker_process,
        args=(job_list, work_queue, results_queue))

    pid = os.getpid()

    try:
        worker.start()
    except AttributeError:
        current_pid = os.getpid()
        if pid != current_pid:
            # We've encountered an unexpected error.
            # We are in the worker process.
            # This matches the strange 'Devnull' error, see comments in the
            # custom exception class for more details
            raise MultiprocessingWorkerFinishError(
                'Worker with pid {} oddly failed to finish.'.format(
                    current_pid))

    return worker

def _worker_process(job_list, work_queue, results_queue):
    while True:
        job_index = work_queue.get()
        if job_index is None:
            break
        job = job_list[job_index]
        results = job()
        results_queue.put((job_index, results))

# -----------------------------------------------------------------------------
# Copyright (C) 2014 Bloomberg Finance L.P.
#
# 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.
# ------------------------------ END-OF-FILE ----------------------------------
<|endoftext|>


<commit_before>"""
A simple VTK input file for PyQt, the qt bindings for python.
See http://www.trolltech.com for qt documentation, and
http://www.river-bank.demon.co.uk or http://www.thekompany.com
for the qt python bindings.

This class is based on the vtkGenericRenderWindowInteractor and is
therefore fairly powerful.  It should also play nicely with the
vtk3DWidget code.

Created by Prabhu Ramachandran, May 2002
Based on David Gobbi's QVTKRenderWidget.py

Changes by Gerard Vermeulen Feb. 2003
 Win32 support

"""

"""
This class works with the UNIX and Win32 versions of Qt.

Depending on the OpenGL graphics drivers, it may not
be possible to have more than one QVTKRenderWidget
per application.

In short, this class is experimental.
"""

# To do for Win32:
# 1. More testing to assure that the widget is always cleaned up
#    properly and does not crash the application.

import math, os, sys
import qt
import vtk

class QVTKRenderWindowInteractor(qt.QWidget):

    """ A QVTKRenderWindowInteractor for Python and Qt.  Uses a
    vtkGenericRenderWindowInteractor to handle the interactions.  Use
    GetRenderWindow() to get the vtkRenderWindow.  Create with the
    keyword stereo=1 in order to generate a stereo-capable window.

    The user interface is summarized in vtkInteractorStyle.h:

    - Keypress j / Keypress t: toggle between joystick (position
    sensitive) and trackball (motion sensitive) styles. In joystick
    style, motion occurs continuously as long as a mouse button is
    pressed. In trackball style, motion occurs when the mouse button
    is pressed and the mouse pointer moves.
    - Keypress c / Keypress o: toggle between camera and object
    (actor) modes. In camera mode, mouse events affect the camera
    position and focal point. In object mode, mouse events affect
    the actor that is under the mouse pointer.
    - Button 1: rotate the camera around its focal point (if camera
    mode) or rotate the actor around its origin (if actor mode). The
    rotation is in the direction defined from the center of the
    renderer's viewport towards the mouse position. In joystick mode,
    the magnitude of the rotation is determined by the distance the
    mouse is from the center of the render window.
    - Button 2: pan the camera (if camera mode) or translate the actor
    (if object mode). In joystick mode, the direction of pan or
    translation is from the center of the viewport towards the mouse
    position. In trackball mode, the direction of motion is the
    direction the mouse moves. (Note: with 2-button mice, pan is
    defined as <Shift>-Button 1.)
    - Button 3: zoom the camera (if camera mode) or scale the actor
    (if object mode). Zoom in/increase scale if the mouse position is
    in the top half of the viewport; zoom out/decrease scale if the
    mouse position is in the bottom half. In joystick mode, the amount
    of zoom is controlled by the distance of the mouse pointer from
    the horizontal centerline of the window.
    - Keypress 3: toggle the render window into and out of stereo mode.
    By default, red-blue stereo pairs are created. Some systems support
    Crystal Eyes LCD stereo glasses; you have to invoke
    SetStereoTypeToCrystalEyes() on the rendering window.
    - Keypress e: exit the application.
    - Keypress f: fly to the picked point
    - Keypress p: perform a pick operation. The render window interactor
    has an internal instance of vtkCellPicker that it uses to pick.
    - Keypress r: reset the camera view along the current view
    direction. Centers the actors and moves the camera so that all actors
    are visible.
    - Keypress s: modify the representation of all actors so that they
    are surfaces.
    - Keypress u: invoke the user-defined function. Typically, this
    keypress will bring up an interactor that you can type commands in.
    - Keypress w: modify the representation of all actors so that they
    are wireframe.
    """

    def __init__(self, parent=None, name=None, *args, **kw):

        # the current button
        self._ActiveButton = 0

        # private attributes
        self.__oldFocus = None
        self.__saveX = 0
        self.__saveY = 0
        self.__saveState = 0
        self.__connected = 0  # is QT->VTK connection done?

        # do special handling of some keywords:
        # stereo, rw

        stereo = 0

        if kw.has_key('stereo'):
            if kw['stereo']:
                stereo = 1
            del kw['stereo']

        rw = None

        if kw.has_key('rw'):
            rw = kw['rw']
            del kw['rw']

        # create qt-level widget
        apply(qt.QWidget.__init__, (self,parent,name) + args, kw)

        if rw: # user-supplied render window
            self._RenderWindow = rw
        else:
            self._RenderWindow = vtk.vtkRenderWindow()

        if stereo: # stereo mode
            self._RenderWindow.StereoCapableWindowOn()
            self._RenderWindow.SetStereoTypeToCrystalEyes()

        self._Iren = vtk.vtkGenericRenderWindowInteractor()
        self._Iren.SetRenderWindow(self._RenderWindow)

        # do all the necessary qt setup
        self.setBackgroundMode(2) # NoBackground
        self.setMouseTracking(1) # get all mouse events
        self.setFocusPolicy(2) # ClickFocus
        if parent == None:
            self.show()

        if self.isVisible():
            if self.__connected == 0:
                size = self.size()
                self._RenderWindow.SetSize(size.width(),size.height())
                self._RenderWindow.SetWindowInfo(str(int(self.winId())))
                self.__connected = 1

        self._Timer = qt.QTimer(self, 'timer handler')
        self.connect(self._Timer, qt.SIGNAL('timeout()'),
                     self.TimerEvent)

        self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
        self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)

    def __getattr__(self, attr):
        """Makes the object behave like a
        vtkGenericRenderWindowInteractor"""
        if attr == '__vtk__':
            return lambda t=self._Iren: t
        elif hasattr(self._Iren, attr):
            return getattr(self._Iren, attr)
        elif hasattr(qt.QWidget, attr):
            return getattr(self.sipThis, attr)
        else:
            raise AttributeError, self.__class__.__name__ + \
                  " has no attribute named " + attr

    def CreateTimer(self, obj, evt):
        self._Timer.start(10)

    def DestroyTimer(self, obj, evt):
        self._Timer.stop()
        return 1

    def TimerEvent(self):
        self._Iren.TimerEvent()

    def show(self):
        qt.QWidget.show(self)
        self.repaint() # needed for initial contents display on Win32

    def paintEvent(self,ev):
        if self.isVisible():
            if self.__connected == 0:
                size = self.size()
                self._Iren.SetSize(size.width(),size.height())
                self._RenderWindow.SetWindowInfo(str(int(self.winId())))
                self._Iren.ConfigureEvent()
                self.__connected = 1
        if self.__connected:
            self.Render()

    def resizeEvent(self,ev):
        size = self.size()
        self._Iren.SetSize(size.width(),size.height())
        self._Iren.ConfigureEvent()
        self.repaint()

    def _GetCtrlShift(self, ev):
        ctrl, shift = 0, 0
        if hasattr(ev, 'state'):
            if (ev.state() & 8):
                shift = 1
            if (ev.state() & 16):
                ctrl = 1
        elif self.__saveState:
            if (self.__saveState & 8):
                shift = 1
            if (self.__saveState & 16):
                ctrl = 1
        return ctrl, shift

    def enterEvent(self,ev):
        if not self.hasFocus():
            self.__oldFocus = self.focusWidget()
            self.setFocus()
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, chr(0), 0, None)
        self._Iren.EnterEvent()

    def leaveEvent(self,ev):
        if (self.__saveState & 0x7) == 0 and self.__oldFocus:
            self.__oldFocus.setFocus()
            self.__oldFocus = None
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, chr(0), 0, None)
        self._Iren.LeaveEvent()

    def mousePressEvent(self,ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
                                            ctrl, shift, chr(0), 0, None)

        self._ActiveButton = 0
        if ev.button() == 1:
            self._Iren.LeftButtonPressEvent()
            self._ActiveButton = 'Left'
        elif ev.button() == 2:
            self._Iren.RightButtonPressEvent()
            self._ActiveButton = 'Right'
        elif ev.button() == 4:
            self._Iren.MiddleButtonPressEvent()
            self._ActiveButton = 'Middle'

    def mouseReleaseEvent(self,ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
                                            ctrl, shift, chr(0), 0, None)

        if self._ActiveButton == 'Right':
            self._Iren.RightButtonReleaseEvent()
        elif self._ActiveButton == 'Left':
            self._Iren.LeftButtonReleaseEvent()
        elif self._ActiveButton == 'Middle':
            self._Iren.MiddleButtonReleaseEvent()

    def mouseMoveEvent(self,ev):
        self.__saveState = ev.state()
        self.__saveX = ev.x()
        self.__saveY = ev.y()
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
                                            ctrl, shift, chr(0), 0, None)
        self._Iren.MouseMoveEvent()

    def keyPressEvent(self,ev):
        ctrl, shift = self._GetCtrlShift(ev)
        key = chr(0)
        if ev.key() < 256:
            key = chr(ev.key())

        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, key, 0,
                                            None)
        self._Iren.KeyPressEvent()
        self._Iren.CharEvent()

    def keyReleaseEvent(self,ev):
        ctrl, shift = self._GetCtrlShift(ev)
        key = chr(0)
        if ev.key() < 256:
            key = chr(ev.key())

        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, key, 0,
                                            None)
        self._Iren.KeyReleaseEvent()

    def GetRenderWindow(self):
        return self._RenderWindow

    def Render(self):
        self._RenderWindow.Render()

#----------------------------------------------------------------------------
def QVTKRenderWidgetConeExample():
    """Like it says, just a simple example
    """
    # every QT app needs an app
    app = qt.QApplication(['QVTKRenderWindowInteractor'])

    # create the widget
    widget = QVTKRenderWindowInteractor()
    widget.Initialize()
    widget.Start()
    # if you dont want the 'q' key to exit comment this.
    widget.AddObserver("ExitEvent", lambda o, e, a=app: a.quit())

    ren = vtk.vtkRenderer()
    widget.GetRenderWindow().AddRenderer(ren)

    cone = vtk.vtkConeSource()
    cone.SetResolution(8)

    coneMapper = vtk.vtkPolyDataMapper()
    coneMapper.SetInput(cone.GetOutput())

    coneActor = vtk.vtkActor()
    coneActor.SetMapper(coneMapper)

    ren.AddActor(coneActor)

    # show the widget
    widget.show()
    # to make the widget display its contents on Win32
    widget.resize(widget.size())
    # close the application when window is closed
    app.setMainWidget(widget)
    # start event processing
    app.exec_loop()

if __name__ == "__main__":
    QVTKRenderWidgetConeExample()

<commit_msg>BUG: Bugfixes from Gerard Vermeulen to fix issues with using this widget as a child widget.  Also performed some minor cleanup of the code.<commit_after>"""
A simple VTK input file for PyQt, the qt bindings for python.
See http://www.trolltech.com for qt documentation, and
http://www.river-bank.demon.co.uk or http://www.thekompany.com
for the qt python bindings.

This class is based on the vtkGenericRenderWindowInteractor and is
therefore fairly powerful.  It should also play nicely with the
vtk3DWidget code.

Created by Prabhu Ramachandran, May 2002
Based on David Gobbi's QVTKRenderWidget.py

Changes by Gerard Vermeulen Feb. 2003
 Win32 support.

Changes by Gerard Vermeulen, May 2003
 Bug fixes and better integration with the Qt framework.
"""

"""
This class works with the UNIX and Win32 versions of Qt.

Depending on the OpenGL graphics drivers, it may not
be possible to have more than one QVTKRenderWidget
per application.

In short, this class is experimental.
"""

# To do for Win32:
# 1. More testing to assure that the widget is always cleaned up
#    properly and does not crash the application.

import qt
import vtk

class QVTKRenderWindowInteractor(qt.QWidget):

    """ A QVTKRenderWindowInteractor for Python and Qt.  Uses a
    vtkGenericRenderWindowInteractor to handle the interactions.  Use
    GetRenderWindow() to get the vtkRenderWindow.  Create with the
    keyword stereo=1 in order to generate a stereo-capable window.

    The user interface is summarized in vtkInteractorStyle.h:

    - Keypress j / Keypress t: toggle between joystick (position
    sensitive) and trackball (motion sensitive) styles. In joystick
    style, motion occurs continuously as long as a mouse button is
    pressed. In trackball style, motion occurs when the mouse button
    is pressed and the mouse pointer moves.

    - Keypress c / Keypress o: toggle between camera and object
    (actor) modes. In camera mode, mouse events affect the camera
    position and focal point. In object mode, mouse events affect
    the actor that is under the mouse pointer.

    - Button 1: rotate the camera around its focal point (if camera
    mode) or rotate the actor around its origin (if actor mode). The
    rotation is in the direction defined from the center of the
    renderer's viewport towards the mouse position. In joystick mode,
    the magnitude of the rotation is determined by the distance the
    mouse is from the center of the render window.

    - Button 2: pan the camera (if camera mode) or translate the actor
    (if object mode). In joystick mode, the direction of pan or
    translation is from the center of the viewport towards the mouse
    position. In trackball mode, the direction of motion is the
    direction the mouse moves. (Note: with 2-button mice, pan is
    defined as <Shift>-Button 1.)

    - Button 3: zoom the camera (if camera mode) or scale the actor
    (if object mode). Zoom in/increase scale if the mouse position is
    in the top half of the viewport; zoom out/decrease scale if the
    mouse position is in the bottom half. In joystick mode, the amount
    of zoom is controlled by the distance of the mouse pointer from
    the horizontal centerline of the window.

    - Keypress 3: toggle the render window into and out of stereo
    mode.  By default, red-blue stereo pairs are created. Some systems
    support Crystal Eyes LCD stereo glasses; you have to invoke
    SetStereoTypeToCrystalEyes() on the rendering window.  Note: to
    use stereo you also need to pass a stereo=1 keyword argument to
    the constructor.

    - Keypress e: exit the application.

    - Keypress f: fly to the picked point

    - Keypress p: perform a pick operation. The render window interactor
    has an internal instance of vtkCellPicker that it uses to pick.

    - Keypress r: reset the camera view along the current view
    direction. Centers the actors and moves the camera so that all actors
    are visible.

    - Keypress s: modify the representation of all actors so that they
    are surfaces.

    - Keypress u: invoke the user-defined function. Typically, this
    keypress will bring up an interactor that you can type commands in.

    - Keypress w: modify the representation of all actors so that they
    are wireframe.
    """

    def __init__(self, parent=None, name=None, *args, **kw):
        # the current button
        self._ActiveButton = 0

        # private attributes
        self.__oldFocus = None
        self.__saveX = 0
        self.__saveY = 0
        self.__saveState = 0
        self.__connected = 0  # is QT->VTK connection done?

        # do special handling of some keywords:
        # stereo, rw

        stereo = 0

        if kw.has_key('stereo'):
            if kw['stereo']:
                stereo = 1
            del kw['stereo']

        rw = None

        if kw.has_key('rw'):
            rw = kw['rw']
            del kw['rw']

        # create qt-level widget
        apply(qt.QWidget.__init__, (self,parent,name) + args, kw)

        if rw: # user-supplied render window
            self._RenderWindow = rw
        else:
            self._RenderWindow = vtk.vtkRenderWindow()

        if stereo: # stereo mode
            self._RenderWindow.StereoCapableWindowOn()
            self._RenderWindow.SetStereoTypeToCrystalEyes()

        self._Iren = vtk.vtkGenericRenderWindowInteractor()
        self._Iren.SetRenderWindow(self._RenderWindow)

        # do all the necessary qt setup
        self.setBackgroundMode(2) # NoBackground
        self.setMouseTracking(1) # get all mouse events
        self.setFocusPolicy(2) # ClickFocus
        if parent == None:
            self.show()

        self._Timer = qt.QTimer(self, 'timer handler')
        self.connect(self._Timer, qt.SIGNAL('timeout()'),
                     self.TimerEvent)

        self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
        self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)

    def __getattr__(self, attr):
        """Makes the object behave like a
        vtkGenericRenderWindowInteractor"""
        if attr == '__vtk__':
            return lambda t=self._Iren: t
        elif hasattr(self._Iren, attr):
            return getattr(self._Iren, attr)
        elif hasattr(qt.QWidget, attr):
            return getattr(self.sipThis, attr)
        else:
            raise AttributeError, self.__class__.__name__ + \
                  " has no attribute named " + attr

    def CreateTimer(self, obj, evt):
        self._Timer.start(10)

    def DestroyTimer(self, obj, evt):
        self._Timer.stop()
        return 1

    def TimerEvent(self):
        self._Iren.TimerEvent()

    def polish(self):
        """Final initialization just before the widget is displayed."""
        size = self.size()
        self._Iren.SetSize(size.width(), size.height())
        self._RenderWindow.SetWindowInfo(str(int(self.winId())))
        self._Iren.ConfigureEvent()
        self.__connected = 1

    def show(self):
        qt.QWidget.show(self)
        self.update() # needed for initial contents display on Win32

    def paintEvent(self,ev):
        if self.__connected:
            self.Render()

    def resizeEvent(self,ev):
        size = self.size()
        self._Iren.SetSize(size.width(), size.height())
        self._Iren.ConfigureEvent()
        self.update()

    def _GetCtrlShift(self, ev):
        ctrl, shift = 0, 0
        if hasattr(ev, 'state'):
            if (ev.state() & 8):
                shift = 1
            if (ev.state() & 16):
                ctrl = 1
        elif self.__saveState:
            if (self.__saveState & 8):
                shift = 1
            if (self.__saveState & 16):
                ctrl = 1
        return ctrl, shift

    def enterEvent(self,ev):
        if not self.hasFocus():
            self.__oldFocus = self.focusWidget()
            self.setFocus()
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, chr(0), 0, None)
        self._Iren.EnterEvent()

    def leaveEvent(self,ev):
        if (self.__saveState & 0x7) == 0 and self.__oldFocus:
            self.__oldFocus.setFocus()
            self.__oldFocus = None
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, chr(0), 0, None)
        self._Iren.LeaveEvent()

    def mousePressEvent(self,ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
                                            ctrl, shift, chr(0), 0, None)

        self._ActiveButton = 0
        if ev.button() == 1:
            self._Iren.LeftButtonPressEvent()
            self._ActiveButton = 'Left'
        elif ev.button() == 2:
            self._Iren.RightButtonPressEvent()
            self._ActiveButton = 'Right'
        elif ev.button() == 4:
            self._Iren.MiddleButtonPressEvent()
            self._ActiveButton = 'Middle'

    def mouseReleaseEvent(self,ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
                                            ctrl, shift, chr(0), 0, None)

        if self._ActiveButton == 'Right':
            self._Iren.RightButtonReleaseEvent()
        elif self._ActiveButton == 'Left':
            self._Iren.LeftButtonReleaseEvent()
        elif self._ActiveButton == 'Middle':
            self._Iren.MiddleButtonReleaseEvent()

    def mouseMoveEvent(self,ev):
        self.__saveState = ev.state()
        self.__saveX = ev.x()
        self.__saveY = ev.y()
        ctrl, shift = self._GetCtrlShift(ev)
        self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
                                            ctrl, shift, chr(0), 0, None)
        self._Iren.MouseMoveEvent()

    def keyPressEvent(self,ev):
        ctrl, shift = self._GetCtrlShift(ev)
        key = chr(0)
        if ev.key() < 256:
            key = chr(ev.key())

        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, key, 0, None)
        self._Iren.KeyPressEvent()
        self._Iren.CharEvent()

    def keyReleaseEvent(self,ev):
        ctrl, shift = self._GetCtrlShift(ev)
        key = chr(0)
        if ev.key() < 256:
            key = chr(ev.key())

        self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
                                            ctrl, shift, key, 0, None)
        self._Iren.KeyReleaseEvent()

    def GetRenderWindow(self):
        return self._RenderWindow

    def Render(self):
        self._RenderWindow.Render()

#-----------------------------------------------------------------------
def QVTKRenderWidgetConeExample():
    """A simple example that uses the QVTKRenderWindowInteractor
    class.  """

    # every QT app needs an app
    app = qt.QApplication(['QVTKRenderWindowInteractor'])

    # create the widget
    widget = QVTKRenderWindowInteractor()
    widget.Initialize()
    widget.Start()
    # if you dont want the 'q' key to exit comment this.
    widget.AddObserver("ExitEvent", lambda o, e, a=app: a.quit())

    ren = vtk.vtkRenderer()
    widget.GetRenderWindow().AddRenderer(ren)

    cone = vtk.vtkConeSource()
    cone.SetResolution(8)

    coneMapper = vtk.vtkPolyDataMapper()
    coneMapper.SetInput(cone.GetOutput())

    coneActor = vtk.vtkActor()
    coneActor.SetMapper(coneMapper)

    ren.AddActor(coneActor)

    # show the widget
    widget.show()
    # close the application when window is closed
    app.setMainWidget(widget)
    # start event processing
    app.exec_loop()

if __name__ == "__main__":
    QVTKRenderWidgetConeExample()

<|endoftext|>


<commit_before>#!/usr/bin/env python3

# name: retrive.py
# description: TODO
# license: MIT
# date: 2016-03-24

from datetime import datetime, timedelta
from os import path, rename
from random import uniform, shuffle
from time import sleep, time
from urllib import request

def month_to_number(month):
    if month == 'januari':
        return '01'
    elif month == 'februari':
        return '02'
    elif month == 'maart':
        return '03'
    elif month == 'april':
        return '04'
    elif month == 'mei':
        return '05'
    elif month == 'juni':
        return '06'
    elif month == 'juli':
        return '07'
    elif month == 'augustus':
        return '08'
    elif month == 'september':
        return '09'
    elif month == 'oktober':
        return '10'
    elif month == 'november':
        return '11'
    elif month == 'december':
        return '12'
    else:
        return ''

utcnow = datetime.utcnow()
year = utcnow.strftime('%Y')
dtstamp = utcnow.strftime('%Y%m%dT%H%M%SZ')
now = time()

collection_header = ''
event_header = open('templates/event-header.txt', 'r')
for line in event_header.readlines():
    collection_header += line.replace('DTSTAMP:', 'DTSTAMP:{}'.format(dtstamp))

collection_footer = ''
event_footer = open('templates/event-footer.txt', 'r')
for line in event_footer.readlines():
    collection_footer += line

addresses = []
for address in open('addresses.tsv', 'r'):
    address = address[:-1].replace('\t', '/')
    if address != '' and address[0] != '#':
        addresses.append(address)

shuffle(addresses)
count = 0
for address in addresses:
    count += 1
    print('{}/{} {}'.format(count, len(addresses), address))
    basename = address.replace('/', '-')
    destination = 'calendars/{}.ics'.format(basename)
    if path.isfile(destination):
        tstamp = path.getmtime(destination)
        if tstamp > now - 3 * 86400:  # not older than three days
#            print('INFO: Cache not yet expired')
            continue

    url = 'http://www.mijnafvalwijzer.nl/nl/{}/'.format(address)
    try:
        data = request.urlopen(url).read().decode('utf-8')
    except:
        print('WARNING: Could not retrieve url {}'.format(url))
        continue

    data = data.split('\n')
    calendar = open('{}.tmp.ics'.format(basename), 'w')
    rename('{}.tmp.ics'.format(basename), destination)

    calendar_header = open('templates/calendar-header.txt', 'r')
    for line in calendar_header.readlines():
        calendar.write(line)

    for line in data:
        if '')[1]
            (day_name, day, month) = second.split(' ')
            month = month_to_number(month)

            calendar.write('{}{}\n'.format(
                collection_header.strip(), name))
            date = datetime.strptime(
                '{}{}{}'.format(year, month, day), '%Y%m%d')
            calendar.write('DTSTART;VALUE=DATE:{}\n'.format(
                date.strftime('%Y%m%d')))
            date += timedelta(days=1)
            calendar.write('DTEND;VALUE=DATE:{}\n'.format(
                date.strftime('%Y%m%d')))
            calendar.write(collection_footer)

    calendar_footer = open('templates/calendar-footer.txt', 'r')
    for line in calendar_footer.readlines():
        calendar.write(line)
    sleep(uniform(5, 20))
<commit_msg>Test alarm<commit_after>#!/usr/bin/env python3

# name: retrive.py
# description: TODO
# license: MIT
# date: 2016-03-24

from datetime import datetime, timedelta
from os import path, rename
from random import uniform, shuffle
from time import sleep, time
from urllib import request

def month_to_number(month):
    if month == 'januari':
        return '01'
    elif month == 'februari':
        return '02'
    elif month == 'maart':
        return '03'
    elif month == 'april':
        return '04'
    elif month == 'mei':
        return '05'
    elif month == 'juni':
        return '06'
    elif month == 'juli':
        return '07'
    elif month == 'augustus':
        return '08'
    elif month == 'september':
        return '09'
    elif month == 'oktober':
        return '10'
    elif month == 'november':
        return '11'
    elif month == 'december':
        return '12'
    else:
        return ''

utcnow = datetime.utcnow()
year = utcnow.strftime('%Y')
dtstamp = utcnow.strftime('%Y%m%dT%H%M%SZ')
now = time()

collection_header = ''
event_header = open('templates/event-header.txt', 'r')
for line in event_header.readlines():
    collection_header += line.replace('DTSTAMP:', 'DTSTAMP:{}'.format(dtstamp))

collection_footer = ''
event_footer = open('templates/event-footer.txt', 'r')
for line in event_footer.readlines():
    collection_footer += line

addresses = []
for address in open('addresses.tsv', 'r'):
    address = address[:-1].replace('\t', '/')
    if address != '' and address[0] != '#':
        addresses.append(address)

shuffle(addresses)
count = 0
for address in addresses:
    count += 1
    print('{}/{} {}'.format(count, len(addresses), address))
    basename = address.replace('/', '-')
    if path.isfile('calendars/{}.ics'.format(basename)):
        tstamp = path.getmtime('calendars/{}.ics'.format(basename))
        if tstamp > now - 3 * 86400:  # not older than three days
#            print('INFO: Cache not yet expired')
            continue

    url = 'http://www.mijnafvalwijzer.nl/nl/{}/'.format(address)
    try:
        data = request.urlopen(url).read().decode('utf-8')
    except:
        print('WARNING: Could not retrieve url {}'.format(url))
        continue

    data = data.split('\n')
    for reminder in ('', 'T30M', 'T1H', 'T9H15M', 'T10H30M'):
        alarm = ''  # no alarm
        if reminder == 'T30M':  # half hour before 08:00
            alarm = '-0730'
        elif reminder == 'T1H':  # one hour before 08:00
            alarm = '-0700'
        elif reminder == 'T9H15M':  # nine hours and a quarter before
            alarm = '-2215'
        elif reminder == 'T10H30M':  # maximum, i.e. 21:30 previous day
            alarm = '-2130'
        temp = '{}{}.tmp.ics'.format(basename, alarm)
        calendar = open(temp, 'w')

        calendar_header = open('templates/calendar-header.txt', 'r')
        for line in calendar_header.readlines():
            calendar.write(line)

        for line in data:
            if '')[1]
                (day_name, day, month) = second.split(' ')
                month = month_to_number(month)

                calendar.write('{}{}\n'.format(
                    collection_header.strip(), name))
                date = datetime.strptime(
                    '{}{}{}'.format(year, month, day), '%Y%m%d')
                calendar.write('DTSTART;VALUE=DATE-TIME:{}T080000\n'.format(
                    date.strftime('%Y%m%d')))
                calendar.write('DTEND;VALUE=DATE-TIME:{}T080000\n'.format(
                    date.strftime('%Y%m%d')))
# for whole day event, remove the T080000 and add endtime one dat later
#                date += timedelta(days=1)
#                calendar.write('DTEND;VALUE=DATE:{}\n'.format(
#                    date.strftime('%Y%m%d')))
                if reminder != '':
                    calendar.write('BEGIN:VALARM\n')
                    calendar.write('ACTION:DISPLAY\n')
                    calendar.write('TRIGGER:-P{}\n'.format(reminder))
                    calendar.write('DESCRIPTION:{} aan de straat zetten\n'.format(name))
                    calendar.write('END:VALARM\n')

                calendar.write(collection_footer)

        calendar_footer = open('templates/calendar-footer.txt', 'r')
        for line in calendar_footer.readlines():
            calendar.write(line)
        rename(temp, 'calendars/{}'.format(temp.replace('.tmp', '')))

    sleep(uniform(5, 20))
<|endoftext|>


<commit_before>"""
Settings and SettingsTrees allow different classes of settings
(e.g. matplotlib specific styles and plot specific parameters) to be
defined separately from the core data structures and away from
visualization specific code.

There are three classes that form the settings system:

Cycle:

   Used to define infinite cycles over a finite set of elements, using
   either an explicit list or the matplotlib rcParams. For instance, a
   Cycle object can be used loop a set of display colors for multiple
   curves on a single axis.

Settings:

   Containers of arbitrary keyword values, including optional keyword
   validation, support for Cycle objects and inheritance.

SettingsTree:

   A subclass of AttrTree that is used to define the inheritance
   relationships between a collection of Settings objects. Each node
   of the tree supports a group of Settings objects and the leaf nodes
   inherit their keyword values from parent nodes up to the root.

"""

from .tree import AttrTree

class Cycle(object):
    """
    A simple container class that specifies cyclic settings. A typical
    example would be to cycle the curve colors in an Overlay composed
    of an arbitrary number of curves.

    A Cycles object accepts either a list of elements or an rckey
    string used to look up the elements in the matplotlib rcParams.
    """

    def __init__(self, elements=[], rckey='axes.color_cycle'):
        self.rckey = rckey
        if len(elements):
            self._elements = elements
        else:
            self._elements = None

    @property
    def elements(self):
        if self._elements is None:
            from matplotlib import rcParams
            return rcParams[self.rckey]
        else:
            return self._elements

    def __len__(self):
        return len(self.elements)

    def __repr__(self):
        return "Cycle(%s)" % self.elements

class Settings(object):
    """
    A Settings object holds a collection of keyword options. In
    addition, Settings support (optional) keyword validation as well
    as infinite indexing over the set of supplied cyclic values.

    Settings support inheritance of setting values via the __call__
    method. By calling a Settings object with additional keywords, you
    can create a new Settings object inheriting the parent settings.

    allowed_keywords: Optional list of strings corresponding to the
                      allowed keywords.
    viewable_name:    The name of object that the settings apply to.
    **kwargs:         The keyword items to be stored.
    """

    def __init__(self, allowed_keywords=None, viewable_name=None, **kwargs):
        self.allowed_keywords = sorted(allowed_keywords) if allowed_keywords else None
        self.viewable_name = viewable_name
        self.kwargs = kwargs

        for kwarg in kwargs:
            if allowed_keywords and kwarg not in allowed_keywords:
                raise KeyError("Invalid option %s, valid settings for %s are: %s"
                               % (repr(kwarg), self.viewable_name, str(self.allowed_keywords)))

        self._settings = self._expand_settings(kwargs)

    def __call__(self, allowed_keywords=None, viewable_name=None, **kwargs):
        """
        Create a new Settings object that inherits the parent settings.
        """
        allowed_keywords=self.allowed_keywords if allowed_keywords is None else allowed_keywords
        viewable_name=self.viewable_name if viewable_name is None else viewable_name

        inherited_style = dict(allowed_keywords=allowed_keywords,
                               viewable_name=viewable_name, **kwargs)
        return self.__class__(**dict(self.kwargs, inherited_style))

    def _expand_settings(self, kwargs):
        """
        Expand out Cycle objects into multiple sets of keyword values.

        To elaborate, the full Cartesian product over the supplied
        Cycle objects is expanded into a list, allowing infinite,
        cyclic indexing in the __getitem__ method."""
        filter_static = dict((k,v) for (k,v) in kwargs.items() if not isinstance(v, Cycle))
        filter_cycles = [(k,v) for (k,v) in kwargs.items() if isinstance(v, Cycle)]

        if not filter_cycles: return [kwargs]

        filter_names, filter_values = list(zip(*filter_cycles))
        if not all(len(c)==len(filter_values[0]) for c in filter_values):
            raise Exception("Cycle objects supplied with different lengths")

        cyclic_tuples = list(zip(*[val.elements for val in filter_values]))
        return [dict(zip(filter_names, tps), **filter_static) for tps in cyclic_tuples]

    def keys(self):
        "The keyword names across the supplied settings."
        return sorted(list(self.kwargs.keys()))

    def __getitem__(self, index):
        """
        Infinite cyclic indexing of settings over the integers,
        looping over the set of defined Cycle objects.
        """
        return dict(self._settings[index % len(self._settings)])

    @property
    def settings(self):
        "Access of the settings keywords when no cycles are defined."
        if len(self._settings) == 1:
            return dict(self._settings[0])
        else:
            raise Exception("The settings property may only be used with non-cyclic Settings.")

    def __repr__(self):
        kws = ', '.join("%s=%r" % (k,v) for (k,v) in self.kwargs.items())
        return "%s(%s)" % (self.__class__.__name__,  kws)

class SettingsTree(AttrTree):
    """
    A subclass of AttrTree that is used to define the inheritance
    relationships between a collection of Settings objects. Each node
    of the tree supports a group of Settings objects and the leaf nodes
    inherit their keyword values from parent nodes up to the root.

    get_closest: Returns either the path or node that is the closest
                 match to a supplied path.

    settings: Returns the inherited resulting Settings from a given
              group.
    """

    def __init__(self, items=None, identifier=None, parent=None, groups=None):
        if groups is None:
            raise ValueError('Please supply groups dictionary')
        self.__dict__['groups'] = groups
        self.__dict__['_instantiated'] = False
        AttrTree.__init__(self, items, identifier, parent)
        self.__dict__['_instantiated'] = True

    def _inherited_settings(self, group_name, settings):
        """
        Computes the inherited Settings object for the given group
        name from the current node given a new set of settings.
        """
        override_kwargs = settings.kwargs
        if not self._instantiated:
            override_kwargs['allowed_keywords'] = settings.allowed_keywords
            override_kwargs['viewable_name'] = settings.viewable_name

        if group_name not in self.groups:
            raise KeyError("Group %s not defined on SettingTree" % group_name)

        return self.groups[group_name](**override_kwargs)

    def __setattr__(self, identifier, groups):
        new_groups = {}
        if isinstance(groups, dict):
            for group_name, settings in groups.items():
                new_groups[group_name] = self._inherited_settings(group_name, settings)
        if new_groups:
            new_node = SettingsTree(None, identifier=identifier, parent=self, groups=new_groups)
        else:
            raise ValueError('SettingsTree only accepts a dictionary of Settings.')
        super(SettingsTree, self).__setattr__(identifier, new_node)

    def get_closest(self, path, mode='node'):
        """
        Get the closest node or path to an the arbitrary path that is
        supplied. The mode argument may be either 'node' or 'path'.
        """
        path = path.split('.') if isinstance(path, str) else list(path)
        item = self

        for idx, child in enumerate(path):
            matching_children = [c for c in item.children if child.endswith(c)]
            matching_children = sorted(matching_children, key=lambda x: len(x))
            if matching_children:
                item = item[matching_children[0]]
            else:
                continue
        return item if mode == 'node' else item.path

    def settings(self, key):
        """
        Using inheritance up to the root, get the complete Settings
        object for the given node and the specified group.
        """
        if self.groups.get(group, None) is None:
            return None
        if self.parent is None:
            return self.groups[group]
        return Settings(**dict(self.parent.settings(group),
                               **self.groups[group].kwargs))

    def _node_identifier(self, node):
        if node.parent is None:
            return '--+'
        else:
            values = ', '.join([str(group) for group in node.groups.values()])
            return "%s: %s" % (node.identifier, values)

    def __repr__(self):
        if len(self) == 0:
            return self._node_identifier(self)
        return super(SettingsTree, self).__repr__()
<commit_msg>All applicable classes in settings.py are now parameterized<commit_after>"""
Settings and SettingsTrees allow different classes of settings
(e.g. matplotlib specific styles and plot specific parameters) to be
defined separately from the core data structures and away from
visualization specific code.

There are three classes that form the settings system:

Cycle:

   Used to define infinite cycles over a finite set of elements, using
   either an explicit list or the matplotlib rcParams. For instance, a
   Cycle object can be used loop a set of display colors for multiple
   curves on a single axis.

Settings:

   Containers of arbitrary keyword values, including optional keyword
   validation, support for Cycle objects and inheritance.

SettingsTree:

   A subclass of AttrTree that is used to define the inheritance
   relationships between a collection of Settings objects. Each node
   of the tree supports a group of Settings objects and the leaf nodes
   inherit their keyword values from parent nodes up to the root.

"""

import param
from .tree import AttrTree

class Cycle(param.Parameterized):
    """
    A simple container class that specifies cyclic settings. A typical
    example would be to cycle the curve colors in an Overlay composed
    of an arbitrary number of curves.

    A Cycles object accepts either a list of items to cycle over or an
    rckey string used to look up the elements in the matplotlib
    rcParams dictionary.
    """

    items = param.List(default=None, allow_None=True,  doc="""
        If supplied, the explicit list of items to be cycled over.""")

    rckey = param.String(default='axes.color_cycle', doc="""
       If elements is None, this is the key in the matplotlib rcParams
       to use to get the cycle elements""")

    def __init__(self, **params):
        super(Cycle, self).__init__(**params)

    @property
    def elements(self):
        if self.items is None:
            from matplotlib import rcParams
            return rcParams[self.rckey]
        else:
            return self.items

    def __len__(self):
        return len(self.elements)

    def __repr__(self):
        return "Cycle(%s)" % self.elements

class Settings(param.Parameterized):
    """
    A Settings object holds a collection of keyword options. In
    addition, Settings support (optional) keyword validation as well
    as infinite indexing over the set of supplied cyclic values.

    Settings support inheritance of setting values via the __call__
    method. By calling a Settings object with additional keywords, you
    can create a new Settings object inheriting the parent settings.
    """

    allowed_keywords = param.List(default=None, allow_None=True, doc="""
       Optional list of strings corresponding to the allowed keywords.""")

    viewable_name = param.String(default=None, allow_None=True, doc="""
       The name of object that the settings apply to.""")

    def __init__(self, allowed_keywords=None, viewable_name=None, **kwargs):

        allowed_keywords = sorted(allowed_keywords) if allowed_keywords else None
        super(Settings, self).__init__(allowed_keywords=allowed_keywords,
                                       viewable_name = viewable_name)

        for kwarg in kwargs:
            if allowed_keywords and kwarg not in allowed_keywords:
                raise KeyError("Invalid option %s, valid settings for %s are: %s"
                               % (repr(kwarg), self.viewable_name, str(self.allowed_keywords)))

        self.kwargs = kwargs
        self._settings = self._expand_settings(kwargs)

    def __call__(self, allowed_keywords=None, viewable_name=None, **kwargs):
        """
        Create a new Settings object that inherits the parent settings.
        """
        allowed_keywords=self.allowed_keywords if allowed_keywords is None else allowed_keywords
        viewable_name=self.viewable_name if viewable_name is None else viewable_name

        inherited_style = dict(allowed_keywords=allowed_keywords,
                               viewable_name=viewable_name, **kwargs)
        return self.__class__(**dict(self.kwargs, inherited_style))

    def _expand_settings(self, kwargs):
        """
        Expand out Cycle objects into multiple sets of keyword values.

        To elaborate, the full Cartesian product over the supplied
        Cycle objects is expanded into a list, allowing infinite,
        cyclic indexing in the __getitem__ method."""
        filter_static = dict((k,v) for (k,v) in kwargs.items() if not isinstance(v, Cycle))
        filter_cycles = [(k,v) for (k,v) in kwargs.items() if isinstance(v, Cycle)]

        if not filter_cycles: return [kwargs]

        filter_names, filter_values = list(zip(*filter_cycles))
        if not all(len(c)==len(filter_values[0]) for c in filter_values):
            raise Exception("Cycle objects supplied with different lengths")

        cyclic_tuples = list(zip(*[val.elements for val in filter_values]))
        return [dict(zip(filter_names, tps), **filter_static) for tps in cyclic_tuples]

    def keys(self):
        "The keyword names across the supplied settings."
        return sorted(list(self.kwargs.keys()))

    def __getitem__(self, index):
        """
        Infinite cyclic indexing of settings over the integers,
        looping over the set of defined Cycle objects.
        """
        return dict(self._settings[index % len(self._settings)])

    @property
    def settings(self):
        "Access of the settings keywords when no cycles are defined."
        if len(self._settings) == 1:
            return dict(self._settings[0])
        else:
            raise Exception("The settings property may only be used with non-cyclic Settings.")

    def __repr__(self):
        kws = ', '.join("%s=%r" % (k,v) for (k,v) in self.kwargs.items())
        return "%s(%s)" % (self.__class__.__name__,  kws)

class SettingsTree(AttrTree):
    """
    A subclass of AttrTree that is used to define the inheritance
    relationships between a collection of Settings objects. Each node
    of the tree supports a group of Settings objects and the leaf nodes
    inherit their keyword values from parent nodes up to the root.

    Supports the ability to query the tree for the closest valid path,
    returning either a node or the closest path as required. In
    addition, the settings method computes a Settings object
    containing the result of inheritance for a given group up to the
    root of the tree.
    """

    def __init__(self, items=None, identifier=None, parent=None, groups=None):
        if groups is None:
            raise ValueError('Please supply groups dictionary')
        self.__dict__['groups'] = groups
        self.__dict__['_instantiated'] = False
        AttrTree.__init__(self, items, identifier, parent)
        self.__dict__['_instantiated'] = True

    def _inherited_settings(self, group_name, settings):
        """
        Computes the inherited Settings object for the given group
        name from the current node given a new set of settings.
        """
        override_kwargs = settings.kwargs
        if not self._instantiated:
            override_kwargs['allowed_keywords'] = settings.allowed_keywords
            override_kwargs['viewable_name'] = settings.viewable_name

        if group_name not in self.groups:
            raise KeyError("Group %s not defined on SettingTree" % group_name)

        return self.groups[group_name](**override_kwargs)

    def __setattr__(self, identifier, groups):
        new_groups = {}
        if isinstance(groups, dict):
            for group_name, settings in groups.items():
                new_groups[group_name] = self._inherited_settings(group_name, settings)
        if new_groups:
            new_node = SettingsTree(None, identifier=identifier, parent=self, groups=new_groups)
        else:
            raise ValueError('SettingsTree only accepts a dictionary of Settings.')
        super(SettingsTree, self).__setattr__(identifier, new_node)

    def get_closest(self, path, mode='node'):
        """
        Get the closest node or path to an the arbitrary path that is
        supplied. The mode argument may be either 'node' or 'path'.
        """
        path = path.split('.') if isinstance(path, str) else list(path)
        item = self

        for idx, child in enumerate(path):
            matching_children = [c for c in item.children if child.endswith(c)]
            matching_children = sorted(matching_children, key=lambda x: len(x))
            if matching_children:
                item = item[matching_children[0]]
            else:
                continue
        return item if mode == 'node' else item.path

    def settings(self, group):
        """
        Using inheritance up to the root, get the complete Settings
        object for the given node and the specified group.
        """
        if self.groups.get(group, None) is None:
            return None
        if self.parent is None:
            return self.groups[group]
        return Settings(**dict(self.parent.settings(group),
                               **self.groups[group].kwargs))

    def _node_identifier(self, node):
        if node.parent is None:
            return '--+'
        else:
            values = ', '.join([str(group) for group in node.groups.values()])
            return "%s: %s" % (node.identifier, values)

    def __repr__(self):
        if len(self) == 0:
            return self._node_identifier(self)
        return super(SettingsTree, self).__repr__()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""pypel.models, models for pypel.

THIS SOFTWARE IS UNDER BSD LICENSE.
Copyright (c) 2012 Daniele Tricoli <eriol@mornie.org>

Read LICENSE for more informations.
"""

import os.path

import pyexiv2

PRICE_KEY = 'Xmp.pypel.Price'
RETAILER_KEY = 'Xmp.pypel.Retailer'
NOTE_KEY = 'Xmp.pypel.Note'

SUPPORTED_EXT = ('.jpg', '.jpeg', '.png', '.eps')

pyexiv2.xmp.register_namespace('http://mornie.org/xmp/pypel/', 'pypel')

class DoesNotExist(IOError):
    """The file or directory does not exist"""

class IsADirectory(IOError):
    """This is a directory so you can't use it as a receipt"""

class ImageNotSupported(IOError):
    """Image is not supported"""

class Receipt(object):

    def __init__(self, file):
        self.file = file
        self._metadata = pyexiv2.ImageMetadata(file)
        self._metadata.read()

    @property
    def price(self):
        try:
            return float(self._metadata[PRICE_KEY].value)
        except KeyError:
            pass

    @price.setter
    def price(self, price):
        self._metadata[PRICE_KEY] = str(price)
        self._metadata.write()

    @price.deleter
    def price(self):
        try:
            del self._metadata[PRICE_KEY]
        except KeyError:
            pass
        self._metadata.write()

    @property
    def retailer(self):
        try:
            return self._metadata[RETAILER_KEY].value
        except KeyError:
            pass

    @retailer.setter
    def retailer(self, retailer):
        self._metadata[RETAILER_KEY] = retailer
        self._metadata.write()

    @retailer.deleter
    def retailer(self):
        try:
            del self._metadata[RETAILER_KEY]
        except KeyError:
            pass
        self._metadata.write()

    @property
    def note(self):
        try:
            return self._metadata[NOTE_KEY].value
        except KeyError:
            pass

    @note.setter
    def note(self, note):
        self._metadata[NOTE_KEY] = note
        self._metadata.write()

    @note.deleter
    def note(self):
        try:
            del self._metadata[NOTE_KEY]
        except KeyError:
            pass
        self._metadata.write()

def delete_metadata(receipt, price=None, retailer=None, note=None):
    """Delete XMP metadata."""
    if not price and not retailer and not note:
        del receipt.price
        del receipt.retailer
        del receipt.note

    if price:
        del receipt.price

    if retailer:
        del receipt.retailer

    if note:
        del receipt.note

def set_metadata(receipt, price=None, retailer=None, note=None):
    """Set XMP metadata."""
    if price:
        receipt.price = price

    if retailer:
        receipt.retailer = retailer

    if note:
        receipt.note = note

def make_receipt(file):
    """Check for errors and create a receipt"""

    if not os.path.exists(file):
        raise DoesNotExist('No such file or directory')
    elif os.path.isdir(file):
        raise IsADirectory('Is a directory')
    elif os.path.splitext(file)[1].lower() not in SUPPORTED_EXT:
        raise ImageNotSupported

    return Receipt(file)
<commit_msg>Refactored to use a class based model<commit_after># -*- coding: utf-8 -*-
"""pypel.models, models for pypel.

THIS SOFTWARE IS UNDER BSD LICENSE.
Copyright (c) 2012 Daniele Tricoli <eriol@mornie.org>

Read LICENSE for more informations.
"""

import os.path

import pyexiv2

XMP_KEY_PREFIX = 'Xmp.pypel.'

SUPPORTED_EXT = ('.jpg', '.jpeg', '.png', '.eps')

pyexiv2.xmp.register_namespace('http://mornie.org/xmp/pypel/', 'pypel')

class DoesNotExist(IOError):
    """The file or directory does not exist"""

class IsADirectory(IOError):
    """This is a directory so you can't use it as a receipt"""

class ImageNotSupported(IOError):
    """Image is not supported"""

class Field(object):
    """Base class for all field types"""

    casting_function = lambda self, value: value

    def __init__(self):
        self.name = None

    @property
    def key(self):
        return XMP_KEY_PREFIX + self.name

    def __get__(self, instance, owner):
        return self.cast(instance.get_value(self.key))

    def __set__(self, instance, value):
        instance.set_value(self.key, value)

    def __delete__(self, instance):
        instance.delete_value(self.key)

    def cast(self, value):
        if value is not None:
            return self.casting_function(value)

class CharField(Field):
    """A field for character strings"""

class FloatField(Field):
    """A field for float numbers"""

    casting_function = float

class Model(object):

    def __init__(self, file):
        self.file = file
        self._metadata = pyexiv2.ImageMetadata(file)
        self._metadata.read()

        for name in self.__class__.__dict__:
            field = self.__class__.__dict__[name]
            if isinstance(field, Field):
                setattr(field, 'name', name.title())

    def get_value(self, key):
        try:
            return self._metadata[key].value
        except KeyError:
            return None

    def set_value(self, key, value):
        self._metadata[key] = str(value)
        self._metadata.write()

    def delete_value(self, key):
        try:
            del self._metadata[key]
        except KeyError:
            pass
        self._metadata.write()

class Receipt(Model):
    price = FloatField()
    retailer = CharField()
    note = CharField()

def delete_metadata(receipt, price=None, retailer=None, note=None):
    """Delete XMP metadata."""
    if not price and not retailer and not note:
        del receipt.price
        del receipt.retailer
        del receipt.note

    if price:
        del receipt.price

    if retailer:
        del receipt.retailer

    if note:
        del receipt.note

def set_metadata(receipt, price=None, retailer=None, note=None):
    """Set XMP metadata."""
    if price:
        receipt.price = price

    if retailer:
        receipt.retailer = retailer

    if note:
        receipt.note = note

def make_receipt(file):
    """Check for errors and create a receipt"""

    if not os.path.exists(file):
        raise DoesNotExist('No such file or directory')
    elif os.path.isdir(file):
        raise IsADirectory('Is a directory')
    elif os.path.splitext(file)[1].lower() not in SUPPORTED_EXT:
        raise ImageNotSupported

    return Receipt(file)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# encoding: utf-8

# The MIT License (MIT)

# Copyright (c) 2015 CNRS

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# AUTHORS
# Hervé BREDIN - http://herve.niderb.fr

import numpy as np
import dlib
import cv2
from .openface import TorchWrap

SMALLEST_DEFAULT = 36

TEMPLATE = np.float32([
    (0.0792396913815, 0.339223741112), (0.0829219487236, 0.456955367943),
    (0.0967927109165, 0.575648016728), (0.122141515615, 0.691921601066),
    (0.168687863544, 0.800341263616), (0.239789390707, 0.895732504778),
    (0.325662452515, 0.977068762493), (0.422318282013, 1.04329000149),
    (0.531777802068, 1.06080371126), (0.641296298053, 1.03981924107),
    (0.738105872266, 0.972268833998), (0.824444363295, 0.889624082279),
    (0.894792677532, 0.792494155836), (0.939395486253, 0.681546643421),
    (0.96111933829, 0.562238253072), (0.970579841181, 0.441758925744),
    (0.971193274221, 0.322118743967), (0.163846223133, 0.249151738053),
    (0.21780354657, 0.204255863861), (0.291299351124, 0.192367318323),
    (0.367460241458, 0.203582210627), (0.4392945113, 0.233135599851),
    (0.586445962425, 0.228141644834), (0.660152671635, 0.195923841854),
    (0.737466449096, 0.182360984545), (0.813236546239, 0.192828009114),
    (0.8707571886, 0.235293377042), (0.51534533827, 0.31863546193),
    (0.516221448289, 0.396200446263), (0.517118861835, 0.473797687758),
    (0.51816430343, 0.553157797772), (0.433701156035, 0.604054457668),
    (0.475501237769, 0.62076344024), (0.520712933176, 0.634268222208),
    (0.565874114041, 0.618796581487), (0.607054002672, 0.60157671656),
    (0.252418718401, 0.331052263829), (0.298663015648, 0.302646354002),
    (0.355749724218, 0.303020650651), (0.403718978315, 0.33867711083),
    (0.352507175597, 0.349987615384), (0.296791759886, 0.350478978225),
    (0.631326076346, 0.334136672344), (0.679073381078, 0.29645404267),
    (0.73597236153, 0.294721285802), (0.782865376271, 0.321305281656),
    (0.740312274764, 0.341849376713), (0.68499850091, 0.343734332172),
    (0.353167761422, 0.746189164237), (0.414587777921, 0.719053835073),
    (0.477677654595, 0.706835892494), (0.522732900812, 0.717092275768),
    (0.569832064287, 0.705414478982), (0.635195811927, 0.71565572516),
    (0.69951672331, 0.739419187253), (0.639447159575, 0.805236879972),
    (0.576410514055, 0.835436670169), (0.525398405766, 0.841706377792),
    (0.47641545769, 0.837505914975), (0.41379548902, 0.810045601727),
    (0.380084785646, 0.749979603086), (0.477955996282, 0.74513234612),
    (0.523389793327, 0.748924302636), (0.571057789237, 0.74332894691),
    (0.672409137852, 0.744177032192), (0.572539621444, 0.776609286626),
    (0.5240106503, 0.783370783245), (0.477561227414, 0.778476346951)])

m, M = np.min(TEMPLATE, axis=0), np.max(TEMPLATE, axis=0)
MINMAX_TEMPLATE = (TEMPLATE - m) / (M - m)

EYES_AND_BOTTOM_LIP = np.array([39, 42, 57])
EYES_AND_NOSE = np.array([39, 42, 33])
EYES_AND_MOUTH = np.array([39, 42, 48, 54])

class Face(object):

    def __init__(self, landmarks=None, smallest=SMALLEST_DEFAULT,
                 normalization='affine', openface=None, size=96):
        """Face detection

        Parameters
        ----------
        landmarks : str
            Path to dlib's 68 facial landmarks predictor model.
        smallest : int
            Approximate size in pixel of the smallest face that should be
            detected.
        size : int
            Size of the normalized face thumbnail.
        normalization : {'affine', 'perspective', 'homography'}
            Normalization.
        openface : str
            Path to openface FaceNet model.
        """
        super(Face, self).__init__()

        # face detection
        self._faceDetector = dlib.get_frontal_face_detector()
        if smallest > SMALLEST_DEFAULT:
            self._upscale = 1
        else:
            self._upscale = int(np.ceil(SMALLEST_DEFAULT / smallest))

        # landmark detection
        if landmarks is not None:
            self._landmarksDetector = dlib.shape_predictor(landmarks)

        # normalization
        self.size = size
        self._landmarks = MINMAX_TEMPLATE * self.size
        self.normalization = normalization

        if openface is not None:
            self._net = TorchWrap(model=openface, size=self.size, cuda=False)

    # face detection

    def iterfaces(self, rgb):
        """Iterate over all detected faces"""
        for face in self._faceDetector(rgb, self._upscale):
            yield face

    # landmarks detection

    def _get_landmarks(self, rgb, face):
        """Return facial landmarks"""
        points = self._landmarksDetector(rgb, face).parts()
        landmarks = np.float32([(p.x, p.y) for p in points])
        return landmarks

    def landmarks(self, rgb, face):
        return self._get_landmarks(rgb, face)

    # face normalization

    def _homography(self, image, landmarks):
        H, _ = cv2.findHomography(landmarks, self._landmarks)
        normalized = cv2.warpPerspective(image, H, (self.size, self.size))
        return normalized

    def _affine(self, image, landmarks):
        H = cv2.getAffineTransform(
            landmarks[EYES_AND_BOTTOM_LIP],
            self._landmarks[EYES_AND_BOTTOM_LIP])
        normalized = cv2.warpAffine(image, H, (self.size, self.size))
        return normalized

    def _perspective(self, image, landmarks):
        H = cv2.getPerspectiveTransform(
            landmarks[EYES_AND_MOUTH],
            self._landmarks[EYES_AND_MOUTH])
        normalized = cv2.warpPerspective(image, H, (self.size, self.size))
        return normalized

    def _get_normalized(self, image, landmarks):
        """Return normalized face"""

        if self.normalization == 'affine':
            return self._affine(image, landmarks)

        if self.normalization == 'perspective':
            return self._perspective(image, landmarks)

        if self.normalization == 'homography':
            return self._homography(image, landmarks)

    def normalize(self, rgb, face):
        landmarks = self._get_landmarks(rgb, face)
        return self._get_normalized(rgb, landmarks)

    # openface feature extraction

    def _get_openface(self, normalized):
        return self._net.forwardImage(normalized)

    def openface(self, rgb, face):
        normalized_rgb = self.normalize(rgb, face)
        normalized_bgr = cv2.cvtColor(normalized_rgb, cv2.COLOR_BGR2RGB)
        return self._get_openface(normalized_bgr)

    # debugging

    def debug(self, image, face, landmarks):
        copy = image.copy()
        for x, y in landmarks:
            cv2.rectangle(copy, (x, y), (x, y), (0, 255, 0), 2)
        copy = copy[face.top():face.bottom(),
                    face.left():face.right()]
        copy = cv2.resize(copy, (self.size, self.size))
        return copy

    def __call__(self, rgb, return_landmarks=False, return_normalized=False,
                 return_openface=False, return_debug=False):
        """Iterate over all faces

        Parameters
        ----------
        rgb : np.array
            RGB image to be processed
        return_landmarks : bool
            Whether to yield landmarks. Defaults to False.
        return_normalized : bool
            Whether to yield normalized face. Defaults to False.
        return_openface : bool
            Whether to yield openface descriptor. Defaults to False.
        return_debug : bool
            Whether to yield debugging image. Defaults to False.
        """

        for face in self.iterfaces(rgb):

            _face = (face.left(), face.top(), face.right(), face.bottom())

            # yield face if nothing else is asked for
            if not (return_landmarks or return_normalized or
                    return_openface or return_debug):
                yield _face
                continue

            # always return face as first tuple element
            result = (_face, )

            # compute landmarks
            landmarks = self._get_landmarks(rgb, face)

            # normalize face
            if (return_normalized or return_openface):
                normalized = self._get_normalized(rgb, landmarks)

            # compute openface descriptor
            if return_openface:
                openface = self._get_openface(normalized)

            # compute debugging image
            if return_debug:
                debug = self.debug(rgb, face, landmarks)

            # append landmarks
            if return_landmarks:
                result = result + (landmarks, )

            # append normalized face
            if return_normalized:
                result = result + (normalized, )

            # append openface descriptor
            if return_openface:
                result = result + (openface, )

            # append debugging image
            if return_debug:
                result = result + (debug, )

            yield result
<commit_msg>doc: clarify RGB vs. BGR requirements<commit_after>#!/usr/bin/env python
# encoding: utf-8

# The MIT License (MIT)

# Copyright (c) 2015 CNRS

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

# AUTHORS
# Hervé BREDIN - http://herve.niderb.fr

import numpy as np
import dlib
import cv2
from .openface import TorchWrap

SMALLEST_DEFAULT = 36

TEMPLATE = np.float32([
    (0.0792396913815, 0.339223741112), (0.0829219487236, 0.456955367943),
    (0.0967927109165, 0.575648016728), (0.122141515615, 0.691921601066),
    (0.168687863544, 0.800341263616), (0.239789390707, 0.895732504778),
    (0.325662452515, 0.977068762493), (0.422318282013, 1.04329000149),
    (0.531777802068, 1.06080371126), (0.641296298053, 1.03981924107),
    (0.738105872266, 0.972268833998), (0.824444363295, 0.889624082279),
    (0.894792677532, 0.792494155836), (0.939395486253, 0.681546643421),
    (0.96111933829, 0.562238253072), (0.970579841181, 0.441758925744),
    (0.971193274221, 0.322118743967), (0.163846223133, 0.249151738053),
    (0.21780354657, 0.204255863861), (0.291299351124, 0.192367318323),
    (0.367460241458, 0.203582210627), (0.4392945113, 0.233135599851),
    (0.586445962425, 0.228141644834), (0.660152671635, 0.195923841854),
    (0.737466449096, 0.182360984545), (0.813236546239, 0.192828009114),
    (0.8707571886, 0.235293377042), (0.51534533827, 0.31863546193),
    (0.516221448289, 0.396200446263), (0.517118861835, 0.473797687758),
    (0.51816430343, 0.553157797772), (0.433701156035, 0.604054457668),
    (0.475501237769, 0.62076344024), (0.520712933176, 0.634268222208),
    (0.565874114041, 0.618796581487), (0.607054002672, 0.60157671656),
    (0.252418718401, 0.331052263829), (0.298663015648, 0.302646354002),
    (0.355749724218, 0.303020650651), (0.403718978315, 0.33867711083),
    (0.352507175597, 0.349987615384), (0.296791759886, 0.350478978225),
    (0.631326076346, 0.334136672344), (0.679073381078, 0.29645404267),
    (0.73597236153, 0.294721285802), (0.782865376271, 0.321305281656),
    (0.740312274764, 0.341849376713), (0.68499850091, 0.343734332172),
    (0.353167761422, 0.746189164237), (0.414587777921, 0.719053835073),
    (0.477677654595, 0.706835892494), (0.522732900812, 0.717092275768),
    (0.569832064287, 0.705414478982), (0.635195811927, 0.71565572516),
    (0.69951672331, 0.739419187253), (0.639447159575, 0.805236879972),
    (0.576410514055, 0.835436670169), (0.525398405766, 0.841706377792),
    (0.47641545769, 0.837505914975), (0.41379548902, 0.810045601727),
    (0.380084785646, 0.749979603086), (0.477955996282, 0.74513234612),
    (0.523389793327, 0.748924302636), (0.571057789237, 0.74332894691),
    (0.672409137852, 0.744177032192), (0.572539621444, 0.776609286626),
    (0.5240106503, 0.783370783245), (0.477561227414, 0.778476346951)])

m, M = np.min(TEMPLATE, axis=0), np.max(TEMPLATE, axis=0)
MINMAX_TEMPLATE = (TEMPLATE - m) / (M - m)

EYES_AND_BOTTOM_LIP = np.array([39, 42, 57])
EYES_AND_NOSE = np.array([39, 42, 33])
EYES_AND_MOUTH = np.array([39, 42, 48, 54])

class Face(object):

    def __init__(self, landmarks=None, smallest=SMALLEST_DEFAULT,
                 normalization='affine', openface=None, size=96):
        """Face detection

        Parameters
        ----------
        landmarks : str
            Path to dlib's 68 facial landmarks predictor model.
        smallest : int
            Approximate size in pixel of the smallest face that should be
            detected.
        size : int
            Size of the normalized face thumbnail.
        normalization : {'affine', 'perspective', 'homography'}
            Normalization.
        openface : str
            Path to openface FaceNet model.
        """
        super(Face, self).__init__()

        # face detection
        self._faceDetector = dlib.get_frontal_face_detector()
        if smallest > SMALLEST_DEFAULT:
            self._upscale = 1
        else:
            self._upscale = int(np.ceil(SMALLEST_DEFAULT / smallest))

        # landmark detection
        if landmarks is not None:
            self._landmarksDetector = dlib.shape_predictor(landmarks)

        # normalization
        self.size = size
        self._landmarks = MINMAX_TEMPLATE * self.size
        self.normalization = normalization

        if openface is not None:
            self._net = TorchWrap(model=openface, size=self.size, cuda=False)

    # face detection

    def iterfaces(self, rgb):
        """Iterate over all detected faces"""
        for face in self._faceDetector(rgb, self._upscale):
            yield face

    # landmarks detection

    def _get_landmarks(self, rgb, face):
        """Return facial landmarks"""
        points = self._landmarksDetector(rgb, face).parts()
        landmarks = np.float32([(p.x, p.y) for p in points])
        return landmarks

    def landmarks(self, rgb, face):
        return self._get_landmarks(rgb, face)

    # face normalization

    def _homography(self, rgb, landmarks):
        H, _ = cv2.findHomography(landmarks, self._landmarks)
        normalized = cv2.warpPerspective(rgb, H, (self.size, self.size))
        return normalized

    def _affine(self, rgb, landmarks):
        H = cv2.getAffineTransform(
            landmarks[EYES_AND_BOTTOM_LIP],
            self._landmarks[EYES_AND_BOTTOM_LIP])
        normalized = cv2.warpAffine(rgb, H, (self.size, self.size))
        return normalized

    def _perspective(self, rgb, landmarks):
        H = cv2.getPerspectiveTransform(
            landmarks[EYES_AND_MOUTH],
            self._landmarks[EYES_AND_MOUTH])
        normalized = cv2.warpPerspective(rgb, H, (self.size, self.size))
        return normalized

    def _get_normalized(self, rgb, landmarks):
        """Return normalized face"""

        if self.normalization == 'affine':
            return self._affine(rgb, landmarks)

        if self.normalization == 'perspective':
            return self._perspective(rgb, landmarks)

        if self.normalization == 'homography':
            return self._homography(rgb, landmarks)

    def normalize(self, rgb, face):
        landmarks = self._get_landmarks(rgb, face)
        return self._get_normalized(rgb, landmarks)

    # openface feature extraction

    def _get_openface(self, bgr):
        return self._net.forwardImage(bgr)

    def openface(self, rgb, face):
        normalized_rgb = self.normalize(rgb, face)
        normalized_bgr = cv2.cvtColor(normalized_rgb, cv2.COLOR_BGR2RGB)
        return self._get_openface(normalized_bgr)

    # debugging

    def debug(self, image, face, landmarks):
        copy = image.copy()
        for x, y in landmarks:
            cv2.rectangle(copy, (x, y), (x, y), (0, 255, 0), 2)
        copy = copy[face.top():face.bottom(),
                    face.left():face.right()]
        copy = cv2.resize(copy, (self.size, self.size))
        return copy

    def __call__(self, rgb, return_landmarks=False, return_normalized=False,
                 return_openface=False, return_debug=False):
        """Iterate over all faces

        Parameters
        ----------
        rgb : np.array
            RGB image to be processed
        return_landmarks : bool
            Whether to yield landmarks. Defaults to False.
        return_normalized : bool
            Whether to yield normalized face. Defaults to False.
        return_openface : bool
            Whether to yield openface descriptor. Defaults to False.
        return_debug : bool
            Whether to yield debugging image. Defaults to False.
        """

        for face in self.iterfaces(rgb):

            _face = (face.left(), face.top(), face.right(), face.bottom())

            # yield face if nothing else is asked for
            if not (return_landmarks or return_normalized or
                    return_openface or return_debug):
                yield _face
                continue

            # always return face as first tuple element
            result = (_face, )

            # compute landmarks
            landmarks = self._get_landmarks(rgb, face)

            # normalize face
            if (return_normalized or return_openface):
                normalized = self._get_normalized(rgb, landmarks)

            # compute openface descriptor
            if return_openface:
                openface = self._get_openface(normalized)

            # compute debugging image
            if return_debug:
                debug = self.debug(rgb, face, landmarks)

            # append landmarks
            if return_landmarks:
                result = result + (landmarks, )

            # append normalized face
            if return_normalized:
                result = result + (normalized, )

            # append openface descriptor
            if return_openface:
                result = result + (openface, )

            # append debugging image
            if return_debug:
                result = result + (debug, )

            yield result
<|endoftext|>