예제 #1
0
 def __init__(self, program_name, load_profile=False, data=None):
     self.name = program_name.replace('\\', '').replace('/', '')
     if data is None and load_profile:
         data = load_data(data)
     self.data = data
     self.file_name = format_name(self.name)
     self.reload()
예제 #2
0
 def __init__(self, program_name, load_profile=False, data=None):
     if program_name is None:
         program_name = DEFAULT_NAME
     self.name = program_name.replace('\\', '').replace('/', '')
     if data is None and load_profile:
         data = LoadData(data)
     self.data = data
     self.file_name = format_name(self.name)
     self.reload()
예제 #3
0
def calculate_colour_map(colour_map):
    try:
        return parse_colour_text(parse_colour_file()['Maps'][format_name(colour_map)]['Colour'])
    except KeyError:
        generated_map = parse_colour_text(colour_map)
        if generated_map:
            if len(generated_map) < 2:
                raise ValueError('not enough colours to generate colour map')
            return generated_map
        else:
            raise ValueError('unknown colour map')
예제 #4
0
def user_generate():
    """Ask for options and generate an image.
    This seriously needs rewriting.
    
    Idea:
        List of profiles (choose page/type id/type name), shows the file size and last modified date of each profile.
        (Load profile)
        Say some stats about the profile
        Ask for mouse tracks, clicks and key presses
        For each of those, ask for colour profile and say the file location
        Ask to open folder (will require image path rewrite for a base path)
        Loop back to start if required
    """
    all_strings = Language().get_strings()
    _string = all_strings['string']
    string = all_strings['string']['image']
    word = all_strings['word']

    Message(string['profile']['list'].format(L=word['list']))
    profile = input()

    if profile.lower() == word['list'].lower():

        #Read the data folder and format names
        all_files = list_data_files()
        if not all_files:
            Message(string['profile']['empty'])
            Message(all_strings['exit'])
            input()
            sys.exit()
        programs = {format_name(DEFAULT_NAME): DEFAULT_NAME}

        app_list = AppList()
        for program_name in app_list.names:
            programs[format_name(program_name)] = program_name

        page = 1
        limit = 15
        maximum = len(all_files)
        total_pages = round_up(maximum / limit)

        sort_date = string['page']['sort']['date']
        sort_name = string['page']['sort']['name']
        change_sort = [sort_name, 2]

        #Ask for user input
        while True:
            offset = (page - 1) * limit

            results = all_files[offset:offset + limit]
            for i, r in enumerate(results):
                try:
                    program_name = programs[r]
                except KeyError:
                    program_name = r
                Message('{}: {}'.format(i + offset + 1, program_name))
            Message(string['page']['current'].format(C=page,
                                                     T=total_pages,
                                                     P=word['page']))

            Message(change_sort[0].format(
                S='{} {}'.format(word['sort'], change_sort[1])))
            Message(string['profile']['number']['input'])

            profile = input()
            last_page = page

            #Change page
            if profile.lower().startswith('{P} '.format(P=word['page'])):
                try:
                    page = int(profile.split()[1])
                    if not 0 < page <= total_pages:
                        raise ValueError
                except IndexError:
                    Message(string['page']['invalid'])
                except ValueError:
                    if page > total_pages:
                        page = total_pages
                    else:
                        page = 1

            #Shortcut to change page
            elif profile.endswith('>'):
                if page < total_pages:
                    page += 1
            elif profile.startswith('<'):
                if page > 1:
                    page -= 1

            #Change sorting of profile list
            elif (profile.lower().startswith('{} '.format(word['sort']))
                  or profile.lower() == word['sort']):
                try:
                    sort_level = int(profile.split()[1])
                except ValueError:
                    sort_level = 0
                except IndexError:
                    sort_level = 1
                try:
                    sort_reverse = int(profile.split()[2])
                except (ValueError, IndexError):
                    sort_reverse = 0
                if sort_level == 1:
                    all_files = list_data_files()
                    change_sort = [sort_name, 2]
                elif sort_level == 2:
                    all_files = sorted(list_data_files())
                    change_sort = [sort_date, 1]
                if sort_reverse:
                    all_files = all_files[::-1]

            #Select profile
            else:
                try:
                    num = int(profile) - 1
                    if not 0 <= num <= maximum:
                        raise IndexError
                    profile_name = all_files[num]
                    try:
                        profile = programs[all_files[num]]
                    except KeyError:
                        profile = all_files[num]
                    break
                except ValueError:
                    break
                except IndexError:
                    Message(string['profile']['number']['nomatch'])

        try:
            profile = programs[profile]
        except KeyError:
            pass

    #Load functions
    Message(_string['import'])
    from core.image import RenderImage

    Message(_string['profile']['load'].format(P=profile))
    try:
        r = RenderImage(profile)
    except ValueError:
        Message('Error: Selected profile is empty or doesn\'t exist.')
        return

    #Check if profile is running
    try:
        current_profile = format_name(RunningApplications().check()[0])
    except TypeError:
        pass
    else:
        selected_profile = format_name(profile)

        if current_profile == selected_profile:
            Message(string['profile']['running']['warning'])

            save_time = ticks_to_seconds(CONFIG['Save']['Frequency'], 1)
            metadata = load_data(profile, _metadata_only=True)
            if metadata['Modified'] is None:
                Message(string['save']['wait'])
                Message(string['save']['frequency'].format(T=save_time))
                Message(_string['exit'])
                input()
                sys.exit()
            else:
                last_save_time = time.time() - metadata['Modified']
                next_save_time = CONFIG['Save']['Frequency'] - last_save_time
                last_save = ticks_to_seconds(last_save_time,
                                             1,
                                             allow_decimals=False)
                next_save = ticks_to_seconds(next_save_time,
                                             1,
                                             allow_decimals=False)
                Message(string['save']['next'].format(T1=last_save,
                                                      T2=next_save))

    generate_tracks = False
    generate_speed = False
    generate_heatmap = False
    generate_keyboard = False
    generate_csv = False

    default_options = [True, False, True, True, False]

    kb_string = string['name']['keyboard']
    kph = r.keys_per_hour()
    if kph < 10:
        default_options[2] = False
        kb_string = '{} ({})'.format(
            kb_string,
            string['name']['low']['keyboard']).format(C=round(kph, 2))

    Message(string['option']['generate'])
    default_option_text = ' '.join(
        str(i + 1) for i, v in enumerate(default_options) if v)
    Message(string['option']['select'].format(V=default_option_text))
    Message('1: {}'.format(string['name']['track']))
    Message('2: {}'.format(string['name']['speed']))
    Message('3: {}'.format(string['name']['click']))
    Message('4: {}'.format(kb_string))
    Message('5: {}'.format(string['name']['csv']))

    selection = list(map(int, input().split()))
    result = value_select(selection, default_options, start=1)

    if result[0]:
        generate_tracks = True

    if result[1]:
        generate_speed = True

    if result[2]:

        generate_heatmap = True
        Message('Which mouse buttons should be included in the heatmap?.')

        default_options = [
            CONFIG['GenerateHeatmap']['_MouseButtonLeft'],
            CONFIG['GenerateHeatmap']['_MouseButtonMiddle'],
            CONFIG['GenerateHeatmap']['_MouseButtonRight']
        ]
        default_option_text = ' '.join(
            str(i + 1) for i, v in enumerate(default_options) if v)
        Message(string['option']['select'].format(V=default_option_text))

        Message('1: {}'.format(word['mousebutton']['left']))
        Message('2: {}'.format(word['mousebutton']['middle']))
        Message('3: {}'.format(word['mousebutton']['right']))
        selection = list(map(int, input().split()))
        heatmap_buttons = value_select(selection, default_options, start=1)
        CONFIG['GenerateHeatmap']['_MouseButtonLeft'] = heatmap_buttons[0]
        CONFIG['GenerateHeatmap']['_MouseButtonMiddle'] = heatmap_buttons[1]
        CONFIG['GenerateHeatmap']['_MouseButtonRight'] = heatmap_buttons[2]
        if not any(heatmap_buttons):
            generate_heatmap = False

    if result[3]:
        generate_keyboard = True

    if result[4]:
        generate_csv = True

    if any((generate_tracks, generate_speed, generate_heatmap,
            generate_keyboard, generate_csv)):

        last_session_start = r.data['Ticks']['Session']['Total']
        last_session_end = r.data['Ticks']['Total']
        all_time = ticks_to_seconds(last_session_end, UPDATES_PER_SECOND)
        last_session_time = ticks_to_seconds(
            last_session_end - last_session_start, UPDATES_PER_SECOND)

        csv_only = generate_csv and not any(
            (generate_tracks, generate_heatmap, generate_keyboard))
        if not last_session_time or last_session_time == all_time or csv_only:
            last_session = False

        else:
            while True:
                Message(string['option']['session']['select'])

                Message('1: {} [{}]'.format(
                    string['option']['session']['all'].format(T=all_time),
                    word['default']))
                Message('2: {}'.format(
                    string['option']['session']['last'].format(
                        T=last_session_time)))

                selection = list(map(int, input().split()))
                result = value_select(selection, [True, False], start=1)
                if result[0] and result[1]:
                    Message(string['option']['error']['single'])
                elif result[1]:
                    last_session = True
                    break
                else:
                    last_session = False
                    break

        #Generate
        if generate_tracks:
            r.tracks(last_session)
        if generate_speed:
            r.speed(last_session)
        if generate_heatmap:
            r.clicks(last_session)
        if generate_keyboard:
            r.keyboard(last_session)
        if generate_csv:
            r.csv()
        if CONFIG['GenerateImages']['OpenOnFinish']:
            Message(string['option']['open'])
            open_folder(r.name.generate())

    else:
        Message(string['option']['error']['nothing'])
예제 #5
0
def user_generate():
    CONFIG.save()
    print('Type profile to load, or type "list" to see them all:')
    profile = raw_input()

    if profile == 'list':

        #Read the data folder and format names
        all_files = sorted(list_data_files())
        if not all_files:
            print('Sorry, nothing was found in the data folder.')
            print('Press enter to exit.')
            raw_input()
            sys.exit()
        programs = {format_name(DEFAULT_NAME): DEFAULT_NAME}
        for program_name in read_app_list().values():
            programs[format_name(program_name)] = program_name

        page = 1
        limit = 15
        maximum = len(all_files)
        total_pages = round_up(maximum / limit)

        #Ask for user input
        while True:
            offset = (page - 1) * limit

            results = all_files[offset:offset + limit]
            for i, r in enumerate(results):
                try:
                    program_name = programs[r]
                except KeyError:
                    program_name = r
                print('{}: {}'.format(i + offset + 1, program_name))
            print('Page {} of {}. Type "page <number>" to switch.'.format(
                page, total_pages))
            print('You can type the number or name of a profile to load it.')

            profile = raw_input()
            last_page = page
            if profile.startswith('page '):
                try:
                    page = int(profile.split()[1])
                    if not 0 < page <= total_pages:
                        raise ValueError
                except IndexError:
                    print('Invalid page number')
                except ValueError:
                    if page > total_pages:
                        page = total_pages
                    else:
                        page = 1
            elif profile == '>':
                if page < total_pages:
                    page += 1
            elif profile == '<':
                if page > 1:
                    page -= 1
            else:
                try:
                    num = int(profile) - 1
                    if not 0 <= num <= maximum:
                        raise IndexError
                    try:
                        profile = programs[all_files[num]]
                    except KeyError:
                        profile = all_files[num]
                    break
                except ValueError:
                    break
                except IndexError:
                    print('Number doesn\'t match any profiles')

    try:
        current_profile = format_name(RunningApplications().check()[0])
    except TypeError:
        pass
    else:
        selected_profile = format_name(profile)

        if current_profile == selected_profile:
            print('Warning: The profile you selected is currently running.')

            save_time = ticks_to_seconds(CONFIG['Save']['Frequency'], 1)
            metadata = load_program(profile, _metadata_only=True)
            if metadata['Modified'] is None:
                print(
                    'It has not had a chance to save yet, please wait a short while before trying again.'
                )
                print('The saving frequency is currently set to {}.'.format(
                    save_time))
                print('Press enter to exit.')
                raw_input()
                sys.exit()
            else:
                last_save_time = time.time() - metadata['Modified']
                next_save_time = CONFIG['Save']['Frequency'] - last_save_time
                last_save = ticks_to_seconds(last_save_time,
                                             1,
                                             allow_decimals=False)
                next_save = ticks_to_seconds(next_save_time,
                                             1,
                                             allow_decimals=False)
                print(
                    'It was last saved {} ago, so any tracks more recent than this will not be shown.'
                    .format(last_save))
                print('The next save is due in roughly {}.'.format(next_save))

    generate_tracks = False
    generate_heatmap = False

    print('What do you want to generate?')
    print('Separate options with a space, or hit enter for all.')
    print('1: Tracks')
    print('2: Click Heatmap')

    result = simple_bit_mask(raw_input().split(), 2)

    if result[0]:
        generate_tracks = True

    if result[1]:

        generate_heatmap = True
        print('Which mouse buttons should be included in the heatmap?.')
        print('Separate options with a space, or hit enter for all.')
        print('1: Left Mouse Button')
        print('2: Middle Mouse Button')
        print('3: Right Mouse Button')
        heatmap_buttons = map(bool, simple_bit_mask(raw_input().split(), 3))
        CONFIG['GenerateHeatmap']['_MouseButtonLeft'] = heatmap_buttons[0]
        CONFIG['GenerateHeatmap']['_MouseButtonMiddle'] = heatmap_buttons[1]
        CONFIG['GenerateHeatmap']['_MouseButtonRight'] = heatmap_buttons[2]
        if not heatmap_buttons:
            generate_heatmap = False

    if generate_tracks or generate_heatmap:
        print('Importing modules...')
        from core.image import RenderImage

        print('Loading profile {}...'.format(profile))
        r = RenderImage(profile)

        last_session_start = r.data['Ticks']['Session']['Total']
        last_session_end = r.data['Ticks']['Total']
        ups = CONFIG['Main']['UpdatesPerSecond']
        all_time = ticks_to_seconds(last_session_end, ups)
        last_session_time = ticks_to_seconds(
            last_session_end - last_session_start, ups)

        if not last_session_time or last_session_time == all_time:
            last_session = False

        else:
            while True:
                print(
                    'Would you like to generate everything or just the last session?'
                )
                print('1: Everything ({}) [Default]'.format(all_time))
                print('2: Last Session ({})'.format(last_session_time))

                result = simple_bit_mask(raw_input().split(),
                                         2,
                                         default_all=False)
                if result[0] and result[1]:
                    print('Please only select one option.')
                elif result[1]:
                    last_session = True
                    break
                else:
                    last_session = False
                    break

        #Generate
        if generate_tracks:
            r.generate('Tracks', last_session)
        if generate_heatmap:
            r.generate('Clicks', last_session)
    else:
        print('Nothing was set to generate.')
예제 #6
0
def parse_colour_file(path=COLOUR_FILE):
    """Read the colours text file to get all the data.
    
    Returns a dictionary containing the keys 'Colours' and 'Maps'.
    
    Colours:
        Syntax: colour.name=value
        The value must be given as a hex code, where it will be converted 
        into an RGBA list with the range of 0 to 255.
        
        Format:
            {name.lower(): {'UpperCase': name, 
                            'Colour': [r, 
                                       g, 
                                       b,
                                       a]}}
            
    Maps:
        Syntax: maps.name.type[.options]=value
        
        Set a colour scheme for a map with "maps.Name.colour=__________".
        That will add a colour map that can be accessed with "Name".
        Add alternative options with "maps.Name.colour.Alter.native=__________"
        That will add a colour map that can be accessed with "nativeAlterName".
        Set if it is allowed for tracks, clicks, or the keyboard 
        with "maps.Name.tracks/clicks/keyboard=True".
        It may be enabled for more than one.
        
        Format:
            {name.lower(): {'Colour': value,
                            'UpperCase': name,
                            'Type': {'tracks': bool,
                                     'clicks': bool,
                                     'keyboard': bool}}}
    """
    with open(path, 'r') as f:
        data = f.read()
    
    colours = {}
    colour_maps = {}
    for i, line in enumerate(data.splitlines()):
        var, value = [i.strip() for i in line.split('=', 1)]
        var_parts = var.split('.')
        
        #Parse colour part
        if var_parts[0] == 'colour':
            if len(var_parts) < 2:
                continue
                
            rgb = tuple(hex_to_colour(value)[1])
            if rgb is not None:
                colours[format_name(var_parts[1])] = {'Uppercase': var_parts[1], 'Colour': rgb}
        
        #Parse colour map part
        elif var_parts[0] == 'map':
            if len(var_parts) < 3:
                continue
        
            map_name = var_parts[1]
            map_name_l = format_name(map_name)
            var_type = var_parts[2].lower()
            
            if map_name_l not in colour_maps:
                colour_maps[map_name_l] = {'Colour': None, 'UpperCase': map_name,
                                         'Type': {'tracks': False, 'clicks': False, 'keyboard': False}}
                                         
            if var_type == 'colour':
            
                #Check if it is an alternative map, and if so, link to the main one
                map_name_ext = ''.join(var_parts[3:][::-1]) + map_name
                map_name_ext_l = format_name(map_name_ext)
                if map_name_l != map_name_ext_l:
                    colour_maps[map_name_ext_l] = {'Colour': value, 'UpperCase': map_name_ext,
                                                   'Type': colour_maps[map_name_l]['Type']}
                else:
                    colour_maps[map_name_ext_l]['Colour'] = value
                    
            elif var_type in ('clicks', 'tracks', 'keyboard'):
                if value.lower().startswith('t') or value.lower().startswith('y'):
                    colour_maps[map_name_l]['Type'][var_type] = True
                    
    return {'Colours': colours, 'Maps': colour_maps}
예제 #7
0
def parse_colour_text(colour_string):
    """Convert text into a colour map.
    It could probably do with a rewrite to make it more efficient,
    as it was first written to only use capitals.

    Mixed Colour:
        Combine multiple colours.
        Examples: BlueRed, BlackYellowGreen
    Hexadecimal Colours:
        As well as typing in words, you may also use hex.
        All the same effects can apply to these.
        Supported formats are #RGB, #RGBA, #RRGGBB, #RRGGBBAA.
    Modified Colour:
        Apply a modification to a colour.
        If multiple ones are applied, they will work in reverse order.
        Light and dark are not opposites so will not cancel each other out.
        Examples: LightBlue, DarkLightYellow
    Transition:
        This ends the current colour mix and starts a new one.
        Examples: BlackToWhite, RedToGreenToBlue
    Duplicate:
        Avoid having to type out multiple versions of the same word.
        Be careful as it has different effects based on its position.
        It basically multiplies the next word, see below for usage.
        Examples:
            Before colour: DarkDoubleRed = DarkRedDarkRed
            Before modifier: TripleDarkLightRed = DarkDarkDarkLightRed
            Before transition: BlueDoubleToDarkRed = BlueToDarkRedToDarkRed
    Any number of these features can be combined together to create different effects.
        
    As an example, here are the values that would result in the heatmap:
        BlackToDarkBlueToBlueToCyanTripleBlueToCyanBlueTo
        + TripleCyanBlueToTripleCyanYellowToCyanYellowTo
        + CyanTripleYellowToYellowToOrangeToRedOrangeToRed 
    """
    colour_string = format_name(colour_string, '#')
    colour_data = parse_colour_file()['Colours']

    current_mix = [[]]
    current_colour = {'Mod': [], 'Dup': 1}
    while colour_string:
        edited = False

        #Check for colours
        colour_selection = None
        for colour, data in get_items(colour_data):
            if colour_string.startswith(colour):
                colour_string = colour_string[len(colour):]
                colour_selection = data['Colour']
                break

        #Check for hex codes
        if colour_string.startswith('#'):
            length, colour_selection = hex_to_colour(colour_string[1:9])
            if colour_selection and length:
                colour_string = colour_string[1 + length:]

        #Process colour with stored modifiers/duplicates
        colour = None
        if colour_selection:
            edited = True
            
            #Apply modifiers in reverse order
            colour = list(colour_selection)
            for modifier in current_colour['Mod']:
                colour_offset = modifier.get('ColourOffset', 0)
                colour_shift = modifier.get('ColourShift', 0)
                alpha_offset = modifier.get('AlphaOffset', 0)
                alpha_shift = modifier.get('AlphaShift', 0)
                colour = [(colour[0] >> colour_shift) + colour_offset,
                          (colour[1] >> colour_shift) + colour_offset,
                          (colour[2] >> colour_shift) + colour_offset,
                          (colour[3] >> alpha_shift) + alpha_offset]
                          
            current_colour['Mod'] = []
            current_mix[-1] += [colour] * current_colour['Dup']
            current_colour['Dup'] = 1
            continue

        #Check for modifiers (dark, light, transparent etc)
        for i in MODIFIERS:
            if colour_string.startswith(i):
                colour_string = colour_string[len(i):]
                edited = True
                current_colour['Mod'] += [MODIFIERS[i]] * current_colour['Dup']
                current_colour['Dup'] = 1
        if edited:
            continue

        #Check for duplicates (double, triple, etc)
        for i in DUPLICATES:
            if colour_string.startswith(i):
                colour_string = colour_string[len(i):]
                edited = True
                current_colour['Dup'] *= DUPLICATES[i]
        if edited:
            continue

        #Start a new groups of colours
        for i in SEPERATORS:
            if colour_string.startswith(i):
                colour_string = colour_string[len(i):]
                edited = True

                #Handle putting a duplicate before 'to'
                new_list = []
                list_len = current_colour['Dup']
                if not current_mix[-1]:
                    new_list = current_mix[-1]
                    list_len -= 1

                #Start the ew list
                current_mix += [new_list] * list_len
                current_colour['Dup'] = 1
                break
        if edited:
            continue

        #Remove the first letter and try again
        colour_string = colour_string[1:]
    
    if not current_mix[0]:
        raise ValueError('invalid colour map')

    #Merge colours together
    final_mix = []
    for colours in current_mix:
        
        result = colours[0]
        for colour in colours[1:]:
            result = [i + j for i, j in zip(result, colour)]
            
        num_colours = len(colours)
        final_mix.append(tuple(i / num_colours for i in result))
    return final_mix