Ejemplo n.º 1
0
    def __init__(self, refresh_token_path, parent=None):
        super().__init__(parent)
        self.set_language()

        self.refresh_token_path = refresh_token_path
        self.hangups_running = False
        self.client = None

        self.create_actions()
        self.create_menu()
        self.create_icon()
        self.update_status()

        # These are populated by on_connect when it's called.
        self.conv_list = None  # hangups.ConversationList
        self.user_list = None  # hangups.UserList
        self.notifier = None   # hangups.notify.Notifier

        # Widgets
        self.conversations_dialog = QHangupsConversationsList(controller=self)
        self.messages_dialog = QHangupsConversations(controller=self)

        # Setup system tray icon doubleclick timer
        self.icon_doubleclick_timer = QtCore.QTimer(self)
        self.icon_doubleclick_timer.setSingleShot(True)
        self.icon_doubleclick_timer.timeout.connect(self.icon_doubleclick_timeout)

        # Handle signals on Unix
        # (add_signal_handler is not implemented on Windows)
        try:
            loop = asyncio.get_event_loop()
            for signum in (signal.SIGINT, signal.SIGTERM):
                loop.add_signal_handler(signum, lambda: self.quit(force=True))
        except NotImplementedError:
            pass
Ejemplo n.º 2
0
    def __init__(self, cookies_path, parent=None):
        super().__init__(parent)
        self.set_language()

        self.cookies_path = cookies_path
        self.hangups_running = False
        self.client = None

        self.create_actions()
        self.create_menu()
        self.create_icon()
        self.update_status()

        # These are populated by on_connect when it's called.
        self.conv_list = None  # hangups.ConversationList
        self.user_list = None  # hangups.UserList
        self.notifier = None  # hangups.notify.Notifier

        # Widgets
        self.conversations_dialog = QHangupsConversationsList(controller=self)
        self.messages_dialog = QHangupsConversations(controller=self)

        # Setup system tray icon doubleclick timer
        self.icon_doubleclick_timer = QtCore.QTimer(self)
        self.icon_doubleclick_timer.setSingleShot(True)
        self.icon_doubleclick_timer.timeout.connect(
            self.icon_doubleclick_timeout)

        # Handle signals on Unix
        # (add_signal_handler is not implemented on Windows)
        try:
            loop = asyncio.get_event_loop()
            for signum in (signal.SIGINT, signal.SIGTERM):
                loop.add_signal_handler(signum, lambda: self.quit(force=True))
        except NotImplementedError:
            pass
Ejemplo n.º 3
0
class QHangupsMainWidget(QtGui.QWidget):
    """QHangups main widget (icon in system tray)"""
    startHangups = QtCore.pyqtSignal()
    stopHangups = QtCore.pyqtSignal()

    def __init__(self, cookies_path, parent=None):
        super().__init__(parent)
        self.set_language()

        self.cookies_path = cookies_path
        self.hangups_running = False
        self.client = None

        self.create_actions()
        self.create_menu()
        self.create_icon()
        self.update_status()

        # These are populated by on_connect when it's called.
        self.conv_list = None  # hangups.ConversationList
        self.user_list = None  # hangups.UserList
        self.notifier = None  # hangups.notify.Notifier

        # Widgets
        self.conversations_dialog = QHangupsConversationsList(controller=self)
        self.messages_dialog = QHangupsConversations(controller=self)

        # Setup system tray icon doubleclick timer
        self.icon_doubleclick_timer = QtCore.QTimer(self)
        self.icon_doubleclick_timer.setSingleShot(True)
        self.icon_doubleclick_timer.timeout.connect(
            self.icon_doubleclick_timeout)

        # Handle signals on Unix
        # (add_signal_handler is not implemented on Windows)
        try:
            loop = asyncio.get_event_loop()
            for signum in (signal.SIGINT, signal.SIGTERM):
                loop.add_signal_handler(signum, lambda: self.quit(force=True))
        except NotImplementedError:
            pass

    def create_actions(self):
        """Create actions and connect relevant signals"""
        self.startAction = QtGui.QAction(self)
        self.startAction.triggered.connect(self.hangups_start)
        self.stopAction = QtGui.QAction(self)
        self.stopAction.triggered.connect(self.hangups_stop)
        self.settingsAction = QtGui.QAction(self)
        self.settingsAction.triggered.connect(self.settings)
        self.aboutAction = QtGui.QAction(self)
        self.aboutAction.triggered.connect(self.about)
        self.quitAction = QtGui.QAction(self)
        self.quitAction.triggered.connect(self.quit)

    def create_menu(self):
        """Create menu and add items to it"""
        self.trayIconMenu = QtGui.QMenu(self)
        self.trayIconMenu.addAction(self.startAction)
        self.trayIconMenu.addAction(self.stopAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.settingsAction)
        self.trayIconMenu.addAction(self.aboutAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.quitAction)

    def create_icon(self):
        """Create system tray icon"""
        self.trayIcon = QtGui.QSystemTrayIcon(self)
        self.iconActive = QtGui.QIcon("{}/qhangups.svg".format(
            os.path.dirname(os.path.abspath(__file__))))
        self.iconDisabled = QtGui.QIcon("{}/qhangups_disabled.svg".format(
            os.path.dirname(os.path.abspath(__file__))))
        self.trayIcon.activated.connect(self.icon_activated)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.setIcon(self.iconDisabled)
        self.trayIcon.setToolTip("QHangups")
        self.trayIcon.show()

    def retranslateUi(self):
        """Retranslate GUI"""
        self.startAction.setText(self.tr("&Connect"))
        self.stopAction.setText(self.tr("&Disconnect"))
        self.settingsAction.setText(self.tr("S&ettings ..."))
        self.aboutAction.setText(self.tr("A&bout ..."))
        self.quitAction.setText(self.tr("&Quit"))

    def login(self, cookies_path):
        """Login to Google account"""
        try:
            cookies = hangups.auth.get_auth(self.get_credentials, self.get_pin,
                                            cookies_path)
            return cookies
        except hangups.GoogleAuthError:
            QtGui.QMessageBox.warning(self, self.tr("QHangups - Warning"),
                                      self.tr("Google login failed!"))
            return False

    def get_credentials(self):
        """Ask user for email and password (callback)"""
        email, ok = QtGui.QInputDialog.getText(self,
                                               self.tr("QHangups - Email"),
                                               self.tr("Email:"),
                                               QtGui.QLineEdit.Normal)
        if ok:
            password, ok = QtGui.QInputDialog.getText(
                self, self.tr("QHangups - Password"), self.tr(u"Password:"******"""Ask user for second factor PIN (callback)"""
        pin, ok = QtGui.QInputDialog.getText(self, self.tr("QHangups - PIN"),
                                             self.tr("PIN:"),
                                             QtGui.QLineEdit.Password)
        if ok:
            return pin
        else:
            return False

    def update_status(self):
        """Update GUI according to Hangups status"""
        if self.hangups_running:
            self.trayIcon.setIcon(self.iconActive)
            self.startAction.setEnabled(False)
            self.stopAction.setEnabled(True)
        else:
            self.trayIcon.setIcon(self.iconDisabled)
            self.startAction.setEnabled(True)
            self.stopAction.setEnabled(False)

    def hangups_start(self):
        """Connect to Hangouts"""
        cookies = self.login(self.cookies_path)
        if cookies:
            self.startHangups.emit()

            self.client = hangups.Client(cookies)
            self.client.on_connect.add_observer(self.on_connect)

            # Run Hangups event loop
            asyncio. async (self.client.connect()).add_done_callback(
                lambda future: future.result())
            self.hangups_running = True
            self.update_status()

    def hangups_stop(self):
        """Disconnect from Hangouts"""
        self.stopHangups.emit()

        asyncio. async (self.client.disconnect()).add_done_callback(
            lambda future: future.result())

        self.conv_list = None
        self.user_list = None
        self.notifier = None

        self.hangups_running = False
        self.client = None
        self.update_status()

    def about(self):
        """Show About dialog"""
        QtGui.QMessageBox.information(
            self, self.tr("About"), self.tr("QHangups {}".format(__version__)))

    def settings(self):
        """Show Settings dialog"""
        dialog = QHangupsSettings(self)
        if dialog.exec_():
            self.set_language()
            if self.hangups_running:
                self.hangups_stop()
                self.hangups_start()

    def set_language(self):
        """Change language"""
        settings = QtCore.QSettings()

        language = settings.value("language")
        if not language:
            language = QtCore.QLocale.system().name().split("_")[0]

        lang_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                 "languages")
        lang_file = "qhangups_{}.qm".format(language)

        qt_lang_path = QtCore.QLibraryInfo.location(
            QtCore.QLibraryInfo.TranslationsPath)
        qt_lang_file = "qt_{}.qm".format(language)

        if os.path.isfile(os.path.join(lang_path, lang_file)):
            translator.load(lang_file, lang_path)
            qt_translator.load(qt_lang_file, qt_lang_path)
        else:
            translator.load("")
            qt_translator.load("")

    def icon_activated(self, reason):
        """Connect or disconnect from Hangouts by double-click on tray icon"""
        if reason == QtGui.QSystemTrayIcon.Trigger or reason == QtGui.QSystemTrayIcon.DoubleClick:
            if self.icon_doubleclick_timer.isActive():
                self.icon_doubleclick_timer.stop()
                if self.hangups_running:
                    self.hangups_stop()
                else:
                    self.hangups_start()
            else:
                self.icon_doubleclick_timer.start(
                    QtGui.qApp.doubleClickInterval())

    def icon_doubleclick_timeout(self):
        """Open or close list of conversations after single-click on tray icon"""
        if self.conversations_dialog:
            if self.conversations_dialog.isVisible(
            ) and not self.conversations_dialog.isMinimized():
                self.conversations_dialog.hide()
            else:
                self.conversations_dialog.showNormal()
                self.conversations_dialog.raise_()
                self.conversations_dialog.activateWindow()

    def quit(self, force=False):
        """Quit QHangups"""
        if self.hangups_running:
            if not force:
                reply = QtGui.QMessageBox.question(
                    self, self.tr("QHangups - Quit"),
                    self.tr("You are still connected to Google Hangouts. "
                            "Do you really want to quit QHangups?"),
                    QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
                    QtGui.QMessageBox.No)
                if reply != QtGui.QMessageBox.Yes:
                    return
            self.hangups_stop()

        loop = asyncio.get_event_loop()
        loop.stop()
        # QtGui.qApp.quit()

    def changeEvent(self, event):
        """Handle LanguageChange event"""
        if (event.type() == QtCore.QEvent.LanguageChange):
            print("Language changed")
            self.retranslateUi()

        super().changeEvent(event)

    def open_messages_dialog(self, conv_id, switch=True):
        """Open conversation in new tab"""
        self.messages_dialog.set_conv_tab(conv_id, switch=switch)
        self.messages_dialog.showNormal()
        if switch:
            self.messages_dialog.raise_()
            self.messages_dialog.activateWindow()

    def on_connect(self, initial_data):
        """Handle connecting for the first time (callback)"""
        print('Connected')
        self.user_list = hangups.UserList(
            self.client, initial_data.self_entity, initial_data.entities,
            initial_data.conversation_participants)
        self.conv_list = hangups.ConversationList(
            self.client, initial_data.conversation_states, self.user_list,
            initial_data.sync_timestamp)
        self.conv_list.on_event.add_observer(self.on_event)

        # Setup notifications
        self.notifier = Notifier(self.conv_list)

        # Setup conversations window
        self.messages_dialog.init_conversations(self.client, self.conv_list)

        # Setup conversations list window and show it
        self.conversations_dialog.init_conversations(self.client,
                                                     self.conv_list)
        self.conversations_dialog.show()

    def on_event(self, conv_event):
        """Open conversation tab for new messages when they arrive (callback)"""
        if isinstance(conv_event, hangups.ChatMessageEvent):
            self.open_messages_dialog(conv_event.conversation_id, switch=False)
Ejemplo n.º 4
0
class QHangupsMainWidget(QtWidgets.QWidget):
    """QHangups main widget (icon in system tray)"""
    startHangups = QtCore.pyqtSignal()
    stopHangups = QtCore.pyqtSignal()

    def __init__(self, refresh_token_path, parent=None):
        super().__init__(parent)
        self.set_language()

        self.refresh_token_path = refresh_token_path
        self.hangups_running = False
        self.client = None

        self.create_actions()
        self.create_menu()
        self.create_icon()
        self.update_status()

        # These are populated by on_connect when it's called.
        self.conv_list = None  # hangups.ConversationList
        self.user_list = None  # hangups.UserList
        self.notifier = None   # hangups.notify.Notifier

        # Widgets
        self.conversations_dialog = QHangupsConversationsList(controller=self)
        self.messages_dialog = QHangupsConversations(controller=self)

        # Setup system tray icon doubleclick timer
        self.icon_doubleclick_timer = QtCore.QTimer(self)
        self.icon_doubleclick_timer.setSingleShot(True)
        self.icon_doubleclick_timer.timeout.connect(self.icon_doubleclick_timeout)

        # Handle signals on Unix
        # (add_signal_handler is not implemented on Windows)
        try:
            loop = asyncio.get_event_loop()
            for signum in (signal.SIGINT, signal.SIGTERM):
                loop.add_signal_handler(signum, lambda: self.quit(force=True))
        except NotImplementedError:
            pass

    def create_actions(self):
        """Create actions and connect relevant signals"""
        self.startAction = QtWidgets.QAction(self)
        self.startAction.triggered.connect(self.hangups_start)
        self.stopAction = QtWidgets.QAction(self)
        self.stopAction.triggered.connect(self.hangups_stop)
        self.settingsAction = QtWidgets.QAction(self)
        self.settingsAction.triggered.connect(self.settings)
        self.aboutAction = QtWidgets.QAction(self)
        self.aboutAction.triggered.connect(self.about)
        self.quitAction = QtWidgets.QAction(self)
        self.quitAction.triggered.connect(self.quit)

    def create_menu(self):
        """Create menu and add items to it"""
        self.trayIconMenu = QtWidgets.QMenu(self)
        self.trayIconMenu.addAction(self.startAction)
        self.trayIconMenu.addAction(self.stopAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.settingsAction)
        self.trayIconMenu.addAction(self.aboutAction)
        self.trayIconMenu.addSeparator()
        self.trayIconMenu.addAction(self.quitAction)

    def create_icon(self):
        """Create system tray icon"""
        self.trayIcon = QtWidgets.QSystemTrayIcon(self)
        self.iconActive = QtGui.QIcon("{}/qhangups.svg".format(os.path.dirname(os.path.abspath(__file__))))
        self.iconDisabled = QtGui.QIcon("{}/qhangups_disabled.svg".format(os.path.dirname(os.path.abspath(__file__))))
        # Workaround for Plasma 5 not showing SVG icons
        self.iconActive = QtGui.QIcon(self.iconActive.pixmap(128, 128))
        self.iconDisabled = QtGui.QIcon(self.iconDisabled.pixmap(128, 128))

        self.trayIcon.activated.connect(self.icon_activated)
        self.trayIcon.setContextMenu(self.trayIconMenu)
        self.trayIcon.setIcon(self.iconDisabled)
        self.trayIcon.setToolTip("QHangups")
        self.trayIcon.show()

    def retranslateUi(self):
        """Retranslate GUI"""
        self.startAction.setText(self.tr("&Connect"))
        self.stopAction.setText(self.tr("&Disconnect"))
        self.settingsAction.setText(self.tr("S&ettings ..."))
        self.aboutAction.setText(self.tr("A&bout ..."))
        self.quitAction.setText(self.tr("&Quit"))

    def login(self, refresh_token_path):
        """Login to Google account"""
        try:
            cookies = hangups.auth.get_auth(self.get_credentials, refresh_token_path)
            return cookies
        except hangups.GoogleAuthError:
            QtWidgets.QMessageBox.warning(self, self.tr("QHangups - Warning"),
                                          self.tr("Google login failed!"))
            return False

    def get_credentials(self):
        """Ask user for OAuth 2 authorization code"""
        browser = QHangupsBrowser(hangups.auth.OAUTH2_LOGIN_URL, self).exec_()
        code, ok = QtWidgets.QInputDialog.getText(self, self.tr("QHangups - Authorization"),
                                                  self.tr("Authorization code:"),
                                                  QtWidgets.QLineEdit.Normal)
        if ok and code:
            return code
        else:
            return None

    def update_status(self):
        """Update GUI according to Hangups status"""
        if self.hangups_running:
            self.trayIcon.setIcon(self.iconActive)
            self.startAction.setEnabled(False)
            self.stopAction.setEnabled(True)
        else:
            self.trayIcon.setIcon(self.iconDisabled)
            self.startAction.setEnabled(True)
            self.stopAction.setEnabled(False)

    def hangups_start(self):
        """Connect to Hangouts"""
        cookies = self.login(self.refresh_token_path)
        if cookies:
            self.startHangups.emit()

            self.client = hangups.Client(cookies)
            self.client.on_connect.add_observer(self.on_connect)

            # Run Hangups event loop
            asyncio.async(
                self.client.connect()
            ).add_done_callback(lambda future: future.result())
            self.hangups_running = True
            self.update_status()

    def hangups_stop(self):
        """Disconnect from Hangouts"""
        self.stopHangups.emit()

        asyncio.async(
            self.client.disconnect()
        ).add_done_callback(lambda future: future.result())

        self.conv_list = None
        self.user_list = None
        self.notifier = None

        self.hangups_running = False
        self.client = None
        self.update_status()

    def about(self):
        """Show About dialog"""
        QtWidgets.QMessageBox.information(self, self.tr("About"), self.tr("QHangups {}".format(__version__)))

    def settings(self):
        """Show Settings dialog"""
        dialog = QHangupsSettings(self)
        if dialog.exec_():
            self.set_language()
            if self.hangups_running:
                self.hangups_stop()
                self.hangups_start()

    def set_language(self):
        """Change language"""
        settings = QtCore.QSettings()

        language = settings.value("language")
        if not language:
            language = QtCore.QLocale.system().name().split("_")[0]

        lang_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "languages")
        lang_file = "qhangups_{}.qm".format(language)

        qt_lang_path = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)
        qt_lang_file = "qt_{}.qm".format(language)

        if os.path.isfile(os.path.join(lang_path, lang_file)):
            translator.load(lang_file, lang_path)
            qt_translator.load(qt_lang_file, qt_lang_path)
        else:
            translator.load("")
            qt_translator.load("")

    def icon_activated(self, reason):
        """Connect or disconnect from Hangouts by double-click on tray icon"""
        if reason == QtWidgets.QSystemTrayIcon.Trigger or reason == QtWidgets.QSystemTrayIcon.DoubleClick:
            if self.icon_doubleclick_timer.isActive():
                self.icon_doubleclick_timer.stop()
                if self.hangups_running:
                    self.hangups_stop()
                else:
                    self.hangups_start()
            else:
                self.icon_doubleclick_timer.start(QtWidgets.qApp.doubleClickInterval())

    def icon_doubleclick_timeout(self):
        """Open or close list of conversations after single-click on tray icon"""
        if self.conversations_dialog:
            if self.conversations_dialog.isVisible() and not self.conversations_dialog.isMinimized():
                self.conversations_dialog.hide()
            else:
                self.conversations_dialog.showNormal()
                self.conversations_dialog.raise_()
                self.conversations_dialog.activateWindow()

    def quit(self, force=False):
        """Quit QHangups"""
        if self.hangups_running:
            if not force:
                reply = QtWidgets.QMessageBox.question(self, self.tr("QHangups - Quit"),
                                                       self.tr("You are still connected to Google Hangouts. "
                                                               "Do you really want to quit QHangups?"),
                                                       QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
                                                       QtWidgets.QMessageBox.No)
                if reply != QtWidgets.QMessageBox.Yes:
                    return
            self.hangups_stop()

        loop = asyncio.get_event_loop()
        loop.stop()
        # QtWidgets.qApp.quit()

    def changeEvent(self, event):
        """Handle LanguageChange event"""
        if (event.type() == QtCore.QEvent.LanguageChange):
            print("Language changed")
            self.retranslateUi()

        super().changeEvent(event)

    def open_messages_dialog(self, conv_id, switch=True):
        """Open conversation in new tab"""
        self.messages_dialog.set_conv_tab(conv_id, switch=switch)
        self.messages_dialog.showNormal()
        if switch:
            self.messages_dialog.raise_()
            self.messages_dialog.activateWindow()

    @asyncio.coroutine
    def on_connect(self):
        """Handle connecting for the first time (callback)"""
        print('Connected')

        self.user_list, self.conv_list = (
            yield from hangups.build_user_conversation_list(self.client)
        )
        self.conv_list.on_event.add_observer(self.on_event)

        # Setup notifications
        self.notifier = Notifier(self.conv_list)

        # Setup conversations window
        self.messages_dialog.init_conversations(self.client, self.conv_list)

        # Setup conversations list window and show it
        self.conversations_dialog.init_conversations(self.client, self.conv_list)
        self.conversations_dialog.show()

    @asyncio.coroutine
    def on_event(self, conv_event):
        """Open conversation tab for new messages when they arrive (callback)"""
        if isinstance(conv_event, hangups.ChatMessageEvent):
            self.open_messages_dialog(conv_event.conversation_id, switch=False)