class EventManager(object):
    """
    Event manager that runs event loop and calls event handlers.
    """
    def __init__(self):
        """
        Constructor.

        :return: None.
        """
        # Create hook manager
        self._hook_manager = HookManager()

        # Add attributes `mouse_hook` and `keyboard_hook`.
        # Without the two attributes, the hook manager's method `__del__`
        # will raise AttributeError if its methods `HookKeyboard` and
        # `HookMouse` have not been called.
        self._hook_manager.mouse_hook = False

        self._hook_manager.keyboard_hook = False

    def start_event_loop(self):
        """
        Start event loop.

        This method will not return until the event loop is stopped by \
        calling :paramref:`stop_event_loop`.

        :return: None.
        """
        # Start hooking key events
        self._hook_manager.HookKeyboard()

        # Start hooking mouse events
        self._hook_manager.HookMouse()

        # Create MSG structure
        msg = MSG()

        # Run event loop
        GetMessageW(byref(msg), 0, 0, 0)

        # Stop hooking key events
        self._hook_manager.UnhookKeyboard()

        # Stop hooking mouse events
        self._hook_manager.UnhookMouse()

    def stop_event_loop(self):
        """
        Stop event loop.

        :return: None.
        """
        # Post a WM_QUIT message to this thread's message queue
        PostQuitMessage(0)

    # Map event handler type to handler attribute name
    _EVENT_HANDLER_TYPE_TO_ATTR_NAME = {
        'KeyDown': 'KeyDown',
        'KeyUp': 'KeyUp',
        'MouseDown': 'MouseAllButtonsDown',
        'MouseUp': 'MouseAllButtonsUp',
        'MouseMove': 'MouseMove',
        'MouseWheel': 'MouseWheel',
    }

    def add_handler(self, handler_type, handler):
        """
        Add event handler.

        :param handler_type: Event handler type.

        Allowed values:
            - 'KeyDown'
            - 'KeyUp'
            - 'MouseDown'
            - 'MouseUp'
            - 'MouseMove'
            - 'MouseWheel'

        :param handler: Event handler.

        :return: None.
        """
        # Get handler attribute name
        attr_name = self._EVENT_HANDLER_TYPE_TO_ATTR_NAME.get(
            handler_type, None)

        # If handler attribute name is not found,
        # it means given handler type is not valid.
        if attr_name is None:
            # Get error message
            msg = 'Error: Invalid handler type: {0}'.format(repr(handler_type))

            # Raise error
            raise ValueError(msg)

        # If handler attribute name is found.

        # Set the handler attribute on the hook manager
        setattr(self._hook_manager, attr_name, handler)

    def remove_handlers(self):
        """
        Remove all event handlers.

        :return: None.
        """
        # Set handler attributes on the hook manager be None
        self._hook_manager.KeyDown = None

        self._hook_manager.KeyUp = None

        self._hook_manager.MouseAllButtonsDown = None

        self._hook_manager.MouseAllButtonsUp = None

        self._hook_manager.MouseMove = None

        self._hook_manager.MouseWheel = None
Exemple #2
0
class Master(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(Master, self).__init__(parent)
        self.old_volume = get_volume()
        self.program_searching = False
        self.fading = False

        self.initUI()

    def initUI(self):
        self.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint
                            | QtCore.Qt.WindowCloseButtonHint)
        ba = QtCore.QByteArray.fromBase64(icon_data)
        pixmap = QtGui.QPixmap()
        pixmap.loadFromData(ba, 'PNG')
        icon = QtGui.QIcon()
        icon.addPixmap(pixmap)
        self.setWindowIcon(icon)

        self.load_settings()

        xspacer = QtWidgets.QSpacerItem(10, 40, QtWidgets.QSizePolicy.Minimum,
                                        QtWidgets.QSizePolicy.Minimum)
        big_xspacer = QtWidgets.QSpacerItem(0, 40,
                                            QtWidgets.QSizePolicy.Expanding,
                                            QtWidgets.QSizePolicy.Minimum)

        self.v_layout = QtWidgets.QVBoxLayout(self)

        self.pretext_layout = QtWidgets.QHBoxLayout()

        self.pretext_label = QtWidgets.QLabel("Pretext:")
        self.pretext_input = QtWidgets.QLineEdit(self.settings["pretext"])
        self.pretext_input.textChanged.connect(self.set_pretext)

        self.gotime_label = QtWidgets.QLabel("Go Time:")
        self.hour_input = QtWidgets.QComboBox()
        hours = [
            '23', '22', '21', '20', '19', '18', '17', '16', '15', '14', '13',
            '12', '11', '10', '09', '08', '07', '06', '05', '04', '03', '02',
            '01', '00'
        ]
        self.hour_input.insertItems(0, hours)
        self.hour_input.setCurrentIndex(
            hours.index(self.settings['gotime'].split(':')[0]))
        self.hour_input.currentIndexChanged.connect(self.set_gotime)
        self.colon_label = QtWidgets.QLabel(":")
        self.colon_label.setFixedWidth(3)
        self.minute_input = QtWidgets.QComboBox()
        minutes = [
            '59', '58', '57', '56', '55', '54', '53', '52', '51', '50', '49',
            '48', '47', '46', '45', '44', '43', '42', '41', '40', '39', '38',
            '37', '36', '35', '34', '33', '32', '31', '30', '29', '28', '27',
            '26', '25', '24', '23', '22', '21', '20', '19', '18', '17', '16',
            '15', '14', '13', '12', '11', '10', '09', '08', '07', '06', '05',
            '04', '03', '02', '01', '00'
        ]
        self.minute_input.insertItems(0, minutes)
        self.minute_input.setCurrentIndex(
            minutes.index(self.settings['gotime'].split(':')[1]))
        self.minute_input.currentIndexChanged.connect(self.set_gotime)

        self.pretext_layout.addWidget(self.pretext_label)
        self.pretext_layout.addWidget(self.pretext_input)
        self.pretext_layout.addWidget(self.gotime_label)
        self.pretext_layout.addWidget(self.hour_input)
        self.pretext_layout.addWidget(self.colon_label)
        self.pretext_layout.addWidget(self.minute_input)
        self.pretext_layout.addItem(xspacer)

        self.v_layout.addLayout(self.pretext_layout)

        self.settings_layout = QtWidgets.QHBoxLayout()

        self.font_size_label = QtWidgets.QLabel("Font Size:")
        font_sizes = [
            "20", "24", "28", "30", "34", "38", "40", "44", "48", "52", "56",
            "60", "64", "68", "72"
        ]
        self.font_size_combobox = QtWidgets.QComboBox()
        self.font_size_combobox.insertItems(0, font_sizes)
        self.font_size_combobox.setCurrentIndex(
            font_sizes.index(self.settings['fontsize']))
        self.font_size_combobox.currentIndexChanged.connect(self.set_font)

        self.font_type_label = QtWidgets.QLabel("Font Type:")
        self.font_type_combobox = QtWidgets.QFontComboBox()
        self.font_type_combobox.setCurrentFont(
            QtGui.QFont(self.settings['fonttype']))
        self.font_type_combobox.currentIndexChanged.connect(self.set_font)
        self.font_type_combobox.setFixedWidth(128)

        self.fg_color_label = QtWidgets.QLabel("Fg:")
        self.fg_color_button = QtWidgets.QPushButton()
        self.fg_color_button.setFixedWidth(32)
        self.fg_color_button.clicked.connect(self.choose_fg_color)
        self.fg_color_button.setStyleSheet("background-color: " +
                                           self.settings['fgcolor'] + ';')
        self.fg_dialog = QtWidgets.QColorDialog()

        self.bg_color_label = QtWidgets.QLabel("Bg:")
        self.bg_color_button = QtWidgets.QPushButton()
        self.bg_color_button.setFixedWidth(32)
        self.bg_color_button.clicked.connect(self.choose_bg_color)
        self.bg_color_button.setStyleSheet("background-color: " +
                                           self.settings['bgcolor'] + ';')
        self.bg_dialog = QtWidgets.QColorDialog()

        self.settings_layout.addWidget(self.font_size_label)
        self.settings_layout.addWidget(self.font_size_combobox)
        self.settings_layout.addItem(xspacer)
        self.settings_layout.addWidget(self.font_type_label)
        self.settings_layout.addWidget(self.font_type_combobox)
        self.settings_layout.addItem(xspacer)
        self.settings_layout.addWidget(self.fg_color_label)
        self.settings_layout.addWidget(self.fg_color_button)
        self.settings_layout.addWidget(self.bg_color_label)
        self.settings_layout.addWidget(self.bg_color_button)

        self.settings_layout.addItem(big_xspacer)

        self.v_layout.addLayout(self.settings_layout)

        self.fade_layout = QtWidgets.QHBoxLayout()
        self.fade_label = QtWidgets.QLabel("Fade Audio and Close Player:")
        self.fade_checkbox = QtWidgets.QCheckBox()
        self.fade_checkbox.setChecked(bool(self.settings['fadeaudio']))
        self.fade_checkbox.setEnabled(False)
        self.close_label = QtWidgets.QLabel("Player:")
        self.close_button = QtWidgets.QPushButton("")
        self.close_button.setFixedWidth(200)
        self.close_button.clicked.connect(self.start_program_search)

        self.fade_layout.addWidget(self.fade_label)
        self.fade_layout.addWidget(self.fade_checkbox)
        self.fade_layout.addItem(xspacer)
        self.fade_layout.addWidget(self.close_label)
        self.fade_layout.addWidget(self.close_button)
        self.fade_layout.addItem(big_xspacer)

        self.v_layout.addLayout(self.fade_layout)

        self.toggle_timer_button = QtWidgets.QPushButton("Toggle Timer")
        self.toggle_timer_button.clicked.connect(self.toggle_timer)
        self.v_layout.addWidget(self.toggle_timer_button)

        self.hookman = HookManager()
        self.hookman.HookMouse()

        if os.name == 'posix':
            self.hookman.buttonpressevent = self.mousedown
            self.hookman.start()
        elif os.name == 'nt':
            self.hookman.MouseLeftDown = self.mousedown

        self.timer = Timer(app)
        self.timer.prefix = self.pretext_input.text()
        self.timer.gotime = self.hour_input.currentText(
        ) + ':' + self.minute_input.currentText() + ':00'
        font = self.font_type_combobox.currentText()
        size = int(self.font_size_combobox.currentText())
        self.timer.label.setFont(QtGui.QFont(font, size, QtGui.QFont.Bold))
        self.timer.label.setStyleSheet("color: " + self.settings['fgcolor'] +
                                       ';')
        self.timer.setStyleSheet("background-color: " +
                                 self.settings['bgcolor'] + ';')
        self.timer.above_ten_signal.connect(self.get_volume)
        self.timer.below_ten_signal.connect(self.start_fade_thread)
        self.timer.zero_signal.connect(self.reset_volume)

        self.resize_thread = ResizeThread()
        self.resize_thread.reset_timer_signal.connect(self.resize_timer)

        self.fade_thread = FadeThread()
        self.player_getter = PlayerGetterThread()
        self.player_getter.found_signal.connect(self.set_player)

    def start_program_search(self):
        self.program_searching = True
        self.close_button.setText("Click Player Window")

    def mousedown(self, event):
        if self.program_searching:
            self.player_getter.start()

        if os.name == 'nt':
            return True

    def set_player(self):
        program = get_active_process()
        self.close_button.setText(program)
        self.program_searching = False
        self.fade_checkbox.setEnabled(True)

    def set_gotime(self, event):
        self.timer.gotime = self.hour_input.currentText(
        ) + ':' + self.minute_input.currentText() + ':00'
        self.timer.update_time()

    def set_pretext(self, event):
        self.timer.prefix = self.pretext_input.text()
        self.timer.update_time()
        self.resize_thread.start()

    def set_font(self, event):
        font = self.font_type_combobox.currentText()
        size = int(self.font_size_combobox.currentText())
        self.timer.label.setFont(QtGui.QFont(font, size, QtGui.QFont.Bold))
        self.resize_thread.start()

    def resize_timer(self):
        self.timer.resize(0, 0)

    def choose_fg_color(self, event):
        self.setEnabled(False)
        color = self.fg_dialog.getColor(QtGui.QColor(self.settings['fgcolor']))
        if color.isValid():
            self.fg_color_button.setStyleSheet("background-color: " +
                                               color.name() + ";")
            self.settings['fgcolor'] = color.name()
            self.timer.label.setStyleSheet("color: " +
                                           self.settings['fgcolor'] + ';')
        self.setEnabled(True)

    def choose_bg_color(self, event):
        self.setEnabled(False)
        color = self.bg_dialog.getColor(QtGui.QColor(self.settings['bgcolor']))
        if color.isValid():
            self.bg_color_button.setStyleSheet("background-color: " +
                                               color.name() + ";")
            self.settings['bgcolor'] = color.name()
            self.timer.setStyleSheet("background-color: " +
                                     self.settings['bgcolor'] + ';')
        self.setEnabled(True)

    def save_settings(self):
        parts = [
            "pretext: " + self.pretext_input.text(),
            "gotime: " + self.hour_input.currentText() + ':' +
            self.minute_input.currentText(),
            "fontsize: " + self.font_size_combobox.currentText(),
            "fonttype: " + self.font_type_combobox.currentText(),
            "fgcolor: " + self.settings['fgcolor'],
            "bgcolor: " + self.settings['bgcolor'],
            "fadeaudio: " + str(self.fade_checkbox.isChecked()),
            "player: " + str(self.close_button.text())
        ]
        with open("pt_settings.ini", "w") as f:
            f.write('\n'.join(parts))

    def load_settings(self):
        self.settings = {
            "pretext": "Lecture Start:",
            "gotime": "12:00",
            "fontsize": "24",
            "fonttype": "Arial",
            "fgcolor": "black",
            "bgcolor": "white",
            "fadeaudio": "True",
            "player": "vlc"
        }
        try:
            with open("pt_settings.ini", 'r') as f:
                text = f.read()
                lines = text.split('\n')
            for line in lines:
                key = line.split(': ')[0].strip()
                val = line.split(': ')[-1].strip()
                self.settings[key] = val

        except FileNotFoundError:
            pass

    def toggle_timer(self, event):
        if self.timer.isVisible():
            self.timer.hide()
        else:
            self.timer.show()

    def get_volume(self):
        self.old_volume = get_volume()
        if self.fading:
            self.fade_thread.exiting = True
            self.fading = False

    def start_fade_thread(self):
        if self.timer.isVisible(
        ) and self.fading == False and self.fade_checkbox.isChecked(
        ) and self.close_button.text().strip() != '':
            self.fade_thread.exiting = False
            self.fade_thread.start()
            self.fading = True

    def reset_volume(self):
        if self.fade_checkbox.isChecked() and self.timer.isVisible(
        ) and self.close_button.text().strip() != '':
            self.fading = False
            self.fade_thread.exiting = True

            player = self.close_button.text()
            if player.strip() != '':
                if os.name == 'posix':
                    subprocess.call(['killall', player])
                elif os.name == "nt":
                    no_window = 0x08000000
                    subprocess.call('taskkill /F /IM ' + player,
                                    creationflags=no_window)
            set_volume(self.old_volume)

    def closeEvent(self, event):
        self.save_settings()
        self.timer.close()
        if os.name == 'nt':
            self.hookman.UnhookMouse()
Exemple #3
0
class WindowsRecorder(BaseRecorder):

    KBFLAG_CTRL = 0x01
    KBFLAG_ALT = 0x02
    KBFLAG_SHIFT = 0x04
    KBFLAG_CAPS = 0x08

    default_radius = 25
    capture_interval = 0.05
    capture_maxnum = 30

    def __init__(self, device=None):
        self.watched_hwnds = set()
        super(WindowsRecorder, self).__init__(device)
        self.kbflag = 0
        self.hm = HookManager()
        self.hm.MouseAllButtons = self._hook_on_mouse
        self.hm.KeyAll = self._hook_on_keyboard

    def attach(self, device):
        if self.device is not None:
            print "Warning: already attached to a device."
            if device is not self.device:
                self.detach()

        handle = device.hwnd

        def callback(hwnd, extra):
            extra.add(hwnd)
            return True

        self.watched_hwnds.add(handle)
        try:
            # EnumChildWindows may crash for windows have no any child.
            # refs: https://mail.python.org/pipermail/python-win32/2005-March/003042.html
            win32gui.EnumChildWindows(handle, callback, self.watched_hwnds)
        except pywintypes.error:
            pass

        self.device = device
        print "attach to device", device

    def detach(self):
        print "detach from device", self.device
        self.device = None
        self.watched_hwnds = set()

    def hook(self):
        self.hm.HookMouse()
        self.hm.HookKeyboard()

    def unhook(self):
        self.hm.UnhookMouse()
        self.hm.UnhookKeyboard()

    def _hook_on_mouse(self, event):
        if self.device is None:
            return True
        if event.Window not in self.watched_hwnds:
            return True
        if event.Message == HookConstants.WM_LBUTTONUP:
            x, y = self.device.norm_position(event.Position)
            # ignore the clicks outside the rect if the window has a frame.
            if x < 0 or y < 0:
                return True
            self.on_click((x, y))

        return True

    def _hook_on_keyboard(self, event):
        if self.device is None:
            return True
        if event.Window not in self.watched_hwnds:
            return True
        print "on_keyboard", event.MessageName, event.Key, repr(
            event.Ascii), event.KeyID, event.ScanCode,
        print event.flags, event.Extended, event.Injected, event.Alt, event.Transition
        return True
Exemple #4
0
class CoordHandler:
    def _add_motionctrl_headline(self):
        '''Convenience function for adding label headline to the coordinate list'''
        # Creating widgets
        lbl_empty = lambda: wx.StaticText(self.coords_settings, label="")
        lbl_x = wx.StaticText(self.coords_settings, label="X")
        lbl_y = wx.StaticText(self.coords_settings, label="Y")

        # Changing font
        font = lbl_x.GetFont()
        font.SetWeight(wx.BOLD)
        lbl_x.SetFont(font)
        lbl_y.SetFont(font)

        # Adding widgets to sizer
        self.motionctrl.AddMany([
            lbl_empty(), (lbl_x, 0, wx.ALIGN_CENTER),
            (lbl_y, 0, wx.ALIGN_CENTER),
            lbl_empty(),
            lbl_empty()
        ])

    def add_coords(self, event):
        '''Add coordinate widgets (X and Y spinners + record button)'''
        count_rows = self.motionctrl.GetEffectiveRowsCount()

        # Creating settings widgets
        number = wx.StaticText(self.coords_settings,
                               label=str(count_rows) + ")")
        move_x = wx.SpinCtrl(self.coords_settings,
                             size=(60, -1),
                             min=-self.screen_dimensions[0],
                             max=self.screen_dimensions[0])
        move_y = wx.SpinCtrl(self.coords_settings,
                             size=(60, -1),
                             min=-self.screen_dimensions[1],
                             max=self.screen_dimensions[1])
        record = wx.Button(self.coords_settings, label="Record", size=(60, -1))
        delete = wx.Button(self.coords_settings, size=(26, -1))

        delete.SetBitmap(wx.Bitmap("icons/delete.png"))

        # Adding widgets to sizers
        self.motionctrl.AddMany([(number, 0, wx.ALIGN_CENTER_VERTICAL), move_x,
                                 move_y, record, delete])

        # Adding tooltips
        move_x.SetToolTipString(tooltip_x)
        move_y.SetToolTipString(tooltip_y)
        record.SetToolTipString(tooltip_record_coord)
        delete.SetToolTipString(tooltip_delete_coord)

        # Make number bold
        font = number.GetFont()
        font.SetWeight(wx.BOLD)
        number.SetFont(font)

        # Binding events
        move_x.Bind(wx.EVT_KILL_FOCUS, self.save_profile)
        move_y.Bind(wx.EVT_KILL_FOCUS, self.save_profile)
        self.Bind(wx.EVT_BUTTON, self.init_record, record)
        self.Bind(wx.EVT_BUTTON, self.delete_coords, delete)

        # Update scrolled window
        self.coords_settings.Layout()
        old_size = self.coords_settings.GetSizer().GetSize()
        new_size = (old_size[0] + 1, old_size[1])
        self.coords_settings.SetVirtualSize(new_size)
        self.coords_settings.Scroll(
            0, self.coords_settings.GetScrollRange(wx.VERTICAL))

        # Add input widgets to list (to be used by main loop later)
        self.coordinates.append([move_x, move_y])

        if event != None:
            self.save_profile(None)

    def delete_coords(self, event):
        '''Function for deleting an already existing coordinate'''
        if self.motionctrl.GetEffectiveRowsCount() > 2:
            obj = event.GetEventObject()

            # Collecting all widgets to be deleted
            delete_widgets = [
                wx.FindWindowById(obj.GetId() - i)
                for i in range(self.motionctrl.GetCols())
            ]

            # Updating coordinate numbers
            all_ids = sorted([
                child.GetWindow().GetId()
                for child in self.motionctrl.GetChildren()
            ])
            for x in all_ids[all_ids.index(delete_widgets[-1].GetId())::5][1:]:
                widget = wx.FindWindowById(x)
                widget.SetLabel(str(int(widget.GetLabel()[0]) - 1) + ")")

            # Removing coordinate from list
            self.coordinates.pop(int(delete_widgets[-1].GetLabel()[0]) - 1)

            # Destroying all widgets related to the deleted coordinate
            for widget in delete_widgets:
                widget.Destroy()
            self.motionctrl.Layout()

            # Saving settings
            self.save_profile(None)

    def clear_coords(self, event):
        '''Function for resetting all coordinates'''
        for coords in self.coordinates:
            for widget in coords:
                widget.SetValue(0)

        self.save_profile(None)

    def init_record(self, event):
        '''Initiate recording of cursor positions'''
        # Preparing variables
        obj = event.GetEventObject()
        self.coord_counter = 0
        self.coord_widgets = []
        if self.movement_relative:
            self.last_coord = None

        # Starting listening for mouse presses
        if not hasattr(self, "hook"):
            self.hook = HookManager()
        self.hook.HookMouse()

        # Indicating recording
        self.frame.statusbar.SetStatusText("Recording cursor positions...")
        self.frame.SetTitle("MouseMove - Recording positions")
        if obj in [self.record, self.frame.page_keyconfig
                   ]:  # Need the keyconfig reference to make hotkey work
            first_coord = self.motionctrl.GetChildren()[5].GetWindow()
            self.hook.MouseLeftDown = self.record_coords
        else:
            first_coord = wx.FindWindowById(obj.GetId() - 3)
            self.first_coord = first_coord
            self.hook.MouseLeftDown = self.record_single_coord
        first_coord.SetForegroundColour("Red")
        first_coord.Refresh()

    def stop_record(self):
        '''Actions to be performed when stopping recording'''
        self.hook.UnhookMouse()
        self.frame.statusbar.SetStatusText("")
        self.frame.SetTitle("MouseMove")

        self.save_profile(None)