From 4363f255fe264bebc608a1f307701f25ee6b287b Mon Sep 17 00:00:00 2001 From: Jeffrey Gill Date: Sun, 17 Jan 2021 00:24:48 -0500 Subject: [PATCH] Run the Google Drive authorization flow in a separate thread Also hides the metadata selector and replaces it with a message when the auth flow is triggered by the menu action. If the auth flow is triggered by a download instead, it will still run on the network thread, but the metadata selector won't be hidden. This is not ideal, but making the download-triggered case the same would be difficult. --- neurotic/gui/standalone.py | 84 ++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/neurotic/gui/standalone.py b/neurotic/gui/standalone.py index 8dbf336..bb643b6 100644 --- a/neurotic/gui/standalone.py +++ b/neurotic/gui/standalone.py @@ -64,8 +64,9 @@ class MainWindow(QT.QMainWindow): """ request_download = QT.pyqtSignal() - request_check_for_updates = QT.pyqtSignal(bool) request_load_dataset = QT.pyqtSignal() + request_gdrive_authorization = QT.pyqtSignal() + request_check_for_updates = QT.pyqtSignal(bool) def __init__(self, file=None, initial_selection=None, lazy=True, theme='light', ui_scale='medium', support_increased_line_width=False, show_datetime=False): """ @@ -118,23 +119,37 @@ def __init__(self, file=None, initial_selection=None, lazy=True, theme='light', self.loading_label.setFrameStyle(QT.QFrame.Panel | QT.QFrame.Sunken) self.loading_label.setAlignment(QT.Qt.AlignCenter) + # gdrive auth flow label + self.gdrive_auth_label = QT.QLabel('Continue Google Drive authorization\nin your web browser') + self.gdrive_auth_label.setFrameStyle(QT.QFrame.Panel | QT.QFrame.Sunken) + self.gdrive_auth_label.setAlignment(QT.Qt.AlignCenter) + # initially stack the metadata selector above the loading label self.stacked_layout = QT.QStackedLayout() self.stacked_layout.addWidget(self.metadata_selector) # index 0 self.stacked_layout.addWidget(self.loading_label) # index 1 + self.stacked_layout.addWidget(self.gdrive_auth_label) # index 2 central_widget = QT.QWidget() central_widget.setLayout(self.stacked_layout) self.setCentralWidget(central_widget) - # create a worker thread for downloading data and checking for updates + # create a worker thread for network activity (e.g., downloading data) self.network_thread = QT.QThread() self.network_worker = _NetworkWorker(self) self.network_worker.moveToThread(self.network_thread) + + # set up the network thread to perform data downloads self.request_download.connect(self.network_worker.download) self.network_worker.download_finished.connect(self.on_download_finished) + + # set up the network thread to perform checks for updates self.request_check_for_updates.connect(self.network_worker.get_latest_release_number) self.network_worker.version_check_finished.connect(self.on_version_check_finished) + # set up the network thread to run the Google Drive authorization flow + self.request_gdrive_authorization.connect(self.network_worker.authorize_gdrive) + self.network_worker.gdrive_authorization_finished.connect(self.on_gdrive_authorization_finished) + # create a worker thread for loading datasets self.load_dataset_thread = QT.QThread() self.load_dataset_worker = _LoadDatasetWorker(self) @@ -287,8 +302,8 @@ def create_menus(self): do_open_gdrive_creds_dir = help_menu.addAction('Open Google Drive credentials directory') do_open_gdrive_creds_dir.triggered.connect(self.open_gdrive_creds_dir) - do_authorize_gdrive = help_menu.addAction('Request Google Drive authorization now') - do_authorize_gdrive.triggered.connect(self.authorize_gdrive) + self.do_authorize_gdrive = help_menu.addAction('Request Google Drive authorization now') + self.do_authorize_gdrive.triggered.connect(self.authorize_gdrive) do_deauthorize_gdrive = help_menu.addAction('Purge Google Drive authorization token') do_deauthorize_gdrive.triggered.connect(self.deauthorize_gdrive) @@ -540,26 +555,36 @@ def authorize_gdrive(self): text = 'Completing this process will allow neurotic to ' \ 'download files from your Google Drive. A web browser ' \ 'will open so that you can log into your Google account ' \ - 'and accept the request for permissions. neurotic ' \ - 'will be unresponsive until this process finishes.' \ + 'and accept the request for permissions. You will not be ' \ + 'able to use neurotic until this process finishes.' \ '

Do you want to continue?' button = QT.QMessageBox.question(self, title, text, defaultButton=QT.QMessageBox.Yes) if button == QT.QMessageBox.Yes: - logger.info(f'Initiating Google Drive authorization flow') - self.statusBar().showMessage('Check web browser to continue ' - 'authorization', msecs=5000) - try: - gdrive_downloader.authorize() - except Exception as e: - logger.error(f'Problem during authorization: {e}') - self.statusBar().showMessage('ERROR: Authorization failed ' - '(see console for details)', - msecs=5000) - else: - logger.info(f'Authorization complete') - self.statusBar().showMessage('Authorization complete', - msecs=5000) + self.do_authorize_gdrive.setText('Continue Google Drive authorization in your browser!') + self.do_authorize_gdrive.setEnabled(False) + self.metadata_selector.setEnabled(False) + self.stacked_layout.setCurrentIndex(2) # show gdrive auth label + self.network_thread.start() + self.request_gdrive_authorization.emit() + + def on_gdrive_authorization_finished(self, success): + """ + Cleanup network thread and display success or failute of Google Drive + authorization flow. + """ + self.network_thread.quit() + if success: + self.statusBar().showMessage('Authorization successful', + msecs=5000) + else: + self.statusBar().showMessage('ERROR: Authorization failed ' + '(see console for details)', + msecs=5000) + self.do_authorize_gdrive.setText('Request Google Drive authorization now') + self.do_authorize_gdrive.setEnabled(True) + self.metadata_selector.setEnabled(True) + self.stacked_layout.setCurrentIndex(0) # show metadata selector def deauthorize_gdrive(self): """ @@ -722,6 +747,10 @@ def set_ui_scale(self, size): font.setPointSize(font_size[size]+4) self.loading_label.setFont(font) + font = self.gdrive_auth_label.font() + font.setPointSize(font_size[size]+4) + self.gdrive_auth_label.setFont(font) + def set_theme(self, theme): self.theme = theme @@ -822,10 +851,11 @@ def load(self): class _NetworkWorker(QT.QObject): """ - A thread worker for downloading data files and checking for updates. + A thread worker for for network activity (e.g., downloading data) """ download_finished = QT.pyqtSignal(bool) + gdrive_authorization_finished = QT.pyqtSignal(bool) version_check_finished = QT.pyqtSignal(str, bool) def __init__(self, mainwindow): @@ -851,6 +881,18 @@ def download(self): finally: self.download_finished.emit(success) + def authorize_gdrive(self): + success = False + try: + logger.info('Initiating Google Drive authorization flow') + gdrive_downloader.authorize() + success = True + logger.info('Authorization successful') + except Exception as e: + logger.error(f'Problem during authorization: {e}') + finally: + self.gdrive_authorization_finished.emit(success) + def get_latest_release_number(self, show_new_only): """ Query GitHub for the version number of the latest release and emit a