PyLint/PyLintExecDialog.py

changeset 0
1c1ac27f3cf1
child 4
2285b6fbf267
equal deleted inserted replaced
-1:000000000000 0:1c1ac27f3cf1
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2005 - 2011 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show the results of the PyLint run.
8 """
9
10 import os
11
12 from PyQt4.QtCore import *
13 from PyQt4.QtGui import *
14
15 from E5Gui import E5MessageBox, E5FileDialog
16 from E5Gui.E5Application import e5App
17
18 from .Ui_PyLintExecDialog import Ui_PyLintExecDialog
19
20 import Preferences
21 import Utilities
22
23
24 class PyLintExecDialog(QWidget, Ui_PyLintExecDialog):
25 """
26 Class implementing a dialog to show the results of the PyLint run.
27
28 This class starts a QProcess and displays a dialog that
29 shows the results of the PyLint command process.
30 """
31 def __init__(self, parent=None):
32 """
33 Constructor
34
35 @param parent parent widget of this dialog (QWidget)
36 """
37 QWidget.__init__(self, parent)
38 self.setupUi(self)
39
40 self.saveButton = self.buttonBox.addButton(
41 self.trUtf8("Save Report..."), QDialogButtonBox.ActionRole)
42 self.saveButton.setToolTip(
43 self.trUtf8("Press to save the report to a file"))
44 self.saveButton.setEnabled(False)
45
46 self.refreshButton = self.buttonBox.addButton(
47 self.trUtf8("Refresh"), QDialogButtonBox.ActionRole)
48 self.refreshButton.setToolTip(self.trUtf8("Press to refresh the result display"))
49 self.refreshButton.setEnabled(False)
50
51 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
52 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
53
54 self.messageList.header().setSortIndicator(0, Qt.AscendingOrder)
55
56 self.process = None
57 self.noResults = True
58 self.htmlOutput = False
59 self.parsedOutput = False
60 self.__scrollPosition = -1 # illegal value
61
62 self.typeDict = {
63 'C': self.trUtf8('Convention'),
64 'R': self.trUtf8('Refactor'),
65 'W': self.trUtf8('Warning'),
66 'E': self.trUtf8('Error'),
67 'F': self.trUtf8('Fatal'),
68 }
69
70 def start(self, args, fn, reportFile, ppath):
71 """
72 Public slot to start PyLint.
73
74 @param args commandline arguments for documentation programPyLint
75 (list of strings)
76 @param fn filename or dirname to be processed by PyLint (string)
77 @param reportFile filename of file to write the report to (string)
78 @param ppath project path (string)
79 @return flag indicating the successful start of the process (boolean)
80 """
81 self.errorGroup.hide()
82
83 self.args = args[:]
84 self.fn = fn
85 self.reportFile = reportFile
86 self.ppath = ppath
87
88 self.pathname = os.path.dirname(fn)
89 self.filename = os.path.basename(fn)
90
91 self.contents.clear()
92 self.errors.clear()
93 self.messageList.clear()
94
95 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
96 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True)
97 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
98 self.saveButton.setEnabled(False)
99 self.refreshButton.setEnabled(False)
100
101 program = args[0]
102 del args[0]
103 args.append(self.filename)
104
105 self.process = QProcess()
106 self.process.setWorkingDirectory(self.pathname)
107
108 self.process.readyReadStandardError.connect(self.__readStderr)
109 self.process.finished.connect(self.__finish)
110
111 self.__ioEncoding = Preferences.getSystem("IOEncoding")
112 if "--output-format=parseable" in args:
113 self.reportFile = None
114 self.contents.hide()
115 self.process.readyReadStandardOutput.connect(self.__readParseStdout)
116 self.parsedOutput = True
117 else:
118 self.process.readyReadStandardOutput.connect(self.__readStdout)
119 self.messageList.hide()
120 if "--output-format=html" in args:
121 self.contents.setAcceptRichText(True)
122 self.contents.setHtml('<b>Processing your request...</b>')
123 self.htmlOutput = True
124 else:
125 self.contents.setAcceptRichText(False)
126 self.contents.setCurrentFont(
127 Preferences.getEditorOtherFonts("MonospacedFont"))
128 self.htmlOutput = False
129 self.parsedOutput = False
130 self.noResults = True
131
132 self.buf = ""
133
134 self.process.start(program, args)
135 procStarted = self.process.waitForStarted()
136 if not procStarted:
137 E5MessageBox.critical(None,
138 self.trUtf8('Process Generation Error'),
139 self.trUtf8(
140 'The process {0} could not be started. '
141 'Ensure, that it is in the search path.'
142 ).format(program))
143 else:
144 self.setCursor(QCursor(Qt.WaitCursor))
145 return procStarted
146
147 def on_buttonBox_clicked(self, button):
148 """
149 Private slot called by a button of the button box clicked.
150
151 @param button button that was clicked (QAbstractButton)
152 """
153 if button == self.buttonBox.button(QDialogButtonBox.Close):
154 self.close()
155 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
156 self.__finish()
157 elif button == self.saveButton:
158 self.on_saveButton_clicked()
159 elif button == self.refreshButton:
160 self.on_refreshButton_clicked()
161
162 def __finish(self):
163 """
164 Private slot called when the process finished.
165
166 It is called when the process finished or
167 the user pressed the button.
168 """
169 self.unsetCursor()
170
171 if self.htmlOutput:
172 self.contents.setHtml(self.buf)
173 else:
174 cursor = self.contents.textCursor()
175 cursor.movePosition(QTextCursor.Start)
176 self.contents.setTextCursor(cursor)
177
178 if self.process is not None and \
179 self.process.state() != QProcess.NotRunning:
180 self.process.terminate()
181 QTimer.singleShot(2000, self.process.kill)
182 self.process.waitForFinished(3000)
183
184 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
185 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
186 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
187 self.refreshButton.setEnabled(True)
188 if self.parsedOutput:
189 QApplication.processEvents()
190 self.messageList.sortItems(self.messageList.sortColumn(),
191 self.messageList.header().sortIndicatorOrder())
192 self.messageList.header().resizeSections(QHeaderView.ResizeToContents)
193 self.messageList.header().setStretchLastSection(True)
194 else:
195 if self.__scrollPosition != -1:
196 self.contents.verticalScrollBar().setValue(self.__scrollPosition)
197
198 self.process = None
199
200 if self.reportFile:
201 self.__writeReport()
202 elif not self.parsedOutput:
203 self.saveButton.setEnabled(True)
204
205 if self.noResults:
206 self.__createItem(self.trUtf8('No PyLint errors found.'), "", "", "")
207
208 @pyqtSlot()
209 def on_refreshButton_clicked(self):
210 """
211 Private slot to refresh the status display.
212 """
213 self.__scrollPosition = self.contents.verticalScrollBar().value()
214 self.start(self.args, self.fn, self.reportFile, self.ppath)
215
216 def __readStdout(self):
217 """
218 Private slot to handle the readyReadStandardOutput signal.
219
220 It reads the output of the process, formats it and inserts it into
221 the contents pane.
222 """
223 self.process.setReadChannel(QProcess.StandardOutput)
224
225 while self.process.canReadLine():
226 s = self.process.readLine()
227 self.buf += s + os.linesep
228 if not self.htmlOutput:
229 self.contents.insertPlainText(s)
230 self.contents.ensureCursorVisible()
231
232 def __createItem(self, file, line, type_, message):
233 """
234 Private method to create an entry in the message list.
235
236 @param file filename of file (string)
237 @param line linenumber of message (integer or string)
238 @param type_ type of message (string)
239 @param message message text (string)
240 """
241 itm = QTreeWidgetItem(self.messageList, [
242 file, str(line), type_, message])
243 itm.setTextAlignment(1, Qt.AlignRight)
244 itm.setTextAlignment(2, Qt.AlignHCenter)
245
246 def __readParseStdout(self):
247 """
248 Private slot to handle the readyReadStandardOutput signal for parseable output.
249
250 It reads the output of the process, formats it and inserts it into
251 the message list pane.
252 """
253 self.process.setReadChannel(QProcess.StandardOutput)
254
255 while self.process.canReadLine():
256 s = str(self.process.readLine(), self.__ioEncoding, 'replace')
257 if s:
258 try:
259 if Utilities.isWindowsPlatform():
260 drive, s = os.path.splitdrive(s)
261 fname, lineno, fullmessage = s.split(':')
262 fname = drive + fname
263 else:
264 fname, lineno, fullmessage = s.split(':')
265 type_, message = fullmessage.strip().split(']', 1)
266 type_ = type_.strip()[1:].split(',', 1)[0]
267 message = message.strip()
268 if type_ and type_[0] in self.typeDict:
269 if len(type_) == 1:
270 self.__createItem(fname, lineno,
271 self.typeDict[type_], message)
272 else:
273 self.__createItem(fname, lineno,
274 "{0} {1}".format(
275 self.typeDict[type_[0]], type_[1:]),
276 message)
277 self.noResults = False
278 except ValueError:
279 continue
280
281 def __readStderr(self):
282 """
283 Private slot to handle the readyReadStandardError signal.
284
285 It reads the error output of the process and inserts it into the
286 error pane.
287 """
288 self.process.setReadChannel(QProcess.StandardError)
289
290 while self.process.canReadLine():
291 self.errorGroup.show()
292 s = str(self.process.readLine(), self.__ioEncoding, 'replace')
293 self.errors.insertPlainText(s)
294 self.errors.ensureCursorVisible()
295
296 def on_messageList_itemActivated(self, itm, column):
297 """
298 Private slot to handle the itemActivated signal of the message list.
299
300 @param itm The message item that was activated (QTreeWidgetItem)
301 @param column column the item was activated in (integer)
302 """
303 if self.noResults:
304 return
305
306 fn = os.path.join(self.pathname, itm.text(0))
307 lineno = int(itm.text(1))
308
309 e5App().getObject("ViewManager").openSourceFile(fn, lineno)
310
311 def __writeReport(self):
312 """
313 Private slot to write the report to a report file.
314 """
315 self.reportFile = self.reportFile
316 if os.path.exists(self.reportFile):
317 res = E5MessageBox.warning(self,
318 self.trUtf8("PyLint Report"),
319 self.trUtf8(
320 """<p>The PyLint report file <b>{0}</b> already exists.</p>""")\
321 .format(self.reportFile),
322 QMessageBox.StandardButtons(
323 QMessageBox.Cancel | \
324 QMessageBox.Ignore),
325 QMessageBox.Cancel)
326 if res == QMessageBox.Cancel:
327 return
328
329 try:
330 f = open(self.reportFile, 'w')
331 f.write(self.buf)
332 f.close()
333 except IOError as why:
334 E5MessageBox.critical(self, self.trUtf8('PyLint Report'),
335 self.trUtf8('<p>The PyLint report file <b>{0}</b> could not be written.'
336 '<br>Reason: {1}</p>')
337 .format(self.reportFile, str(why)))
338
339 @pyqtSlot()
340 def on_saveButton_clicked(self):
341 """
342 Private slot to save the report to a file.
343 """
344 if self.htmlOutput:
345 filter = self.trUtf8("HTML Files (*.html);;All Files (*)")
346 else:
347 filter = self.trUtf8("Text Files (*.txt);;All Files (*)")
348
349 self.reportFile = E5FileDialog.getSaveFileName(
350 self,
351 self.trUtf8("PyLint Report"),
352 self.ppath,
353 filter,
354 None,
355 QFileDialog.Options(QFileDialog.DontConfirmOverwrite))
356 if self.reportFile:
357 self.__writeReport()

eric ide

mercurial