<commit_before>#! /usr/local/bin Python3

import unittest
from proj_1.iitable.iitable import process_doc
from proj_1.iitable.iitable import WordChain
from proj_1.iitable.iitable import doc_loc
from proj_1.iitable.iitable import build_sitable
from proj_1.iitable.iitable import build_iitable
import os

DATA_FILE_DIR = os.path.join(os.path.dirname(__file__), 'test_data/')

class TestIITable(unittest.TestCase):

    def test_word_chain_init(self):
        new_chain = WordChain('test')
        self.assertEqual(new_chain.word, 'test')
        self.assertEqual(new_chain.freq, 0)
        self.assertIs(new_chain.head, None)
        self.assertIs(new_chain.tail, None)

        new_chain = WordChain('unit', freq=10)
        self.assertEqual(new_chain.freq, 10)

    def test_word_chain_insert_and_str(self):
        new_chain = WordChain('test')
        new_node = WordChain.node()
        self.assertEqual(new_node.doc_id, 0)
        new_chain.insert_node(new_node)
        self.assertEqual(new_chain.head, new_node)
        self.assertEqual(new_chain.tail, new_node)
        self.assertEqual(new_chain.freq, 1)

        another_node = WordChain.node(1)
        self.assertEqual(another_node.doc_id, 1)
        new_chain.insert_node(another_node)
        self.assertEqual(new_chain.freq, 2)
        self.assertEqual(new_chain.head.doc_id, 0)
        self.assertEqual(new_chain.tail.doc_id, 1)
        self.assertIs(new_chain.tail.next, None)
        self.assertIs(new_chain.head.next, new_chain.tail)

        new_chain_str = '(test, freq:2) * --> 0 --> 1'
        self.assertEqual(str(new_chain), new_chain_str)

        chain_one = WordChain('test')
        chain_one_str = '(test, freq:0) *'
        self.assertEqual(str(chain_one), chain_one_str)

        third_node = WordChain.node(-1)
        with self.assertRaises(ValueError) as wrong_order:
            new_chain.insert_node(third_node)
        self.assertEqual(
                str(wrong_order.exception),
                'the inserting node have the wrong order.')

        with self.assertRaises(ValueError) as wrong_order:
            new_chain.insert_node(None)
        self.assertEqual(
                str(wrong_order.exception),
                'the inserting node cannot be None.')

    def test_node_init(self):
        node = WordChain.node()
        self.assertEqual(node.doc_id, 0)
        self.assertIsNone(node.next)
        new_node = WordChain.node('test')
        self.assertEqual(new_node.doc_id, 'test')

        node.next = new_node
        self.assertIs(node.next, new_node)

    def test_node_copy(self):
        node = WordChain.node(1)
        new_node = node.copy()
        self.assertEqual(node.doc_id, new_node.doc_id)
        self.assertIsNot(node, new_node)

    def test_node_str(self):
        new_node = WordChain.node()
        self.assertEqual(str(new_node), '0')

    def test_node_cmp(self):
        node_one = WordChain.node(1)
        node_two = WordChain.node(2)
        node_three = WordChain.node(1)
        node_four = WordChain.node(2)

        self.assertNotEqual(node_one, node_two)
        self.assertEqual(node_one, node_three)
        self.assertLessEqual(node_one, node_two)
        self.assertLessEqual(node_one, node_three)
        self.assertGreater(node_two, node_one)
        self.assertGreaterEqual(node_two, node_four)

    def test_doc_loc(self):
        file_path = doc_loc(1)
        self.assertTrue(os.path.exists(file_path))

    def test_process_doc(self):
        print(DATA_FILE_DIR)
        doc_path = os.path.join(DATA_FILE_DIR, 'test_data.txt')
        word_count = 0
        for word, doc_id in process_doc(doc_path, 1):
            self.assertTrue(word.isalnum())
            self.assertEqual(doc_id, 1)
            word_count += 1
        self.assertEqual(word_count, 15)

    def test_build_sitable(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        item_count = 0
        pre_word = ''
        pre_id = 0
        for word, doc_id in build_sitable(doc_list):
            self.assertGreaterEqual(word, pre_word)
            if word == pre_word:
                self.assertGreater(doc_id, pre_id)
            pre_word = word
            pre_id = doc_id
            item_count += 1
        self.assertEqual(item_count, 49)

    def test_build_iitable(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        iitable = build_iitable(build_sitable(doc_list))
        chain_a = iitable['a']
        self.assertEqual('a', chain_a.word)
        self.assertEqual(chain_a.freq, 3)
        chain_deepen = iitable['deepen']
        self.assertEqual(chain_deepen.word, 'deepen')
        self.assertEqual(chain_deepen.freq, 1)

    def test_chain_union(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        iitable = build_iitable(build_sitable(doc_list))

        chain = WordChain.union(iitable['a'], iitable['in'])
        chain_str = '(a OR in, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.union(iitable['a'], iitable['deepen'])
        chain_str = '(a OR deepen, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.union(iitable['a'], WordChain('test'))
        chain_str = '(a OR test, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.union(WordChain('test'), iitable['a'])
        chain_str = '(test OR a, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.union(iitable['nature'], iitable['a'])
        chain_str = '(nature OR a, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

    def test_chain_intersection(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        iitable = build_iitable(build_sitable(doc_list))

        chain = WordChain.intersection(iitable['a'], iitable['deepen'])
        chain_str = '(a AND deepen, freq:1) * --> 2'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.intersection(iitable['nature'], iitable['deepen'])
        chain_str = '(nature AND deepen, freq:0) *'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.intersection(iitable['nature'], WordChain('test'))
        chain_str = '(nature AND test, freq:0) *'
        self.assertEqual(str(chain), chain_str)

    def test_chain_diff(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        iitable = build_iitable(build_sitable(doc_list))

        chain = WordChain.diff(iitable['a'], iitable['deepen'])
        chain_str = '(a AND NOT deepen, freq:2) * --> 1 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.diff(iitable['nature'], iitable['deepen'])
        chain_str = '(nature AND NOT deepen, freq:1) * --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.diff(iitable['nature'], WordChain('test'))
        chain_str = '(nature AND NOT test, freq:1) * --> 3'
        self.assertEqual(str(chain), chain_str)

if __name__ == '__main__':
    unittest.main()
<commit_msg>modfified test file<commit_after>#! /usr/local/bin Python3

import unittest
from proj_1.iitable.iitable import process_doc
from proj_1.iitable.iitable import WordChain
from proj_1.iitable.iitable import doc_loc
from proj_1.iitable.iitable import build_sitable
from proj_1.iitable.iitable import build_iitable
import os

DATA_FILE_DIR = os.path.join(os.path.dirname(__file__), 'test_data/')

class TestIITable(unittest.TestCase):

    def test_word_chain_init(self):
        new_chain = WordChain('test')
        self.assertEqual(new_chain.word, 'test')
        self.assertEqual(new_chain.freq, 0)
        self.assertIs(new_chain.head, None)
        self.assertIs(new_chain.tail, None)

        new_chain = WordChain('unit', freq=10)
        self.assertEqual(new_chain.freq, 10)

    def test_word_chain_insert_and_str(self):
        new_chain = WordChain('test')
        new_node = WordChain.node()
        self.assertEqual(new_node.doc_id, 0)
        new_chain.insert_node(new_node)
        self.assertEqual(new_chain.head, new_node)
        self.assertEqual(new_chain.tail, new_node)
        self.assertEqual(new_chain.freq, 1)

        another_node = WordChain.node(1)
        self.assertEqual(another_node.doc_id, 1)
        new_chain.insert_node(another_node)
        self.assertEqual(new_chain.freq, 2)
        self.assertEqual(new_chain.head.doc_id, 0)
        self.assertEqual(new_chain.tail.doc_id, 1)
        self.assertIs(new_chain.tail.next, None)
        self.assertIs(new_chain.head.next, new_chain.tail)

        new_chain_str = '(test, freq:2) * --> 0 --> 1'
        self.assertEqual(str(new_chain), new_chain_str)

        chain_one = WordChain('test')
        chain_one_str = '(test, freq:0) *'
        self.assertEqual(str(chain_one), chain_one_str)

        third_node = WordChain.node(-1)
        with self.assertRaises(ValueError) as wrong_order:
            new_chain.insert_node(third_node)
        self.assertEqual(
                str(wrong_order.exception),
                'the inserting node have the wrong order.')

        with self.assertRaises(ValueError) as wrong_order:
            new_chain.insert_node(None)
        self.assertEqual(
                str(wrong_order.exception),
                'the inserting node cannot be None.')

    def test_node_init(self):
        node = WordChain.node()
        self.assertEqual(node.doc_id, 0)
        self.assertIsNone(node.next)
        new_node = WordChain.node('test')
        self.assertEqual(new_node.doc_id, 'test')

        node.next = new_node
        self.assertIs(node.next, new_node)

    def test_node_copy(self):
        node = WordChain.node(1)
        new_node = node.copy()
        self.assertEqual(node.doc_id, new_node.doc_id)
        self.assertIsNot(node, new_node)

    def test_node_str(self):
        new_node = WordChain.node()
        self.assertEqual(str(new_node), '0')

    def test_node_cmp(self):
        node_one = WordChain.node(1)
        node_two = WordChain.node(2)
        node_three = WordChain.node(1)
        node_four = WordChain.node(2)

        self.assertNotEqual(node_one, node_two)
        self.assertEqual(node_one, node_three)
        self.assertLessEqual(node_one, node_two)
        self.assertLessEqual(node_one, node_three)
        self.assertGreater(node_two, node_one)
        self.assertGreaterEqual(node_two, node_four)

    def test_doc_loc(self):
        file_path = doc_loc(1)
        self.assertTrue(os.path.exists(file_path))

    def test_process_doc(self):
        print(DATA_FILE_DIR)
        doc_path = os.path.join(DATA_FILE_DIR, 'test_data.txt')
        word_count = 0
        for word, doc_id in process_doc(doc_path, 1):
            self.assertTrue(word.isalnum())
            self.assertEqual(doc_id, 1)
            word_count += 1
        self.assertEqual(word_count, 15)

    def test_build_sitable(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        item_count = 0
        pre_word = ''
        pre_id = 0
        for word, doc_id in build_sitable(doc_list):
            self.assertGreaterEqual(word, pre_word)
            if word == pre_word:
                self.assertGreater(doc_id, pre_id)
            pre_word = word
            pre_id = doc_id
            item_count += 1
        self.assertEqual(item_count, 49)

    def test_build_iitable(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        iitable = build_iitable(build_sitable(doc_list))
        chain_a = iitable['a']
        self.assertEqual('a', chain_a.word)
        self.assertEqual(chain_a.freq, 3)
        chain_deepen = iitable['deepen']
        self.assertEqual(chain_deepen.word, 'deepen')
        self.assertEqual(chain_deepen.freq, 1)

    def test_chain_union(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        iitable = build_iitable(build_sitable(doc_list))

        chain = WordChain.union(iitable['a'], iitable['in'])
        chain_str = '(a OR in, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.union(iitable['a'], iitable['deepen'])
        chain_str = '(a OR deepen, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.union(iitable['a'], WordChain('test'))
        chain_str = '(a OR test, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.union(WordChain('test'), iitable['a'])
        chain_str = '(test OR a, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.union(iitable['nature'], iitable['a'])
        chain_str = '(nature OR a, freq:3) * --> 1 --> 2 --> 3'
        self.assertEqual(str(chain), chain_str)

    def test_chain_intersection(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        iitable = build_iitable(build_sitable(doc_list))

        chain = WordChain.intersection(iitable['a'], iitable['deepen'])
        chain_str = '(a AND deepen, freq:1) * --> 2'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.intersection(iitable['nature'], iitable['deepen'])
        chain_str = '(nature AND deepen, freq:0) *'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.intersection(iitable['nature'], WordChain('test'))
        chain_str = '(nature AND test, freq:0) *'
        self.assertEqual(str(chain), chain_str)

    def test_chain_diff(self):
        doc_path_list = []
        for i in range(3):
            file_name = 'test_data%d.txt' % (i + 1)
            doc_path_list.append(os.path.join(DATA_FILE_DIR, file_name))
        doc_list = (process_doc(doc_path_list[i], i + 1) for i in range(3))
        iitable = build_iitable(build_sitable(doc_list))

        chain = WordChain.diff(iitable['a'], iitable['deepen'])
        chain_str = '(a AND NOT deepen, freq:2) * --> 1 --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.diff(iitable['nature'], iitable['deepen'])
        chain_str = '(nature AND NOT deepen, freq:1) * --> 3'
        self.assertEqual(str(chain), chain_str)

        chain = WordChain.diff(iitable['nature'], WordChain('test'))
        chain_str = '(nature AND NOT test, freq:1) * --> 3'
        self.assertEqual(str(chain), chain_str)
<|endoftext|>


<commit_before>from collections import deque
import io
import os
import tarfile
from tempfile import NamedTemporaryFile
import unittest
import gzip

import numpy
from PIL import Image
from six.moves import xrange
import zmq

# from fuel.server import recv_arrays, send_arrays
from fuel.datasets import H5PYDataset
from fuel.converters.ilsvrc2010 import (extract_patch_images,
                                        image_consumer,
                                        load_from_tar_or_patch,
                                        prepare_hdf5_file,
                                        read_devkit,
                                        read_metadata_mat_file,
                                        DEVKIT_META_PATH)
from fuel.utils import find_in_data_path
from tests import skip_if_not_available

class MockSocket(object):
    """Mock of a ZeroMQ PUSH or PULL socket."""
    def __init__(self, socket_type, to_recv=()):
        self.socket_type = socket_type
        if self.socket_type not in (zmq.PUSH, zmq.PULL):
            raise NotImplementedError('only PUSH and PULL currently supported')
        self.sent = deque()
        self.to_recv = deque(to_recv)

    def send(self, data, flags=0, copy=True, track=False):
        assert self.socket_type == zmq.PUSH
        if track:
            # We don't emulate the behaviour required by this flag.
            raise NotImplementedError
        message = {'type': 'send', 'data': data, 'flags': flags, 'copy': copy}
        self.sent.append(message)

    def send_pyobj(self, obj, flags=0, protocol=2):
        assert self.socket_type == zmq.PUSH
        message = {'type': 'send_pyobj', 'obj': obj, 'flags': flags,
                   'protocol': protocol}
        self.sent.append(message)

    def recv(self, flags=0, copy=True, track=False):
        if track:
            # We don't emulate the behaviour required by this flag.
            raise NotImplementedError
        message = self.to_recv.popleft()
        assert message['type'] == 'recv'
        if 'flags' in message:
            assert message['flags'] == flags, 'flags did not match expected'
        if 'copy' in message:
            assert message['copy'] == copy, 'copy did not match expected'
        return message['data']

    def recv_pyobj(self, flags=0):
        message = self.to_recv.popleft()
        assert message['type'] == 'recv_pyobj'
        if 'flags' in message:
            assert flags == message['flags']
        return message['obj']

class MockH5PYData(object):
    def __init__(self, shape, dtype):
        self.data = numpy.empty(shape, dtype)
        self.dims = MockH5PYDims(len(shape))

    def __setitem__(self, where, what):
        self.data[where] = what

    def __getitem__(self, index):
        return self.data[index]

    @property
    def shape(self):
        return self.data.shape

    @property
    def dtype(self):
        return self.data.dtype

class MockH5PYFile(dict):
    filename = 'NOT_A_REAL_FILE.hdf5'

    def __init__(self):
        self.attrs = {}
        self.flushed = 0
        self.opened = False
        self.closed = False

    def create_dataset(self, name, shape, dtype):
        self[name] = MockH5PYData(shape, dtype)

    def flush(self):
        self.flushed += 1

    def __enter__(self):
        self.opened = True

    def __exit__(self, type, value, traceback):
        self.closed = True

class MockH5PYDim(object):
    def __init__(self, dims):
        self.dims = dims
        self.scales = []

    def attach_scale(self, dataset):
        # I think this is necessary for it to be valid?
        assert dataset in self.dims.scales.values()
        self.scales.append(dataset)

class MockH5PYDims(object):
    def __init__(self, ndim):
        self._dims = [MockH5PYDim(self) for _ in xrange(ndim)]
        self.scales = {}

    def create_scale(self, dataset, name):
        self.scales[name] = dataset

    def __getitem__(self, index):
        return self._dims[index]

def create_jpeg_data(image):
    """Create a JPEG in memory.

    Parameters
    ----------
    image : ndarray, 3-dimensional
        Array data representing the image to save. Mode ('L', 'RGB',
        'CMYK') will be determined from the last (third) axis.

    Returns
    -------
    jpeg_data : bytes
        The image encoded as a JPEG, returned as raw bytes.

    """
    if image.shape[-1] == 1:
        mode = 'L'
    elif image.shape[-1] == 3:
        mode = 'RGB'
    elif image.shape[-1] == 4:
        mode = 'CMYK'
    else:
        raise ValueError("invalid shape")
    pil_image = Image.frombytes(mode=mode, size=image.shape[:2],
                                data=image.tobytes())
    jpeg_data = io.BytesIO()
    pil_image.save(jpeg_data, format='JPEG')
    return jpeg_data.getvalue()

def create_fake_jpeg_tar(seed, min_num_images=5, max_num_images=50,
                         min_size=20, size_range=30, filenames=None,
                         random=True, gzip_probability=0.2):
    """Create a TAR file of randomly generated JPEG files.

    Parameters
    ----------
    seed : int or sequence
        Seed for a `numpy.random.RandomState`.
    min_num_images : int, optional
        The minimum number of images to put in the TAR file.
    max_num_images : int, optional
        The maximum number of images to put in the TAR file.
    min_size : int, optional
        The minimum width and minimum height of each image.
    size_range : int, optional
        Maximum number of pixels added to `min_size` for image
        dimensions.
    filenames : list, optional
        If provided, use these filenames. Otherwise generate them
        randomly. Must be at least `max_num_images` long.
    random : bool, optional
        If `False`, substitute an image full of a single number,
        the order of that image in processing.
    gzip_probability : float
        With this probability, randomly gzip the JPEG file without
        appending a gzip suffix.

    Returns
    -------
    tar_data : bytes
        A TAR file represented as raw bytes, containing between
        `min_num_images` and `max_num_images` JPEG files (inclusive).

    Notes
    -----
    Randomly choose between RGB, L and CMYK mode images. Also randomly
    gzips JPEGs to simulate the idiotic distribution format of
    ILSVRC2010.

    """
    rng = numpy.random.RandomState(seed)
    images = []
    if filenames is None:
        files = []
    else:
        if len(filenames) < max_num_images:
            raise ValueError('need at least max_num_images = %d filenames' %
                             max_num_images)
        files = filenames
    for i in xrange(rng.random_integers(min_num_images, max_num_images)):
        if filenames is None:
            files.append('%x.JPEG' % abs(hash(str(i))))
        im = rng.random_integers(0, 255,
                                 size=(rng.random_integers(min_size,
                                                           min_size +
                                                           size_range),
                                       rng.random_integers(min_size,
                                                           min_size +
                                                           size_range),
                                       rng.random_integers(1, 4)))
        if not random:
            im *= 0
            assert (im == 0).all()
            im += i
            assert numpy.isscalar(i)
            assert (im == i).all()
        if im.shape[-1] == 2:
            im = im[:, :, :1]
        images.append(im)
    files = sorted(files)
    temp_tar = io.BytesIO()
    with tarfile.open(fileobj=temp_tar, mode='w') as tar:
        for fn, image in zip(files, images):
            try:
                with NamedTemporaryFile(mode='wb', suffix='.JPEG',
                                        delete=False) as f:
                    if rng.uniform() < gzip_probability:
                        gzip_data = io.BytesIO()
                        with gzip.GzipFile(mode='wb', fileobj=gzip_data) as gz:
                            gz.write(create_jpeg_data(image))
                        f.write(gzip_data.getvalue())
                    else:
                        f.write(create_jpeg_data(image))
                tar.add(f.name, arcname=fn)
            finally:
                os.remove(f.name)
    return temp_tar.getvalue(), files[:len(images)]

def create_fake_tar_of_tars(seed, num_inner_tars, *args, **kwargs):
    """Create a nested TAR of TARs of JPEGs.

    Parameters
    ----------
    seed : int or sequence
        Seed for a `numpy.random.RandomState`.
    num_inner_tars : int
        Number of TAR files to place inside.

    Returns
    -------
    tar_data : bytes
        A TAR file represented as raw bytes, TAR files of generated
        JPEGs.
    names : list
        Names of the inner TAR files.
    jpeg_names : list of lists
        A list of lists containing the names of JPEGs in each inner TAR.

    Notes
    -----
    Remainder of positional and keyword arguments are passed on to
    :func:`create_fake_jpeg_tars`.

    """
    rng = numpy.random.RandomState(seed)
    seeds = rng.random_integers(0, 500000, size=(num_inner_tars,))
    tars, fns = list(zip(*[create_fake_jpeg_tar(s, *args, **kwargs)
                           for s in seeds]))
    names = sorted(str(abs(hash(str(-i - 1)))) + '.tar'
                   for i, t in enumerate(tars))
    data = io.BytesIO()
    with tarfile.open(fileobj=data, mode='w') as outer:
        for tar, name in zip(tars, names):
            try:
                with NamedTemporaryFile(mode='wb', suffix='.tar',
                                        delete=False) as f:
                    f.write(tar)
                outer.add(f.name, arcname=name)
            finally:
                os.remove(f.name)
    return data.getvalue(), names, fns

def create_fake_patch_images(filenames=None, num_train=14, num_valid=15,
                             num_test=21):
    if filenames is None:
        filenames = ['%x' % abs(hash(str(i))) + '.JPEG' for i in xrange(50)]
    assert num_train + num_valid + num_test == len(filenames)
    filenames[:num_train] = ['train/' + f
                             for f in filenames[:num_train]]
    filenames[num_train:num_train + num_valid] = [
        'val/' + f for f in filenames[num_train:num_train + num_valid]
    ]
    filenames[num_train + num_valid:] = [
        'test/' + f for f in filenames[num_train + num_valid:]
    ]
    tar = create_fake_jpeg_tar(1, min_num_images=len(filenames),
                               max_num_images=len(filenames),
                               filenames=filenames, random=False,
                               gzip_probability=0.0)[0]
    return tar, filenames

def test_prepare_metadata():
    raise unittest.SkipTest("TODO")

def test_prepare_hdf5_file():
    hdf5_file = MockH5PYFile()
    prepare_hdf5_file(hdf5_file, 10, 5, 2)

    # Verify properties of the train split.
    train_splits = H5PYDataset.get_start_stop(hdf5_file, 'train')
    assert all(v == (0, 10) for v in train_splits.values())
    assert train_splits.keys() == set(['encoded_images', 'targets',
                                       'filenames'])

    # Verify properties of the valid split.
    valid_splits = H5PYDataset.get_start_stop(hdf5_file, 'valid')
    assert all(v == (10, 15) for v in valid_splits.values())
    assert valid_splits.keys() == set(['encoded_images', 'targets',
                                       'filenames'])

    # Verify properties of the test split.
    test_splits = H5PYDataset.get_start_stop(hdf5_file, 'test')
    assert all(v == (15, 17) for v in test_splits.values())
    assert test_splits.keys() == set(['encoded_images', 'targets',
                                      'filenames'])

    from numpy import dtype

    # Verify properties of the encoded_images HDF5 dataset.
    assert hdf5_file['encoded_images'].shape[0] == 17
    assert len(hdf5_file['encoded_images'].shape) == 1
    assert hdf5_file['encoded_images'].dtype.kind == 'O'
    assert hdf5_file['encoded_images'].dtype.metadata['vlen'] == dtype('uint8')

    # Verify properties of the filenames dataset.
    assert hdf5_file['filenames'].shape[0] == 17
    assert len(hdf5_file['filenames'].shape) == 2
    assert hdf5_file['filenames'].dtype == dtype('S32')

    # Verify properties of the targets dataset.
    assert hdf5_file['targets'].shape[0] == 17
    assert hdf5_file['targets'].shape[1] == 1
    assert len(hdf5_file['targets'].shape) == 2
    assert hdf5_file['targets'].dtype == dtype('int16')

def test_process_train_set():
    raise unittest.SkipTest("TODO")

def test_process_other_set():
    raise unittest.SkipTest("TODO")

def test_train_set_producer():
    raise unittest.SkipTest("TODO")

def test_image_consumer():
    raise unittest.SkipTest("TODO")

def test_other_set_producer():
    raise unittest.SkipTest("TODO")

def test_load_from_tar_or_patch():
    images, all_filenames = create_fake_jpeg_tar(3, min_num_images=200,
                                                 max_num_images=200,
                                                 gzip_probability=0.0)
    patch_data, _ = create_fake_patch_images(all_filenames[::4], num_train=50,
                                             num_valid=0, num_test=0)
    patches = extract_patch_images(io.BytesIO(patch_data), 'train')
    assert len(patches) == 50
    with tarfile.open(fileobj=io.BytesIO(images)) as tar:
        for fn in all_filenames:
            image, patched = load_from_tar_or_patch(tar, fn, patches)
            if fn in patches:
                assert image == patches[fn]
                assert patched
            else:
                tar_image = tar.extractfile(fn).read()
                assert image == tar_image
                assert not patched

def test_read_devkit():
    devkit_filename = 'ILSVRC2010_devkit-1.0.tar.gz'
    skip_if_not_available(datasets=[devkit_filename])
    synsets, cost_mat, raw_valid_gt = read_devkit(
        os.path.join(config.data_path, devkit_filename))
    assert (synsets['ILSVRC2010_ID'] ==
            numpy.arange(1, len(synsets) + 1)).all()
    assert synsets['num_train_images'][1000:].sum() == 0
    assert (synsets['num_train_images'][:1000] > 0).all()
    assert synsets.ndim == 1
    assert cost_mat.shape == (1000, 1000)
    assert cost_mat.dtype == 'uint8'
    assert (cost_mat.flat[::1001] == 0).all()

def test_read_metadata_mat_file():
    raise unittest.SkipTest("TODO")

def test_extract_patch_images():
    tar, _ = create_fake_patch_images()
    assert len(extract_patch_images(io.BytesIO(tar), 'train')) == 14
    assert len(extract_patch_images(io.BytesIO(tar), 'valid')) == 15
    assert len(extract_patch_images(io.BytesIO(tar), 'test')) == 21
<commit_msg>Improved tests for metadata reading.<commit_after>from collections import deque
import io
import os
import tarfile
from tempfile import NamedTemporaryFile
import unittest
import gzip

import numpy
from PIL import Image
from six.moves import xrange
import zmq

# from fuel.server import recv_arrays, send_arrays
from fuel.datasets import H5PYDataset
from fuel.converters.ilsvrc2010 import (extract_patch_images,
                                        image_consumer,
                                        load_from_tar_or_patch,
                                        prepare_hdf5_file,
                                        read_devkit,
                                        read_metadata_mat_file,
                                        DEVKIT_META_PATH)
from fuel.utils import find_in_data_path
from tests import skip_if_not_available

class MockSocket(object):
    """Mock of a ZeroMQ PUSH or PULL socket."""
    def __init__(self, socket_type, to_recv=()):
        self.socket_type = socket_type
        if self.socket_type not in (zmq.PUSH, zmq.PULL):
            raise NotImplementedError('only PUSH and PULL currently supported')
        self.sent = deque()
        self.to_recv = deque(to_recv)

    def send(self, data, flags=0, copy=True, track=False):
        assert self.socket_type == zmq.PUSH
        if track:
            # We don't emulate the behaviour required by this flag.
            raise NotImplementedError
        message = {'type': 'send', 'data': data, 'flags': flags, 'copy': copy}
        self.sent.append(message)

    def send_pyobj(self, obj, flags=0, protocol=2):
        assert self.socket_type == zmq.PUSH
        message = {'type': 'send_pyobj', 'obj': obj, 'flags': flags,
                   'protocol': protocol}
        self.sent.append(message)

    def recv(self, flags=0, copy=True, track=False):
        if track:
            # We don't emulate the behaviour required by this flag.
            raise NotImplementedError
        message = self.to_recv.popleft()
        assert message['type'] == 'recv'
        if 'flags' in message:
            assert message['flags'] == flags, 'flags did not match expected'
        if 'copy' in message:
            assert message['copy'] == copy, 'copy did not match expected'
        return message['data']

    def recv_pyobj(self, flags=0):
        message = self.to_recv.popleft()
        assert message['type'] == 'recv_pyobj'
        if 'flags' in message:
            assert flags == message['flags']
        return message['obj']

class MockH5PYData(object):
    def __init__(self, shape, dtype):
        self.data = numpy.empty(shape, dtype)
        self.dims = MockH5PYDims(len(shape))

    def __setitem__(self, where, what):
        self.data[where] = what

    def __getitem__(self, index):
        return self.data[index]

    @property
    def shape(self):
        return self.data.shape

    @property
    def dtype(self):
        return self.data.dtype

class MockH5PYFile(dict):
    filename = 'NOT_A_REAL_FILE.hdf5'

    def __init__(self):
        self.attrs = {}
        self.flushed = 0
        self.opened = False
        self.closed = False

    def create_dataset(self, name, shape, dtype):
        self[name] = MockH5PYData(shape, dtype)

    def flush(self):
        self.flushed += 1

    def __enter__(self):
        self.opened = True

    def __exit__(self, type, value, traceback):
        self.closed = True

class MockH5PYDim(object):
    def __init__(self, dims):
        self.dims = dims
        self.scales = []

    def attach_scale(self, dataset):
        # I think this is necessary for it to be valid?
        assert dataset in self.dims.scales.values()
        self.scales.append(dataset)

class MockH5PYDims(object):
    def __init__(self, ndim):
        self._dims = [MockH5PYDim(self) for _ in xrange(ndim)]
        self.scales = {}

    def create_scale(self, dataset, name):
        self.scales[name] = dataset

    def __getitem__(self, index):
        return self._dims[index]

def create_jpeg_data(image):
    """Create a JPEG in memory.

    Parameters
    ----------
    image : ndarray, 3-dimensional
        Array data representing the image to save. Mode ('L', 'RGB',
        'CMYK') will be determined from the last (third) axis.

    Returns
    -------
    jpeg_data : bytes
        The image encoded as a JPEG, returned as raw bytes.

    """
    if image.shape[-1] == 1:
        mode = 'L'
    elif image.shape[-1] == 3:
        mode = 'RGB'
    elif image.shape[-1] == 4:
        mode = 'CMYK'
    else:
        raise ValueError("invalid shape")
    pil_image = Image.frombytes(mode=mode, size=image.shape[:2],
                                data=image.tobytes())
    jpeg_data = io.BytesIO()
    pil_image.save(jpeg_data, format='JPEG')
    return jpeg_data.getvalue()

def create_fake_jpeg_tar(seed, min_num_images=5, max_num_images=50,
                         min_size=20, size_range=30, filenames=None,
                         random=True, gzip_probability=0.2):
    """Create a TAR file of randomly generated JPEG files.

    Parameters
    ----------
    seed : int or sequence
        Seed for a `numpy.random.RandomState`.
    min_num_images : int, optional
        The minimum number of images to put in the TAR file.
    max_num_images : int, optional
        The maximum number of images to put in the TAR file.
    min_size : int, optional
        The minimum width and minimum height of each image.
    size_range : int, optional
        Maximum number of pixels added to `min_size` for image
        dimensions.
    filenames : list, optional
        If provided, use these filenames. Otherwise generate them
        randomly. Must be at least `max_num_images` long.
    random : bool, optional
        If `False`, substitute an image full of a single number,
        the order of that image in processing.
    gzip_probability : float
        With this probability, randomly gzip the JPEG file without
        appending a gzip suffix.

    Returns
    -------
    tar_data : bytes
        A TAR file represented as raw bytes, containing between
        `min_num_images` and `max_num_images` JPEG files (inclusive).

    Notes
    -----
    Randomly choose between RGB, L and CMYK mode images. Also randomly
    gzips JPEGs to simulate the idiotic distribution format of
    ILSVRC2010.

    """
    rng = numpy.random.RandomState(seed)
    images = []
    if filenames is None:
        files = []
    else:
        if len(filenames) < max_num_images:
            raise ValueError('need at least max_num_images = %d filenames' %
                             max_num_images)
        files = filenames
    for i in xrange(rng.random_integers(min_num_images, max_num_images)):
        if filenames is None:
            files.append('%x.JPEG' % abs(hash(str(i))))
        im = rng.random_integers(0, 255,
                                 size=(rng.random_integers(min_size,
                                                           min_size +
                                                           size_range),
                                       rng.random_integers(min_size,
                                                           min_size +
                                                           size_range),
                                       rng.random_integers(1, 4)))
        if not random:
            im *= 0
            assert (im == 0).all()
            im += i
            assert numpy.isscalar(i)
            assert (im == i).all()
        if im.shape[-1] == 2:
            im = im[:, :, :1]
        images.append(im)
    files = sorted(files)
    temp_tar = io.BytesIO()
    with tarfile.open(fileobj=temp_tar, mode='w') as tar:
        for fn, image in zip(files, images):
            try:
                with NamedTemporaryFile(mode='wb', suffix='.JPEG',
                                        delete=False) as f:
                    if rng.uniform() < gzip_probability:
                        gzip_data = io.BytesIO()
                        with gzip.GzipFile(mode='wb', fileobj=gzip_data) as gz:
                            gz.write(create_jpeg_data(image))
                        f.write(gzip_data.getvalue())
                    else:
                        f.write(create_jpeg_data(image))
                tar.add(f.name, arcname=fn)
            finally:
                os.remove(f.name)
    return temp_tar.getvalue(), files[:len(images)]

def create_fake_tar_of_tars(seed, num_inner_tars, *args, **kwargs):
    """Create a nested TAR of TARs of JPEGs.

    Parameters
    ----------
    seed : int or sequence
        Seed for a `numpy.random.RandomState`.
    num_inner_tars : int
        Number of TAR files to place inside.

    Returns
    -------
    tar_data : bytes
        A TAR file represented as raw bytes, TAR files of generated
        JPEGs.
    names : list
        Names of the inner TAR files.
    jpeg_names : list of lists
        A list of lists containing the names of JPEGs in each inner TAR.

    Notes
    -----
    Remainder of positional and keyword arguments are passed on to
    :func:`create_fake_jpeg_tars`.

    """
    rng = numpy.random.RandomState(seed)
    seeds = rng.random_integers(0, 500000, size=(num_inner_tars,))
    tars, fns = list(zip(*[create_fake_jpeg_tar(s, *args, **kwargs)
                           for s in seeds]))
    names = sorted(str(abs(hash(str(-i - 1)))) + '.tar'
                   for i, t in enumerate(tars))
    data = io.BytesIO()
    with tarfile.open(fileobj=data, mode='w') as outer:
        for tar, name in zip(tars, names):
            try:
                with NamedTemporaryFile(mode='wb', suffix='.tar',
                                        delete=False) as f:
                    f.write(tar)
                outer.add(f.name, arcname=name)
            finally:
                os.remove(f.name)
    return data.getvalue(), names, fns

def create_fake_patch_images(filenames=None, num_train=14, num_valid=15,
                             num_test=21):
    if filenames is None:
        filenames = ['%x' % abs(hash(str(i))) + '.JPEG' for i in xrange(50)]
    assert num_train + num_valid + num_test == len(filenames)
    filenames[:num_train] = ['train/' + f
                             for f in filenames[:num_train]]
    filenames[num_train:num_train + num_valid] = [
        'val/' + f for f in filenames[num_train:num_train + num_valid]
    ]
    filenames[num_train + num_valid:] = [
        'test/' + f for f in filenames[num_train + num_valid:]
    ]
    tar = create_fake_jpeg_tar(1, min_num_images=len(filenames),
                               max_num_images=len(filenames),
                               filenames=filenames, random=False,
                               gzip_probability=0.0)[0]
    return tar, filenames

def test_prepare_metadata():
    raise unittest.SkipTest("TODO")

def test_prepare_hdf5_file():
    hdf5_file = MockH5PYFile()
    prepare_hdf5_file(hdf5_file, 10, 5, 2)

    # Verify properties of the train split.
    train_splits = H5PYDataset.get_start_stop(hdf5_file, 'train')
    assert all(v == (0, 10) for v in train_splits.values())
    assert train_splits.keys() == set(['encoded_images', 'targets',
                                       'filenames'])

    # Verify properties of the valid split.
    valid_splits = H5PYDataset.get_start_stop(hdf5_file, 'valid')
    assert all(v == (10, 15) for v in valid_splits.values())
    assert valid_splits.keys() == set(['encoded_images', 'targets',
                                       'filenames'])

    # Verify properties of the test split.
    test_splits = H5PYDataset.get_start_stop(hdf5_file, 'test')
    assert all(v == (15, 17) for v in test_splits.values())
    assert test_splits.keys() == set(['encoded_images', 'targets',
                                      'filenames'])

    from numpy import dtype

    # Verify properties of the encoded_images HDF5 dataset.
    assert hdf5_file['encoded_images'].shape[0] == 17
    assert len(hdf5_file['encoded_images'].shape) == 1
    assert hdf5_file['encoded_images'].dtype.kind == 'O'
    assert hdf5_file['encoded_images'].dtype.metadata['vlen'] == dtype('uint8')

    # Verify properties of the filenames dataset.
    assert hdf5_file['filenames'].shape[0] == 17
    assert len(hdf5_file['filenames'].shape) == 2
    assert hdf5_file['filenames'].dtype == dtype('S32')

    # Verify properties of the targets dataset.
    assert hdf5_file['targets'].shape[0] == 17
    assert hdf5_file['targets'].shape[1] == 1
    assert len(hdf5_file['targets'].shape) == 2
    assert hdf5_file['targets'].dtype == dtype('int16')

def test_process_train_set():
    raise unittest.SkipTest("TODO")

def test_process_other_set():
    raise unittest.SkipTest("TODO")

def test_train_set_producer():
    raise unittest.SkipTest("TODO")

def test_image_consumer():
    raise unittest.SkipTest("TODO")

def test_other_set_producer():
    raise unittest.SkipTest("TODO")

def test_load_from_tar_or_patch():
    # Setup fake tar files.
    images, all_filenames = create_fake_jpeg_tar(3, min_num_images=200,
                                                 max_num_images=200,
                                                 gzip_probability=0.0)
    patch_data, _ = create_fake_patch_images(all_filenames[::4], num_train=50,
                                             num_valid=0, num_test=0)

    patches = extract_patch_images(io.BytesIO(patch_data), 'train')

    assert len(patches) == 50
    with tarfile.open(fileobj=io.BytesIO(images)) as tar:
        for fn in all_filenames:
            image, patched = load_from_tar_or_patch(tar, fn, patches)
            if fn in patches:
                assert image == patches[fn]
                assert patched
            else:
                tar_image = tar.extractfile(fn).read()
                assert image == tar_image
                assert not patched

def test_read_devkit():
    devkit_filename = 'ILSVRC2010_devkit-1.0.tar.gz'
    skip_if_not_available(datasets=[devkit_filename])
    synsets, cost_mat, raw_valid_gt = read_devkit(
        find_in_data_path(devkit_filename))
    # synset and cost_matrix sanity tests appear in test_read_metadata_mat_file
    assert raw_valid_gt.min() == 1
    assert raw_valid_gt.max() == 1000
    assert raw_valid_gt.dtype.kind == 'i'
    assert raw_valid_gt.shape == (50000,)

def test_read_metadata_mat_file():
    devkit_filename = 'ILSVRC2010_devkit-1.0.tar.gz'
    skip_if_not_available(datasets=[devkit_filename])
    with tarfile.open(find_in_data_path(devkit_filename)) as tar:
        meta_mat = tar.extractfile(DEVKIT_META_PATH)
        synsets, cost_mat = read_metadata_mat_file(meta_mat)
    assert (synsets['ILSVRC2010_ID'] ==
            numpy.arange(1, len(synsets) + 1)).all()
    assert synsets['num_train_images'][1000:].sum() == 0
    assert (synsets['num_train_images'][:1000] > 0).all()
    assert synsets.ndim == 1
    assert synsets['wordnet_height'].min() == 0
    assert synsets['wordnet_height'].max() == 19
    assert synsets['WNID'].dtype == numpy.dtype('S9')
    assert (synsets['num_children'][:1000] == 0).all()
    assert (synsets['children'][:1000] == -1).all()

    # Assert the basics about the cost matrix.
    assert cost_mat.shape == (1000, 1000)
    assert cost_mat.dtype == 'uint8'
    assert cost_mat.min() == 0
    assert cost_mat.max() == 18
    assert (cost_mat == cost_mat.T).all()
    # Assert that the diagonal is 0.
    assert (cost_mat.flat[::1001] == 0).all()

def test_extract_patch_images():
    tar, _ = create_fake_patch_images()
    assert len(extract_patch_images(io.BytesIO(tar), 'train')) == 14
    assert len(extract_patch_images(io.BytesIO(tar), 'valid')) == 15
    assert len(extract_patch_images(io.BytesIO(tar), 'test')) == 21
<|endoftext|>


<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Meta-Model Editor

This program allows users to operate
on meta-models and models using loadable
libraries to perform meta-model specific
operations such as generation, analysis,
and deployment.

* author: William Emfinger
* website: github.com/finger563/editor
* last edited: July 2015
"""

from PyQt4 import QtCore
from PyQt4 import QtGui

from action import Action
from worker import Worker
from editor_widget import TabbedEditor, EditorView
from output import TabbedOutputWidget

from item_model import ItemModel, SortFilterProxyModel

import metamodel.importer as rosmod

rootNode = rosmod.Project()
rootNode['name'].value = "Project Root"

dep = rosmod.Deployment()
dep['name'].value = "My Deployment"
rootNode.add_child(dep)

sw = rosmod.Software()
sw['name'].value = "My Software"
rootNode.add_child(sw)

pkg = rosmod.Package()
pkg['name'].value = "My Package"
sw.add_child(pkg)

msg = rosmod.Message()
msg['name'].value = "My Message"
msg['definition'].value = "int8 testInt = 2\nbool testBool = False"
pkg.add_child(msg)

srv = rosmod.Service()
srv['name'].value = "My Service"
srv['definition'].value = "int8 testInt = 2\nbool testBool = False\n---\nbool retVal"
pkg.add_child(srv)

comp = rosmod.Component()
comp['name'].value = "My Component"
pkg.add_child(comp)

tmr = rosmod.Timer()
tmr['name'].value = "My Timer"
comp.add_child(tmr)

hw = rosmod.Hardware()
hw['name'].value = "My Hardware"
rootNode.add_child(hw)

root_node = rootNode

class Editor(QtGui.QMainWindow):

    editor_modes = ['model','meta model','view model']

    def __init__(self):
        super(Editor, self).__init__()
        self.editor_mode = 'model'
        self.init_ui()
        self.setWindowIcon(QtGui.QIcon("icons/editor.png"))

    def init_ui(self):
        QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Cleanlooks'))
        self.setStyleSheet("""QToolTip {
                           background-color: black;
                           color: white;
                           border: black solid 1px
                           }""")
        self.setGeometry(300,300,800,600)
        self.setWindowTitle("Editor")

        exitAction = Action('icons/toolbar/stop.png', '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.close) # note that this will call closeEvent

        testAction = Action('icons/toolbar/build.png', '&Build', self)
        testAction.setStatusTip('Build code.')
        testAction.triggered.connect(self.testEvent)

        saveAction = Action('icons/toolbar/save.png', '&Save', self)
        saveAction.setStatusTip('Save.')
        saveAction.triggered.connect(self.saveEvent)

        self.mode_selector = QtGui.QComboBox(self)
        self.mode_selector.addItems(self.editor_modes)
        self.mode_selector.setCurrentIndex(self.editor_modes.index(self.editor_mode))
        self.mode_selector.currentIndexChanged.connect(self.changeMode)

        self.menubar_init()
        self.menubar_add_menu('&File')
        self.menu_add_action('&File',exitAction)

        self.toolbar_init()
        self.toolbar_create("toolbar1")
        self.toolbar_add_action("toolbar1",exitAction)
        self.toolbar_add_widget('toolbar1',self.mode_selector)
        self.toolbar_create("toolbar2")
        self.toolbar_add_action("toolbar2",testAction)
        self.toolbar_add_action("toolbar2",saveAction)

        self.proxy_model = SortFilterProxyModel(self)
        self.proxy_model.setDynamicSortFilter(True)
        self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.proxy_model.setSortRole(ItemModel.sort_role)
        self.proxy_model.setFilterRole(ItemModel.filter_role)

        self.model = ItemModel(root_node)

        self.proxy_model.setSourceModel(self.model)

        self.tree_view = QtGui.QTreeView()
        self.tree_view.setModel(self.proxy_model)
        self.tree_view.doubleClicked.connect(self.treeItemDoubleClicked)
        self.tree_view.setSortingEnabled(False)
        #self.tree_view.selectionModel().currentChanged.connnect(self.treeItemDoubleClicked)

        self.filter_edit = QtGui.QLineEdit()
        self.filter_edit.textChanged.connect(self.proxy_model.setFilterRegExp)
        self.filter_label = QtGui.QLabel("Filter:")

        self.navigator = QtGui.QWidget()
        self.navigator_vbox = QtGui.QVBoxLayout()
        self.navigator_vbox.addWidget(self.filter_label)
        self.navigator_vbox.addWidget(self.filter_edit)
        self.navigator_vbox.addWidget(self.tree_view)
        self.navigator.setLayout(self.navigator_vbox)

        self.tabbedEditorWidget = TabbedEditor(self)
        self.openEditorTabs = {}

        self.splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal)
        self.splitter1.addWidget(self.navigator)
        self.splitter1.addWidget(self.tabbedEditorWidget)
        self.splitter1.setSizes([self.geometry().x()/4.0, 3.0 * self.geometry().x()/4.0])

        self.splitter2 = QtGui.QSplitter(QtCore.Qt.Vertical)
        self.splitter2.addWidget(self.splitter1)
        self.tabbedOutput = TabbedOutputWidget(self)
        self.splitter2.addWidget(self.tabbedOutput)
        self.splitter2.setSizes([3.0 * self.geometry().y()/4.0, self.geometry().y()/4.0])

        self.setCentralWidget(self.splitter2)

        self.center()
        self.show()

    def center(self):
        qr = self.frameGeometry()
        cp = QtGui.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def clearEditor(self):
        self.openEditorTabs = {}
        self.tabbedEditorWidget.clear()

    def buildTree(self, tree):
        tree.clear()
        if self.editor_mode in ['model']:
            tree.load_model(project)
        elif self.editor_mode in ['view model','meta model']:
            tree.load_meta_model(project)

    def treeItemDoubleClicked(self, modelIndex):
        mi = self.proxy_model.mapToSource(modelIndex)
        item = self.model.getModel( mi )
        name = item["name"].value
        if name not in self.openEditorTabs:
            ev = EditorView( self.tabbedEditorWidget )
            ev.init_ui(
                obj = item,
                fname = item.kind + '.view',
                view_type = self.editor_mode
            )
            ev.getEditor().setModel(self.proxy_model)
            # TODO: FIX THIS SO THAT THE VIEW'S EDITOR MODEL IS SET TO THE VIEW'S SELECTION CHANGED
            self.tree_view.selectionModel().currentChanged.connect(ev.getEditor().setSelection)
            self.openEditorTabs[name] = ev
            self.tabbedEditorWidget.addTab( ev, name )
        elif self.tabbedEditorWidget.indexOf(self.openEditorTabs[name]) < 0:
            ev = self.openEditorTabs[name]
            self.tabbedEditorWidget.addTab( ev, name )
        self.tabbedEditorWidget.setCurrentIndex(
            self.tabbedEditorWidget.indexOf(self.openEditorTabs[name]))

    def saveEvent(self, event):
        if self.editor_mode == 'view model':
            self.saveCurrentViewModel()
        elif self.editor_mode == 'model':
            self.saveCurrentModel()

    def saveCurrentModel(self):
        pass

    def saveCurrentViewModel(self):
        fileName = QtGui.QFileDialog.getSaveFileName(self,
                                                     "Save view file",
                                                     '',
                                                     "View Files (*.view)",
                                                     options = QtGui.QFileDialog.Options())
        if fileName:
            if fileName[-5:] != '.view': fileName += '.view'
            if self.tabbedEditorWidget.currentWidget().saveVM(fileName) == 0:
                test = QtGui.QMessageBox.information(self, 'Build',
                                                     'Saved {}.'.format(fileName))

    def changeMode(self, index):
        text = self.mode_selector.currentText()
        if text != self.editor_mode:
            self.editor_mode = text
            self.clearEditor()
            # TODO: NEED TO UPDATE TREE VIEW MODEL WITH NEW TYPE OF MODEL

    def testEvent(self, event):
        test = QtGui.QMessageBox.information(self, 'Build',
                                             'Build succeeded.')

    def closeEvent(self, event):
        event.accept()
        return
        reply = QtGui.QMessageBox.question(self, 'Quit',
                                           "You're sure you want to quit?", QtGui.QMessageBox.Yes |
                                           QtGui.QMessageBox.No, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

    from menubar import menubar_init, menubar_add_menu, menu_add_action
    from toolbar import toolbar_init, toolbar_create, toolbar_add_action, toolbar_add_widget, toolbar_remove
    from action import action_init, action_create
    from context_menu import context_menu_init, context_menu_create, context_menu_add_action

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    editor = Editor()
    sys.exit(app.exec_())
<commit_msg>adding more functionality to tree viewer.<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Meta-Model Editor

This program allows users to operate
on meta-models and models using loadable
libraries to perform meta-model specific
operations such as generation, analysis,
and deployment.

* author: William Emfinger
* website: github.com/finger563/editor
* last edited: July 2015
"""

from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4.QtCore import Qt

from action import Action
from worker import Worker
from editor_widget import TabbedEditor, EditorView
from output import TabbedOutputWidget

from item_model import ItemModel, SortFilterProxyModel

import metamodel.importer as rosmod

rootNode = rosmod.Project()
rootNode['name'].value = "Project Root"

dep = rosmod.Deployment()
dep['name'].value = "My Deployment"
rootNode.add_child(dep)

sw = rosmod.Software()
sw['name'].value = "My Software"
rootNode.add_child(sw)

pkg = rosmod.Package()
pkg['name'].value = "My Package"
sw.add_child(pkg)

msg = rosmod.Message()
msg['name'].value = "My Message"
msg['definition'].value = "int8 testInt = 2\nbool testBool = False"
pkg.add_child(msg)

srv = rosmod.Service()
srv['name'].value = "My Service"
srv['definition'].value = "int8 testInt = 2\nbool testBool = False\n---\nbool retVal"
pkg.add_child(srv)

comp = rosmod.Component()
comp['name'].value = "My Component"
pkg.add_child(comp)

tmr = rosmod.Timer()
tmr['name'].value = "My Timer"
comp.add_child(tmr)

hw = rosmod.Hardware()
hw['name'].value = "My Hardware"
rootNode.add_child(hw)

root_node = rootNode

class Editor(QtGui.QMainWindow):

    editor_modes = ['model','meta model','view model']

    def __init__(self):
        super(Editor, self).__init__()
        self.editor_mode = 'model'
        self.init_ui()
        self.setWindowIcon(QtGui.QIcon("icons/editor.png"))

    def init_ui(self):
        QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Cleanlooks'))
        self.setStyleSheet("""QToolTip {
                           background-color: black;
                           color: white;
                           border: black solid 1px
                           }""")
        self.setGeometry(300,300,800,600)
        self.setWindowTitle("Editor")

        exitAction = Action('icons/toolbar/stop.png', '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.close) # note that this will call closeEvent

        testAction = Action('icons/toolbar/build.png', '&Build', self)
        testAction.setStatusTip('Build code.')
        testAction.triggered.connect(self.testEvent)

        saveAction = Action('icons/toolbar/save.png', '&Save', self)
        saveAction.setStatusTip('Save.')
        saveAction.triggered.connect(self.saveEvent)

        self.mode_selector = QtGui.QComboBox(self)
        self.mode_selector.addItems(self.editor_modes)
        self.mode_selector.setCurrentIndex(self.editor_modes.index(self.editor_mode))
        self.mode_selector.currentIndexChanged.connect(self.changeMode)

        self.menubar_init()
        self.menubar_add_menu('&File')
        self.menu_add_action('&File',exitAction)

        self.toolbar_init()
        self.toolbar_create("toolbar1")
        self.toolbar_add_action("toolbar1",exitAction)
        self.toolbar_add_widget('toolbar1',self.mode_selector)
        self.toolbar_create("toolbar2")
        self.toolbar_add_action("toolbar2",testAction)
        self.toolbar_add_action("toolbar2",saveAction)

        self.proxy_model = SortFilterProxyModel(self)
        self.proxy_model.setDynamicSortFilter(True)
        self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.proxy_model.setSortRole(ItemModel.sort_role)
        self.proxy_model.setFilterRole(ItemModel.filter_role)

        self.model = ItemModel(root_node)

        self.proxy_model.setSourceModel(self.model)

        self.tree_view = QtGui.QTreeView()
        self.tree_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree_view.customContextMenuRequested.connect(self.treeViewContextMenuEvent)
        self.tree_view.setModel(self.proxy_model)
        self.tree_view.doubleClicked.connect(self.treeItemDoubleClicked)
        self.tree_view.setSortingEnabled(False)
        #self.tree_view.selectionModel().currentChanged.connnect(self.treeItemDoubleClicked)

        self.filter_edit = QtGui.QLineEdit()
        self.filter_edit.textChanged.connect(self.proxy_model.setFilterRegExp)
        self.filter_label = QtGui.QLabel("Filter:")

        self.navigator = QtGui.QWidget()
        self.navigator_vbox = QtGui.QVBoxLayout()
        self.navigator_vbox.addWidget(self.filter_label)
        self.navigator_vbox.addWidget(self.filter_edit)
        self.navigator_vbox.addWidget(self.tree_view)
        self.navigator.setLayout(self.navigator_vbox)

        self.tabbedEditorWidget = TabbedEditor(self)
        self.openEditorTabs = {}

        self.splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal)
        self.splitter1.addWidget(self.navigator)
        self.splitter1.addWidget(self.tabbedEditorWidget)
        self.splitter1.setSizes([self.geometry().x()/4.0, 3.0 * self.geometry().x()/4.0])

        self.splitter2 = QtGui.QSplitter(QtCore.Qt.Vertical)
        self.splitter2.addWidget(self.splitter1)
        self.tabbedOutput = TabbedOutputWidget(self)
        self.splitter2.addWidget(self.tabbedOutput)
        self.splitter2.setSizes([3.0 * self.geometry().y()/4.0, self.geometry().y()/4.0])

        self.setCentralWidget(self.splitter2)

        self.center()
        self.show()

    def center(self):
        qr = self.frameGeometry()
        cp = QtGui.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def clearEditor(self):
        self.openEditorTabs = {}
        self.tabbedEditorWidget.clear()

    def buildTree(self, tree):
        tree.clear()
        if self.editor_mode in ['model']:
            tree.load_model(project)
        elif self.editor_mode in ['view model','meta model']:
            tree.load_meta_model(project)

    def treeViewContextMenuEvent(self, position):
        indexes = self.tree_view.selectedIndexes()
        if len(indexes) > 1:
            return

        mi = self.proxy_model.mapToSource(indexes[0])
        item = self.model.getModel( mi )

        menu = QtGui.QMenu()

        delSelf = Action('', 'Delete', self)
        menu.addAction(delSelf)

        for a in item.children._allowed:
            addAction = Action('', 'Add new {}'.format(a.__name__), self)
            menu.addAction(addAction)
        menu.exec_(self.tree_view.viewport().mapToGlobal(position))

    def treeItemDoubleClicked(self, modelIndex):
        mi = self.proxy_model.mapToSource(modelIndex)
        item = self.model.getModel( mi )
        name = item["name"].value
        if name not in self.openEditorTabs:
            ev = EditorView( self.tabbedEditorWidget )
            ev.init_ui(
                obj = item,
                fname = item.kind + '.view',
                view_type = self.editor_mode
            )
            ev.getEditor().setModel(self.proxy_model)
            # TODO: FIX THIS SO THAT THE VIEW'S EDITOR MODEL IS SET TO THE VIEW'S SELECTION CHANGED
            self.tree_view.selectionModel().currentChanged.connect(ev.getEditor().setSelection)
            self.openEditorTabs[name] = ev
            self.tabbedEditorWidget.addTab( ev, name )
        elif self.tabbedEditorWidget.indexOf(self.openEditorTabs[name]) < 0:
            ev = self.openEditorTabs[name]
            self.tabbedEditorWidget.addTab( ev, name )
        self.tabbedEditorWidget.setCurrentIndex(
            self.tabbedEditorWidget.indexOf(self.openEditorTabs[name]))

    def saveEvent(self, event):
        if self.editor_mode == 'view model':
            self.saveCurrentViewModel()
        elif self.editor_mode == 'model':
            self.saveCurrentModel()

    def saveCurrentModel(self):
        pass

    def saveCurrentViewModel(self):
        fileName = QtGui.QFileDialog.getSaveFileName(self,
                                                     "Save view file",
                                                     '',
                                                     "View Files (*.view)",
                                                     options = QtGui.QFileDialog.Options())
        if fileName:
            if fileName[-5:] != '.view': fileName += '.view'
            if self.tabbedEditorWidget.currentWidget().saveVM(fileName) == 0:
                test = QtGui.QMessageBox.information(self, 'Build',
                                                     'Saved {}.'.format(fileName))

    def changeMode(self, index):
        text = self.mode_selector.currentText()
        if text != self.editor_mode:
            self.editor_mode = text
            self.clearEditor()
            # TODO: NEED TO UPDATE TREE VIEW MODEL WITH NEW TYPE OF MODEL

    def testEvent(self, event):
        test = QtGui.QMessageBox.information(self, 'Build',
                                             'Build succeeded.')

    def closeEvent(self, event):
        event.accept()
        return
        reply = QtGui.QMessageBox.question(self, 'Quit',
                                           "You're sure you want to quit?", QtGui.QMessageBox.Yes |
                                           QtGui.QMessageBox.No, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

    from menubar import menubar_init, menubar_add_menu, menu_add_action
    from toolbar import toolbar_init, toolbar_create, toolbar_add_action, toolbar_add_widget, toolbar_remove
    from action import action_init, action_create
    from context_menu import context_menu_init, context_menu_create, context_menu_add_action

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    editor = Editor()
    sys.exit(app.exec_())
<|endoftext|>


<commit_before>####################################
# Driftwood 2D Game Dev. Suite     #
# inputmanager.py                  #
# Copyright 2014 PariahSoft LLC    #
# Copyright 2017 Michael D. Reiley #
# & Paul Merrill                   #
####################################

# **********
# 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.
# **********

import types

from sdl2 import SDL_GetKeyName

class InputManager:
    """The Input Manager

    This class manages keyboard input.

    Attributes:
        driftwood: Base class instance.
        handler: Optional callback to a function that receives keypresses after we do.
    """

    ONDOWN, ONREPEAT, ONUP = range(3)

    def __init__(self, driftwood):
        """InputManager class initializer.

        Args:
            driftwood: Base class instance.
        """
        self.driftwood = driftwood

        self.handler = None

        # {keysym: {callback, throttle, delay, last_called, repeats, mod}}
        self.__registry = {}

        self.__stack = []
        self.__modifier_stack = []

        self.__now = 0.0

        # Register the tick callback.
        self.driftwood.tick.register(self._tick, during_pause=True)

    def __contains__(self, item):
        if item in self.__registry:
            return True
        return False

    def __getitem__(self, item):
        return self.__registry[item][0]

    def __setitem__(self, item, value):
        self.register(item, value)

    def __delitem__(self, item):
        self.unregister(item)

    def keyname(self, keysym):
        """Return a string naming a keysym. The up arrow key returns "Up," etc.

        Args:
            keysym: SDLKey which should be named

        Returns:
            Keyname if succeeded, None if failed.
        """
        # Input Check
        try:
            CHECK(keysym, int, _min=0)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "keyname", "bad argument", e)
            return None

        # Retrieve.
        try:
            return SDL_GetKeyName(keysym).decode()
        except:
            return None

    def keysym(self, keyname):
        """Return a keysym for the named keybinding.

        Args:
            keyname: The name of the keybinding for which to return a keysym.

        Returns:
            Keysym if succeeded, None if failed.
        """
        # Input Check
        try:
            CHECK(keyname, str)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "keysym", "bad argument", e)
            return None

        # Retrieve
        try:
            return getattr(self.driftwood.keycode, self.driftwood.config["input"]["keybinds"][keyname])
        except:
            return None

    def register(self, keyid, callback, throttle=0.0, delay=0.0, mod=False):
        """Register an input callback.

        The callback function will receive a call every tick that the key is on top of the input stack. (the key which
        was most recently pressed.)

        Args:
            keyid: SDLKey keysym or key name for the key which triggers the callback. (int or string)
            callback: Function to be called on the registered keypress.  It should take a single integer parameter with
                a value of InputManager.ONDOWN, ONREPEAT, or ONUP.
            throttle: Number of seconds to wait between ONREPEAT calls when the key is held down.
            delay: Number of seconds to wait after the key is pressed before making the first ONREPEAT call.
            mod: Whether this is a modifier key. Modifier keys do not compete for the top of the input stack.

        Returns:
            True if succeeded, False if failed.
        """
        # Input Check
        try:
            CHECK(keyid, [int, str], _min=0)
            CHECK(callback, [types.FunctionType, types.MethodType])
            CHECK(throttle, [int, float], _min=0)
            CHECK(delay, [int, float], _min=0)
            CHECK(mod, bool)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "register", "bad argument", e)
            return False

        # Find keysym.
        if type(keyid) == int:
            keysym = keyid
        elif type(keyid) == str:
            keysym = self.keysym(keyid)
        else:
            self.driftwood.log.msg("WARNING", "InputManager", "register", "no such key", str(keyid))
            return False

        if delay == 0.0:
            delay = throttle

        self.__registry[keysym] = {
            "callback": callback,
            "throttle": throttle,
            "delay": delay,
            "last_called": self.__now,
            "repeats": 0,
            "mod": mod
        }

        return True

    def unregister(self, keysym):
        """Unregister an input callback.

        Args:
            keysym: SDLKey for the key which triggers the callback.

        Returns:
            True if succeeded, False if failed.
        """
        # Input Check
        try:
            CHECK(keysym, int, _min=0)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "unregister", "bad argument", e)
            return False

        # Unregister.
        if keysym in self.__registry:
            del self.__registry[keysym]
            return True
        else:
            self.driftwood.log.msg("WARNING", "InputManager", "unregister", "key not registered",
                                   self.keyname(keysym))
            return False

    def pressed(self, keysym):
        """Check if a key is currently being pressed.

        Args:
            keysym: SDLKey for the keypress to check.
        """
        # Input Check
        try:
            CHECK(keysym, int, _min=0)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "pressed", "bad argument", e)
            return False

        # Is pressed.
        if keysym in self.__stack:
            return True

        return False

    def _key_down(self, keysym):
        """Push a keypress onto the input stack if not present.

        Args:
            keysym: SDLKey for the key which was pressed.
        """
        if keysym not in self.__stack:
            if keysym in self.__registry and not self.__registry[keysym]["mod"]:
                self.__stack.insert(0, keysym)
                self.__registry[keysym]["callback"](InputManager.ONDOWN)
            elif keysym in self.__registry:
                self.__modifier_stack.insert(0, keysym)
                self.__registry[keysym]["callback"](InputManager.ONDOWN)
        else:
            # SDL2 gives us key-repeat events so this is actually okay.
            # self.driftwood.log.msg("WARNING", "InputManager", "key_down", "key already down", self.keyname(keysym))
            pass

    def _key_up(self, keysym):
        """Remove a keypress from the input stack if present.

        Args:
            keysym: SDLKey for the key which was released.
        """
        found = False
        if keysym in self.__stack:
            self.__stack.remove(keysym)
            found = True
        elif keysym in self.__modifier_stack:
            self.__modifier_stack.remove(keysym)
            found = True

        if found:
            # Set the key callback as not called yet.
            if keysym in self.__registry:
                self.__registry[keysym]["repeats"] = 0
                self.__registry[keysym]["callback"](InputManager.ONUP)

    def _tick(self, seconds_past):
        """Tick callback.

        If there is a keypress on top of the stack and it maps to a callback in the registry, call it. Also pass the
        keypress to the secondary handler if it exists.

        If a second-callback delay is set, make sure to wait the proper amount of time before the second call.
        """
        self.__now += seconds_past

        # TODO: Remove duplicate code.
        if self.__modifier_stack:
            for mod_key in self.__modifier_stack:
                mod_callback = self.__registry[mod_key]

                # Have we waited long enough between calls?
                if mod_callback["repeats"] == 0:
                    waiting_until = mod_callback["delay"]
                else:
                    waiting_until = mod_callback["throttle"]

                if self.__now - mod_callback["last_called"] >= waiting_until:
                    # Update time last called.
                    mod_callback["last_called"] = self.__now

                    # Call the callback.
                    mod_callback["callback"](InputManager.ONREPEAT)

                    # Update number of times called.
                    mod_callback["repeats"] += 1

            # Call the handler if set.
            if self.handler:
                self.handler(mod_key)

        if self.__stack:
            # The user's current (or latest, if multiple ongoing,) keydown.
            top_key = self.__stack[0]

            # Is the keypress in the registry?
            if top_key in self.__registry:
                # The callback entry for the top_key.
                top_callback = self.__registry[top_key]

                # Have we waited long enough between calls?
                if top_callback["repeats"] == 0:
                    waiting_until = top_callback["delay"]
                else:
                    waiting_until = top_callback["throttle"]

                if self.__now - top_callback["last_called"] >= waiting_until:
                    # Update time last called.
                    top_callback["last_called"] = self.__now

                    # Call the callback.
                    top_callback["callback"](InputManager.ONREPEAT)

                    # Update number of times called.
                    top_callback["repeats"] += 1

            # Call the handler if set.
            if self.handler:
                self.handler(top_key)
<commit_msg>InputManager: Add type annotations<commit_after>####################################
# Driftwood 2D Game Dev. Suite     #
# inputmanager.py                  #
# Copyright 2014 PariahSoft LLC    #
# Copyright 2017 Michael D. Reiley #
# & Paul Merrill                   #
####################################

# **********
# 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.
# **********

import types
from typing import Callable, Optional, Union

from sdl2 import SDL_GetKeyName

from __main__ import _Driftwood, CHECK, CheckFailure

class InputManager:
    """The Input Manager

    This class manages keyboard input.

    Attributes:
        driftwood: Base class instance.
        handler: Optional callback to a function that receives keypresses after we do.
    """

    ONDOWN, ONREPEAT, ONUP = range(3)

    def __init__(self, driftwood: _Driftwood):
        """InputManager class initializer.

        Args:
            driftwood: Base class instance.
        """
        self.driftwood = driftwood

        self.handler = None

        # {keysym: {callback, throttle, delay, last_called, repeats, mod}}
        self.__registry = {}

        self.__stack = []
        self.__modifier_stack = []

        self.__now = 0.0

        # Register the tick callback.
        self.driftwood.tick.register(self._tick, during_pause=True)

    def __contains__(self, item: int) -> bool:
        if item in self.__registry:
            return True
        return False

    def __setitem__(self, item: Union[int, str], value: Callable) -> None:
        self.register(item, value)

    def __delitem__(self, item: int) -> None:
        self.unregister(item)

    def keyname(self, keysym: int) -> Optional[str]:
        """Return a string naming a keysym. The up arrow key returns "Up," etc.

        Args:
            keysym: SDLKey which should be named

        Returns:
            Keyname if succeeded, None if failed.
        """
        # Input Check
        try:
            CHECK(keysym, int, _min=0)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "keyname", "bad argument", e)
            return None

        # Retrieve.
        try:
            return SDL_GetKeyName(keysym).decode()
        except:
            return None

    def keysym(self, keyname: str) -> Optional[int]:
        """Return a keysym for the named keybinding.

        Args:
            keyname: The name of the keybinding for which to return a keysym.

        Returns:
            Keysym if succeeded, None if failed.
        """
        # Input Check
        try:
            CHECK(keyname, str)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "keysym", "bad argument", e)
            return None

        # Retrieve
        try:
            return getattr(self.driftwood.keycode, self.driftwood.config["input"]["keybinds"][keyname])
        except:
            return None

    def register(self,
                 keyid: Union[int, str],
                 callback: Callable,
                 throttle: float=0.0,
                 delay: float=0.0,
                 mod: bool=False) -> bool:
        """Register an input callback.

        The callback function will receive a call every tick that the key is on top of the input stack. (the key which
        was most recently pressed.)

        Args:
            keyid: SDLKey keysym or key name for the key which triggers the callback. (int or string)
            callback: Function to be called on the registered keypress.  It should take a single integer parameter with
                a value of InputManager.ONDOWN, ONREPEAT, or ONUP.
            throttle: Number of seconds to wait between ONREPEAT calls when the key is held down.
            delay: Number of seconds to wait after the key is pressed before making the first ONREPEAT call.
            mod: Whether this is a modifier key. Modifier keys do not compete for the top of the input stack.

        Returns:
            True if succeeded, False if failed.
        """
        # Input Check
        try:
            CHECK(keyid, [int, str], _min=0)
            CHECK(callback, [types.FunctionType, types.MethodType])
            CHECK(throttle, [int, float], _min=0)
            CHECK(delay, [int, float], _min=0)
            CHECK(mod, bool)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "register", "bad argument", e)
            return False

        # Find keysym.
        if type(keyid) == int:
            keysym = keyid
        elif type(keyid) == str:
            keysym = self.keysym(keyid)
        else:
            self.driftwood.log.msg("WARNING", "InputManager", "register", "no such key", str(keyid))
            return False

        if delay == 0.0:
            delay = throttle

        self.__registry[keysym] = {
            "callback": callback,
            "throttle": throttle,
            "delay": delay,
            "last_called": self.__now,
            "repeats": 0,
            "mod": mod
        }

        return True

    def unregister(self, keysym: int) -> bool:
        """Unregister an input callback.

        Args:
            keysym: SDLKey for the key which triggers the callback.

        Returns:
            True if succeeded, False if failed.
        """
        # Input Check
        try:
            CHECK(keysym, int, _min=0)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "unregister", "bad argument", e)
            return False

        # Unregister.
        if keysym in self.__registry:
            del self.__registry[keysym]
            return True
        else:
            self.driftwood.log.msg("WARNING", "InputManager", "unregister", "key not registered",
                                   self.keyname(keysym))
            return False

    def pressed(self, keysym: int) -> bool:
        """Check if a key is currently being pressed.

        Args:
            keysym: SDLKey for the keypress to check.
        """
        # Input Check
        try:
            CHECK(keysym, int, _min=0)
        except CheckFailure as e:
            self.driftwood.log.msg("ERROR", "Input", "pressed", "bad argument", e)
            return False

        # Is pressed.
        if keysym in self.__stack:
            return True

        return False

    def _key_down(self, keysym: int) -> None:
        """Push a keypress onto the input stack if not present.

        Args:
            keysym: SDLKey for the key which was pressed.
        """
        if keysym not in self.__stack:
            if keysym in self.__registry and not self.__registry[keysym]["mod"]:
                self.__stack.insert(0, keysym)
                self.__registry[keysym]["callback"](InputManager.ONDOWN)
            elif keysym in self.__registry:
                self.__modifier_stack.insert(0, keysym)
                self.__registry[keysym]["callback"](InputManager.ONDOWN)
        else:
            # SDL2 gives us key-repeat events so this is actually okay.
            # self.driftwood.log.msg("WARNING", "InputManager", "key_down", "key already down", self.keyname(keysym))
            pass

    def _key_up(self, keysym: int) -> None:
        """Remove a keypress from the input stack if present.

        Args:
            keysym: SDLKey for the key which was released.
        """
        found = False
        if keysym in self.__stack:
            self.__stack.remove(keysym)
            found = True
        elif keysym in self.__modifier_stack:
            self.__modifier_stack.remove(keysym)
            found = True

        if found:
            # Set the key callback as not called yet.
            if keysym in self.__registry:
                self.__registry[keysym]["repeats"] = 0
                self.__registry[keysym]["callback"](InputManager.ONUP)

    def _tick(self, seconds_past: float) -> None:
        """Tick callback.

        If there is a keypress on top of the stack and it maps to a callback in the registry, call it. Also pass the
        keypress to the secondary handler if it exists.

        If a second-callback delay is set, make sure to wait the proper amount of time before the second call.
        """
        self.__now += seconds_past

        # TODO: Remove duplicate code.
        if self.__modifier_stack:
            for mod_key in self.__modifier_stack:
                mod_callback = self.__registry[mod_key]

                # Have we waited long enough between calls?
                if mod_callback["repeats"] == 0:
                    waiting_until = mod_callback["delay"]
                else:
                    waiting_until = mod_callback["throttle"]

                if self.__now - mod_callback["last_called"] >= waiting_until:
                    # Update time last called.
                    mod_callback["last_called"] = self.__now

                    # Call the callback.
                    mod_callback["callback"](InputManager.ONREPEAT)

                    # Update number of times called.
                    mod_callback["repeats"] += 1

            # Call the handler if set.
            if self.handler:
                self.handler(mod_key)

        if self.__stack:
            # The user's current (or latest, if multiple ongoing,) keydown.
            top_key = self.__stack[0]

            # Is the keypress in the registry?
            if top_key in self.__registry:
                # The callback entry for the top_key.
                top_callback = self.__registry[top_key]

                # Have we waited long enough between calls?
                if top_callback["repeats"] == 0:
                    waiting_until = top_callback["delay"]
                else:
                    waiting_until = top_callback["throttle"]

                if self.__now - top_callback["last_called"] >= waiting_until:
                    # Update time last called.
                    top_callback["last_called"] = self.__now

                    # Call the callback.
                    top_callback["callback"](InputManager.ONREPEAT)

                    # Update number of times called.
                    top_callback["repeats"] += 1

            # Call the handler if set.
            if self.handler:
                self.handler(top_key)
<|endoftext|>


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

# Copyright 2017 EMBL - European Bioinformatics Institute
#
# 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.

from rest_framework import routers

class ApiBrowserView(routers.APIRootView):
    """
    EBI Metagenomics API
    """
    pass

class DefaultRouter(routers.DefaultRouter):

    """
    Custom default router extends the rest_framework DefaultRouter and
    adds in a default API root view
    """

    APIRootView = ApiBrowserView

    def extend(self, router):
        self.registry.extend(router.registry)
<commit_msg>rename class to better present main api title<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright 2017 EMBL - European Bioinformatics Institute
#
# 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.

from rest_framework import routers

class EBIMetagenomicsAPIView(routers.APIRootView):
    """
    EBI Metagenomics API provides programmatic access to the data for cross-database complex queries. For more details review the documentation.
    """
    pass

class DefaultRouter(routers.DefaultRouter):

    """
    Custom default router extends the rest_framework DefaultRouter and
    adds in a default API root view
    """

    APIRootView = EBIMetagenomicsAPIView

    def extend(self, router):
        self.registry.extend(router.registry)
<|endoftext|>


<commit_before>import logging

from gi.repository import Gio

from gaphor.abc import ActionProvider, Service
from gaphor.core import action, gettext
from gaphor.ui.filedialog import FileDialog
from gaphor.ui.questiondialog import QuestionDialog

log = logging.getLogger(__name__)

FILTERS = [
    {"name": gettext("Gaphor Models"), "pattern": "*.gaphor"},
    {"name": gettext("All Files"), "pattern": "*"},
]

class AppFileManager(Service, ActionProvider):
    """
    Handle application level file loading
    """

    def __init__(self, session):
        self.session = session

    def shutdown(self):
        pass

    @property
    def application(self):
        return Gio.Application.get_default()

    def load(self, filename):
        if self.active_session_is_new():
            file_manager = self.session.get_service("file_manager")
            file_manager.load(filename)
        else:
            self.application.open([Gio.File.new_for_path(filename)], "")

    def new(self):
        if self.active_session_is_new():
            file_manager = self.session.get_service("file_manager")
            file_manager.new()
        else:
            self.application.open([], "__new__")

    def active_session_is_new(self):
        """
        If it's a new model, there is no state change (undo & redo)
        and no file name is defined.
        """
        undo_manager = self.session.get_service("undo_manager")
        file_manager = self.session.get_service("file_manager")

        return (
            not undo_manager.can_undo()
            and not undo_manager.can_redo()
            and not file_manager.filename
        )

    @property
    def main_window(self):
        return self.session.get_service("main_window")

    def file_dialog(self, title):
        file_dialog = FileDialog(title, filters=FILTERS, parent=self.main_window.window)

        filename = file_dialog.selection

        file_dialog.destroy()

        log.debug(filename)

        return filename

    @action(name="app.file-new", shortcut="<Primary>n")
    def action_new(self):
        """The new model menu action.  This action will create a new
        UML model.  This will trigger a FileManagerStateChange event."""

        self.new()

    @action(name="app.file-new-template")
    def action_new_from_template(self):
        """This menu action opens the new model from template dialog."""

        filename = self.file_dialog(gettext("New Gaphor Model From Template"))
        if filename:
            self.load(filename)
            self.filename = None

    @action(name="app.file-open", shortcut="<Primary>o")
    def action_open(self):
        """This menu action opens the standard model open dialog."""

        filename = self.file_dialog(gettext("Open Gaphor Model"))

        if filename:
            self.load(filename)

    @action(name="app.file-open-recent")
    def action_open_recent(self, filename: str):
        self.load(filename)
<commit_msg>"New" action should always create a new session<commit_after>import logging

from gi.repository import Gio

from gaphor.abc import ActionProvider, Service
from gaphor.core import action, gettext
from gaphor.ui.filedialog import FileDialog
from gaphor.ui.questiondialog import QuestionDialog

log = logging.getLogger(__name__)

FILTERS = [
    {"name": gettext("Gaphor Models"), "pattern": "*.gaphor"},
    {"name": gettext("All Files"), "pattern": "*"},
]

class AppFileManager(Service, ActionProvider):
    """
    Handle application level file loading
    """

    def __init__(self, session):
        self.session = session

    def shutdown(self):
        pass

    @property
    def application(self):
        return Gio.Application.get_default()

    def load(self, filename):
        if self.active_session_is_new():
            file_manager = self.session.get_service("file_manager")
            file_manager.load(filename)
        else:
            self.application.open([Gio.File.new_for_path(filename)], "")

    def new(self):
        self.application.open([], "__new__")

    def active_session_is_new(self):
        """
        If it's a new model, there is no state change (undo & redo)
        and no file name is defined.
        """
        undo_manager = self.session.get_service("undo_manager")
        file_manager = self.session.get_service("file_manager")

        return (
            not undo_manager.can_undo()
            and not undo_manager.can_redo()
            and not file_manager.filename
        )

    @property
    def main_window(self):
        return self.session.get_service("main_window")

    def file_dialog(self, title):
        file_dialog = FileDialog(title, filters=FILTERS, parent=self.main_window.window)

        filename = file_dialog.selection

        file_dialog.destroy()

        log.debug(filename)

        return filename

    @action(name="app.file-new", shortcut="<Primary>n")
    def action_new(self):
        """The new model menu action.  This action will create a new
        UML model.  This will trigger a FileManagerStateChange event."""

        self.new()

    @action(name="app.file-new-template")
    def action_new_from_template(self):
        """This menu action opens the new model from template dialog."""

        filename = self.file_dialog(gettext("New Gaphor Model From Template"))
        if filename:
            self.load(filename)
            self.filename = None

    @action(name="app.file-open", shortcut="<Primary>o")
    def action_open(self):
        """This menu action opens the standard model open dialog."""

        filename = self.file_dialog(gettext("Open Gaphor Model"))

        if filename:
            self.load(filename)

    @action(name="app.file-open-recent")
    def action_open_recent(self, filename: str):
        self.load(filename)
<|endoftext|>