<commit_before>#!/usr/bin/python
#
# Copyright (c) SAS Institute Inc.
#
# 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.
#

"""
Test various methods of the RbuildConfiguration object.
"""

import os
from rbuild_test import rbuildhelp

import tempfile

from rbuild import rbuildcfg

class ConfigTest(rbuildhelp.RbuildHelper):
    def testWriteCheckoutFile(self):
        """
        Validate the output of the function used to write the rbuildrc
        in new product checkouts.
        """
        config = rbuildcfg.RbuildConfiguration()
        config.configLine('name Mr. User')
        config.configLine('contact mr.user@foo.com')
        config.configLine('repositoryMap []')
        config.configLine('repositoryMap foo.com https://repo.foo.com/conary/')
        config.configLine('repositoryMap bar.com https://dev.bar.com/conary/')
        config.configLine('repositoryUser foo.com foouser foopassword')
        config.configLine('repositoryUser bar.com baruser barpassword')
        config.configLine('user someuser somepassword')
        config.configLine('serverUrl http://myrbuilder.foo')
        config.configLine('signatureKey ASDF')
        config.configLine('signatureKeyMap foo FDSA')
        config.configLine('quiet True')

        dumpFile = tempfile.NamedTemporaryFile()
        config.writeCheckoutFile(dumpFile.name)
        dumpFile.seek(0)
        dump = dumpFile.read()

        expected = '''# contact (Default: None) (At `rbuild init': mr.user@foo.com)
# name (Default: None) (At `rbuild init': Mr. User)
# pluginDirs (Default: /usr/share/rbuild/plugins:~/.rbuild/plugins.d) (At `rbuild init': /usr/share/rbuild/plugins:~/.rbuild/plugins.d)
# quiet (Default: False) (At `rbuild init': True)
# repositoryMap (Default: [])
repositoryMap             foo.com                   https://repo.foo.com/conary/
repositoryMap             bar.com                   https://dev.bar.com/conary/
# repositoryUser (Default: []) (At `rbuild init': foo.com foouser foopassword, bar.com baruser barpassword)
# rmakePluginDirs (Default: /usr/share/rmake/plugins:~/.rmake/plugins.d) (At `rbuild init': /usr/share/rmake/plugins:~/.rmake/plugins.d)
# rmakeUrl (Default: None) (At `rbuild init': None)
# serverUrl (Default: None) (At `rbuild init': http://myrbuilder.foo)
# signatureKey (Default: None) (At `rbuild init': ASDF)
# signatureKeyMap (Default: []) (At `rbuild init': foo FDSA)
'''

        self.failUnlessEqual(dump, expected)

    def testPluginConfigurations(self):
        # Dump config file
        cfgFile = os.path.join(self.workDir, "rbuildrc")
        self.rbuildCfg.store(file(cfgFile, "w"))
        # Add a few sections
        with file(cfgFile, "a") as f:
            f.write("""
[edit]
name Marvin the Martian
device Illudium Q-36 Explosive Space Modulator
[dummy-section]
dummy-option-1 1
dummy-option-2 2
""")
        self.rbuildCfg.__init__(readConfigFiles=False, ignoreErrors=True)
        self.rbuildCfg.read(cfgFile)

        from rbuild import handle
        _C = rbuildcfg.cfg
        # Mock plugin loading so we can add some config options to one
        # of the plugins
        origLoadPluginFromFileName = handle.pluginloader.PluginManager.loadPluginFromFileName
        def mockedLoadPluginFromFileName(slf, dir, fileName, *args, **kwargs):
            plugin = origLoadPluginFromFileName(slf, dir, fileName, *args, **kwargs)
            if plugin and plugin.name == 'edit':
                class MyPluginConfiguration(plugin.PluginConfiguration):
                    name = (_C.CfgString, 'Jean Valjean')
                    device = _C.CfgString
                self.mock(plugin, 'PluginConfiguration', MyPluginConfiguration)
            return plugin
        self.mock(handle.pluginloader.PluginManager, 'loadPluginFromFileName',
                mockedLoadPluginFromFileName)
        handle = self.getRbuildHandle()
        handle.Edit.registerCommands()
        handle.Edit.initialize()

        section = handle.getConfig().getSection('edit')
        self.assertEquals(section.name, 'Marvin the Martian')
        self.assertEquals(section.device, 'Illudium Q-36 Explosive Space Modulator')
        # The section is passed by reference to the plugin
        self.assertTrue(handle.Edit.pluginCfg is section)

        # Dump the handler's config. It should include just the edit
        # section
        newCfg = cfgFile + '.new'
        handle.getConfig().writeToFile(newCfg)
        self.assertEquals(file(newCfg).read(), file(newCfg).read())
<commit_msg>Fix test broken by conary config changes<commit_after>#!/usr/bin/python
#
# Copyright (c) SAS Institute Inc.
#
# 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.
#

"""
Test various methods of the RbuildConfiguration object.
"""

import os
from rbuild_test import rbuildhelp

import tempfile

from rbuild import rbuildcfg

class ConfigTest(rbuildhelp.RbuildHelper):
    def testWriteCheckoutFile(self):
        """
        Validate the output of the function used to write the rbuildrc
        in new product checkouts.
        """
        config = rbuildcfg.RbuildConfiguration()
        config.configLine('name Mr. User')
        config.configLine('contact mr.user@foo.com')
        config.configLine('repositoryMap []')
        config.configLine('repositoryMap bar.com https://dev.bar.com/conary/')
        config.configLine('repositoryMap foo.com https://repo.foo.com/conary/')
        config.configLine('repositoryUser bar.com baruser barpassword')
        config.configLine('repositoryUser foo.com foouser foopassword')
        config.configLine('user someuser somepassword')
        config.configLine('serverUrl http://myrbuilder.foo')
        config.configLine('signatureKey ASDF')
        config.configLine('signatureKeyMap foo FDSA')
        config.configLine('quiet True')

        dumpFile = tempfile.NamedTemporaryFile()
        config.writeCheckoutFile(dumpFile.name)
        dumpFile.seek(0)
        dump = dumpFile.read()

        expected = '''# contact (Default: None) (At `rbuild init': mr.user@foo.com)
# name (Default: None) (At `rbuild init': Mr. User)
# pluginDirs (Default: /usr/share/rbuild/plugins:~/.rbuild/plugins.d) (At `rbuild init': /usr/share/rbuild/plugins:~/.rbuild/plugins.d)
# quiet (Default: False) (At `rbuild init': True)
# repositoryMap (Default: [])
repositoryMap             bar.com                   https://dev.bar.com/conary/
repositoryMap             foo.com                   https://repo.foo.com/conary/
# repositoryUser (Default: []) (At `rbuild init': bar.com baruser barpassword, foo.com foouser foopassword)
# rmakePluginDirs (Default: /usr/share/rmake/plugins:~/.rmake/plugins.d) (At `rbuild init': /usr/share/rmake/plugins:~/.rmake/plugins.d)
# rmakeUrl (Default: None) (At `rbuild init': None)
# serverUrl (Default: None) (At `rbuild init': http://myrbuilder.foo)
# signatureKey (Default: None) (At `rbuild init': ASDF)
# signatureKeyMap (Default: []) (At `rbuild init': foo FDSA)
'''

        self.assertEqualWithDiff(dump, expected)

    def testPluginConfigurations(self):
        # Dump config file
        cfgFile = os.path.join(self.workDir, "rbuildrc")
        self.rbuildCfg.store(file(cfgFile, "w"))
        # Add a few sections
        with file(cfgFile, "a") as f:
            f.write("""
[edit]
name Marvin the Martian
device Illudium Q-36 Explosive Space Modulator
[dummy-section]
dummy-option-1 1
dummy-option-2 2
""")
        self.rbuildCfg.__init__(readConfigFiles=False, ignoreErrors=True)
        self.rbuildCfg.read(cfgFile)

        from rbuild import handle
        _C = rbuildcfg.cfg
        # Mock plugin loading so we can add some config options to one
        # of the plugins
        origLoadPluginFromFileName = handle.pluginloader.PluginManager.loadPluginFromFileName
        def mockedLoadPluginFromFileName(slf, dir, fileName, *args, **kwargs):
            plugin = origLoadPluginFromFileName(slf, dir, fileName, *args, **kwargs)
            if plugin and plugin.name == 'edit':
                class MyPluginConfiguration(plugin.PluginConfiguration):
                    name = (_C.CfgString, 'Jean Valjean')
                    device = _C.CfgString
                self.mock(plugin, 'PluginConfiguration', MyPluginConfiguration)
            return plugin
        self.mock(handle.pluginloader.PluginManager, 'loadPluginFromFileName',
                mockedLoadPluginFromFileName)
        handle = self.getRbuildHandle()
        handle.Edit.registerCommands()
        handle.Edit.initialize()

        section = handle.getConfig().getSection('edit')
        self.assertEquals(section.name, 'Marvin the Martian')
        self.assertEquals(section.device, 'Illudium Q-36 Explosive Space Modulator')
        # The section is passed by reference to the plugin
        self.assertTrue(handle.Edit.pluginCfg is section)

        # Dump the handler's config. It should include just the edit
        # section
        newCfg = cfgFile + '.new'
        handle.getConfig().writeToFile(newCfg)
        self.assertEquals(file(newCfg).read(), file(newCfg).read())
<|endoftext|>


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

# The MIT License (MIT)

# Copyright (c) 2016 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 itertools
import datetime

from keras.callbacks import Callback
from keras.models import model_from_yaml
from pyannote.audio.keras_utils import CUSTOM_OBJECTS

from pyannote.audio.generators.labels import FixedDurationSequences
from pyannote.audio.generators.labels import VariableDurationSequences

from pyannote.audio.embedding.utils import pdist, cdist, l2_normalize, get_range
from pyannote.metrics.binary_classification import det_curve
from pyannote.metrics.plot.binary_classification import plot_det_curve
from pyannote.metrics.plot.binary_classification import plot_distributions

import matplotlib
import warnings
with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    matplotlib.use('Agg')
import matplotlib.pyplot as plt

class UpdateGeneratorEmbedding(Callback):

    def __init__(self, generator, extract_embedding, name='embedding'):
        super(UpdateGeneratorEmbedding, self).__init__()
        self.generator = generator
        self.extract_embedding = extract_embedding
        self.name = name

    def on_train_begin(self, logs={}):
        current_embedding = self.extract_embedding(self.model)
        architecture = model_from_yaml(
            current_embedding.to_yaml(),
            custom_objects=CUSTOM_OBJECTS)
        current_weights = current_embedding.get_weights()

        from pyannote.audio.embedding.base import SequenceEmbedding
        sequence_embedding = SequenceEmbedding()

        sequence_embedding.embedding_ = architecture
        sequence_embedding.embedding_.set_weights(current_weights)
        setattr(self.generator, self.name, sequence_embedding)

    def on_batch_end(self, batch, logs={}):
        current_embedding = self.extract_embedding(self.model)
        current_weights = current_embedding.get_weights()
        sequence_embedding = getattr(self.generator, self.name)
        sequence_embedding.embedding_.set_weights(current_weights)

class SpeakerDiarizationValidation(Callback):

    def __init__(self, glue, protocol, subset, log_dir):
        super(SpeakerDiarizationValidation, self).__init__()

        self.distance = glue.distance
        self.extract_embedding = glue.extract_embedding
        self.log_dir = log_dir

        np.random.seed(1337)

        # initialize fixed duration sequence generator
        if glue.min_duration is None:
            # initialize fixed duration sequence generator
            generator = FixedDurationSequences(
                glue.feature_extractor,
                duration=glue.duration,
                step=glue.duration,
                batch_size=-1)
        else:
            # initialize variable duration sequence generator
            generator = VariableDurationSequences(
                glue.feature_extractor,
                max_duration=glue.duration,
                min_duration=glue.min_duration,
                batch_size=-1)

        # randomly select (at most) 100 sequences from each label to ensure
        # all labels have (more or less) the same weight in the evaluation
        file_generator = getattr(protocol, subset)()
        X, y = zip(*generator(file_generator))
        X = np.vstack(X)
        y = np.hstack(y)
        unique, y, counts = np.unique(y, return_inverse=True, return_counts=True)
        n_labels = len(unique)
        indices = []
        for label in range(n_labels):
            i = np.random.choice(np.where(y == label)[0], size=min(100, counts[label]), replace=False)
            indices.append(i)
        indices = np.hstack(indices)
        X, y = X[indices], y[indices, np.newaxis]

        # precompute same/different groundtruth
        self.y_ = pdist(y, metric='chebyshev') < 1
        self.X_ = X

        self.EER_TEMPLATE_ = '{epoch:04d} {now} {eer:5f}\n'
        self.eer_ = []

    def on_epoch_end(self, epoch, logs={}):

        # keep track of current time
        now = datetime.datetime.now().isoformat()

        embedding = self.extract_embedding(self.model)
        fX = embedding.predict(self.X_)
        distance = pdist(fX, metric=self.distance)
        prefix = self.log_dir + '/plot.{epoch:04d}'.format(epoch=epoch)

        # plot distance distribution every 20 epochs (and 10 first epochs)
        xlim = get_range(metric=self.distance)
        if (epoch < 10) or (epoch % 20 == 0):
            plot_distributions(self.y_, distance, prefix,
                               xlim=xlim, ymax=3, nbins=100, dpi=150)

        # plot DET curve once every 20 epochs (and 10 first epochs)
        if (epoch < 10) or (epoch % 20 == 0):
            eer = plot_det_curve(self.y_, distance, prefix,
                                 distances=True, dpi=150)
        else:
            eer = det_curve(self.y_, distance, distances=True)

        # store equal error rate in file
        mode = 'a' if epoch else 'w'
        with open(self.log_dir + '/eer.txt', mode=mode) as fp:
            fp.write(self.EER_TEMPLATE_.format(epoch=epoch, eer=eer, now=now))
            fp.flush()

        # plot eer = f(epoch)
        self.eer_.append(eer)
        best_epoch = np.argmin(self.eer_)
        best_value = np.min(self.eer_)
        fig = plt.figure()
        plt.plot(self.eer_, 'b')
        plt.plot([best_epoch], [best_value], 'bo')
        plt.plot([0, epoch], [best_value, best_value], 'k--')
        plt.grid(True)
        plt.xlabel('epoch')
        plt.ylabel('EER on test')
        TITLE = 'EER = {best_value:.5g} on test @ epoch #{best_epoch:d}'
        title = TITLE.format(best_value=best_value, best_epoch=best_epoch)
        plt.title(title)
        plt.tight_layout()
        plt.savefig(self.log_dir + '/eer.png', dpi=150)
        plt.close(fig)

class SpeakerRecognitionValidation(Callback):

    def __init__(self, glue, protocol, subset, log_dir):
        super(SpeakerRecognitionValidation, self).__init__()
        self.glue = glue
        self.protocol = protocol
        self.subset = subset
        self.log_dir = log_dir

        self.EER_TEMPLATE_ = '{epoch:04d} {now} {eer:5f}\n'
        self.eer_ = []

    def on_epoch_end(self, epoch, logs={}):

        # keep track of current time
        now = datetime.datetime.now().isoformat()
        prefix = self.log_dir + '/plot.{epoch:04d}'.format(epoch=epoch)

        from pyannote.audio.embedding.base import SequenceEmbedding
        sequence_embedding = SequenceEmbedding()
        sequence_embedding.embedding_ = self.glue.extract_embedding(self.model)

        from pyannote.audio.embedding.aggregation import \
            SequenceEmbeddingAggregation
        aggregation = SequenceEmbeddingAggregation(
            sequence_embedding,
            self.glue.feature_extractor,
            duration=self.glue.duration,
            min_duration=self.glue.min_duration,
            step=.5 * self.glue.duration,
            layer_index=-2)

        # TODO / pass layer_index as parameter

        aggregation.cache_preprocessed_ = False

        # embed enroll and test recordings

        method = '{subset}_enroll'.format(subset=self.subset)
        enroll = getattr(self.protocol, method)(yield_name=True)

        method = '{subset}_test'.format(subset=self.subset)
        test = getattr(self.protocol, method)(yield_name=True)

        fX = {}
        for name, item in itertools.chain(enroll, test):
                if name in fX:
                        continue
                wav = item['wav']
                sad = item['annotation']
                embeddings = aggregation.apply(wav, sad)
                fX[name] = np.sum(embeddings.data, axis=0)

        # perform trials

        method = '{subset}_keys'.format(subset=self.subset)
        keys = getattr(self.protocol, method)()

        enroll_fX = l2_normalize(np.vstack([fX[name] for name in keys.index]))
        test_fX = l2_normalize(np.vstack([fX[name] for name in keys]))

        D = cdist(enroll_fX, test_fX, metric=self.glue.distance)

        y_true = []
        y_pred = []
        key_mapping = {0: None, -1: 0, 1: 1}
        for i, _ in enumerate(keys.index):
            for j, _ in enumerate(keys):
                y = key_mapping[keys.iloc[i, j]]
                if y is None:
                    continue

                y_true.append(y)
                y_pred.append(D[i, j])

        y_true = np.array(y_true)
        y_pred = np.array(y_pred)

        # plot DET curve once every 20 epochs (and 10 first epochs)
        if (epoch < 10) or (epoch % 20 == 0):
            eer = plot_det_curve(y_true, y_pred, prefix,
                                 distances=True, dpi=150)
        else:
            eer = det_curve(y_true, y_pred, prefix,
                            distances=True)

        # store equal error rate in file
        mode = 'a' if epoch else 'w'
        with open(self.log_dir + '/eer.txt', mode=mode) as fp:
            fp.write(self.EER_TEMPLATE_.format(epoch=epoch, eer=eer, now=now))
            fp.flush()

        # plot eer = f(epoch)
        self.eer_.append(eer)
        best_epoch = np.argmin(self.eer_)
        best_value = np.min(self.eer_)
        fig = plt.figure()
        plt.plot(self.eer_, 'b')
        plt.plot([best_epoch], [best_value], 'bo')
        plt.plot([0, epoch], [best_value, best_value], 'k--')
        plt.grid(True)
        plt.xlabel('epoch')
        plt.ylabel('EER on test')
        TITLE = 'EER = {best_value:.5g} on test @ epoch #{best_epoch:d}'
        title = TITLE.format(best_value=best_value, best_epoch=best_epoch)
        plt.title(title)
        plt.tight_layout()
        plt.savefig(self.log_dir + '/eer.png', dpi=150)
        plt.close(fig)
<commit_msg>fix: fix return values of et_curve<commit_after>#!/usr/bin/env python
# encoding: utf-8

# The MIT License (MIT)

# Copyright (c) 2016 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 itertools
import datetime

from keras.callbacks import Callback
from keras.models import model_from_yaml
from pyannote.audio.keras_utils import CUSTOM_OBJECTS

from pyannote.audio.generators.labels import FixedDurationSequences
from pyannote.audio.generators.labels import VariableDurationSequences

from pyannote.audio.embedding.utils import pdist, cdist, l2_normalize, get_range
from pyannote.metrics.binary_classification import det_curve
from pyannote.metrics.plot.binary_classification import plot_det_curve
from pyannote.metrics.plot.binary_classification import plot_distributions

import matplotlib
import warnings
with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    matplotlib.use('Agg')
import matplotlib.pyplot as plt

class UpdateGeneratorEmbedding(Callback):

    def __init__(self, generator, extract_embedding, name='embedding'):
        super(UpdateGeneratorEmbedding, self).__init__()
        self.generator = generator
        self.extract_embedding = extract_embedding
        self.name = name

    def on_train_begin(self, logs={}):
        current_embedding = self.extract_embedding(self.model)
        architecture = model_from_yaml(
            current_embedding.to_yaml(),
            custom_objects=CUSTOM_OBJECTS)
        current_weights = current_embedding.get_weights()

        from pyannote.audio.embedding.base import SequenceEmbedding
        sequence_embedding = SequenceEmbedding()

        sequence_embedding.embedding_ = architecture
        sequence_embedding.embedding_.set_weights(current_weights)
        setattr(self.generator, self.name, sequence_embedding)

    def on_batch_end(self, batch, logs={}):
        current_embedding = self.extract_embedding(self.model)
        current_weights = current_embedding.get_weights()
        sequence_embedding = getattr(self.generator, self.name)
        sequence_embedding.embedding_.set_weights(current_weights)

class SpeakerDiarizationValidation(Callback):

    def __init__(self, glue, protocol, subset, log_dir):
        super(SpeakerDiarizationValidation, self).__init__()

        self.distance = glue.distance
        self.extract_embedding = glue.extract_embedding
        self.log_dir = log_dir

        np.random.seed(1337)

        # initialize fixed duration sequence generator
        if glue.min_duration is None:
            # initialize fixed duration sequence generator
            generator = FixedDurationSequences(
                glue.feature_extractor,
                duration=glue.duration,
                step=glue.duration,
                batch_size=-1)
        else:
            # initialize variable duration sequence generator
            generator = VariableDurationSequences(
                glue.feature_extractor,
                max_duration=glue.duration,
                min_duration=glue.min_duration,
                batch_size=-1)

        # randomly select (at most) 100 sequences from each label to ensure
        # all labels have (more or less) the same weight in the evaluation
        file_generator = getattr(protocol, subset)()
        X, y = zip(*generator(file_generator))
        X = np.vstack(X)
        y = np.hstack(y)
        unique, y, counts = np.unique(y, return_inverse=True, return_counts=True)
        n_labels = len(unique)
        indices = []
        for label in range(n_labels):
            i = np.random.choice(np.where(y == label)[0], size=min(100, counts[label]), replace=False)
            indices.append(i)
        indices = np.hstack(indices)
        X, y = X[indices], y[indices, np.newaxis]

        # precompute same/different groundtruth
        self.y_ = pdist(y, metric='chebyshev') < 1
        self.X_ = X

        self.EER_TEMPLATE_ = '{epoch:04d} {now} {eer:5f}\n'
        self.eer_ = []

    def on_epoch_end(self, epoch, logs={}):

        # keep track of current time
        now = datetime.datetime.now().isoformat()

        embedding = self.extract_embedding(self.model)
        fX = embedding.predict(self.X_)
        distance = pdist(fX, metric=self.distance)
        prefix = self.log_dir + '/plot.{epoch:04d}'.format(epoch=epoch)

        # plot distance distribution every 20 epochs (and 10 first epochs)
        xlim = get_range(metric=self.distance)
        if (epoch < 10) or (epoch % 20 == 0):
            plot_distributions(self.y_, distance, prefix,
                               xlim=xlim, ymax=3, nbins=100, dpi=150)

        # plot DET curve once every 20 epochs (and 10 first epochs)
        if (epoch < 10) or (epoch % 20 == 0):
            eer = plot_det_curve(self.y_, distance, prefix,
                                 distances=True, dpi=150)
        else:
            _, _, _, eer = det_curve(self.y_, distance, distances=True)

        # store equal error rate in file
        mode = 'a' if epoch else 'w'
        with open(self.log_dir + '/eer.txt', mode=mode) as fp:
            fp.write(self.EER_TEMPLATE_.format(epoch=epoch, eer=eer, now=now))
            fp.flush()

        # plot eer = f(epoch)
        self.eer_.append(eer)
        best_epoch = np.argmin(self.eer_)
        best_value = np.min(self.eer_)
        fig = plt.figure()
        plt.plot(self.eer_, 'b')
        plt.plot([best_epoch], [best_value], 'bo')
        plt.plot([0, epoch], [best_value, best_value], 'k--')
        plt.grid(True)
        plt.xlabel('epoch')
        plt.ylabel('EER on test')
        TITLE = 'EER = {best_value:.5g} on test @ epoch #{best_epoch:d}'
        title = TITLE.format(best_value=best_value, best_epoch=best_epoch)
        plt.title(title)
        plt.tight_layout()
        plt.savefig(self.log_dir + '/eer.png', dpi=150)
        plt.close(fig)

class SpeakerRecognitionValidation(Callback):

    def __init__(self, glue, protocol, subset, log_dir):
        super(SpeakerRecognitionValidation, self).__init__()
        self.glue = glue
        self.protocol = protocol
        self.subset = subset
        self.log_dir = log_dir

        self.EER_TEMPLATE_ = '{epoch:04d} {now} {eer:5f}\n'
        self.eer_ = []

    def on_epoch_end(self, epoch, logs={}):

        # keep track of current time
        now = datetime.datetime.now().isoformat()
        prefix = self.log_dir + '/plot.{epoch:04d}'.format(epoch=epoch)

        from pyannote.audio.embedding.base import SequenceEmbedding
        sequence_embedding = SequenceEmbedding()
        sequence_embedding.embedding_ = self.glue.extract_embedding(self.model)

        from pyannote.audio.embedding.aggregation import \
            SequenceEmbeddingAggregation
        aggregation = SequenceEmbeddingAggregation(
            sequence_embedding,
            self.glue.feature_extractor,
            duration=self.glue.duration,
            min_duration=self.glue.min_duration,
            step=.5 * self.glue.duration,
            layer_index=-2)

        # TODO / pass layer_index as parameter

        aggregation.cache_preprocessed_ = False

        # embed enroll and test recordings

        method = '{subset}_enroll'.format(subset=self.subset)
        enroll = getattr(self.protocol, method)(yield_name=True)

        method = '{subset}_test'.format(subset=self.subset)
        test = getattr(self.protocol, method)(yield_name=True)

        fX = {}
        for name, item in itertools.chain(enroll, test):
                if name in fX:
                        continue
                wav = item['wav']
                sad = item['annotation']
                embeddings = aggregation.apply(wav, sad)
                fX[name] = np.sum(embeddings.data, axis=0)

        # perform trials

        method = '{subset}_keys'.format(subset=self.subset)
        keys = getattr(self.protocol, method)()

        enroll_fX = l2_normalize(np.vstack([fX[name] for name in keys.index]))
        test_fX = l2_normalize(np.vstack([fX[name] for name in keys]))

        D = cdist(enroll_fX, test_fX, metric=self.glue.distance)

        y_true = []
        y_pred = []
        key_mapping = {0: None, -1: 0, 1: 1}
        for i, _ in enumerate(keys.index):
            for j, _ in enumerate(keys):
                y = key_mapping[keys.iloc[i, j]]
                if y is None:
                    continue

                y_true.append(y)
                y_pred.append(D[i, j])

        y_true = np.array(y_true)
        y_pred = np.array(y_pred)

        # plot DET curve once every 20 epochs (and 10 first epochs)
        if (epoch < 10) or (epoch % 20 == 0):
            eer = plot_det_curve(y_true, y_pred, prefix,
                                 distances=True, dpi=150)
        else:
            _, _, _, eer = det_curve(y_true, y_pred, prefix, distances=True)

        # store equal error rate in file
        mode = 'a' if epoch else 'w'
        with open(self.log_dir + '/eer.txt', mode=mode) as fp:
            fp.write(self.EER_TEMPLATE_.format(epoch=epoch, eer=eer, now=now))
            fp.flush()

        # plot eer = f(epoch)
        self.eer_.append(eer)
        best_epoch = np.argmin(self.eer_)
        best_value = np.min(self.eer_)
        fig = plt.figure()
        plt.plot(self.eer_, 'b')
        plt.plot([best_epoch], [best_value], 'bo')
        plt.plot([0, epoch], [best_value, best_value], 'k--')
        plt.grid(True)
        plt.xlabel('epoch')
        plt.ylabel('EER on test')
        TITLE = 'EER = {best_value:.5g} on test @ epoch #{best_epoch:d}'
        title = TITLE.format(best_value=best_value, best_epoch=best_epoch)
        plt.title(title)
        plt.tight_layout()
        plt.savefig(self.log_dir + '/eer.png', dpi=150)
        plt.close(fig)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# ----------------------------------------------------------------------------
# pyglet
# Copyright (c) 2006-2007 Alex Holkner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#  * Neither the name of the pyglet nor the names of its
#    contributors may be used to endorse or promote products
#    derived from this software without specific prior written
#    permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------

'''Linux ALSA audio implementation.
'''

__docformat__ = 'restructuredtext'
__version__ = '$Id$'

import ctypes

import pyglet
from pyglet.media import AudioPlayer, Listener, MediaException

from pyglet.media.drivers.alsa import asound

alsa_debug = None
if pyglet.options['debug_media']:
    alsa_debug = 'alsa.log'

class ALSAException(MediaException):
    pass

def check(err):
    if err < 0:
        raise ALSAException(asound.snd_strerror(err))
    return err

class ALSAAudioPlayer(AudioPlayer):
    _device_name = 'plug:front'
    _buffer_time = 0.3
    _min_write_bytes = 10000

    def __init__(self, audio_format):
        super(ALSAAudioPlayer, self).__init__(audio_format)

        format = {
            8:  asound.SND_PCM_FORMAT_U8,
            16: asound.SND_PCM_FORMAT_S16,
            24: asound.SND_PCM_FORMAT_S24,  # probably won't work
            32: asound.SND_PCM_FORMAT_S32
        }.get(audio_format.sample_size)
        if format is None:
            raise ALSAException('Unsupported audio format.')

        self.pcm = ctypes.POINTER(asound.snd_pcm_t)()
        self.hwparams = ctypes.POINTER(asound.snd_pcm_hw_params_t)()
        self.swparams = ctypes.POINTER(asound.snd_pcm_sw_params_t)()

        check(asound.snd_pcm_open(ctypes.byref(self.pcm),
                                  self._device_name,
                                  asound.SND_PCM_STREAM_PLAYBACK,
                                  asound.SND_PCM_NONBLOCK))
        check(asound.snd_pcm_hw_params_malloc(ctypes.byref(self.hwparams)))
        check(asound.snd_pcm_sw_params_malloc(ctypes.byref(self.swparams)))
        check(asound.snd_pcm_hw_params_any(self.pcm, self.hwparams))

        check(asound.snd_pcm_hw_params_set_access(self.pcm, self.hwparams,
            asound.SND_PCM_ACCESS_RW_INTERLEAVED))
        check(asound.snd_pcm_hw_params_set_format(self.pcm, self.hwparams,
            format))
        check(asound.snd_pcm_hw_params_set_channels(self.pcm, self.hwparams,
            audio_format.channels))

        # ---

        # Set sample rate.  Issue 149: This doesn't work for anthony.briggs:
        #check(asound.snd_pcm_hw_params_set_rate(self.pcm, self.hwparams,
        #    audio_format.sample_rate, 0))

        # Apparently by default only real hardware sample rates are supported,
        # so this disables that restriction (presumably allowing ALSA to
        # resample the audio -- but this is not in the docs).  Try
        # uncommenting this.
        #check(asound.snd_pcm_hw_params_set_rate_resample(self.pcm,
        #    self.hwparams, 0))

        # This should be less picky about the exact sample rate, however the
        # output will sound incorrect (we can address that later if this
        # works):
        rate = ctypes.c_uint(audio_format.sample_rate)
        if alsa_debug:
            print 'Requested sample rate', rate.value
        dir = ctypes.c_int(0)
        check(asound.snd_pcm_hw_params_set_rate_near(self.pcm, self.hwparams,
            rate, dir))
        if alsa_debug:
            print 'Actual sample rate', rate.value

        # ---

        # Set buffer size.  Issue 149: This doesn't work for andy.larrymite:
        #check(asound.snd_pcm_hw_params_set_buffer_size(self.pcm, self.hwparams,
        #    int(self._buffer_time * audio_format.sample_rate)))

        # This should be less picky about the exact buffer size, and will
        # hopefully choose a buffer size that is compatible.  This will
        # produce bad sounding audio or even crash, but should at least get
        # us past this configuration step:
        buffer_size = int(self._buffer_time * audio_format.sample_rate)
        if alsa_debug:
            print 'Requested buffer size', buffer_size
        bs = asound.snd_pcm_uframes_t(buffer_size)
        check(asound.snd_pcm_hw_params_set_buffer_size_near(self.pcm,
            self.hwparams, bs))
        if alsa_debug:
            print 'Actual buffer size', bs.value

        # Note we could also have used
        # snd_pcm_hw_params_get_buffer_size_max(..) to obtain the maximum
        # buffer size.  All in good time...

        # ---

        check(asound.snd_pcm_hw_params(self.pcm, self.hwparams))

        if alsa_debug:
            asound.snd_output_printf(debug_output,
                'New device: %s\n' % self._device_name)
            check(asound.snd_pcm_dump(self.pcm, debug_output))

        self.can_pause = asound.snd_pcm_hw_params_can_pause(self.hwparams)

        # List of (alsatime, timestamp)
        self._timestamps = []
        self._stop_alsatime = None
        self._eos_count = 0
        self._playing = False

    def __del__(self):
        try:
            check(asound.snd_pcm_close(self.pcm))
        except (NameError, AttributeError):
            pass

    def get_write_size(self):
        state = asound.snd_pcm_state(self.pcm)
        if state == asound.SND_PCM_STATE_PAUSED:
            return 0

        avail = max(0, asound.snd_pcm_avail_update(self.pcm))
        bytes = avail * self.audio_format.bytes_per_sample
        if bytes < self._min_write_bytes:
            return 0
        return bytes

    def write(self, audio_data):
        samples = audio_data.length // self.audio_format.bytes_per_sample
        samples_out = asound.snd_pcm_writei(self.pcm, audio_data.data,
                                            samples)
        if samples_out < 0:
            if samples_out == -11: # EAGAIN
                return
            elif samples_out == -32: # EPIPE (xrun)
                check(asound.snd_pcm_prepare(self.pcm))
                return
            else:
                raise ALSAException(asound.snd_strerror(samples_out))

        delay = asound.snd_pcm_sframes_t()
        check(asound.snd_pcm_delay(self.pcm, delay))
        alsatime = self._get_asound_time() + \
            delay.value / float(self.audio_format.sample_rate)

        self._timestamps.append((alsatime, audio_data.timestamp))

        audio_data.consume(samples_out * self.audio_format.bytes_per_sample,
                           self.audio_format)

    def write_eos(self):
        if self._timestamps:
            self._timestamps.append((None, None))

    def write_end(self):
        pass

    def play(self):
        if self._playing:
            return

        state = asound.snd_pcm_state(self.pcm)
        if self.can_pause and state == asound.SND_PCM_STATE_PAUSED:
            check(asound.snd_pcm_pause(self.pcm, 0))
        elif state not in (asound.SND_PCM_STATE_RUNNING,
                           asound.SND_PCM_STATE_PREPARED):
            check(asound.snd_pcm_prepare(self.pcm))
        self._playing = True

        if self._stop_alsatime is not None:
            diff = self._get_asound_time() - self._stop_alsatime
            self._timestamps = [(a + diff, t) for a, t in self._timestamps]
            self._stop_alsatime = None

    def stop(self):
        if not self._playing:
            return

        if self.can_pause and self._playing:
            check(asound.snd_pcm_pause(self.pcm, 1))
            self._stop_alsatime = self._get_asound_time()
        else:
            # Hardware can't pause, so we'll just drop everything that's
            # been buffered.  Improvement could be to rebuffer data that
            # wasn't played.
            self.clear()
        self._playing = False

    def clear(self):
        check(asound.snd_pcm_drop(self.pcm))
        self._stop_alsatime = None
        self._timestamps = []

    def _get_asound_time(self):
        status = ctypes.POINTER(asound.snd_pcm_status_t)()
        timestamp = asound.snd_timestamp_t()

        check(asound.snd_pcm_status_malloc(ctypes.byref(status)))
        check(asound.snd_pcm_status(self.pcm, status))
        asound.snd_pcm_status_get_tstamp(status, ctypes.byref(timestamp))
        asound.snd_pcm_status_free(status)
        return timestamp.tv_sec + timestamp.tv_usec * 0.000001

    def pump(self):
        if self._stop_alsatime is not None:
            return

        alsatime = self._get_asound_time()
        try:
            while self._timestamps[0][0] < alsatime:
                self._timestamps.pop(0)
                while self._timestamps[0][0] is None:
                    self._eos_count += 1
                    self._timestamps.pop(0)
        except IndexError:
            pass

    def get_time(self):
        if self._stop_alsatime is None:
            alsatime = self._get_asound_time()
        else:
            alsatime = self._stop_alsatime

        if not self._timestamps:
            self._playing = False
            return 0.0

        alsats, ts = self._timestamps[0]
        t =  alsatime - alsats + ts
        return t

    def clear_eos(self):
        if self._eos_count > 0:
            self._eos_count -= 1
            return True
        return False

class ALSAListener(Listener):
    def _set_volume(self, volume):
        # TODO master volume
        self._volume = volume

    # All other properties are silently ignored.

    def _set_position(self, position):
        self._position = position

    def _set_velocity(self, velocity):
        self._velocity = velocity

    def _set_forward_orientation(self, orientation):
        self._forward_orientation = orientation

    def _set_up_orientation(self, orientation):
        self._up_orientation = orientation

    def _set_doppler_factor(self, factor):
        self._doppler_factor = factor

    def _set_speed_of_sound(self, speed_of_sound):
        self._speed_of_sound = speed_of_sound

def driver_init():
    global debug_output
    debug_output = ctypes.POINTER(asound.snd_output_t)()
    if alsa_debug:
        check(asound.snd_output_stdio_open(ctypes.byref(debug_output),
                                           alsa_debug,
                                           'w'))

driver_listener = ALSAListener()
driver_audio_player_class = ALSAAudioPlayer
<commit_msg>Default ALSA device is "default" not "plug:front" to fix problems with single-buffer audio cards.<commit_after>#!/usr/bin/env python
# ----------------------------------------------------------------------------
# pyglet
# Copyright (c) 2006-2007 Alex Holkner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#  * Neither the name of the pyglet nor the names of its
#    contributors may be used to endorse or promote products
#    derived from this software without specific prior written
#    permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------

'''Linux ALSA audio implementation.
'''

__docformat__ = 'restructuredtext'
__version__ = '$Id$'

import ctypes

import pyglet
from pyglet.media import AudioPlayer, Listener, MediaException

from pyglet.media.drivers.alsa import asound

alsa_debug = None
if pyglet.options['debug_media']:
    alsa_debug = 'alsa.log'

class ALSAException(MediaException):
    pass

def check(err):
    if err < 0:
        raise ALSAException(asound.snd_strerror(err))
    return err

class ALSAAudioPlayer(AudioPlayer):
    _device_name = 'default'
    _buffer_time = 0.3
    _min_write_bytes = 10000

    def __init__(self, audio_format):
        super(ALSAAudioPlayer, self).__init__(audio_format)

        format = {
            8:  asound.SND_PCM_FORMAT_U8,
            16: asound.SND_PCM_FORMAT_S16,
            24: asound.SND_PCM_FORMAT_S24,  # probably won't work
            32: asound.SND_PCM_FORMAT_S32
        }.get(audio_format.sample_size)
        if format is None:
            raise ALSAException('Unsupported audio format.')

        self.pcm = ctypes.POINTER(asound.snd_pcm_t)()
        self.hwparams = ctypes.POINTER(asound.snd_pcm_hw_params_t)()
        self.swparams = ctypes.POINTER(asound.snd_pcm_sw_params_t)()

        check(asound.snd_pcm_open(ctypes.byref(self.pcm),
                                  self._device_name,
                                  asound.SND_PCM_STREAM_PLAYBACK,
                                  asound.SND_PCM_NONBLOCK))
        check(asound.snd_pcm_hw_params_malloc(ctypes.byref(self.hwparams)))
        check(asound.snd_pcm_sw_params_malloc(ctypes.byref(self.swparams)))
        check(asound.snd_pcm_hw_params_any(self.pcm, self.hwparams))

        check(asound.snd_pcm_hw_params_set_access(self.pcm, self.hwparams,
            asound.SND_PCM_ACCESS_RW_INTERLEAVED))
        check(asound.snd_pcm_hw_params_set_format(self.pcm, self.hwparams,
            format))
        check(asound.snd_pcm_hw_params_set_channels(self.pcm, self.hwparams,
            audio_format.channels))

        # ---

        # Set sample rate.  Issue 149: This doesn't work for anthony.briggs:
        #check(asound.snd_pcm_hw_params_set_rate(self.pcm, self.hwparams,
        #    audio_format.sample_rate, 0))

        # Apparently by default only real hardware sample rates are supported,
        # so this disables that restriction (presumably allowing ALSA to
        # resample the audio -- but this is not in the docs).  Try
        # uncommenting this.
        #check(asound.snd_pcm_hw_params_set_rate_resample(self.pcm,
        #    self.hwparams, 0))

        # This should be less picky about the exact sample rate, however the
        # output will sound incorrect (we can address that later if this
        # works):
        rate = ctypes.c_uint(audio_format.sample_rate)
        if alsa_debug:
            print 'Requested sample rate', rate.value
        dir = ctypes.c_int(0)
        check(asound.snd_pcm_hw_params_set_rate_near(self.pcm, self.hwparams,
            rate, dir))
        if alsa_debug:
            print 'Actual sample rate', rate.value

        # ---

        # Set buffer size.  Issue 149: This doesn't work for andy.larrymite:
        #check(asound.snd_pcm_hw_params_set_buffer_size(self.pcm, self.hwparams,
        #    int(self._buffer_time * audio_format.sample_rate)))

        # This should be less picky about the exact buffer size, and will
        # hopefully choose a buffer size that is compatible.  This will
        # produce bad sounding audio or even crash, but should at least get
        # us past this configuration step:
        buffer_size = int(self._buffer_time * audio_format.sample_rate)
        if alsa_debug:
            print 'Requested buffer size', buffer_size
        bs = asound.snd_pcm_uframes_t(buffer_size)
        check(asound.snd_pcm_hw_params_set_buffer_size_near(self.pcm,
            self.hwparams, bs))
        if alsa_debug:
            print 'Actual buffer size', bs.value

        # Note we could also have used
        # snd_pcm_hw_params_get_buffer_size_max(..) to obtain the maximum
        # buffer size.  All in good time...

        # ---

        check(asound.snd_pcm_hw_params(self.pcm, self.hwparams))

        if alsa_debug:
            asound.snd_output_printf(debug_output,
                'New device: %s\n' % self._device_name)
            check(asound.snd_pcm_dump(self.pcm, debug_output))

        self.can_pause = asound.snd_pcm_hw_params_can_pause(self.hwparams)

        # List of (alsatime, timestamp)
        self._timestamps = []
        self._stop_alsatime = None
        self._eos_count = 0
        self._playing = False

    def __del__(self):
        try:
            check(asound.snd_pcm_close(self.pcm))
        except (NameError, AttributeError):
            pass

    def get_write_size(self):
        state = asound.snd_pcm_state(self.pcm)
        if state == asound.SND_PCM_STATE_PAUSED:
            return 0

        avail = max(0, asound.snd_pcm_avail_update(self.pcm))
        bytes = avail * self.audio_format.bytes_per_sample
        if bytes < self._min_write_bytes:
            return 0
        return bytes

    def write(self, audio_data):
        samples = audio_data.length // self.audio_format.bytes_per_sample
        samples_out = asound.snd_pcm_writei(self.pcm, audio_data.data,
                                            samples)
        if samples_out < 0:
            if samples_out == -11: # EAGAIN
                return
            elif samples_out == -32: # EPIPE (xrun)
                check(asound.snd_pcm_prepare(self.pcm))
                return
            else:
                raise ALSAException(asound.snd_strerror(samples_out))

        delay = asound.snd_pcm_sframes_t()
        check(asound.snd_pcm_delay(self.pcm, delay))
        alsatime = self._get_asound_time() + \
            delay.value / float(self.audio_format.sample_rate)

        self._timestamps.append((alsatime, audio_data.timestamp))

        audio_data.consume(samples_out * self.audio_format.bytes_per_sample,
                           self.audio_format)

    def write_eos(self):
        if self._timestamps:
            self._timestamps.append((None, None))

    def write_end(self):
        pass

    def play(self):
        if self._playing:
            return

        state = asound.snd_pcm_state(self.pcm)
        if self.can_pause and state == asound.SND_PCM_STATE_PAUSED:
            check(asound.snd_pcm_pause(self.pcm, 0))
        elif state not in (asound.SND_PCM_STATE_RUNNING,
                           asound.SND_PCM_STATE_PREPARED):
            check(asound.snd_pcm_prepare(self.pcm))
        self._playing = True

        if self._stop_alsatime is not None:
            diff = self._get_asound_time() - self._stop_alsatime
            self._timestamps = [(a + diff, t) for a, t in self._timestamps]
            self._stop_alsatime = None

    def stop(self):
        if not self._playing:
            return

        if self.can_pause and self._playing:
            check(asound.snd_pcm_pause(self.pcm, 1))
            self._stop_alsatime = self._get_asound_time()
        else:
            # Hardware can't pause, so we'll just drop everything that's
            # been buffered.  Improvement could be to rebuffer data that
            # wasn't played.
            self.clear()
        self._playing = False

    def clear(self):
        check(asound.snd_pcm_drop(self.pcm))
        self._stop_alsatime = None
        self._timestamps = []

    def _get_asound_time(self):
        status = ctypes.POINTER(asound.snd_pcm_status_t)()
        timestamp = asound.snd_timestamp_t()

        check(asound.snd_pcm_status_malloc(ctypes.byref(status)))
        check(asound.snd_pcm_status(self.pcm, status))
        asound.snd_pcm_status_get_tstamp(status, ctypes.byref(timestamp))
        asound.snd_pcm_status_free(status)
        return timestamp.tv_sec + timestamp.tv_usec * 0.000001

    def pump(self):
        if self._stop_alsatime is not None:
            return

        alsatime = self._get_asound_time()
        try:
            while self._timestamps[0][0] < alsatime:
                self._timestamps.pop(0)
                while self._timestamps[0][0] is None:
                    self._eos_count += 1
                    self._timestamps.pop(0)
        except IndexError:
            pass

    def get_time(self):
        if self._stop_alsatime is None:
            alsatime = self._get_asound_time()
        else:
            alsatime = self._stop_alsatime

        if not self._timestamps:
            self._playing = False
            return 0.0

        alsats, ts = self._timestamps[0]
        t =  alsatime - alsats + ts
        return t

    def clear_eos(self):
        if self._eos_count > 0:
            self._eos_count -= 1
            return True
        return False

class ALSAListener(Listener):
    def _set_volume(self, volume):
        # TODO master volume
        self._volume = volume

    # All other properties are silently ignored.

    def _set_position(self, position):
        self._position = position

    def _set_velocity(self, velocity):
        self._velocity = velocity

    def _set_forward_orientation(self, orientation):
        self._forward_orientation = orientation

    def _set_up_orientation(self, orientation):
        self._up_orientation = orientation

    def _set_doppler_factor(self, factor):
        self._doppler_factor = factor

    def _set_speed_of_sound(self, speed_of_sound):
        self._speed_of_sound = speed_of_sound

def driver_init():
    global debug_output
    debug_output = ctypes.POINTER(asound.snd_output_t)()
    if alsa_debug:
        check(asound.snd_output_stdio_open(ctypes.byref(debug_output),
                                           alsa_debug,
                                           'w'))

driver_listener = ALSAListener()
driver_audio_player_class = ALSAAudioPlayer
<|endoftext|>


<commit_before>from projects.exceptions import ProjectImportError
from vcs_support.base import BaseVCS, VCSVersion

class Backend(BaseVCS):
    supports_tags = True
    fallback_branch = 'tip'

    def update(self):
        super(Backend, self).update()
        retcode = self._run_command('hg', 'status')[0]
        if retcode == 0:
            self._pull()
        else:
            self._clone()

    def _pull(self):
        retcode = self._run_command('hg', 'pull')[0]
        if retcode != 0:
            raise ProjectImportError(
                "Failed to get code from '%s' (hg pull): %s" % (self.repo_url, retcode)
            )
        retcode = self._run_command('hg', 'update', '-C')[0]
        if retcode != 0:
            raise ProjectImportError(
                "Failed to get code from '%s' (hg update): %s" % (self.repo_url, retcode)
            )

    def _clone(self):
        retcode = self._run_command('hg', 'clone', self.repo_url, '.')[0]
        if retcode != 0:
            raise ProjectImportError(
                "Failed to get code from '%s' (hg clone): %s" % (self.repo_url, retcode)
            )

    def get_tags(self):
        retcode, stdout = self._run_command('hg', 'tags')[:2]
        # error (or no tags found)
        if retcode != 0:
            return []
        return self._parse_tags(stdout)

    def _parse_tags(self, data):
        """
        Parses output of show-ref --tags, eg:

        tip                              278:c4b2d21db51a
        0.2.2                            152:6b0364d98837
        0.2.1                            117:a14b7b6ffa03
        0.1                               50:30c2c6b3a055
        """
        # parse the lines into a list of tuples (commit-hash, tag ref name)
        raw_tags = [line.rsplit(' ', 1) for line in data.strip('\n').split('\n')]
        vcs_tags = []
        for name, commit in raw_tags:
            clean_name = name.strip(' ')
            if clean_name == 'tip':
                continue
            commit_hash = commit.split(':')[1]
            vcs_tags.append(VCSVersion(self, commit_hash, clean_name))
        return vcs_tags

    def checkout(self, identifier=None):
        super(Backend, self).checkout()
        if not identifier:
            identifier = 'tip'
        retcode = self._run_command('hg', 'status')[0]
        if retcode == 0:
            self._pull()
        else:
            self._clone()
<commit_msg>Fix hg support. Alex Gaynor says we need tests.<commit_after>from projects.exceptions import ProjectImportError
from vcs_support.base import BaseVCS, VCSVersion

class Backend(BaseVCS):
    supports_tags = True
    fallback_branch = 'tip'

    def update(self):
        super(Backend, self).update()
        retcode = self._run_command('hg', 'status')[0]
        if retcode == 0:
            self._pull()
        else:
            self._clone()

    def _pull(self):
        retcode = self._run_command('hg', 'pull')[0]
        if retcode != 0:
            raise ProjectImportError(
                "Failed to get code from '%s' (hg pull): %s" % (self.repo_url, retcode)
            )
        retcode = self._run_command('hg', 'update', '-C')[0]
        if retcode != 0:
            raise ProjectImportError(
                "Failed to get code from '%s' (hg update): %s" % (self.repo_url, retcode)
            )

    def _clone(self):
        retcode = self._run_command('hg', 'clone', self.repo_url, '.')[0]
        if retcode != 0:
            raise ProjectImportError(
                "Failed to get code from '%s' (hg clone): %s" % (self.repo_url, retcode)
            )

    def get_tags(self):
        retcode, stdout = self._run_command('hg', 'tags')[:2]
        # error (or no tags found)
        if retcode != 0:
            return []
        return self._parse_tags(stdout)

    def _parse_tags(self, data):
        """
        Parses output of show-ref --tags, eg:

        tip                              278:c4b2d21db51a
        0.2.2                            152:6b0364d98837
        0.2.1                            117:a14b7b6ffa03
        0.1                               50:30c2c6b3a055
        """
        # parse the lines into a list of tuples (commit-hash, tag ref name)
        raw_tags = [line.rsplit(' ', 1) for line in data.strip('\n').split('\n')]
        vcs_tags = []
        for name, commit in raw_tags:
            clean_name = name.strip(' ')
            if clean_name == 'tip':
                continue
            commit_hash = commit.split(':')[1]
            vcs_tags.append(VCSVersion(self, commit_hash, clean_name))
        return vcs_tags

    def checkout(self, identifier=None):
        super(Backend, self).checkout()
        if not identifier:
            identifier = 'tip'
        retcode = self._run_command('hg', 'status')[0]
        if retcode == 0:
            self._run_command('hg', 'update', identifier)
        else:
            self._clone()
<|endoftext|>


<commit_before>import functools
import itertools

try:
    import theano
    import theano.tensor as T
    from theano.gradient import disconnected_grad
except ImportError:
    theano = None
    T = None

from ._backend import Backend
from .. import make_graph_backend_decorator
from ...tools import unpack_arguments, flatten_arguments, group_return_values

class _TheanoBackend(Backend):
    def __init__(self):
        super().__init__("Theano")

    @staticmethod
    def is_available():
        return theano is not None

    @Backend._assert_backend_available
    def is_compatible(self, function, arguments):
        flattened_arguments = flatten_arguments(arguments)
        return all([isinstance(argument, T.TensorVariable)
                    for argument in flattened_arguments])

    def _compile_function_without_warnings(self, *args, **kwargs):
        return theano.function(*args, **kwargs, on_unused_input="ignore")

    @Backend._assert_backend_available
    def compile_function(self, function, arguments):
        """Compiles a Theano graph into a python function."""
        flattened_arguments = flatten_arguments(arguments)
        compiled_function = self._compile_function_without_warnings(
            flattened_arguments, function)
        if len(flattened_arguments) == 1:
            return compiled_function
        return unpack_arguments(compiled_function)

    @Backend._assert_backend_available
    def compute_gradient(self, function, arguments):
        """Returns a compiled function computing the gradient of 'function'
        with respect to 'arguments'.
        """
        flattened_arguments = flatten_arguments(arguments)
        # For functions expecting one argument, make sure we don't return the
        # gradient evaluated at some point as a singleton tuple.
        if len(flattened_arguments) == 1:
            (argument,) = flattened_arguments
            gradient = T.grad(function, argument)
            return self._compile_function_without_warnings(
                flattened_arguments, gradient)

        gradient = T.grad(function, flattened_arguments)
        compiled_gradient = self._compile_function_without_warnings(
            flattened_arguments, gradient)
        return group_return_values(
            unpack_arguments(compiled_gradient), arguments)

    def _compute_unary_hessian_vector_product(self, gradient, argument):
        """Returns a function accepting two arguments to compute a
        Hessian-vector product of a scalar-valued unary function.
        """
        argument_type = argument.type()
        try:
            Rop = T.Rop(gradient, argument, argument_type)
        except NotImplementedError:
            proj = T.sum(gradient * disconnected_grad(argument_type))
            Rop = T.grad(proj, argument)
        return self._compile_function_without_warnings(
            [argument, argument_type], Rop)

    def _compute_nary_hessian_vector_product(self, gradients, arguments):
        """Returns a function accepting `2 * len(arguments)` arguments to
        compute a Hessian-vector product of a multivariate function.

        Notes
        -----
        The implementation is based on TensorFlow's '_hessian_vector_product'
        function in 'tensorflow.python.ops.gradients_impl'.
        """
        argument_types = [argument.type() for argument in arguments]
        try:
            Rop = T.Rop(gradients, arguments, argument_types)
        except NotImplementedError:
            proj = [T.sum(gradient * disconnected_grad(argument_type))
                    for gradient, argument_type in zip(gradients,
                                                       argument_types)]
            proj_grad = [T.grad(proj_elem, arguments,
                                disconnected_inputs="ignore",
                                return_disconnected="None")
                         for proj_elem in proj]
            proj_grad_transpose = map(list, zip(*proj_grad))
            proj_grad_stack = [
                T.stacklists([c for c in row if c is not None])
                for row in proj_grad_transpose]
            Rop = [T.sum(stack, axis=0) for stack in proj_grad_stack]
        return self._compile_function_without_warnings(
            list(itertools.chain(arguments, argument_types)), Rop)

    @Backend._assert_backend_available
    def compute_hessian(self, function, arguments):
        """Computes the directional derivative of the gradient, which is
        equivalent to computing a Hessian-vector product with the direction
        vector.
        """
        flattened_arguments = flatten_arguments(arguments)
        if len(flattened_arguments) == 1:
            (argument,) = flattened_arguments
            gradient = T.grad(function, argument)
            return self._compute_unary_hessian_vector_product(
                gradient, argument)

        gradients = T.grad(function, flattened_arguments)
        hessian_vector_product = self._compute_nary_hessian_vector_product(
            gradients, flattened_arguments)

        @functools.wraps(hessian_vector_product)
        def wrapper(point, vector):
            return hessian_vector_product(*flatten_arguments(point),
                                          *flatten_arguments(vector))

        return group_return_values(wrapper, arguments)

Theano = make_graph_backend_decorator(_TheanoBackend)
<commit_msg>_theano.py: verify function is a 'TensorVariable'<commit_after>import functools
import itertools

try:
    import theano
    import theano.tensor as T
    from theano.gradient import disconnected_grad
except ImportError:
    theano = None
    T = None

from ._backend import Backend
from .. import make_graph_backend_decorator
from ...tools import unpack_arguments, flatten_arguments, group_return_values

class _TheanoBackend(Backend):
    def __init__(self):
        super().__init__("Theano")

    @staticmethod
    def is_available():
        return theano is not None

    @Backend._assert_backend_available
    def is_compatible(self, function, arguments):
        if not isinstance(function, T.TensorVariable):
            return False
        flattened_arguments = flatten_arguments(arguments)
        return all([isinstance(argument, T.TensorVariable)
                    for argument in flattened_arguments])

    def _compile_function_without_warnings(self, *args, **kwargs):
        return theano.function(*args, **kwargs, on_unused_input="ignore")

    @Backend._assert_backend_available
    def compile_function(self, function, arguments):
        """Compiles a Theano graph into a python function."""
        flattened_arguments = flatten_arguments(arguments)
        compiled_function = self._compile_function_without_warnings(
            flattened_arguments, function)
        if len(flattened_arguments) == 1:
            return compiled_function
        return unpack_arguments(compiled_function)

    @Backend._assert_backend_available
    def compute_gradient(self, function, arguments):
        """Returns a compiled function computing the gradient of 'function'
        with respect to 'arguments'.
        """
        flattened_arguments = flatten_arguments(arguments)
        # For functions expecting one argument, make sure we don't return the
        # gradient evaluated at some point as a singleton tuple.
        if len(flattened_arguments) == 1:
            (argument,) = flattened_arguments
            gradient = T.grad(function, argument)
            return self._compile_function_without_warnings(
                flattened_arguments, gradient)

        gradient = T.grad(function, flattened_arguments)
        compiled_gradient = self._compile_function_without_warnings(
            flattened_arguments, gradient)
        return group_return_values(
            unpack_arguments(compiled_gradient), arguments)

    def _compute_unary_hessian_vector_product(self, gradient, argument):
        """Returns a function accepting two arguments to compute a
        Hessian-vector product of a scalar-valued unary function.
        """
        argument_type = argument.type()
        try:
            Rop = T.Rop(gradient, argument, argument_type)
        except NotImplementedError:
            proj = T.sum(gradient * disconnected_grad(argument_type))
            Rop = T.grad(proj, argument)
        return self._compile_function_without_warnings(
            [argument, argument_type], Rop)

    def _compute_nary_hessian_vector_product(self, gradients, arguments):
        """Returns a function accepting `2 * len(arguments)` arguments to
        compute a Hessian-vector product of a multivariate function.

        Notes
        -----
        The implementation is based on TensorFlow's '_hessian_vector_product'
        function in 'tensorflow.python.ops.gradients_impl'.
        """
        argument_types = [argument.type() for argument in arguments]
        try:
            Rop = T.Rop(gradients, arguments, argument_types)
        except NotImplementedError:
            proj = [T.sum(gradient * disconnected_grad(argument_type))
                    for gradient, argument_type in zip(gradients,
                                                       argument_types)]
            proj_grad = [T.grad(proj_elem, arguments,
                                disconnected_inputs="ignore",
                                return_disconnected="None")
                         for proj_elem in proj]
            proj_grad_transpose = map(list, zip(*proj_grad))
            proj_grad_stack = [
                T.stacklists([c for c in row if c is not None])
                for row in proj_grad_transpose]
            Rop = [T.sum(stack, axis=0) for stack in proj_grad_stack]
        return self._compile_function_without_warnings(
            list(itertools.chain(arguments, argument_types)), Rop)

    @Backend._assert_backend_available
    def compute_hessian(self, function, arguments):
        """Computes the directional derivative of the gradient, which is
        equivalent to computing a Hessian-vector product with the direction
        vector.
        """
        flattened_arguments = flatten_arguments(arguments)
        if len(flattened_arguments) == 1:
            (argument,) = flattened_arguments
            gradient = T.grad(function, argument)
            return self._compute_unary_hessian_vector_product(
                gradient, argument)

        gradients = T.grad(function, flattened_arguments)
        hessian_vector_product = self._compute_nary_hessian_vector_product(
            gradients, flattened_arguments)

        @functools.wraps(hessian_vector_product)
        def wrapper(point, vector):
            return hessian_vector_product(*flatten_arguments(point),
                                          *flatten_arguments(vector))

        return group_return_values(wrapper, arguments)

Theano = make_graph_backend_decorator(_TheanoBackend)
<|endoftext|>


<commit_before>from django.http import HttpResponse
from django.http import Http404
from django.template import RequestContext
from django.template.loader import select_template

from regulations.generator import api_reader
from regulations.generator.versions import fetch_grouped_history

def regulation_exists(label_id):
    client = api_reader.ApiReader()
    vr = client.regversions(label_id)
    return (vr and len(vr) > 0)

def get_versions(label_id):
    """ Get the current and next version of the regulation. """
    history = fetch_grouped_history(label_id)
    if history:
        future = [h for h in history if h['timeline'] == 'future']
        if len(future) > 0:
            next_version = future[-1]
        else:
            next_version = None

        current = [h for h in history if h['timeline'] == 'current']
        current_version = current[0]
        return (current_version, next_version)

def first_section(label_id):
    """ Return a label to the first section of a regulation. """
    return '%s-1' % label_id

def regulation(request, label_id):

    if not regulation_exists(label_id):
        raise Http404

    context = {}
    current_version, new_version = get_versions(label_id)
    if new_version:
        context['new_version'] = new_version
    context['current_version'] = current_version

    context['label_id'] = label_id
    context['reg_first_section'] = first_section(label_id)

    c = RequestContext(request, context)

    t = select_template([
        'landing_%s.html' % label_id,
        'generic_landing.html'])
    return HttpResponse(t.render(c))
<commit_msg>Removed regulation check; since we do this in the chrome part<commit_after>from django.http import HttpResponse
from django.http import Http404
from django.template import RequestContext
from django.template.loader import select_template

from regulations.generator import api_reader
from regulations.generator.versions import fetch_grouped_history

def regulation_exists(label_id):
    client = api_reader.ApiReader()
    vr = client.regversions(label_id)
    return (vr and len(vr) > 0)

def get_versions(label_id):
    """ Get the current and next version of the regulation. """
    history = fetch_grouped_history(label_id)
    if history:
        future = [h for h in history if h['timeline'] == 'future']
        if len(future) > 0:
            next_version = future[-1]
        else:
            next_version = None

        current = [h for h in history if h['timeline'] == 'current']
        current_version = current[0]
        return (current_version, next_version)

def first_section(label_id):
    """ Return a label to the first section of a regulation. """
    return '%s-1' % label_id

def regulation(request, label_id):

    context = {}
    current_version, new_version = get_versions(label_id)
    if new_version:
        context['new_version'] = new_version
    context['current_version'] = current_version

    context['label_id'] = label_id
    context['reg_first_section'] = first_section(label_id)

    c = RequestContext(request, context)

    t = select_template([
        'landing_%s.html' % label_id,
        'generic_landing.html'])
    return HttpResponse(t.render(c))
<|endoftext|>


<commit_before>from .GraphicsWidget import GraphicsWidget
from .LabelItem import LabelItem
from ..Qt import QtGui, QtCore
from .. import functions as fn
from ..Point import Point
from .ScatterPlotItem import ScatterPlotItem, drawSymbol
from .PlotDataItem import PlotDataItem
from .GraphicsWidgetAnchor import GraphicsWidgetAnchor
__all__ = ['LegendItem']

class LegendItem(GraphicsWidget, GraphicsWidgetAnchor):
    """
    Displays a legend used for describing the contents of a plot.
    LegendItems are most commonly created by calling PlotItem.addLegend().

    Note that this item should not be added directly to a PlotItem. Instead,
    Make it a direct descendant of the PlotItem::

        legend.setParentItem(plotItem)

    """
    def __init__(self, size=None, offset=None):
        """
        ==============  ===============================================================
        **Arguments:**
        size            Specifies the fixed size (width, height) of the legend. If
                        this argument is omitted, the legend will autimatically resize
                        to fit its contents.
        offset          Specifies the offset position relative to the legend's parent.
                        Positive values offset from the left or top; negative values
                        offset from the right or bottom. If offset is None, the
                        legend must be anchored manually by calling anchor() or
                        positioned by calling setPos().
        ==============  ===============================================================

        """

        GraphicsWidget.__init__(self)
        GraphicsWidgetAnchor.__init__(self)
        self.setFlag(self.ItemIgnoresTransformations)
        self.layout = QtGui.QGraphicsGridLayout()
        self.setLayout(self.layout)
        self.items = []
        self.size = size
        self.offset = offset
        if size is not None:
            self.setGeometry(QtCore.QRectF(0, 0, self.size[0], self.size[1]))

    def setParentItem(self, p):
        ret = GraphicsWidget.setParentItem(self, p)
        if self.offset is not None:
            offset = Point(self.offset)
            anchorx = 1 if offset[0] <= 0 else 0
            anchory = 1 if offset[1] <= 0 else 0
            anchor = (anchorx, anchory)
            self.anchor(itemPos=anchor, parentPos=anchor, offset=offset)
        return ret

    def addItem(self, item, name):
        """
        Add a new entry to the legend.

        ==============  ========================================================
        **Arguments:**
        item            A PlotDataItem from which the line and point style
                        of the item will be determined or an instance of
                        ItemSample (or a subclass), allowing the item display
                        to be customized.
        title           The title to display for this item. Simple HTML allowed.
        ==============  ========================================================
        """
        label = LabelItem(name)
        if isinstance(item, ItemSample):
            sample = item
        else:
            sample = ItemSample(item)
        row = self.layout.rowCount()
        self.items.append((sample, label))
        self.layout.addItem(sample, row, 0)
        self.layout.addItem(label, row, 1)
        self.updateSize()

    def removeItem(self, name):
        """
        Removes one item from the legend.

        ==============  ========================================================
        **Arguments:**
        title           The title displayed for this item.
        ==============  ========================================================
        """
        # Thanks, Ulrich!
        # cycle for a match
        for sample, label in self.items:
            if label.text == name:  # hit
                self.items.remove( (sample, label) )    # remove from itemlist
                self.layout.removeItem(sample)          # remove from layout
                sample.close()                          # remove from drawing
                self.layout.removeItem(label)
                label.close()
                self.updateSize()                       # redraq box

    def updateSize(self):
        if self.size is not None:
            return

        height = 0
        width = 0
        #print("-------")
        for sample, label in self.items:
            height += max(sample.height(), label.height()) + 3
            width = max(width, sample.width()+label.width())
            #print(width, height)
        #print width, height
        self.setGeometry(0, 0, width+25, height)

    def boundingRect(self):
        return QtCore.QRectF(0, 0, self.width(), self.height())

    def paint(self, p, *args):
        p.setPen(fn.mkPen(255,255,255,100))
        p.setBrush(fn.mkBrush(100,100,100,50))
        p.drawRect(self.boundingRect())

    def hoverEvent(self, ev):
        ev.acceptDrags(QtCore.Qt.LeftButton)

    def mouseDragEvent(self, ev):
        if ev.button() == QtCore.Qt.LeftButton:
            dpos = ev.pos() - ev.lastPos()
            self.autoAnchor(self.pos() + dpos)

class ItemSample(GraphicsWidget):
    """ Class responsible for drawing a single item in a LegendItem (sans label).

    This may be subclassed to draw custom graphics in a Legend.
    """
    ## Todo: make this more generic; let each item decide how it should be represented.
    def __init__(self, item):
        GraphicsWidget.__init__(self)
        self.item = item

    def boundingRect(self):
        return QtCore.QRectF(0, 0, 20, 20)

    def paint(self, p, *args):
        #p.setRenderHint(p.Antialiasing)  # only if the data is antialiased.
        opts = self.item.opts

        if opts.get('fillLevel',None) is not None and opts.get('fillBrush',None) is not None:
            p.setBrush(fn.mkBrush(opts['fillBrush']))
            p.setPen(fn.mkPen(None))
            p.drawPolygon(QtGui.QPolygonF([QtCore.QPointF(2,18), QtCore.QPointF(18,2), QtCore.QPointF(18,18)]))

        if not isinstance(self.item, ScatterPlotItem):
            p.setPen(fn.mkPen(opts['pen']))
            p.drawLine(2, 18, 18, 2)

        symbol = opts.get('symbol', None)
        if symbol is not None:
            if isinstance(self.item, PlotDataItem):
                opts = self.item.scatter.opts

            pen = fn.mkPen(opts['pen'])
            brush = fn.mkBrush(opts['brush'])
            size = opts['size']

            p.translate(10,10)
            path = drawSymbol(p, symbol, size, pen, brush)

<commit_msg>LegendItem: make it possible to remove items directly, rather than by name<commit_after>from .GraphicsWidget import GraphicsWidget
from .LabelItem import LabelItem
from ..Qt import QtGui, QtCore
from .. import functions as fn
from ..Point import Point
from .ScatterPlotItem import ScatterPlotItem, drawSymbol
from .PlotDataItem import PlotDataItem
from .GraphicsWidgetAnchor import GraphicsWidgetAnchor
__all__ = ['LegendItem']

class LegendItem(GraphicsWidget, GraphicsWidgetAnchor):
    """
    Displays a legend used for describing the contents of a plot.
    LegendItems are most commonly created by calling PlotItem.addLegend().

    Note that this item should not be added directly to a PlotItem. Instead,
    Make it a direct descendant of the PlotItem::

        legend.setParentItem(plotItem)

    """
    def __init__(self, size=None, offset=None):
        """
        ==============  ===============================================================
        **Arguments:**
        size            Specifies the fixed size (width, height) of the legend. If
                        this argument is omitted, the legend will autimatically resize
                        to fit its contents.
        offset          Specifies the offset position relative to the legend's parent.
                        Positive values offset from the left or top; negative values
                        offset from the right or bottom. If offset is None, the
                        legend must be anchored manually by calling anchor() or
                        positioned by calling setPos().
        ==============  ===============================================================

        """

        GraphicsWidget.__init__(self)
        GraphicsWidgetAnchor.__init__(self)
        self.setFlag(self.ItemIgnoresTransformations)
        self.layout = QtGui.QGraphicsGridLayout()
        self.setLayout(self.layout)
        self.items = []
        self.size = size
        self.offset = offset
        if size is not None:
            self.setGeometry(QtCore.QRectF(0, 0, self.size[0], self.size[1]))

    def setParentItem(self, p):
        ret = GraphicsWidget.setParentItem(self, p)
        if self.offset is not None:
            offset = Point(self.offset)
            anchorx = 1 if offset[0] <= 0 else 0
            anchory = 1 if offset[1] <= 0 else 0
            anchor = (anchorx, anchory)
            self.anchor(itemPos=anchor, parentPos=anchor, offset=offset)
        return ret

    def addItem(self, item, name):
        """
        Add a new entry to the legend.

        ==============  ========================================================
        **Arguments:**
        item            A PlotDataItem from which the line and point style
                        of the item will be determined or an instance of
                        ItemSample (or a subclass), allowing the item display
                        to be customized.
        title           The title to display for this item. Simple HTML allowed.
        ==============  ========================================================
        """
        label = LabelItem(name)
        if isinstance(item, ItemSample):
            sample = item
        else:
            sample = ItemSample(item)
        row = self.layout.rowCount()
        self.items.append((sample, label))
        self.layout.addItem(sample, row, 0)
        self.layout.addItem(label, row, 1)
        self.updateSize()

    def removeItem(self, item):
        """
        Removes one item from the legend.

        ==============  ========================================================
        **Arguments:**
        item            The item to remove or its name.
        ==============  ========================================================
        """
        # Thanks, Ulrich!
        # cycle for a match
        for sample, label in self.items:
            if sample.item is item or label.text == item:
                self.items.remove( (sample, label) )    # remove from itemlist
                self.layout.removeItem(sample)          # remove from layout
                sample.close()                          # remove from drawing
                self.layout.removeItem(label)
                label.close()
                self.updateSize()                       # redraq box

    def updateSize(self):
        if self.size is not None:
            return

        height = 0
        width = 0
        #print("-------")
        for sample, label in self.items:
            height += max(sample.height(), label.height()) + 3
            width = max(width, sample.width()+label.width())
            #print(width, height)
        #print width, height
        self.setGeometry(0, 0, width+25, height)

    def boundingRect(self):
        return QtCore.QRectF(0, 0, self.width(), self.height())

    def paint(self, p, *args):
        p.setPen(fn.mkPen(255,255,255,100))
        p.setBrush(fn.mkBrush(100,100,100,50))
        p.drawRect(self.boundingRect())

    def hoverEvent(self, ev):
        ev.acceptDrags(QtCore.Qt.LeftButton)

    def mouseDragEvent(self, ev):
        if ev.button() == QtCore.Qt.LeftButton:
            dpos = ev.pos() - ev.lastPos()
            self.autoAnchor(self.pos() + dpos)

class ItemSample(GraphicsWidget):
    """ Class responsible for drawing a single item in a LegendItem (sans label).

    This may be subclassed to draw custom graphics in a Legend.
    """
    ## Todo: make this more generic; let each item decide how it should be represented.
    def __init__(self, item):
        GraphicsWidget.__init__(self)
        self.item = item

    def boundingRect(self):
        return QtCore.QRectF(0, 0, 20, 20)

    def paint(self, p, *args):
        #p.setRenderHint(p.Antialiasing)  # only if the data is antialiased.
        opts = self.item.opts

        if opts.get('fillLevel',None) is not None and opts.get('fillBrush',None) is not None:
            p.setBrush(fn.mkBrush(opts['fillBrush']))
            p.setPen(fn.mkPen(None))
            p.drawPolygon(QtGui.QPolygonF([QtCore.QPointF(2,18), QtCore.QPointF(18,2), QtCore.QPointF(18,18)]))

        if not isinstance(self.item, ScatterPlotItem):
            p.setPen(fn.mkPen(opts['pen']))
            p.drawLine(2, 18, 18, 2)

        symbol = opts.get('symbol', None)
        if symbol is not None:
            if isinstance(self.item, PlotDataItem):
                opts = self.item.scatter.opts

            pen = fn.mkPen(opts['pen'])
            brush = fn.mkBrush(opts['brush'])
            size = opts['size']

            p.translate(10,10)
            path = drawSymbol(p, symbol, size, pen, brush)

<|endoftext|>