Ejemplo n.º 1
0
 def __init__(self, file_name, default_data, group_order=None):
     self.file_name = format_file_path(file_name)
     self._default_data = default_data
     self.default_data = {}
     self.order = list(group_order) if group_order is not None else []
     for group, data in get_items(self._default_data):
         self.default_data[group] = self._default_data[group]
     self.load()
Ejemplo n.º 2
0
    def generate(self, image_type, reload=False):

        if reload:
            self.reload()

        if image_type.lower() == 'clicks':
            name = CONFIG['GenerateHeatmap']['NameFormat']
            name = name.replace('[ExpMult]', self.heatmap_exp)
            name = name.replace('[GaussianSize]', self.heatmap_gaussian)
            name = name.replace('[ColourProfile]', self.heatmap_colour)

            selected_buttons = [
                k for k, v in get_items(self.heatmap_buttons) if v
            ]
            if all(self.heatmap_buttons.values()):
                name = name.replace('[MouseButtons]', 'Combined')
            elif len(selected_buttons) == 2:
                name = name.replace('[MouseButtons]',
                                    '+'.join(selected_buttons))
            elif len(selected_buttons) == 1:
                name = name.replace('[MouseButtons]', selected_buttons[0])
            else:
                name = name.replace('[MouseButtons]', 'Empty')

        elif image_type.lower() == 'tracks':
            name = CONFIG['GenerateTracks']['NameFormat']
            name = name.replace('[ColourProfile]', self.track_colour)
        else:
            raise ValueError('incorred image type: {}, '
                             'must be tracks or clicks'.format(image_type))
        name = name.replace('[UResX]', self.upscale_res_x)
        name = name.replace('[UResY]', self.upscale_res_y)
        name = name.replace('[ResX]', self.output_res_x)
        name = name.replace('[ResY]', self.output_res_y)
        name = name.replace('[FriendlyName]', self.name)

        #Replace invalid characters
        invalid_chars = ':*?"<>|'
        for char in invalid_chars:
            if char in name:
                name = name.replace(char, '')

        return '{}.{}'.format(format_file_path(name),
                              CONFIG['GenerateImages']['FileType'])
Ejemplo n.º 3
0
    def load(self):
        """Open config file and validate values.
        
        
        Allowed formats:
            value, type, [comment] 
            value, int/float, [min, [max]], [comment]
            value, str, [is case sensitive, item1, item2...], [comment]
        """
        try:
            with open(self.file_name, 'r') as f:
                config_lines = [i.strip() for i in f.readlines()]
        except IOError:
            config_lines = []
        
        #Read user values
        config_data = {}
        for line in config_lines:
            if not line:
                continue
            
            #Start new heading
            if line.startswith('['):
                current_group = line[1:].split(']', 1)[0]
                config_data[current_group] = {}
            
            #Skip comment
            elif line[0] in (';', '/', '#'):
                pass
            
            #Process value
            else:
                name, value = [i.strip() for i in line.split('=', 1)]
                value = value.replace('#', ';').replace('//', ';').split(';', 1)[0]
                
                #Compare value in file to default settings
                try:
                    default_value, default_type = self.default_data[current_group][name][:2]
                except KeyError:
                    pass
                else:
                    #Process differently depending on variable type
                    if default_type == bool:
                        if value.lower() in ('0', 'false'):
                            value = False
                        elif value.lower() in ('1', 'true'):
                            value = True
                        else:
                            value = default_value
                            
                    elif default_type == int:
                        if '.' in value:
                            value = value.split('.')[0]
                        try:
                            value = int(value)
                        except ValueError:
                            value = default_value
                            
                    elif default_type == str:
                        value = str(value).rstrip()
                        
                    else:
                        value = default_type(value)
                    
                    #Handle min/max values
                    if default_type in (int, float):
                        no_text = [i for i in self.default_data[current_group][name] if not isinstance(i, str)]
                        if len(no_text) >= 3:
                            if no_text[2] is not None and no_text[2] > value:
                                value = no_text[2]
                            elif len(no_text) >= 4:
                                if no_text[3] is not None and no_text[3] < value:
                                    value = no_text[3]
                    if default_type == str:
                        if len(self.default_data[current_group][name]) >= 3:
                            if isinstance(self.default_data[current_group][name][2], tuple):
                                allowed_values = list(self.default_data[current_group][name][2])
                                case_sensitive = allowed_values.pop(0)
                                if case_sensitive:
                                    if not any(value == i for i in allowed_values):
                                        value = default_value
                                else:
                                    value_lower = value.lower()
                                    if not any(value_lower == i.lower() for i in allowed_values):
                                        value = default_value
                            
                config_data[current_group][name] = value
        
        #Add any remaining values that weren't in the file
        for group, variables in get_items(self.default_data):
            for variable, defaults in get_items(variables):
                try:
                    config_data[group][variable]
                except KeyError:
                    try:
                        config_data[group][variable] = defaults[0]
                    except KeyError:
                        config_data[group] = {variable: defaults[0]}

        self.data = config_data        
        return self.data
Ejemplo n.º 4
0
def background_process(q_recv, q_send):
    """Function to handle all the data from the main thread."""
    try:
        NOTIFY(START_THREAD)
        NOTIFY.send(q_send)
        
        store = {'Data': load_program(),
                 'LastProgram': None,
                 'Resolution': None,
                 'ResolutionTemp': None,
                 'ResolutionList': set(),
                 'Offset': (0, 0),
                 'LastResolution': None,
                 'ActivitySinceLastSave': False,
                 'SavesSkipped': 0,
                 'PingTimeout': CONFIG['Timer']['_Ping'] + 1}
        
        NOTIFY(DATA_LOADED)
        NOTIFY(QUEUE_SIZE, q_recv.qsize())
        NOTIFY.send(q_send)
        
        
        while True:
            
            received_data = q_recv.get()
            '''
            #Quit if data is not received by the ping interval
            #Seems buggy so disabling for now
            try:
                received_data = q_recv.get(timeout=store['PingTimeout'])
            except Empty:
                break
                '''
            
            check_resolution = False
            
            if 'Save' in received_data:
                if store['ActivitySinceLastSave']:
                    _save_wrapper(q_send, store['LastProgram'], store['Data'], False)
                    NOTIFY(QUEUE_SIZE, q_recv.qsize())
                    store['ActivitySinceLastSave'] = False
                    store['SavesSkipped'] = 0
                else:
                    store['SavesSkipped'] += 1
                    NOTIFY(SAVE_SKIP, CONFIG['Save']['Frequency'] * store['SavesSkipped'], q_recv.qsize())
                q_send.put({'SaveFinished': None})

            if 'Program' in received_data:
                current_program = received_data['Program']
                
                if current_program != store['LastProgram']:
                    
                    if current_program is None:
                        NOTIFY(APPLICATION_LOADING)
                    else:
                        NOTIFY(APPLICATION_LOADING, current_program)
                    NOTIFY.send(q_send)
                    
                    #Save old profile
                    _save_wrapper(q_send, store['LastProgram'], store['Data'], True)
                    
                    #Load new profile
                    store['LastProgram'] = current_program
                    store['Data'] = load_program(current_program)
                    store['ActivitySinceLastSave'] = False
                    
                    #Check new resolution
                    _check_resolution(store, store['Resolution'])
                    store['ResolutionList'] = set()
                        
                    if store['Data']['Ticks']['Total']:
                        NOTIFY(DATA_LOADED)
                    else:
                        NOTIFY(DATA_NOTFOUND)
                            
                    NOTIFY(QUEUE_SIZE, q_recv.qsize())
                NOTIFY.send(q_send)

            if 'Resolution' in received_data:
                store['Resolution'] = received_data['Resolution']
                _check_resolution(store, store['Resolution'])
            
            if 'MonitorLimits' in received_data:
                store['ResolutionTemp'] = received_data['MonitorLimits']
            
            #Record key presses
            if 'KeyPress' in received_data:
                store['ActivitySinceLastSave'] = True
                
                for key in received_data['KeyPress']:
                    try:
                        store['Data']['Keys']['All']['Pressed'][key] += 1
                    except KeyError:
                        store['Data']['Keys']['All']['Pressed'][key] = 1
                    try:
                        store['Data']['Keys']['Session']['Pressed'][key] += 1
                    except KeyError:
                        store['Data']['Keys']['Session']['Pressed'][key] = 1
            
            #Record time keys are held down
            if 'KeyHeld' in received_data:
                store['ActivitySinceLastSave'] = True
                
                for key in received_data['KeyHeld']:
                    try:
                        store['Data']['Keys']['All']['Held'][key] += 1
                    except KeyError:
                        store['Data']['Keys']['All']['Held'][key] = 1
                    try:
                        store['Data']['Keys']['Session']['Held'][key] += 1
                    except KeyError:
                        store['Data']['Keys']['Session']['Held'][key] = 1
            
            #Calculate and track mouse movement
            if 'MouseMove' in received_data:
                store['ActivitySinceLastSave'] = True
                resolution = 0
                _resolution = -1
                
                start, end = received_data['MouseMove']
                #distance = find_distance(end, start)
                
                #Calculate the pixels in the line
                if end is None:
                    raise TypeError('debug - mouse moved without coordinates')
                if start is None:
                    mouse_coordinates = [end]
                else:
                    mouse_coordinates = [start, end] + calculate_line(start, end)
                    
                    #Don't bother calculating offset for each pixel
                    #if both start and end are on the same monitor
                    try:
                        resolution, offset = monitor_offset(start, store['ResolutionTemp'])
                        _resolution = monitor_offset(end, store['ResolutionTemp'])[0]
                    except TypeError:
                        pass
                        
                #Write each pixel to the dictionary
                for pixel in mouse_coordinates:
                    if MULTI_MONITOR:
                        
                        if resolution != _resolution:
                            try:
                                resolution, offset = monitor_offset(pixel, store['ResolutionTemp'])
                            except TypeError:
                                store['ResolutionTemp'] = monitor_info()
                                try:
                                    resolution, offset = monitor_offset(pixel, store['ResolutionTemp'])
                                except TypeError:
                                    continue
                        
                        pixel = (pixel[0] - offset[0], pixel[1] - offset[1])
                        if resolution not in store['ResolutionList']:
                            _check_resolution(store, resolution)
                            store['ResolutionList'].add(resolution)
                            
                    else:
                        resolution = store['Resolution']
                        
                    store['Data']['Maps']['Tracks'][resolution][pixel] = store['Data']['Ticks']['Current']['Tracks']
                
                store['Data']['Ticks']['Current']['Tracks'] += 1
                
                #Compress tracks if the count gets too high
                if store['Data']['Ticks']['Current']['Tracks'] > CONFIG['CompressMaps']['TrackMaximum']:
                    compress_multplier = CONFIG['CompressMaps']['TrackReduction']
                    NOTIFY(TRACK_COMPRESS_START, 'track')
                    NOTIFY.send(q_send)
                    
                    tracks = store['Data']['Maps']['Tracks']
                    for resolution in tracks.keys():
                        tracks[resolution] = {k: int(v // compress_multplier)
                                              for k, v in get_items(tracks[resolution])}
                        tracks[resolution] = {k: v for k, v in get_items(tracks[resolution]) if v}
                        if not tracks[resolution]:
                            del tracks[resolution]
                            
                    NOTIFY(TRACK_COMPRESS_END, 'track')
                    NOTIFY(QUEUE_SIZE, q_recv.qsize())
                    store['Data']['Ticks']['Current']['Tracks'] //= compress_multplier
                    store['Data']['Ticks']['Session']['Current'] //= compress_multplier

            #Record mouse clicks
            if 'MouseClick' in received_data:
                store['ActivitySinceLastSave'] = True
                
                for mouse_button, pixel in received_data['MouseClick']:
                    if MULTI_MONITOR:
                    
                        try:
                            resolution, offset = monitor_offset(pixel, store['ResolutionTemp'])
                        except TypeError:
                            store['ResolutionTemp'] = monitor_info()
                            try:
                                resolution, offset = monitor_offset(pixel, store['ResolutionTemp'])
                            except TypeError:
                                continue
                            
                        pixel = (pixel[0] - offset[0], pixel[1] - offset[1])
                        
                    else:
                        resolution = store['Resolution']
                        
                    try:
                        store['Data']['Maps']['Clicks'][resolution][mouse_button][pixel] += 1
                    except KeyError:
                        store['Data']['Maps']['Clicks'][resolution][mouse_button][pixel] = 1
                
            #Increment the amount of time the script has been running for
            if 'Ticks' in received_data:
                store['Data']['Ticks']['Total'] += received_data['Ticks']
            store['Data']['Ticks']['Recorded'] += 1

            NOTIFY.send(q_send)
        
        #Exit process (this shouldn't happen for now)
        NOTIFY(THREAD_EXIT)
        NOTIFY.send(q_send)
        _save_wrapper(q_send, store['LastProgram'], store['Data'], False)
            
    except Exception as e:
        q_send.put(traceback.format_exc())
        return
Ejemplo n.º 5
0
def start_tracking():

    mouse_inactive_delay = 2

    updates_per_second = CONFIG['Main']['UpdatesPerSecond']
    timer = {
        'UpdateScreen': CONFIG['Timer']['CheckResolution'],
        'UpdatePrograms': CONFIG['Timer']['CheckPrograms'],
        'Save': CONFIG['Save']['Frequency'],
        'ReloadProgramList': CONFIG['Timer']['ReloadPrograms'],
        'UpdateQueuedCommands': CONFIG['Timer']['_ShowQueuedCommands']
    }
    timer = {k: v * updates_per_second for k, v in get_items(timer)}

    store = {
        'Resolution': {
            'Current': monitor_info(),
            'Previous': None,
            'Boundaries': None
        },
        'Mouse': {
            'Position': {
                'Current': None,
                'Previous': None
            },
            'NotMoved': 0,
            'Inactive': False,
            'Clicked': {},
            'OffScreen': False
        },
        'Keyboard': {
            'KeysPressed': {k: False
                            for k in KEYS.keys()}
        },
        'LastActivity': 0,
        'LastSent': 0,
        'Save': {
            'Finished': True,
            'Next': timer['Save']
        }
    }
    mouse_pos = store['Mouse']['Position']

    #Start background process
    q_send = Queue()
    q_recv = Queue()
    p = Process(target=background_process, args=(q_send, q_recv))
    #p.daemon = True
    p.start()

    q_send2 = Queue()
    q_recv2 = Queue()
    running_programs = ThreadHelper(running_processes, q_send2, q_recv2,
                                    q_send)
    running_programs.start()

    i = 0
    NOTIFY(START_MAIN)
    while True:
        with RefreshRateLimiter(updates_per_second) as limiter:

            #Send data to thread
            try:
                if frame_data or frame_data_rp:
                    last_sent = i - store['LastSent']
                    if frame_data:
                        if last_sent:
                            frame_data['Ticks'] = last_sent
                        q_send.put(frame_data)
                    if frame_data_rp:
                        q_send2.put(frame_data_rp)
                    store['LastSent'] = i
            except NameError:
                pass

            while not q_recv2.empty():
                print_override('{} {}'.format(time_format(limiter.time),
                                              q_recv2.get()))

            #Print any messages from previous loop
            notify_extra = ''
            received_data = []
            while not q_recv.empty():

                received_message = q_recv.get()

                #Receive text messages
                try:
                    if received_message.startswith(
                            'Traceback (most recent call last)'):
                        return received_message
                except AttributeError:
                    pass
                else:
                    received_data.append(received_message)

                #Get notification when saving is finished
                try:
                    received_message.pop('SaveFinished')
                except (KeyError, AttributeError):
                    pass
                else:
                    store['Save']['Finished'] = True
                    store['Save']['Next'] = i + timer['Save']

            if received_data:
                notify_extra = u' | '.join(received_data)
            notify_output = NOTIFY.get_output()

            if notify_extra:
                if notify_output:
                    notify_output = notify_extra + ' | ' + notify_output
                else:
                    notify_output = notify_extra
            if notify_output:
                print_override(u'{} {}'.format(time_format(limiter.time),
                                               notify_output))

            frame_data = {}
            frame_data_rp = {}
            mouse_pos['Current'] = get_cursor_pos()

            #Check if mouse is inactive (such as in a screensaver)
            if mouse_pos['Current'] is None:
                if not store['Mouse']['Inactive']:
                    NOTIFY(MOUSE_UNDETECTED)
                    store['Mouse']['Inactive'] = True
                time.sleep(mouse_inactive_delay)
                continue

            #Check if mouse left the monitor
            elif (
                    not MULTI_MONITOR and
                (not 0 <= mouse_pos['Current'][0] <
                 store['Resolution']['Current'][0] or not 0 <=
                 mouse_pos['Current'][1] < store['Resolution']['Current'][1])):
                if not store['Mouse']['OffScreen']:
                    NOTIFY(MOUSE_OFFSCREEN)
                    store['Mouse']['OffScreen'] = True
            elif store['Mouse']['OffScreen']:
                NOTIFY(MOUSE_ONSCREEN)
                store['Mouse']['OffScreen'] = False

            #Notify once if mouse is no longer inactive
            if store['Mouse']['Inactive']:
                store['Mouse']['Inactive'] = False
                NOTIFY(MOUSE_DETECTED)

            #Check if mouse is in a duplicate position
            if mouse_pos['Current'] is None or mouse_pos[
                    'Current'] == mouse_pos['Previous']:
                store['Mouse']['NotMoved'] += 1
            elif store['Mouse']['NotMoved']:
                store['Mouse']['NotMoved'] = 0
            if not store['Mouse']['NotMoved']:
                if not store['Mouse']['OffScreen']:
                    frame_data['MouseMove'] = (mouse_pos['Previous'],
                                               mouse_pos['Current'])
                    NOTIFY(MOUSE_POSITION, mouse_pos['Current'])
                    store['LastActivity'] = i

            #Mouse clicks
            click_repeat = CONFIG['Main']['RepeatClicks']
            for mouse_button, clicked in enumerate(get_mouse_click()):

                mb_clicked = store['Mouse']['Clicked'].get(mouse_button, False)
                mb_data = (mouse_button, mouse_pos['Current'])

                if clicked:
                    store['LastActivity'] = i

                    #First click
                    if not mb_clicked:
                        store['Mouse']['Clicked'][mouse_button] = limiter.time
                        if not store['Mouse']['OffScreen']:
                            NOTIFY(MOUSE_CLICKED, mouse_pos['Current'],
                                   mouse_button)
                            try:
                                frame_data['MouseClick'].append(mb_data)
                            except KeyError:
                                frame_data['MouseClick'] = [mb_data]
                        else:
                            NOTIFY(MOUSE_CLICKED_OFFSCREEN, mouse_button)

                    #Held clicks
                    elif click_repeat and mb_clicked < limiter.time - click_repeat:
                        store['Mouse']['Clicked'][mouse_button] = limiter.time
                        if not store['Mouse']['OffScreen']:
                            NOTIFY(MOUSE_CLICKED_HELD, mouse_pos['Current'],
                                   mouse_button)
                            try:
                                frame_data['MouseClick'].append(mb_data)
                            except KeyError:
                                frame_data['MouseClick'] = [mb_data]
                elif mb_clicked:
                    NOTIFY(MOUSE_UNCLICKED)
                    del store['Mouse']['Clicked'][mouse_button]
                    store['LastActivity'] = i

            #Key presses
            keys_pressed = []
            keys_held = []
            key_status = store['Keyboard']['KeysPressed']
            key_press_repeat = CONFIG['Main']['RepeatKeyPress']
            _keys_held = []
            _keys_pressed = []
            for k in KEYS:
                if get_key_press(KEYS[k]):
                    keys_held.append(k)

                    #If key is currently being held down
                    if key_status[k]:
                        if key_press_repeat and key_status[
                                k] < limiter.time - key_press_repeat:
                            keys_pressed.append(k)
                            _keys_held.append(k)
                            key_status[k] = limiter.time

                    #If key has been pressed
                    else:
                        keys_pressed.append(k)
                        _keys_pressed.append(k)
                        key_status[k] = limiter.time
                        notify_key_press = list(keys_pressed)

                #If key has been released
                elif key_status[k]:
                    key_status[k] = False

            if keys_pressed:
                frame_data['KeyPress'] = keys_pressed
            if keys_held:
                frame_data['KeyHeld'] = keys_held
                store['LastActivity'] = i
            if _keys_held:
                NOTIFY(KEYBOARD_PRESSES_HELD, _keys_held)
            if _keys_pressed:
                NOTIFY(KEYBOARD_PRESSES, _keys_pressed)

            #Check if resolution has changed
            if not i % timer['UpdateScreen']:

                if MULTI_MONITOR:
                    frame_data['MonitorLimits'] = monitor_info()
                    store['Resolution']['Boundaries'] = frame_data[
                        'MonitorLimits']
                else:
                    store['Resolution']['Current'] = monitor_info()
                    if store['Resolution']['Previous'] != store['Resolution'][
                            'Current']:
                        if store['Resolution']['Previous'] is not None:
                            NOTIFY(RESOLUTION_CHANGED,
                                   store['Resolution']['Previous'],
                                   store['Resolution']['Current'])
                        frame_data['Resolution'] = ['Resolution']['Current']
                        store['Resolution']['Previous'] = store['Resolution'][
                            'Current']

            #Display message that mouse has switched monitors
            if MULTI_MONITOR and 'MouseMove' in frame_data:

                try:
                    try:
                        res = monitor_offset(
                            frame_data['MouseMove'][1],
                            store['Resolution']['Boundaries'])[0]
                    except TypeError:
                        frame_data['MonitorLimits'] = monitor_info()
                        store['Resolution']['Boundaries'] = frame_data[
                            'MonitorLimits']
                        res = monitor_offset(
                            frame_data['MouseMove'][1],
                            store['Resolution']['Boundaries'])[0]
                except TypeError:
                    pass
                else:
                    store['Resolution']['Current'] = res
                    if store['Resolution']['Previous'] is not None:
                        if store['Resolution']['Current'] != store[
                                'Resolution']['Previous']:
                            NOTIFY(MONITOR_CHANGED,
                                   store['Resolution']['Previous'],
                                   store['Resolution']['Current'])
                    store['Resolution']['Previous'] = store['Resolution'][
                        'Current']

            #Send request to update programs
            if not i % timer['UpdatePrograms']:
                frame_data_rp['Update'] = True

            #Send request to reload program list
            if not i % timer['ReloadProgramList']:
                frame_data_rp['Reload'] = True

            #Update user about the queue size
            if not i % timer['UpdateQueuedCommands'] and store[
                    'LastActivity'] > i - timer['Save']:
                NOTIFY(QUEUE_SIZE, q_send.qsize())

            #Send save request
            if store['Save'][
                    'Finished'] and i and not i % store['Save']['Next']:
                frame_data['Save'] = True
                store['Save']['Finished'] = False

            if store['Mouse']['OffScreen']:
                mouse_pos['Previous'] = None
            else:
                mouse_pos['Previous'] = mouse_pos['Current']
            i += 1