【问题标题】:How to properly add OAuth2 Popup in existing PyQt5 Application如何在现有 PyQt5 应用程序中正确添加 OAuth2 弹出窗口
【发布时间】:2019-08-10 05:44:48
【问题描述】:

我对 PyQt 还很陌生,我已经设法通过使用 Qt Designer 组合一个工作应用程序来摸索自己的方式。但是,我需要添加一个 OAuth2 流来登录特定服务,但我无法让它适应我现有的应用程序。

我在 Github (https://github.com/alonraiz/QT-OAuth-Example) 中找到了一个处理 PyQt5/OAuth2 登录的好例子——只要我独立运行它。我现在正在尝试集成它,以便在用户选择特定菜单项时弹出它......我遇到了麻烦。

默认代码使用这个:

    app = QApplication(sys.argv)
    browser = LoginWindow(app)

由于我已经有一个应用程序,所以我尝试调用:

    browser = LoginWindow(app)

使用我现有的应用程序对象。然后让我遇到这个错误: QCoreApplication::exec: 事件循环已经在运行

这似乎是有道理的。所以我删除了这个:

        sys.exit(app.exec_())

然后我什么也得不到。

根据我有限的 PyQt5 经验,我确定我的做法完全错误......

这是一个说明我现在所处位置的示例。感谢任何帮助!

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'test.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!

import sys
from urllib.parse import urlencode, parse_qs
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication


"""
login   anxious-elephant@example.com
password    Frantic-Magpie-Tame-Cow-9
"""

ClientId = '0oamu4rr08hdGKd9u0h7'
RedirectUrl = 'www.oauth.com/playground/authorization-code.html'
RedirectScheme = 'https://'
Scopes = ['photo offline_access']

ResponseType = 'code'

Headers = {'client_id': ClientId, 'redirect_uri': RedirectScheme+RedirectUrl, 'response_type': ResponseType,
           'scope': str.join(' ', Scopes), 'state': 'RT6TfGb4jEWbz7SI'}

AuthUrl = 'https://dev-396343.oktapreview.com/oauth2/default/v1/authorize?{headers}'.format(
    headers=urlencode(Headers))


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(234, 167)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 234, 22))
        self.menubar.setObjectName("menubar")
        self.menuFile = QtWidgets.QMenu(self.menubar)
        self.menuFile.setObjectName("menuFile")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionLogin = QtWidgets.QAction(MainWindow)
        self.actionLogin.setObjectName("actionLogin")
        self.menuFile.addAction(self.actionLogin)
        self.menubar.addAction(self.menuFile.menuAction())

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.menuFile.setTitle(_translate("MainWindow", "File"))
        self.actionLogin.setText(_translate("MainWindow", "Login"))


class RequestInterceptor(QWebEngineUrlRequestInterceptor):
    def __init__(self, app):
        super(RequestInterceptor, self).__init__()
        self.app = app

    def interceptRequest(self, info):
        if RedirectUrl == (info.requestUrl().host()+info.requestUrl().path()):
            params = parse_qs(info.requestUrl().query())
            if 'code' in params.keys():
                print('OAuth code is {code}'.format(code=params['code']))
                self.app.quit()
                # return params['code']


class LoginWindow(QWebEngineView):
    logged_in = QtCore.pyqtSignal(['QString'])

    def __init__(self, app):
        super(LoginWindow, self).__init__()
        self.nam = self.page()
        self.app = app
        self.setUrl(QUrl(AuthUrl))
        self.show()
        self.loadFinished.connect(self._loadFinished)
        interceptor = RequestInterceptor(app)
        self.page().profile().setUrlRequestInterceptor(interceptor)
        # This needs enabled to get the working example running:
        # sys.exit(app.exec_())

    def _loadFinished(self, result):
        self.page().toHtml(self.callable)

    def callable(self, data):
        self.html = data


class MainMenu(Ui_MainWindow):
    def __init__(self, dialog, mainapp):
        global fn
        Ui_MainWindow.__init__(self)
        self.setupUi(dialog)
        self.actionLogin.triggered.connect(self.login)
        self.mainapp = mainapp

    def login(self):
        print("login")
        browser = LoginWindow(self.mainapp)


if __name__ == "__main__":
    # This doesn't work
    app = QtWidgets.QApplication(sys.argv)
    menu = QtWidgets.QMainWindow()
    prog = MainMenu(menu, app)
    menu.show()
    sys.exit(app.exec_())

    # This works:
    # app = QApplication(sys.argv)
    # browser = LoginWindow(app)

我想做的是让 OAuth 流作为主应用程序的弹出窗口工作,而不是一个独立的应用程序(或者让两个应用程序和平共处)。

【问题讨论】:

    标签: python python-3.x oauth-2.0 pyqt5


    【解决方案1】:

    我建议您阅读 PyQt5(1) 文档,以便了解如何使用 Qt Designer 生成的类。考虑到上述情况,解决方案是继承自QMainWindow,使用Qt Designer类作为接口类。拦截器对象是一个局部变量,也会产生问题,所以不发生,把窗口作为父级传递。

    综合以上情况,解决办法是:

    from urllib.parse import urlencode, parse_qs
    
    from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineCore, QtWebEngineWidgets
    
    
    """
    login   anxious-elephant@example.com
    password    Frantic-Magpie-Tame-Cow-9
    """
    
    ClientId = "0oamu4rr08hdGKd9u0h7"
    RedirectUrl = "www.oauth.com/playground/authorization-code.html"
    RedirectScheme = "https://"
    Scopes = ["photo offline_access"]
    
    ResponseType = "code"
    
    Headers = {
        "client_id": ClientId,
        "redirect_uri": RedirectScheme + RedirectUrl,
        "response_type": ResponseType,
        "scope": str.join(" ", Scopes),
        "state": "RT6TfGb4jEWbz7SI",
    }
    
    AuthUrl = "https://dev-396343.oktapreview.com/oauth2/default/v1/authorize?{headers}".format(
        headers=urlencode(Headers)
    )
    
    
    class Ui_MainWindow(object):
        def setupUi(self, MainWindow):
            MainWindow.setObjectName("MainWindow")
            MainWindow.resize(234, 167)
            self.centralwidget = QtWidgets.QWidget(MainWindow)
            self.centralwidget.setObjectName("centralwidget")
            MainWindow.setCentralWidget(self.centralwidget)
            self.menubar = QtWidgets.QMenuBar(MainWindow)
            self.menubar.setGeometry(QtCore.QRect(0, 0, 234, 22))
            self.menubar.setObjectName("menubar")
            self.menuFile = QtWidgets.QMenu(self.menubar)
            self.menuFile.setObjectName("menuFile")
            MainWindow.setMenuBar(self.menubar)
            self.statusbar = QtWidgets.QStatusBar(MainWindow)
            self.statusbar.setObjectName("statusbar")
            MainWindow.setStatusBar(self.statusbar)
            self.actionLogin = QtWidgets.QAction(MainWindow)
            self.actionLogin.setObjectName("actionLogin")
            self.menuFile.addAction(self.actionLogin)
            self.menubar.addAction(self.menuFile.menuAction())
    
            self.retranslateUi(MainWindow)
            QtCore.QMetaObject.connectSlotsByName(MainWindow)
    
        def retranslateUi(self, MainWindow):
            _translate = QtCore.QCoreApplication.translate
            MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
            self.menuFile.setTitle(_translate("MainWindow", "File"))
            self.actionLogin.setText(_translate("MainWindow", "Login"))
    
    
    class RequestInterceptor(QtWebEngineCore.QWebEngineUrlRequestInterceptor):
        codeChanged = QtCore.pyqtSignal(str)
    
        def interceptRequest(self, info):
            if RedirectUrl == (info.requestUrl().host() + info.requestUrl().path()):
                params = parse_qs(info.requestUrl().query())
                if "code" in params.keys():
                    code = params["code"][0][0]
                    print("OAuth code is {code}".format(code=params["code"][0][0]))
                    self.codeChanged.emit(code)
    
    
    class LoginWindow(QtWebEngineWidgets.QWebEngineView):
        codeChanged = QtCore.pyqtSignal(str)
    
        def __init__(self, parent=None):
            super(LoginWindow, self).__init__(parent)
            self.setUrl(QtCore.QUrl(AuthUrl))
            self.loadFinished.connect(self._loadFinished)
            interceptor = RequestInterceptor(self)
            self.page().profile().setUrlRequestInterceptor(interceptor)
            self.show()
    
            self.interceptor.codeChanged.connect(self.codeChanged)
    
        @QtCore.pyqtSlot(bool)
        def _loadFinished(self, result):
            self.page().toHtml(self.callable)
    
        def callable(self, data):
            self.html = data
    
    
    class MainMenu(QtWidgets.QMainWindow, Ui_MainWindow):
        def __init__(self, parent=None):
            super(MainMenu, self).__init__(parent)
            self.setupUi(self)
            self.actionLogin.triggered.connect(self.login)
    
            lay = QtWidgets.QVBoxLayout(self.centralwidget)
            self.code_label = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
            lay.addWidget(self.code_label)
    
        @QtCore.pyqtSlot()
        def login(self):
            self.browser = LoginWindow()
            self.browser.codeChanged.connect(self.onCodeChanged)
    
        @QtCore.pyqtSlot(str)
        def onCodeChanged(self, code):
            self.code_label.setText(code)
            # self.browser.close()
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        menu = MainMenu()
        menu.show()
        sys.exit(app.exec_())
    

    (1)Using the Generated Code

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-11
      • 1970-01-01
      • 2020-08-10
      • 2020-07-01
      • 2016-08-31
      • 2019-05-25
      • 1970-01-01
      相关资源
      最近更新 更多