Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: third_party/pylint/gui.py

Issue 10447014: Add pylint to depot_tools. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Fix unittests. Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/pylint/epylint.py ('k') | third_party/pylint/interfaces.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 """Tkinker gui for pylint"""
2
3 import os
4 import sys
5 import re
6 import Queue
7 from threading import Thread
8 from Tkinter import (Tk, Frame, Listbox, Entry, Label, Button, Scrollbar,
9 Checkbutton, Radiobutton, IntVar, StringVar)
10 from Tkinter import (TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W,
11 HORIZONTAL, DISABLED, NORMAL, W, E)
12 from tkFileDialog import askopenfilename, askdirectory
13
14 import pylint.lint
15 from pylint.reporters.guireporter import GUIReporter
16
17 HOME = os.path.expanduser('~/')
18 HISTORY = '.pylint-gui-history'
19 COLORS = {'(I)':'lightblue',
20 '(C)':'blue', '(R)':'darkblue',
21 '(W)':'black', '(E)':'darkred',
22 '(F)':'red'}
23
24 class BasicStream:
25 '''
26 used in gui reporter instead of writing to stdout, it is written to
27 this stream and saved in contents
28 '''
29 def __init__(self, gui):
30 """init"""
31 self.curline = ""
32 self.gui = gui
33 self.contents = []
34 self.outdict = {}
35 self.currout = None
36 self.nextTitle = None
37
38 def write(self, text):
39 """write text to the stream"""
40 if re.match('^--+$', text.strip()) or re.match('^==+$', text.strip()):
41 if self.currout:
42 self.outdict[self.currout].remove(self.nextTitle)
43 self.outdict[self.currout].pop()
44 self.currout = self.nextTitle
45 self.outdict[self.currout] = ['']
46
47 if text.strip():
48 self.nextTitle = text.strip()
49
50 if text.startswith('\n'):
51 self.contents.append('')
52 if self.currout: self.outdict[self.currout].append('')
53 self.contents[-1] += text.strip('\n')
54 if self.currout: self.outdict[self.currout][-1] += text.strip('\n')
55 if text.endswith('\n') and text.strip():
56 self.contents.append('')
57 if self.currout: self.outdict[self.currout].append('')
58
59 def fix_contents(self):
60 """finalize what the contents of the dict should look like before output """
61 for item in self.outdict:
62 numEmpty = self.outdict[item].count('')
63 for i in range(numEmpty):
64 self.outdict[item].remove('')
65 if self.outdict[item]:
66 self.outdict[item].pop(0)
67
68 def output_contents(self):
69 """output contents of dict to the gui, and set the rating"""
70 self.fix_contents()
71 self.gui.tabs = self.outdict
72 try:
73 self.gui.rating.set(self.outdict['Global evaluation'][0])
74 except:
75 self.gui.rating.set('Error')
76 self.gui.refresh_results_window()
77
78 #reset stream variables for next run
79 self.contents = []
80 self.outdict = {}
81 self.currout = None
82 self.nextTitle = None
83
84
85 class LintGui:
86 """Build and control a window to interact with pylint"""
87
88 def __init__(self, root=None):
89 """init"""
90 self.root = root or Tk()
91 self.root.title('Pylint')
92 #reporter
93 self.reporter = None
94 #message queue for output from reporter
95 self.msg_queue = Queue.Queue()
96 self.msgs = []
97 self.filenames = []
98 self.rating = StringVar()
99 self.tabs = {}
100 self.report_stream = BasicStream(self)
101 #gui objects
102 self.lbMessages = None
103 self.showhistory = None
104 self.results = None
105 self.btnRun = None
106 self.information_box = None
107 self.convention_box = None
108 self.refactor_box = None
109 self.warning_box = None
110 self.error_box = None
111 self.fatal_box = None
112 self.txtModule = None
113 self.status = None
114 self.msg_type_dict = None
115 self.init_gui()
116
117 def init_gui(self):
118 """init helper"""
119 #setting up frames
120 top_frame = Frame(self.root)
121 mid_frame = Frame(self.root)
122 radio_frame = Frame(self.root)
123 res_frame = Frame(self.root)
124 msg_frame = Frame(self.root)
125 check_frame = Frame(self.root)
126 history_frame = Frame(self.root)
127 btn_frame = Frame(self.root)
128 rating_frame = Frame(self.root)
129 top_frame.pack(side=TOP, fill=X)
130 mid_frame.pack(side=TOP, fill=X)
131 history_frame.pack(side=TOP, fill=BOTH, expand=True)
132 radio_frame.pack(side=TOP, fill=BOTH, expand=True)
133 rating_frame.pack(side=TOP, fill=BOTH, expand=True)
134 res_frame.pack(side=TOP, fill=BOTH, expand=True)
135 check_frame.pack(side=TOP, fill=BOTH, expand=True)
136 msg_frame.pack(side=TOP, fill=BOTH, expand=True)
137 btn_frame.pack(side=TOP, fill=X)
138
139 #Message ListBox
140 rightscrollbar = Scrollbar(msg_frame)
141 rightscrollbar.pack(side=RIGHT, fill=Y)
142 bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL)
143 bottomscrollbar.pack(side=BOTTOM, fill=X)
144 self.lbMessages = Listbox(msg_frame,
145 yscrollcommand=rightscrollbar.set,
146 xscrollcommand=bottomscrollbar.set,
147 bg="white")
148 self.lbMessages.pack(expand=True, fill=BOTH)
149 rightscrollbar.config(command=self.lbMessages.yview)
150 bottomscrollbar.config(command=self.lbMessages.xview)
151
152 #History ListBoxes
153 rightscrollbar2 = Scrollbar(history_frame)
154 rightscrollbar2.pack(side=RIGHT, fill=Y)
155 bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL)
156 bottomscrollbar2.pack(side=BOTTOM, fill=X)
157 self.showhistory = Listbox(history_frame,
158 yscrollcommand=rightscrollbar2.set,
159 xscrollcommand=bottomscrollbar2.set,
160 bg="white")
161 self.showhistory.pack(expand=True, fill=BOTH)
162 rightscrollbar2.config(command=self.showhistory.yview)
163 bottomscrollbar2.config(command=self.showhistory.xview)
164 self.showhistory.bind('<Double-Button-1>', self.select_recent_file)
165 self.set_history_window()
166
167 #status bar
168 self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W)
169 self.status.pack(side=BOTTOM, fill=X)
170
171 #labels
172 self.lblRatingLabel = Label(rating_frame, text='Rating:')
173 self.lblRatingLabel.pack(side=LEFT)
174 self.lblRating = Label(rating_frame, textvariable=self.rating)
175 self.lblRating.pack(side=LEFT)
176 Label(mid_frame, text='Recently Used:').pack(side=LEFT)
177 Label(top_frame, text='Module or package').pack(side=LEFT)
178
179 #file textbox
180 self.txtModule = Entry(top_frame, background='white')
181 self.txtModule.bind('<Return>', self.run_lint)
182 self.txtModule.pack(side=LEFT, expand=True, fill=X)
183
184 #results box
185 rightscrollbar = Scrollbar(res_frame)
186 rightscrollbar.pack(side=RIGHT, fill=Y)
187 bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL)
188 bottomscrollbar.pack(side=BOTTOM, fill=X)
189 self.results = Listbox(res_frame,
190 yscrollcommand=rightscrollbar.set,
191 xscrollcommand=bottomscrollbar.set,
192 bg="white", font="Courier")
193 self.results.pack(expand=True, fill=BOTH, side=BOTTOM)
194 rightscrollbar.config(command=self.results.yview)
195 bottomscrollbar.config(command=self.results.xview)
196
197 #buttons
198 Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT)
199 Button(top_frame, text='Open Package',
200 command=(lambda : self.file_open(package=True))).pack(side=LEFT)
201
202 self.btnRun = Button(top_frame, text='Run', command=self.run_lint)
203 self.btnRun.pack(side=LEFT)
204 Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM)
205
206 #radio buttons
207 self.information_box = IntVar()
208 self.convention_box = IntVar()
209 self.refactor_box = IntVar()
210 self.warning_box = IntVar()
211 self.error_box = IntVar()
212 self.fatal_box = IntVar()
213 i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'],
214 variable=self.information_box, command=self.refresh_msg_ window)
215 c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'],
216 variable=self.convention_box, command=self.refresh_msg_w indow)
217 r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'],
218 variable=self.refactor_box, command=self.refresh_msg_win dow)
219 w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'],
220 variable=self.warning_box, command=self.refresh_msg_wind ow)
221 e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'],
222 variable=self.error_box, command=self.refresh_msg_window )
223 f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'],
224 variable=self.fatal_box, command=self.refresh_msg_window )
225 i.select()
226 c.select()
227 r.select()
228 w.select()
229 e.select()
230 f.select()
231 i.pack(side=LEFT)
232 c.pack(side=LEFT)
233 r.pack(side=LEFT)
234 w.pack(side=LEFT)
235 e.pack(side=LEFT)
236 f.pack(side=LEFT)
237
238 #check boxes
239 self.box = StringVar()
240 # XXX should be generated
241 report = Radiobutton(radio_frame, text="Report", variable=self.box,
242 value="Report", command=self.refresh_results_window )
243 rawMet = Radiobutton(radio_frame, text="Raw metrics", variable=self.box,
244 value="Raw metrics", command=self.refresh_results_w indow)
245 dup = Radiobutton(radio_frame, text="Duplication", variable=self.box,
246 value="Duplication", command=self.refresh_results_wind ow)
247 ext = Radiobutton(radio_frame, text="External dependencies",
248 variable=self.box, value="External dependencies",
249 command=self.refresh_results_window)
250 stat = Radiobutton(radio_frame, text="Statistics by type",
251 variable=self.box, value="Statistics by type",
252 command=self.refresh_results_window)
253 msgCat = Radiobutton(radio_frame, text="Messages by category",
254 variable=self.box, value="Messages by category",
255 command=self.refresh_results_window)
256 msg = Radiobutton(radio_frame, text="Messages", variable=self.box,
257 value="Messages", command=self.refresh_results_windo w)
258 report.select()
259 report.grid(column=0, row=0, sticky=W)
260 rawMet.grid(column=1, row=0, sticky=W)
261 dup.grid(column=2, row=0, sticky=W)
262 msg.grid(column=3, row=0, sticky=E)
263 stat.grid(column=0, row=1, sticky=W)
264 msgCat.grid(column=1, row=1, sticky=W)
265 ext.grid(column=2, row=1, columnspan=2, sticky=W)
266
267 #dictionary for check boxes and associated error term
268 self.msg_type_dict = {
269 'I' : lambda : self.information_box.get() == 1,
270 'C' : lambda : self.convention_box.get() == 1,
271 'R' : lambda : self.refactor_box.get() == 1,
272 'E' : lambda : self.error_box.get() == 1,
273 'W' : lambda : self.warning_box.get() == 1,
274 'F' : lambda : self.fatal_box.get() == 1
275 }
276 self.txtModule.focus_set()
277
278
279 def select_recent_file(self, event):
280 """adds the selected file in the history listbox to the Module box"""
281 if not self.showhistory.size():
282 return
283
284 selected = self.showhistory.curselection()
285 item = self.showhistory.get(selected)
286 #update module
287 self.txtModule.delete(0, END)
288 self.txtModule.insert(0, item)
289
290 def refresh_msg_window(self):
291 """refresh the message window with current output"""
292 #clear the window
293 self.lbMessages.delete(0, END)
294 for msg in self.msgs:
295 if (self.msg_type_dict.get(msg[0])()):
296 msg_str = self.convert_to_string(msg)
297 self.lbMessages.insert(END, msg_str)
298 fg_color = COLORS.get(msg_str[:3], 'black')
299 self.lbMessages.itemconfigure(END, fg=fg_color)
300
301 def refresh_results_window(self):
302 """refresh the results window with current output"""
303 #clear the window
304 self.results.delete(0, END)
305 try:
306 for res in self.tabs[self.box.get()]:
307 self.results.insert(END, res)
308 except:
309 pass
310
311 def convert_to_string(self, msg):
312 """make a string representation of a message"""
313 if (msg[2] != ""):
314 return "(" + msg[0] + ") " + msg[1] + "." + msg[2] + " [" + msg[3] + "]: " + msg[4]
315 else:
316 return "(" + msg[0] + ") " + msg[1] + " [" + msg[3] + "]: " + msg[4]
317
318 def process_incoming(self):
319 """process the incoming messages from running pylint"""
320 while self.msg_queue.qsize():
321 try:
322 msg = self.msg_queue.get(0)
323 if msg == "DONE":
324 self.report_stream.output_contents()
325 return False
326
327 #adding message to list of msgs
328 self.msgs.append(msg)
329
330 #displaying msg if message type is selected in check box
331 if (self.msg_type_dict.get(msg[0])()):
332 msg_str = self.convert_to_string(msg)
333 self.lbMessages.insert(END, msg_str)
334 fg_color = COLORS.get(msg_str[:3], 'black')
335 self.lbMessages.itemconfigure(END, fg=fg_color)
336
337 except Queue.Empty:
338 pass
339 return True
340
341 def periodic_call(self):
342 """determine when to unlock the run button"""
343 if self.process_incoming():
344 self.root.after(100, self.periodic_call)
345 else:
346 #enabling button so it can be run again
347 self.btnRun.config(state=NORMAL)
348
349 def mainloop(self):
350 """launch the mainloop of the application"""
351 self.root.mainloop()
352
353 def quit(self, _=None):
354 """quit the application"""
355 self.root.quit()
356
357 def halt(self):
358 """program halt placeholder"""
359 return
360
361 def file_open(self, package=False, _=None):
362 """launch a file browser"""
363 if not package:
364 filename = askopenfilename(parent=self.root, filetypes=[('pythonfile s', '*.py'),
365 ('allfiles', '*')], title='S elect Module')
366 else:
367 filename = askdirectory(title="Select A Folder", mustexist=1)
368
369 if filename == ():
370 return
371
372 self.txtModule.delete(0, END)
373 self.txtModule.insert(0, filename)
374
375 def update_filenames(self):
376 """update the list of recent filenames"""
377 filename = self.txtModule.get()
378 if not filename:
379 filename = os.getcwd()
380 if filename+'\n' in self.filenames:
381 index = self.filenames.index(filename+'\n')
382 self.filenames.pop(index)
383
384 #ensure only 10 most recent are stored
385 if len(self.filenames) == 10:
386 self.filenames.pop()
387 self.filenames.insert(0, filename+'\n')
388
389 def set_history_window(self):
390 """update the history window with info from the history file"""
391 #clear the window
392 self.showhistory.delete(0, END)
393 # keep the last 10 most recent files
394 try:
395 view_history = open(HOME+HISTORY, 'r')
396 for hist in view_history.readlines():
397 if not hist in self.filenames:
398 self.filenames.append(hist)
399 self.showhistory.insert(END, hist.split('\n')[0])
400 view_history.close()
401 except IOError:
402 # do nothing since history file will be created later
403 return
404
405 def run_lint(self, _=None):
406 """launches pylint"""
407 self.update_filenames()
408 self.root.configure(cursor='watch')
409 self.reporter = GUIReporter(self, output=self.report_stream)
410 module = self.txtModule.get()
411 if not module:
412 module = os.getcwd()
413
414 #cleaning up msgs and windows
415 self.msgs = []
416 self.lbMessages.delete(0, END)
417 self.tabs = {}
418 self.results.delete(0, END)
419 self.btnRun.config(state=DISABLED)
420
421 #setting up a worker thread to run pylint
422 worker = Thread(target=lint_thread, args=(module, self.reporter, self,))
423 self.periodic_call()
424 worker.start()
425
426 # Overwrite the .pylint-gui-history file with all the new recently added files
427 # in order from filenames but only save last 10 files
428 write_history = open(HOME+HISTORY, 'w')
429 write_history.writelines(self.filenames)
430 write_history.close()
431 self.set_history_window()
432
433 self.root.configure(cursor='')
434
435
436 def lint_thread(module, reporter, gui):
437 """thread for pylint"""
438 gui.status.text = "processing module(s)"
439 lint_obj = pylint.lint.Run(args=[module], reporter=reporter, exit=False)
440 gui.msg_queue.put("DONE")
441
442
443 def Run(args):
444 """launch pylint gui from args"""
445 if args:
446 print 'USAGE: pylint-gui\n launch a simple pylint gui using Tk'
447 return
448 gui = LintGui()
449 gui.mainloop()
450
451 if __name__ == '__main__':
452 Run(sys.argv[1:])
OLDNEW
« no previous file with comments | « third_party/pylint/epylint.py ('k') | third_party/pylint/interfaces.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698