Sun, 27 Jan 2019 19:52:37 +0100
Continued implementing environment creation with conda.
# -*- coding: utf-8 -*- # Copyright (c) 2019 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a dialog to show the output of a conda execution. """ from __future__ import unicode_literals try: str = unicode except NameError: pass import json from PyQt5.QtCore import pyqtSlot, QProcess, QTimer from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton from E5Gui import E5MessageBox from .Ui_CondaExecDialog import Ui_CondaExecDialog import Preferences class CondaExecDialog(QDialog, Ui_CondaExecDialog): """ Class documentation goes here. """ def __init__(self, configuration, venvManager, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(CondaExecDialog, self).__init__(parent) self.setupUi(self) self.__venvName = configuration["logicalName"] self.__venvManager = venvManager self.__process = None self.__condaExe = Preferences.getConda("CondaExecutable") if not self.__condaExe: self.__condaExe = "conda" @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. @param button button that was clicked @type QAbstractButton """ if button == self.buttonBox.button(QDialogButtonBox.Close): self.accept() elif button == self.buttonBox.button(QDialogButtonBox.Cancel): self.__finish() def start(self, arguments): """ Public slot to start the conda command. @param arguments commandline arguments for conda program @type list of str """ self.errorGroup.hide() self.progressLabel.hide() self.progressBar.hide() self.contents.clear() self.errors.clear() self.progressLabel.clear() self.progressBar.setValue(0) self.__bufferedStdout = None self.__json = "--json" in arguments self.__firstProgress = True self.__process = QProcess() self.__process.readyReadStandardOutput.connect(self.__readStdout) self.__process.readyReadStandardError.connect(self.__readStderr) self.__process.finished.connect(self.__finish) self.__process.start(self.__condaExe, arguments) procStarted = self.__process.waitForStarted(5000) if not procStarted: E5MessageBox.critical( self, self.tr("Conda Execution"), self.tr("""The conda executable could not be started. Is it""" """ configured correctly?""")) self.__finish(1, 0) else: self.__logOutput(self.tr("Operation started.\n")) def __finish(self, exitCode, exitStatus, giveUp=False): """ Private slot called when the process finished. It is called when the process finished or the user pressed the button. @param exitCode exit code of the process (integer) @param exitStatus exit status of the process (QProcess.ExitStatus) @keyparam giveUp flag indicating to not start another attempt (boolean) """ if self.__process is not None and \ self.__process.state() != QProcess.NotRunning: self.__process.terminate() QTimer.singleShot(2000, self.__process.kill) self.__process.waitForFinished(3000) self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) self.__logOutput(self.tr("Operation finished.\n")) if self.__json: if self.__bufferedStdout: try: jsonDict = json.loads(self.__bufferedStdout) except Exception as error: self.__logError(str(error)) return if "success" in jsonDict and jsonDict["success"]: if "prefix" in jsonDict: prefix = jsonDict["prefix"] elif "dst_prefix" in jsonDict: prefix = jsonDict["dst_prefix"] else: prefix = "" self.__venvManager.addVirtualEnv(self.__venvName, prefix, isConda=True) def __progressLabelString(self, text): """ Private method to process a string and format it for the progress label. @param text text to be processed @type str @return formatted progress label string @rtype str """ parts = text.split("|") return self.tr("{0} (Size: {1})".format(parts[0].strip(), parts[1].strip())) def __readStdout(self): """ Private slot to handle the readyReadStandardOutput signal. It reads the output of the process, formats it and inserts it into the contents pane. """ all_stdout = str(self.__process.readAllStandardOutput(), Preferences.getSystem("IOEncoding"), 'replace') all_stdout = all_stdout.replace("\x00", "") if self.__json: for stdout in all_stdout.splitlines(): try: jsonDict = json.loads(stdout.replace("\x00", "").strip()) if "progress" in jsonDict: self.progressLabel.setText( self.__progressLabelString(jsonDict["fetch"])) self.progressBar.setValue( int(jsonDict["progress"] * 100)) if self.__firstProgress: self.progressLabel.show() self.progressBar.show() self.__firstProgress = False else: if self.__bufferedStdout is None: self.__bufferedStdout = stdout else: self.__bufferedStdout += stdout except (TypeError, ValueError): if self.__bufferedStdout is None: self.__bufferedStdout = stdout else: self.__bufferedStdout += stdout else: self.__logOutput(all_stdout) def __readStderr(self): """ Private slot to handle the readyReadStandardError signal. It reads the error output of the process and inserts it into the error pane. """ self.__process.setReadChannel(QProcess.StandardError) while self.__process.canReadLine(): stderr = str(self.__process.readLine(), Preferences.getSystem("IOEncoding"), 'replace') self.__logError(stderr) def __logOutput(self, stdout): """ Private method to log some output. @param stdout output string to log @type str """ self.contents.insertPlainText(stdout) self.contents.ensureCursorVisible() def __logError(self, stderr): """ Private method to log an error. @param stderr error string to log @type str """ self.errorGroup.show() self.errors.insertPlainText(stderr) self.errors.ensureCursorVisible()