1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2003 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing a dialog to show the output of the ericapi process.
8"""
9
10import os.path
11
12from PyQt5.QtCore import QProcess, QTimer
13from PyQt5.QtWidgets import QDialog, QDialogButtonBox
14
15from E5Gui import E5MessageBox
16
17from .Ui_EricapiExecDialog import Ui_EricapiExecDialog
18
19import Preferences
20
21
22class EricapiExecDialog(QDialog, Ui_EricapiExecDialog):
23    """
24    Class implementing a dialog to show the output of the ericapi process.
25
26    This class starts a QProcess and displays a dialog that
27    shows the output of the documentation command process.
28    """
29    def __init__(self, cmdname, parent=None):
30        """
31        Constructor
32
33        @param cmdname name of the ericapi generator (string)
34        @param parent parent widget of this dialog (QWidget)
35        """
36        super().__init__(parent)
37        self.setModal(True)
38        self.setupUi(self)
39
40        self.buttonBox.button(
41            QDialogButtonBox.StandardButton.Close).setEnabled(False)
42        self.buttonBox.button(
43            QDialogButtonBox.StandardButton.Cancel).setDefault(True)
44
45        self.process = None
46        self.cmdname = cmdname
47
48    def start(self, args, fn):
49        """
50        Public slot to start the ericapi command.
51
52        @param args commandline arguments for ericapi program (list of strings)
53        @param fn filename or dirname to be processed by ericapi program
54            (string)
55        @return flag indicating the successful start of the process (boolean)
56        """
57        self.errorGroup.hide()
58
59        self.filename = fn
60        if os.path.isdir(self.filename):
61            dname = os.path.abspath(self.filename)
62            fname = "."
63            if os.path.exists(os.path.join(dname, "__init__.py")):
64                fname = os.path.basename(dname)
65                dname = os.path.dirname(dname)
66        else:
67            dname = os.path.dirname(self.filename)
68            fname = os.path.basename(self.filename)
69
70        self.contents.clear()
71        self.errors.clear()
72
73        program = args[0]
74        del args[0]
75        args.append(fname)
76
77        self.process = QProcess()
78        self.process.setWorkingDirectory(dname)
79
80        self.process.readyReadStandardOutput.connect(self.__readStdout)
81        self.process.readyReadStandardError.connect(self.__readStderr)
82        self.process.finished.connect(self.__finish)
83
84        self.setWindowTitle(
85            self.tr('{0} - {1}').format(self.cmdname, self.filename))
86        self.process.start(program, args)
87        procStarted = self.process.waitForStarted(5000)
88        if not procStarted:
89            E5MessageBox.critical(
90                self,
91                self.tr('Process Generation Error'),
92                self.tr(
93                    'The process {0} could not be started. '
94                    'Ensure, that it is in the search path.'
95                ).format(program))
96        return procStarted
97
98    def on_buttonBox_clicked(self, button):
99        """
100        Private slot called by a button of the button box clicked.
101
102        @param button button that was clicked (QAbstractButton)
103        """
104        if button == self.buttonBox.button(
105            QDialogButtonBox.StandardButton.Close
106        ):
107            self.accept()
108        elif button == self.buttonBox.button(
109            QDialogButtonBox.StandardButton.Cancel
110        ):
111            self.__finish()
112
113    def __finish(self):
114        """
115        Private slot called when the process finished.
116
117        It is called when the process finished or
118        the user pressed the button.
119        """
120        if (
121            self.process is not None and
122            self.process.state() != QProcess.ProcessState.NotRunning
123        ):
124            self.process.terminate()
125            QTimer.singleShot(2000, self.process.kill)
126            self.process.waitForFinished(3000)
127
128        self.buttonBox.button(
129            QDialogButtonBox.StandardButton.Close).setEnabled(True)
130        self.buttonBox.button(
131            QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
132        self.buttonBox.button(
133            QDialogButtonBox.StandardButton.Close).setDefault(True)
134
135        self.process = None
136
137        self.contents.insertPlainText(
138            self.tr('\n{0} finished.\n').format(self.cmdname))
139        self.contents.ensureCursorVisible()
140
141    def __readStdout(self):
142        """
143        Private slot to handle the readyReadStandardOutput signal.
144
145        It reads the output of the process, formats it and inserts it into
146        the contents pane.
147        """
148        self.process.setReadChannel(QProcess.ProcessChannel.StandardOutput)
149
150        while self.process.canReadLine():
151            s = str(self.process.readLine(),
152                    Preferences.getSystem("IOEncoding"),
153                    'replace')
154            self.contents.insertPlainText(s)
155            self.contents.ensureCursorVisible()
156
157    def __readStderr(self):
158        """
159        Private slot to handle the readyReadStandardError signal.
160
161        It reads the error output of the process and inserts it into the
162        error pane.
163        """
164        self.process.setReadChannel(QProcess.ProcessChannel.StandardError)
165
166        while self.process.canReadLine():
167            self.errorGroup.show()
168            s = str(self.process.readLine(),
169                    Preferences.getSystem("IOEncoding"),
170                    'replace')
171            self.errors.insertPlainText(s)
172            self.errors.ensureCursorVisible()
173