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()
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()
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')
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'])
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.')
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}
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