예제 #1
0
class TestPython(object):

    def __init__(self, ID=None):
        self.Test = QProcess()
        self.ID = ID
        #a and b to test if i get a output or command error
        self.a = True
        self.Settings = QtCore.QSettings('Tyrant.cfg',
                                                    QtCore.QSettings.IniFormat)

    def TestVersion(self):
        print('Test python version')
        self.Test.readyReadStandardError.connect(self.getOutput)
        self.Test.readyReadStandardOutput.connect(self.getOutput)
        if self.ID is not None:
            self.Test.error.connect(self.Error)
        #if i have a custom pythonpath, i'll use it here
        Res = Resources()
        Python = Res.getPref('Python')
        #test if is a pythonpath, if not open a default
        if len(Python) < 5:
            Python = 'python'
        self.Test.start(Python, ['-V'])
        self.Test.waitForFinished()
        self.Test.terminate()
        if (self.a is False) & (self.ID is None):
            #test a diferent command to run python 2.7
            a = self.TestVersion2()
            print a
            return a
        elif self.a:
            #say to program that python is working
            print('Python version found')
            self.Settings.setValue('Python/PythonPath', str(Python))
            self.Settings.sync()
            return True
        elif ((self.ID is not None) & (self.a is False)):
            return False

    def Error(self):
        self.a = False

    def TestVersion2(self):
        print('Testing python version 2')
        self.Test.readyReadStandardError.connect(self.getOutput)
        Python = 'python2.7'
        self.Test.start(Python, ['-V'])
        self.Test.waitForStarted()
        self.Test.waitForFinished()
        self.Test.terminate()
        if (self.a is False):
            #say thar python is not working
            return False
        else:
            self.Settings.setValue('Python/PythonPath', str(Python))
            self.Settings.sync()
            return True

    def getOutput(self):
        Out = str(self.Test.readAllStandardError())
        print(Out)
        if Out > 'Python 2.8':
            self.a = False
            return
        self.a = True
예제 #2
0
class TestPython(object):
    def __init__(self, ID=None):
        self.Test = QProcess()
        self.ID = ID
        #a and b to test if i get a output or command error
        self.a = True
        self.Settings = QtCore.QSettings('Tyrant.cfg',
                                         QtCore.QSettings.IniFormat)

    def TestVersion(self):
        print('Test python version')
        self.Test.readyReadStandardError.connect(self.getOutput)
        self.Test.readyReadStandardOutput.connect(self.getOutput)
        if self.ID is not None:
            self.Test.error.connect(self.Error)
        #if i have a custom pythonpath, i'll use it here
        Res = Resources()
        Python = Res.getPref('Python')
        #test if is a pythonpath, if not open a default
        if len(Python) < 5:
            Python = 'python'
        self.Test.start(Python, ['-V'])
        self.Test.waitForFinished()
        self.Test.terminate()
        if (self.a is False) & (self.ID is None):
            #test a diferent command to run python 2.7
            a = self.TestVersion2()
            print a
            return a
        elif self.a:
            #say to program that python is working
            print('Python version found')
            self.Settings.setValue('Python/PythonPath', str(Python))
            self.Settings.sync()
            return True
        elif ((self.ID is not None) & (self.a is False)):
            return False

    def Error(self):
        self.a = False

    def TestVersion2(self):
        print('Testing python version 2')
        self.Test.readyReadStandardError.connect(self.getOutput)
        Python = 'python2.7'
        self.Test.start(Python, ['-V'])
        self.Test.waitForStarted()
        self.Test.waitForFinished()
        self.Test.terminate()
        if (self.a is False):
            #say thar python is not working
            return False
        else:
            self.Settings.setValue('Python/PythonPath', str(Python))
            self.Settings.sync()
            return True

    def getOutput(self):
        Out = str(self.Test.readAllStandardError())
        print(Out)
        if Out > 'Python 2.8':
            self.a = False
            return
        self.a = True
예제 #3
0
class CfdConsoleProcess:
    """ Class to run a console process asynchronously, printing output and
    errors to the FreeCAD console and allowing clean termination in Linux
    and Windows """
    def __init__(self, finishedHook=None, stdoutHook=None, stderrHook=None):
        self.process = QProcess()
        self.finishedHook = finishedHook
        self.stdoutHook = stdoutHook
        self.stderrHook = stderrHook
        self.process.finished.connect(self.finished)
        self.process.readyReadStandardOutput.connect(self.readStdout)
        self.process.readyReadStandardError.connect(self.readStderr)
        self.print_next_error_lines = 0
        self.print_next_error_file = False

    def __del__(self):
        self.terminate()

    def start(self, cmd, env_vars=None, working_dir=None):
        """ Start process and return immediately """
        self.print_next_error_lines = 0
        self.print_next_error_file = False
        env = QtCore.QProcessEnvironment.systemEnvironment()
        if env_vars:
            for key in env_vars:
                env.insert(key, env_vars[key])
        self.process.setProcessEnvironment(env)
        if working_dir:
            self.process.setWorkingDirectory(working_dir)
        if platform.system() == "Windows":
            # Run through a wrapper process to allow clean termination
            cmd = [
                os.path.join(FreeCAD.getHomePath(), "bin", "python.exe"),
                '-u',  # Prevent python from buffering stdout
                os.path.join(os.path.dirname(__file__), "WindowsRunWrapper.py")
            ] + cmd
        print("Raw command: ", cmd)
        self.process.start(cmd[0], cmd[1:])

    def terminate(self):
        if platform.system() == "Windows":
            # terminate() doesn't operate and kill() doesn't allow cleanup and leaves mpi processes running
            # Instead, instruct wrapper program to kill child process and itself cleanly with ctrl-break signal
            self.process.write(b"terminate\n")
            self.process.waitForBytesWritten()  # 'flush'
        else:
            self.process.terminate()
        self.process.waitForFinished()

    def finished(self, exit_code):
        if self.finishedHook:
            self.finishedHook(exit_code)

    def readStdout(self):
        # Ensure only complete lines are passed on
        text = ""
        while self.process.canReadLine():
            byteArr = self.process.readLine()
            text += QTextStream(byteArr).readAll()
        if text:
            print(text, end='')  # Avoid displaying on FreeCAD status bar
            if self.stdoutHook:
                self.stdoutHook(text)
            # Must be at the end as it can cause re-entrance
            if FreeCAD.GuiUp:
                FreeCAD.Gui.updateGui()

    def readStderr(self):
        # Ensure only complete lines are passed on
        # Print any error output to console
        self.process.setReadChannel(QProcess.StandardError)
        text = ""
        while self.process.canReadLine():
            byteArr = self.process.readLine()
            text += QTextStream(byteArr).readAll()
        if text:
            if self.stderrHook:
                self.stderrHook(text)
            FreeCAD.Console.PrintError(text)
            # Must be at the end as it can cause re-entrance
            if FreeCAD.GuiUp:
                FreeCAD.Gui.updateGui()
        self.process.setReadChannel(QProcess.StandardOutput)

    def state(self):
        return self.process.state()

    def waitForStarted(self):
        return self.process.waitForStarted()

    def waitForFinished(self):
        # For some reason waitForFinished doesn't always return - so we resort to a failsafe timeout:
        while True:
            ret = self.process.waitForFinished(1000)
            if self.process.error() != self.process.Timedout:
                self.readStdout()
                self.readStderr()
                return ret
            if self.process.state() == self.process.NotRunning:
                self.readStdout()
                self.readStderr()
                return True

    def exitCode(self):
        return self.process.exitCode()

    def processErrorOutput(self, err):
        """
        Process standard error text output from OpenFOAM
        :param err: Standard error output, single or multiple lines
        :return: A message to be printed on console, or None
        """
        ret = ""
        errlines = err.split('\n')
        for errline in errlines:
            if len(errline) > 0:  # Ignore blanks
                if self.print_next_error_lines > 0:
                    ret += errline + "\n"
                    self.print_next_error_lines -= 1
                if self.print_next_error_file and "file:" in errline:
                    ret += errline + "\n"
                    self.print_next_error_file = False
                words = errline.split(' ',
                                      1)  # Split off first field for parallel
                FATAL = "--> FOAM FATAL ERROR"
                FATALIO = "--> FOAM FATAL IO ERROR"
                if errline.startswith(FATAL) or (len(words) > 1 and
                                                 words[1].startswith(FATAL)):
                    self.print_next_error_lines = 1
                    ret += "OpenFOAM fatal error:\n"
                elif errline.startswith(FATALIO) or (
                        len(words) > 1 and words[1].startswith(FATALIO)):
                    self.print_next_error_lines = 1
                    self.print_next_error_file = True
                    ret += "OpenFOAM IO error:\n"
        if len(ret) > 0:
            return ret
        else:
            return None
예제 #4
0
class LaunchGUI(QtGui.QDialog):
    def __init__(self, app):
        super(LaunchGUI, self).__init__()
        self.app = app
        self.init_gui_layout()

    def init_gui_layout(self):
        gui_fname = pkg_resources.resource_filename(
            'circus', os.path.join('qt_GUI', 'qt_launcher.ui'))
        self.ui = uic.loadUi(gui_fname)
        self.task_comboboxes = [
            cb for cb in self.ui.grp_tasks.children()
            if isinstance(cb, QCheckBox)
        ]

        logo = pkg_resources.resource_filename(
            'circus', os.path.join('icons', 'icon.png'))
        self.ui.setWindowIcon(QtGui.QIcon(logo))

        try:
            import cudamat as cmt
            cmt.init()
            self.HAVE_CUDA = True
        except Exception:
            self.HAVE_CUDA = False

        self.ui.btn_run.clicked.connect(self.run)
        self.ui.btn_plots.clicked.connect(self.open_plot_folder)
        self.ui.btn_param.clicked.connect(self.view_param)
        self.ui.btn_phy.clicked.connect(self.help_phy)
        self.ui.btn_matlab.clicked.connect(self.help_matlab)
        self.ui.btn_help_cpus.clicked.connect(self.help_cpus)
        self.ui.btn_help_gpus.clicked.connect(self.help_gpus)
        self.ui.btn_help_file_format.clicked.connect(self.help_file_format)
        self.ui.tabWidget.currentChanged.connect(self.changing_tab)
        self.ui.btn_stop.clicked.connect(self.stop)
        self.ui.btn_file.clicked.connect(self.update_data_file)
        self.ui.btn_about.clicked.connect(self.show_about)
        self.ui.rb_gui_matlab.clicked.connect(self.update_gui_command)
        self.ui.rb_gui_python.clicked.connect(self.update_gui_command)
        self.ui.btn_output.clicked.connect(self.update_output_file)
        self.ui.btn_hostfile.clicked.connect(self.update_host_file)
        self.ui.btn_log.clicked.connect(self.open_log_file)
        self.ui.cb_batch.toggled.connect(self.update_batch_mode)
        self.ui.cb_preview.toggled.connect(self.update_preview_mode)
        self.ui.cb_results.toggled.connect(self.update_results_mode)
        self.ui.cb_benchmarking.toggled.connect(self.update_benchmarking)
        self.ui.cb_merging.toggled.connect(self.update_extension)
        self.ui.cb_converting.toggled.connect(self.update_extension)
        self.update_benchmarking()
        self.update_extension()
        for cb in self.task_comboboxes:
            cb.toggled.connect(self.store_tasks)
            cb.toggled.connect(self.update_command)
        self.ui.edit_file.textChanged.connect(self.update_command)
        self.ui.edit_output.textChanged.connect(self.update_command)
        self.ui.edit_hostfile.textChanged.connect(self.update_command)
        self.ui.edit_extension.textChanged.connect(self.update_command)
        self.ui.gui_extension.textChanged.connect(self.update_gui_command)
        self.ui.spin_cpus.valueChanged.connect(self.update_command)
        if not self.HAVE_CUDA:
            self.ui.spin_gpus.setEnabled(False)
        self.ui.spin_gpus.valueChanged.connect(self.update_command)
        self.store_tasks()
        self.process = None
        self.ui.closeEvent = self.closeEvent
        self.last_log_file = None
        try:
            import sklearn
        except ImportError:
            self.ui.cb_validating.setEnabled(False)
        self.ui.show()

    def store_tasks(self):
        self.stored_tasks = [cb.isChecked() for cb in self.task_comboboxes]
        if not numpy.any(self.stored_tasks):
            self.ui.btn_run.setEnabled(False)
        elif str(self.ui.edit_file.text()) != '':
            self.ui.btn_run.setEnabled(True)
            self.ui.btn_plots.setEnabled(True)

    def restore_tasks(self):
        for cb, prev_state in zip(self.task_comboboxes, self.stored_tasks):
            cb.setEnabled(True)
            cb.setChecked(prev_state)

    def update_batch_mode(self):
        batch_mode = self.ui.cb_batch.isChecked()
        self.ui.spin_cpus.setEnabled(not batch_mode)
        self.ui.spin_gpus.setEnabled(not batch_mode)
        self.ui.edit_hostfile.setEnabled(not batch_mode)
        self.ui.btn_hostfile.setEnabled(not batch_mode)
        self.update_tasks()
        self.update_extension()
        self.update_benchmarking()
        if batch_mode:
            self.ui.spin_cpus.setEnabled(not batch_mode)
            self.ui.spin_gpus.setEnabled(not batch_mode)
            self.ui.edit_hostfile.setEnabled(not batch_mode)
            self.ui.btn_hostfile.setEnabled(not batch_mode)
            self.update_tasks()
            self.update_extension()
            self.update_benchmarking()
            self.update_command()
            self.ui.cb_preview.setChecked(False)
            self.ui.cb_results.setChecked(False)
            self.ui.lbl_file.setText('Command file')
        else:
            self.last_mode = None
            self.ui.lbl_file.setText('Data file')
        self.update_command()

    def changing_tab(self):
        if self.ui.tabWidget.currentIndex() == 0:
            self.update_command()
        elif self.ui.tabWidget.currentIndex() == 1:
            self.update_gui_command()

    def update_preview_mode(self):
        preview_mode = self.ui.cb_preview.isChecked()

        self.update_tasks()

        if preview_mode:
            self.ui.cb_batch.setChecked(False)
            self.ui.cb_results.setChecked(False)

        self.update_command()

    def update_results_mode(self):
        results_mode = self.ui.cb_results.isChecked()
        self.ui.spin_cpus.setEnabled(not results_mode)
        self.ui.spin_gpus.setEnabled(not results_mode)
        self.ui.edit_hostfile.setEnabled(not results_mode)
        self.ui.btn_hostfile.setEnabled(not results_mode)
        self.update_tasks()
        self.update_extension()
        self.update_benchmarking()
        self.update_command()
        if results_mode:
            self.ui.cb_batch.setChecked(False)
            self.ui.cb_preview.setChecked(False)

    def update_result_tab(self):
        if str(self.ui.edit_file.text()) != '':
            f_next, _ = os.path.splitext(str(self.ui.edit_file.text()))
            ft = os.path.basename(os.path.normpath(f_next))
            f_results = os.path.join(f_next, ft + '.result.hdf5')
            if os.path.exists(f_results):
                self.ui.selection_gui.setEnabled(True)
                self.ui.extension_gui.setEnabled(True)
        else:
            self.ui.selection_gui.setEnabled(False)
            self.ui.extension_gui.setEnabled(False)

    def update_extension(self):
        batch_mode = self.ui.cb_batch.isChecked()
        if (not batch_mode and (self.ui.cb_merging.isChecked()
                                or self.ui.cb_converting.isChecked())):
            self.ui.edit_extension.setEnabled(True)
        else:
            self.ui.edit_extension.setEnabled(False)

    def update_benchmarking(self):
        batch_mode = self.ui.cb_batch.isChecked()
        enable = not batch_mode and self.ui.cb_benchmarking.isChecked()
        self.ui.edit_output.setEnabled(enable)
        self.ui.btn_output.setEnabled(enable)
        self.ui.cmb_type.setEnabled(enable)
        self.update_command()

    def update_tasks(self):
        batch_mode = self.ui.cb_batch.isChecked()
        preview_mode = self.ui.cb_preview.isChecked()
        results_mode = self.ui.cb_results.isChecked()
        if batch_mode or results_mode:
            self.restore_tasks()
            for cb in self.task_comboboxes:
                cb.setEnabled(False)
        elif preview_mode:
            prev_stored_tasks = self.stored_tasks
            for cb in self.task_comboboxes:
                cb.setEnabled(False)
                cb.setChecked(False)
            self.ui.cb_filtering.setChecked(True)
            self.ui.cb_whitening.setChecked(True)
            self.stored_tasks = prev_stored_tasks
        else:  # We come back from batch or preview mode
            self.restore_tasks()
        self.update_command()

    def update_data_file(self):
        if self.ui.cb_batch.isChecked():
            title = 'Select file with list of commands'
        else:
            title = 'Select data file'
        fname = QFileDialog.getOpenFileName(self, title,
                                            self.ui.edit_file.text())
        if fname:
            self.ui.edit_file.setText(fname)

        if str(self.ui.edit_file.text()) != '':
            self.ui.btn_run.setEnabled(True)
            f_next, _ = os.path.splitext(str(self.ui.edit_file.text()))
            f_params = f_next + '.params'
            if os.path.exists(f_params):
                self.ui.btn_param.setEnabled(True)
                self.ui.btn_plots.setEnabled(True)
        else:
            self.ui.btn_run.setEnabled(False)

        if self.ui.tabWidget.currentIndex() == 0:
            self.update_command()
        elif self.ui.tabWidget.currentIndex() == 1:
            self.update_gui_command()

        self.update_result_tab()

    def update_host_file(self):
        fname = QFileDialog.getOpenFileName(self, 'Select MPI host file',
                                            self.ui.edit_hostfile.text())
        if fname:
            self.ui.edit_hostfile.setText(fname)

    def update_output_file(self):
        fname = QFileDialog.getSaveFileName(self, 'Output file name',
                                            self.ui.edit_output.text())
        if fname:
            self.ui.edit_output.setText(fname)

    def open_log_file(self):
        assert self.last_log_file is not None
        QDesktopServices.openUrl(QUrl(self.last_log_file))

    def gui_command_line_args(self):

        if self.ui.rb_gui_matlab.isChecked():
            args = ['circus-gui-matlab']
        elif self.ui.rb_gui_python.isChecked():
            args = ['circus-gui-python']

        fname = str(self.ui.edit_file.text()).strip()
        if fname:
            args.append(fname)

        extension = str(self.ui.gui_extension.text()).strip()

        if extension:
            args.extend(['--extension', extension])

        return args

    def command_line_args(self):
        batch_mode = self.ui.cb_batch.isChecked()
        preview_mode = self.ui.cb_preview.isChecked()
        results_mode = self.ui.cb_results.isChecked()

        args = ['spyking-circus']
        fname = str(self.ui.edit_file.text()).strip()
        if fname:
            args.append(fname)

        if batch_mode:
            args.append('--batch')
        elif preview_mode:
            args.append('--preview')
            if self.ui.spin_cpus.value() > 1:
                args.extend(['--cpu', str(self.ui.spin_cpus.value())])
            if self.ui.spin_gpus.value() > 0:
                args.extend(['--gpu', str(self.ui.spin_gpus.value())])
        elif results_mode:
            args.append('--result')
        else:
            tasks = []
            for cb in self.task_comboboxes:
                if cb.isChecked():
                    label = str(cb.text()).lower()
                    tasks.append(label)
            if len(tasks) > 0:
                args.extend(['--method', ','.join(tasks)])
            if self.ui.spin_cpus.value() > 1:
                args.extend(['--cpu', str(self.ui.spin_cpus.value())])
            if self.ui.spin_gpus.value() > 0:
                args.extend(['--gpu', str(self.ui.spin_gpus.value())])
            hostfile = str(self.ui.edit_hostfile.text()).strip()
            if hostfile:
                args.extend(['--hostfile', hostfile])
            if 'merging' in tasks or 'converting' in tasks:
                extension = str(self.ui.edit_extension.text()).strip()
                if extension:
                    args.extend(['--extension', extension])
            if 'benchmarking' in tasks:
                args.extend(['--output', str(self.ui.edit_output.text())])
                args.extend(['--type', str(self.ui.cmb_type.currentText())])
        return args

    def update_gui_command(self):
        args = ' '.join(self.gui_command_line_args())
        self.ui.edit_command.setPlainText(args)

    def update_command(self, text=None):
        args = ' '.join(self.command_line_args())
        self.ui.edit_command.setPlainText(args)

    def run(self):
        if self.ui.cb_batch.isChecked():
            self.last_log_file = None
        else:
            f_next, _ = os.path.splitext(str(self.ui.edit_file.text()))
            f_params = f_next + '.params'
            if not os.path.exists(f_params):
                self.create_params_file(f_params)
                self.ui.btn_param.setEnabled(True)
                return
            self.last_log_file = f_next + '.log'

        if self.ui.tabWidget.currentIndex() == 0:
            args = self.command_line_args()
        elif self.ui.tabWidget.currentIndex() == 1:
            args = self.gui_command_line_args()

        self.update_result_tab()

        # # Start process
        self.ui.edit_stdout.clear()
        format = self.ui.edit_stdout.currentCharFormat()
        format.setFontWeight(QFont.Normal)
        format.setForeground(QtCore.Qt.blue)
        self.ui.edit_stdout.setCurrentCharFormat(format)
        time_str = datetime.datetime.now().ctime()
        start_msg = '''\
                       Starting spyking circus at {time_str}.

                       Command line call:
                       {call}
                    '''.format(time_str=time_str, call=' '.join(args))
        self.ui.edit_stdout.appendPlainText(textwrap.dedent(start_msg))
        format.setForeground(QtCore.Qt.black)
        self.ui.edit_stdout.setCurrentCharFormat(format)
        self.ui.edit_stdout.appendPlainText('\n')

        self.process = QProcess(self)
        self.connect(self.process, SIGNAL('readyReadStandardOutput()'),
                     self.append_output)
        self.connect(self.process, SIGNAL('readyReadStandardError()'),
                     self.append_error)
        self.connect(self.process, SIGNAL('started()'), self.process_started)
        self.connect(self.process, SIGNAL('finished(int)'),
                     self.process_finished)
        self.connect(self.process, SIGNAL('error()'), self.process_errored)
        self._interrupted = False
        self.process.start(args[0], args[1:])

    def restore_gui(self):
        for widget, previous_state in self._previous_state:
            widget.setEnabled(previous_state)
        self.app.restoreOverrideCursor()

    def process_started(self):
        # Disable everything except for the stop button and the output area
        all_children = [
            obj for obj in self.ui.findChildren(QWidget)
            if isinstance(obj, (QCheckBox, QPushButton, QLineEdit))
        ]
        self._previous_state = [(obj, obj.isEnabled()) for obj in all_children]
        for obj in all_children:
            obj.setEnabled(False)
        self.ui.btn_stop.setEnabled(True)
        # If we let the user interact, this messes with the cursor we use to
        # support the progress bar display
        self.ui.edit_stdout.setTextInteractionFlags(Qt.NoTextInteraction)
        self.app.setOverrideCursor(Qt.WaitCursor)

    def process_finished(self, exit_code):
        format = self.ui.edit_stdout.currentCharFormat()
        format.setFontWeight(QFont.Bold)
        if exit_code == 0:
            if self._interrupted:
                color = QtCore.Qt.red
                msg = ('Process interrupted by user')
            else:
                color = QtCore.Qt.green
                msg = ('Process exited normally')
        else:
            color = QtCore.Qt.red
            msg = ('Process exited with exit code %d' % exit_code)
        format.setForeground(color)
        self.ui.edit_stdout.setCurrentCharFormat(format)
        self.ui.edit_stdout.appendPlainText(msg)
        self.restore_gui()
        self.ui.edit_stdout.setTextInteractionFlags(
            Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
        self.process = None
        self.ui.btn_log.setEnabled(self.last_log_file is not None
                                   and os.path.isfile(self.last_log_file))

    def process_errored(self):
        exit_code = self.process.exitCode()
        format = self.ui.edit_stdout.currentCharFormat()
        format.setFontWeight(QFont.Bold)
        format.setForeground(QtCore.Qt.red)
        self.ui.edit_stdout.setCurrentCharFormat(format)
        self.ui.edit_stdout.appendPlainText('Process exited with exit code' %
                                            exit_code)
        self.restore_gui()
        self.ui.edit_stdout.setTextInteractionFlags(
            Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)
        self.process = None

    def add_output_lines(self, lines):
        '''
        Add the output line by line to the text area, jumping back to the
        beginning of the line when we encounter a carriage return (to
        correctly display progress bars)
        '''
        cursor = self.ui.edit_stdout.textCursor()
        cursor.clearSelection()
        splitted_lines = lines.split('\n')
        for idx, line in enumerate(splitted_lines):
            if '\r' in line:
                chunks = line.split('\r')
                overwrite_text(cursor,
                               chunks[0])  # No going back for the first chunk
                for chunk in chunks[
                        1:]:  # Go back to start of line for each \r
                    cursor.movePosition(QTextCursor.StartOfLine)
                    overwrite_text(cursor, chunk)
            else:
                overwrite_text(cursor, line)

            # Take care to not introduce new newlines
            if '\n' in lines and (idx == 0 or idx != len(splitted_lines) - 1):
                cursor.movePosition(QTextCursor.EndOfLine)
                cursor.insertText('\n')
        self.ui.edit_stdout.setTextCursor(cursor)

    def append_output(self):
        if self.process is None:  # Can happen when manually interrupting
            return
        lines = strip_ansi_codes(to_str(self.process.readAllStandardOutput()))
        self.add_output_lines(lines)
        # We manually deal with keyboard input in the output
        if 'Export already made! Do you want to erase everything? (y)es / (n)o' in lines:
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Question)
            msg.setWindowTitle('Erase everything?')
            msg.setText(
                'Export already made! Do you want to erase everything?')
            msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            answer = msg.exec_()
            if answer == QMessageBox.Yes:
                answer_string = 'y'
            else:
                answer_string = 'n'
        elif 'Do you want SpyKING CIRCUS to export PCs? (a)ll / (s)ome / (n)o' in lines:
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Question)
            msg.setWindowTitle('Export PCs?')
            msg.setText('Do you want SpyKING CIRCUS to export PCs?')
            no_button = msg.addButton('No', QMessageBox.NoRole)
            some_button = msg.addButton('Some', QMessageBox.YesRole)
            all_button = msg.addButton('All', QMessageBox.YesRole)
            msg.exec_()
            if msg.clickedButton() == no_button:
                answer_string = 'n'
            elif msg.clickedButton() == some_button:
                answer_string = 's'
            elif msg.clickedButton() == all_button:
                answer_string = 'a'
            else:
                answer_string = 'n'
        else:
            answer_string = ''

        if answer_string:
            self.process.write(answer_string + '\n')
            self.add_output_lines(answer_string + '\n')

    def append_error(self):
        if self.process is None:  # Can happen when manually interrupting
            return
        lines = strip_ansi_codes(to_str(self.process.readAllStandardError()))
        self.add_output_lines(lines)

    def stop(self, force=False):
        if self.process is not None:

            if not force:
                msg = QMessageBox()
                msg.setIcon(QMessageBox.Warning)
                msg.setWindowTitle('Confirm process termination')
                msg.setText(
                    'This will terminate the running process. Are you sure '
                    'you want to do this?')
                msg.setInformativeText(
                    'Interrupting the process may leave partly '
                    'created files that cannot be used for '
                    'further analysis.')
                msg.addButton('Stop process', QMessageBox.YesRole)
                cancel_button = msg.addButton('Cancel', QMessageBox.NoRole)
                msg.setDefaultButton(cancel_button)
                msg.exec_()
                if msg.clickedButton() == cancel_button:
                    # Continue to run
                    return

            self._interrupted = True
            # Terminate child processes as well
            pid = int(self.process.pid())
            if sys.platform == 'win32' and pid != 0:
                # The returned value is not a PID but a pointer
                lp = ctypes.cast(pid, LPWinProcInfo)
                pid = lp.contents.dwProcessID

            if pid != 0:
                process = psutil.Process(pid)
                children = process.children(recursive=True)
                for proc in children:
                    proc.terminate()
                gone, alive = psutil.wait_procs(children, timeout=3)
                for proc in alive:
                    proc.kill()

                self.process.terminate()
                self.process = None

    def create_params_file(self, fname):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Question)
        msg.setText(
            "Parameter file %r not found, do you want SpyKING CIRCUS to "
            "create it for you?" % fname)
        msg.setWindowTitle("Generate parameter file?")
        msg.setInformativeText("This will create a parameter file from a "
                               "template file and open it in your system's "
                               "standard text editor. Fill properly before "
                               "launching the code. See the documentation "
                               "for details")
        msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        answer = msg.exec_()
        if answer == QMessageBox.Yes:
            user_path = os.path.join(os.path.expanduser('~'), 'spyking-circus')
            if os.path.exists(user_path + 'config.params'):
                config_file = os.path.abspath(user_path + 'config.params')
            else:
                config_file = os.path.abspath(
                    pkg_resources.resource_filename('circus', 'config.params'))
            shutil.copyfile(config_file, fname)
            QDesktopServices.openUrl(QUrl(fname))

    def view_param(self):
        f_next, _ = os.path.splitext(str(self.ui.edit_file.text()))
        f_params = f_next + '.params'
        QDesktopServices.openUrl(QUrl(f_params))

    def show_about(self):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Question)
        msg.setText("SpyKING CIRCUS v%s" % circus.__version__)
        msg.setWindowTitle("About")
        msg.setInformativeText("Documentation can be found at\n"
                               "http://spyking-circus.rtfd.org\n"
                               "\n"
                               "Open a browser to see the online help?")
        msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        msg.setDefaultButton(QMessageBox.No)
        answer = msg.exec_()
        if answer == QMessageBox.Yes:
            QDesktopServices.openUrl(QUrl("http://spyking-circus.rtfd.org"))

    def help_cpus(self):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Question)
        msg.setText("Setting the number of CPUs")
        msg.setWindowTitle("Number of CPUs")
        msg.setInformativeText("SpyKING CIRCUS can use several CPUs "
                               "either locally or on multiple machines "
                               "using MPI (see documentation) "
                               "\n"
                               "\n"
                               "You have %d local CPUs available" %
                               psutil.cpu_count())
        msg.setStandardButtons(QMessageBox.Close)
        msg.setDefaultButton(QMessageBox.Close)
        answer = msg.exec_()

    def help_gpus(self):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Question)
        msg.setText("Setting the number of GPUs")
        msg.setWindowTitle("Number of GPUs")
        if not self.HAVE_CUDA:
            info = "No GPUs are detected on your system"
        else:
            gpu_id = 0
            is_available = True
            while is_available:
                try:
                    cmt.cuda_set_device(gpu_id)
                    is_available = True
                except Exception:
                    is_available = False
            info = "%d GPU is detected on your system" % (gpu_id + 1)

        msg.setInformativeText("SpyKING CIRCUS can use several GPUs\n"
                               "either locally or on multiple machine\n"
                               "using MPI (see documentation)"
                               "\n"
                               "\n"
                               "%s" % info)
        msg.setStandardButtons(QMessageBox.Close)
        msg.setDefaultButton(QMessageBox.Close)
        answer = msg.exec_()

    def help_file_format(self):
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Question)
        msg.setText("Supported file formats")
        msg.setWindowTitle("File formats")

        msg.setInformativeText("\n".join(list_all_file_format()))
        msg.setStandardButtons(QMessageBox.Close)
        msg.setDefaultButton(QMessageBox.Close)
        answer = msg.exec_()

    def open_plot_folder(self):
        f_next, _ = os.path.splitext(str(self.ui.edit_file.text()))
        plot_folder = os.path.join(f_next, 'plots')
        QDesktopServices.openUrl(QUrl(plot_folder))

    def help_phy(self):
        QDesktopServices.openUrl(
            QUrl("https://github.com/kwikteam/phy-contrib"))

    def help_matlab(self):
        QDesktopServices.openUrl(
            QUrl("http://ch.mathworks.com/products/matlab/"))

    def closeEvent(self, event):
        if self.process is not None:
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Warning)
            msg.setWindowTitle('Confirm process interruption')
            msg.setText(
                'Closing the window will terminate the running process. '
                'Do you really want to exit?')
            msg.setInformativeText('Interrupting the process may leave partly '
                                   'created files that cannot be used for '
                                   'further analysis.')
            close_button = msg.addButton("Stop and close", QMessageBox.YesRole)
            cancel_button = msg.addButton("Cancel", QMessageBox.NoRole)
            msg.setDefaultButton(cancel_button)
            msg.exec_()
            if msg.clickedButton() == close_button:
                self.stop(force=True)
                super(LaunchGUI, self).closeEvent(event)
            else:
                event.ignore()