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

"""
ElGateau:
A Library for Using the Elgato Stream Deck
for Experimental Psychology Research
"""

# import dependencies
import os
import time
import numpy as np

import hid
from PIL import Image, ImageDraw, ImageFont

__author__ = "<NAME>"
__copyright__ = "Copyright 2017-2018, <NAME>"

__license__ = "MIT"
__version__ = "1.0.0"
__maintainer__ = "<NAME>"
__email__ = "<EMAIL>"
__status__ = "Production"

# define constants
HID_VENDOR = 4057
HID_PRODUCT = 96

NUM_KEYS = 15
NUM_KEYS_ROW = 5
ICON_SIZE = 72, 72

NUM_TOTAL_PIXELS = ICON_SIZE[0]*ICON_SIZE[1]
NUM_PAGE1_PIXELS = 2583
NUM_PAGE2_PIXELS = NUM_TOTAL_PIXELS-NUM_PAGE1_PIXELS

RESET_DATA = [11, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
BRIGHTNESS_DATA = [5, 85, 170, 209, 1, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

HEADER_PAGE1 = [2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 77, 246,
                60, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 40, 0, 0, 0, 72, 0, 0, 0,
                72, 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0, 192, 60, 0, 0, 196, 14,
                0, 0, 196, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

HEADER_PAGE2 = [2, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


def hex2rgb(col):
    """
    Convert from hex color string to RGB tuple.

    Parameters
    ----------
    col : Hex color string for background.

    Returns
    ----------
    color : RGB tuple (0-255,0-255,0-255)
    """
    if isinstance(col, tuple):
        # assume this is RGB in (0-1) format
        # not an intended input, but we can work with it
        rgb = tuple(np.array(col)*255)
        return rgb

    # if preceded by a '#', remove it
    col = col.replace('#', '')

    rgb = tuple(bytes.fromhex(col))
    return rgb

#
#


class Icon(object):
    """
    Object for preparing icons for ElGateau.
    """

    ########################################
    #
    # Icon generation functions
    #
    # hex2rgb, solid, prep, text
    #
    ########################################

    def solid(col='000000'):
        """
        Create a icon that is a solid color.

        Parameters
        ----------
        col : Hex color string for background.

        Returns
        ----------
        icon : Icon object (from Icon class)
        """
        # make blank image of a solid color
        rgb = hex2rgb(col)
        ico = Image.new('RGBA', ICON_SIZE, rgb+(255,))

        icon = {}
        icon['ico'] = ico
        icon['label'] = 'solid'
        icon['contents'] = col
        return icon

    def prep(filename, pad=0):
        """
        Prepare icon (read from file, pad, resize).

        Parameters
        ----------
        filename : Filename for icon to prepare,
                   needs to be a PNG in the "icons" folder.
        bright : px
            Pad the icon before resizing, this way the icon
            doesn't go right to edge of display.

        Returns
        ----------
        icon : Icon object (from Icon class)
        """
        # read icon
        ico = Image.open(os.path.join(os.path.dirname(os.path.abspath(__file__)),"icons", filename+".png"))

        if pad != 0:
            # pad with blank space if don't know
            padded_size = ico.size[0]+pad, ico.size[1]+pad
            padded_im = Image.new("RGBA", padded_size)
            padded_im.paste(ico, (int((padded_size[0]-ico.size[0])/2),
                                  int((padded_size[1]-ico.size[1])/2)))
            ico = padded_im

        # ensure final image is 72x72
        ico.thumbnail(ICON_SIZE)

        icon = {}
        icon['ico'] = ico
        icon['label'] = 'image'
        icon['contents'] = filename
        return icon

    def text(text, ico=None, col='ffffff', back='000000',
             font='VeraMono-Bold', size=14, position=(31, 31)):
        """
        Overlay text over icon.

        Parameters
        ----------
        text : str
            Text to write.
        ico : 72x72 RGBA image
            Should have been output from icon_prep or icon_solid.
            Optional, defaults to black background.
        col : Hex color code string for text.
            Optional, defaults to white ('ffffff').
        back : Hex color string for background.
            Optional, defaults to black ('000000').
        font : Font filename, should be in "fonts" folder.
            Optional, defaults to VeraMono-Bold.
        size : Font size.
            Optional, defaults to 14.
        position : (int, int), Center of where to draw the text

        Returns
        ----------
        icon : Icon object (from Icon class)
        """
        # make a solid color background if necessary
        if back != '000000':
            ico = Icon.solid(back)
        elif ico is None:
            ico = Icon.key_blank

        # underlay
        base = ico['ico']

        # make a blank image for the text,
        # initialized to transparent text color
        txt = Image.new('RGBA', base.size, (255, 255, 255, 0))

        # setup font
        fnt = ImageFont.truetype(os.path.join(os.path.dirname(os.path.abspath(__file__)),"fonts", font+".ttf"), size)
        rgb = hex2rgb(col)
        # get a drawing context
        draw = ImageDraw.Draw(txt)

        # write text
        width, height = draw.textsize(text, font=fnt)
        position = (position[0]-width/2+4, position[1]-height/2+4)
        # convert location positions to int (rather than float)
        position = tuple(map(int, position))

        draw.text(position, text, font=fnt, fill=rgb+(255,), align='center')<commit_msg>fix for external icons and fonts<commit_after>#!/usr/bin/env python

"""
ElGateau:
A Library for Using the Elgato Stream Deck
for Experimental Psychology Research
"""

# import dependencies
import os
import time
import numpy as np

import hid
from PIL import Image, ImageDraw, ImageFont

__author__ = "<NAME>"
__copyright__ = "Copyright 2017-2018, <NAME>"

__license__ = "MIT"
__version__ = "1.0.2"
__maintainer__ = "<NAME>"
__email__ = "<EMAIL>"
__status__ = "Production"

# define constants
HID_VENDOR = 4057
HID_PRODUCT = 96

NUM_KEYS = 15
NUM_KEYS_ROW = 5
ICON_SIZE = 72, 72

NUM_TOTAL_PIXELS = ICON_SIZE[0]*ICON_SIZE[1]
NUM_PAGE1_PIXELS = 2583
NUM_PAGE2_PIXELS = NUM_TOTAL_PIXELS-NUM_PAGE1_PIXELS

RESET_DATA = [11, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
BRIGHTNESS_DATA = [5, 85, 170, 209, 1, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

HEADER_PAGE1 = [2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 77, 246,
                60, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 40, 0, 0, 0, 72, 0, 0, 0,
                72, 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0, 192, 60, 0, 0, 196, 14,
                0, 0, 196, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

HEADER_PAGE2 = [2, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


def hex2rgb(col):
    """
    Convert from hex color string to RGB tuple.

    Parameters
    ----------
    col : Hex color string for background.

    Returns
    ----------
    color : RGB tuple (0-255,0-255,0-255)
    """
    if isinstance(col, tuple):
        # assume this is RGB in (0-1) format
        # not an intended input, but we can work with it
        rgb = tuple(np.array(col)*255)
        return rgb

    # if preceded by a '#', remove it
    col = col.replace('#', '')

    rgb = tuple(bytes.fromhex(col))
    return rgb

#
#


class Icon(object):
    """
    Object for preparing icons for ElGateau.
    """

    ########################################
    #
    # Icon generation functions
    #
    # hex2rgb, solid, prep, text
    #
    ########################################

    def solid(col='000000'):
        """
        Create a icon that is a solid color.

        Parameters
        ----------
        col : Hex color string for background.

        Returns
        ----------
        icon : Icon object (from Icon class)
        """
        # make blank image of a solid color
        rgb = hex2rgb(col)
        ico = Image.new('RGBA', ICON_SIZE, rgb+(255,))

        icon = {}
        icon['ico'] = ico
        icon['label'] = 'solid'
        icon['contents'] = col
        return icon

    def prep(filename, pad=0, external_file=False):
        """
        Prepare icon (read from file, pad, resize).

        Parameters
        ----------
        filename : Filename for icon to prepare.
                   Ordinarily expects a PNG in the "icons" folder. 
                   Otherwise, see external_file.
        pad : px
            Pad the icon before resizing, this way the icon
            doesn't go right to edge of display.
            
        external_file : If False, filename is expected to be in 
                        "icons" folder relative to ElGateau.py.
                        If True, filename should be path relative 
                        to experiment.

        Returns
        ----------
        icon : Icon object (from Icon class)
        """
        # read icon
        if external_file == True:
            ico = Image.open(filename)
        else:
            # if the icon is in the 'icon' folder
            ico = Image.open(os.path.join(os.path.dirname(os.path.abspath(__file__)),"icons", filename+".png"))

        if pad != 0:
            # pad with blank space if don't know
            padded_size = ico.size[0]+pad, ico.size[1]+pad
            padded_im = Image.new("RGBA", padded_size)
            padded_im.paste(ico, (int((padded_size[0]-ico.size[0])/2),
                                  int((padded_size[1]-ico.size[1])/2)))
            ico = padded_im

        # ensure final image is 72x72
        ico.thumbnail(ICON_SIZE)

        icon = {}
        icon['ico'] = ico
        icon['label'] = 'image'
        icon['contents'] = filename
        return icon

    def text(text, ico=None, col='ffffff', back='000000',
             font='VeraMono-Bold', size=14, position=(31, 31),
             external_file=False):
        """
        Overlay text over icon.

        Parameters
        ----------
        text : str
            Text to write.
            
        ico : 72x72 RGBA image
            Should have been output from icon_prep or icon_solid.
            Optional, defaults to black background.
            
        col : Hex color code string for text.
            Optional, defaults to white ('ffffff').
            
        back : Hex color string for background.
            Optional, defaults to black ('000000').
            
        font : Font filename, ordinarily should be in "fonts" folder.
            Otherwise, see external_file.
            Optional, defaults to VeraMono-Bold.
            
        size : Font size.
            Optional, defaults to 14.
            
        position : (int, int), Center of where to draw the text
        
        external_file : If False (default), filename is expected to be in 
                        "fonts" folder relative to ElGateau.py.
                         If True, filename should be path relative 
                        to experiment.        
        
        Returns
        ----------
        icon : Icon object (from Icon class)
        """
        # make a solid color background if necessary
        if back != '000000':
            ico = Icon.solid(back)
        elif ico is None:
            ico = Icon.key_blank

        # underlay
        base = ico['ico']

        # make a blank image for the text,
        # initialized to transparent text color
        txt = Image.new('RGBA', base.size, (255, 255, 255, 0))

        # setup font
        if external_file == True:
            fnt = ImageFont.truetype(font, size)
        else:
            fnt = ImageFont.truetype(os.path.join(os.path.dirname(os.path.abspath(__file__)),"fonts", font+".ttf"), size)
            
        rgb = hex2rgb(col)
        # get a drawing context
        draw = ImageDraw.Draw(txt)

        # write text
        width, height = draw.textsize(text, font=fnt)
        position = (position[0]-width/2+4, position[1]-height/2+4)
        # convert location positions to int (rather than float)
        position = tuple(map(int, position))

        draw.text(position, text, font=fnt, fill=rgb+(255,), align='center')
{'fraction_non_alphanumeric': 0.1372241051500372, 'fraction_numerical': 0.04546581797139787, 'mean_word_length': 1.80761197493618, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1415938', 'n_tokens_mistral': 4278, 'n_tokens_neox': 3685, 'n_words': 1234}