class FindDesktopWindowInteractive(QObject):
    tracking_rate = 300
    painting_rate = 15

    result = Signal(QRect)

    def __init__(self, app: QApplication, origin_draw_position: QPoint):
        """
        Locate a desktop window rectangle with the mouse cursor

        :param QApplication app: App to install event filter on
        :param QPoint origin_draw_position: Position of the line, indicating the tracked cursor, is drawn from
        """
        super(FindDesktopWindowInteractive, self).__init__()
        self.app = app

        self.tracking_timer = QTimer()
        self.tracking_timer.setInterval(self.tracking_rate)
        self.tracking_timer.timeout.connect(self._track_cursor)

        self.paint_timer = QTimer()
        self.paint_timer.setInterval(self.painting_rate)
        self.paint_timer.timeout.connect(self._trigger_paint)

        self.cursor = QCursor()
        self.pywin_desktop = Desktop()

        self.highlight_rect = QRect()

        self.origin_draw_pos = origin_draw_position

        # -- Transparent widget across the desktop to draw on --
        self.desk_draw_widget = DesktopDrawWidget(app.desktop())
        self.desk_draw_widget.paintEvent = self._paint_event_override

    def eventFilter(self, watched: QObject, event: QEvent) -> bool:
        """ Install on QApplication to grab result as soon as we loose focus
            aka mousePress outside of any of our widgets.

            Cancel if any unbound keys are pressed. Shortcut keys will be ignored.
        """
        if event.type() == QEvent.FocusOut:
            self.stop()
            return True
        elif event.type() == QEvent.KeyPress:
            self.abort()
            return True
        return False

    def _trigger_paint(self):
        """ Trigger a desktop overlay widget paint event """
        self.desk_draw_widget.update()

    def _paint_event_override(self, event: QPaintEvent):
        pen = QPen(QColor(250, 120, 20))
        w = 4
        m = round(w / 2)
        pen.setWidth(w)
        pen.setJoinStyle(Qt.RoundJoin)

        inner_rect = self.highlight_rect.marginsRemoved(QMargins(m, m, m, m))

        p = QPainter()
        p.begin(self.desk_draw_widget)
        p.setPen(pen)
        p.drawLine(self.origin_draw_pos, self.cursor.pos())
        p.drawRect(inner_rect)
        p.end()

    def start(self):
        self.desk_draw_widget.show()
        self.app.installEventFilter(self)

        self.tracking_timer.start()
        self.paint_timer.start()

    def stop(self):
        LOGGER.debug('Selected Area: %s', self.highlight_rect)
        self.result.emit(self.highlight_rect)
        self.finish()

    def abort(self):
        self.finish()

    def finish(self):
        self.app.removeEventFilter(self)

        self.tracking_timer.stop()
        self.paint_timer.stop()

        self.desk_draw_widget.close()
        self.desk_draw_widget.deleteLater()
        self.deleteLater()

    def _track_cursor(self):
        wrapper = self.find_window_by_point(self.cursor.pos().x(),
                                            self.cursor.pos().y())
        if not wrapper:
            return

        r = wrapper.rectangle()
        self.highlight_rect = QRect(r.left, r.top, r.width(), r.height())

    def find_window_by_point(self, x: int, y: int) -> Union[BaseWrapper, None]:
        try:
            return self.pywin_desktop.from_point(x, y)
        except Exception as e:
            LOGGER.debug(e, exc_info=1)
            return None

    @staticmethod
    def _highlight_outline(wrapper):
        """ Alternative highlight method from pywinauto """
        wrapper.draw_outline(colour='red',
                             thickness=2,
                             rect=wrapper.rectangle())