| OLD | NEW |
| 1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). | 1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). |
| 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr | 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 3 # | 3 # |
| 4 # This program is free software; you can redistribute it and/or modify it under | 4 # This program is free software; you can redistribute it and/or modify it under |
| 5 # the terms of the GNU General Public License as published by the Free Software | 5 # the terms of the GNU General Public License as published by the Free Software |
| 6 # Foundation; either version 2 of the License, or (at your option) any later | 6 # Foundation; either version 2 of the License, or (at your option) any later |
| 7 # version. | 7 # version. |
| 8 # | 8 # |
| 9 # This program is distributed in the hope that it will be useful, but WITHOUT | 9 # This program is distributed in the hope that it will be useful, but WITHOUT |
| 10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details | 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details |
| 12 # | 12 # |
| 13 # You should have received a copy of the GNU General Public License along with | 13 # You should have received a copy of the GNU General Public License along with |
| 14 # this program; if not, write to the Free Software Foundation, Inc., | 14 # this program; if not, write to the Free Software Foundation, Inc., |
| 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 16 """Tkinker gui for pylint""" | 16 """Tkinker gui for pylint""" |
| 17 from __future__ import print_function |
| 17 | 18 |
| 18 import os | 19 import os |
| 19 import sys | 20 import sys |
| 20 import re | 21 import re |
| 21 import Queue | |
| 22 from threading import Thread | 22 from threading import Thread |
| 23 from Tkinter import (Tk, Frame, Listbox, Entry, Label, Button, Scrollbar, | 23 |
| 24 Checkbutton, Radiobutton, IntVar, StringVar) | 24 import six |
| 25 from Tkinter import (TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W, | 25 |
| 26 HORIZONTAL, DISABLED, NORMAL, W) | 26 from six.moves.tkinter import ( |
| 27 from tkFileDialog import askopenfilename, askdirectory | 27 Tk, Frame, Listbox, Entry, Label, Button, Scrollbar, |
| 28 Checkbutton, Radiobutton, IntVar, StringVar, |
| 29 TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W, |
| 30 HORIZONTAL, DISABLED, NORMAL, W, |
| 31 ) |
| 32 from six.moves.tkinter_tkfiledialog import ( |
| 33 askopenfilename, askdirectory, |
| 34 ) |
| 28 | 35 |
| 29 import pylint.lint | 36 import pylint.lint |
| 30 from pylint.reporters.guireporter import GUIReporter | 37 from pylint.reporters.guireporter import GUIReporter |
| 31 | 38 |
| 32 HOME = os.path.expanduser('~/') | 39 HOME = os.path.expanduser('~/') |
| 33 HISTORY = '.pylint-gui-history' | 40 HISTORY = '.pylint-gui-history' |
| 34 COLORS = {'(I)':'lightblue', | 41 COLORS = {'(I)':'lightblue', |
| 35 '(C)':'blue', '(R)':'darkblue', | 42 '(C)':'blue', '(R)':'darkblue', |
| 36 '(W)':'black', '(E)':'darkred', | 43 '(W)':'black', '(E)':'darkred', |
| 37 '(F)':'red'} | 44 '(F)':'red'} |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 self.outdict[self.currout][-1] += text.strip(os.linesep) | 86 self.outdict[self.currout][-1] += text.strip(os.linesep) |
| 80 if text.endswith(os.linesep) and text.strip(): | 87 if text.endswith(os.linesep) and text.strip(): |
| 81 self.contents.append('') | 88 self.contents.append('') |
| 82 if self.currout: | 89 if self.currout: |
| 83 self.outdict[self.currout].append('') | 90 self.outdict[self.currout].append('') |
| 84 | 91 |
| 85 def fix_contents(self): | 92 def fix_contents(self): |
| 86 """finalize what the contents of the dict should look like before output
""" | 93 """finalize what the contents of the dict should look like before output
""" |
| 87 for item in self.outdict: | 94 for item in self.outdict: |
| 88 num_empty = self.outdict[item].count('') | 95 num_empty = self.outdict[item].count('') |
| 89 for _ in xrange(num_empty): | 96 for _ in range(num_empty): |
| 90 self.outdict[item].remove('') | 97 self.outdict[item].remove('') |
| 91 if self.outdict[item]: | 98 if self.outdict[item]: |
| 92 self.outdict[item].pop(0) | 99 self.outdict[item].pop(0) |
| 93 | 100 |
| 94 def output_contents(self): | 101 def output_contents(self): |
| 95 """output contents of dict to the gui, and set the rating""" | 102 """output contents of dict to the gui, and set the rating""" |
| 96 self.fix_contents() | 103 self.fix_contents() |
| 97 self.gui.tabs = self.outdict | 104 self.gui.tabs = self.outdict |
| 98 try: | 105 try: |
| 99 self.gui.rating.set(self.outdict['Global evaluation'][0]) | 106 self.gui.rating.set(self.outdict['Global evaluation'][0]) |
| 100 except: | 107 except KeyError: |
| 101 self.gui.rating.set('Error') | 108 self.gui.rating.set('Error') |
| 102 self.gui.refresh_results_window() | 109 self.gui.refresh_results_window() |
| 103 | 110 |
| 104 #reset stream variables for next run | 111 #reset stream variables for next run |
| 105 self.contents = [] | 112 self.contents = [] |
| 106 self.outdict = {} | 113 self.outdict = {} |
| 107 self.currout = None | 114 self.currout = None |
| 108 self.next_title = None | 115 self.next_title = None |
| 109 | 116 |
| 110 | 117 |
| 111 class LintGui(object): | 118 class LintGui(object): |
| 112 """Build and control a window to interact with pylint""" | 119 """Build and control a window to interact with pylint""" |
| 113 | 120 |
| 114 def __init__(self, root=None): | 121 def __init__(self, root=None): |
| 115 """init""" | 122 """init""" |
| 116 self.root = root or Tk() | 123 self.root = root or Tk() |
| 117 self.root.title('Pylint') | 124 self.root.title('Pylint') |
| 118 #reporter | 125 #reporter |
| 119 self.reporter = None | 126 self.reporter = None |
| 120 #message queue for output from reporter | 127 #message queue for output from reporter |
| 121 self.msg_queue = Queue.Queue() | 128 self.msg_queue = six.moves.queue.Queue() |
| 122 self.msgs = [] | 129 self.msgs = [] |
| 123 self.visible_msgs = [] | 130 self.visible_msgs = [] |
| 124 self.filenames = [] | 131 self.filenames = [] |
| 125 self.rating = StringVar() | 132 self.rating = StringVar() |
| 126 self.tabs = {} | 133 self.tabs = {} |
| 127 self.report_stream = BasicStream(self) | 134 self.report_stream = BasicStream(self) |
| 128 #gui objects | 135 #gui objects |
| 129 self.lb_messages = None | 136 self.lb_messages = None |
| 130 self.showhistory = None | 137 self.showhistory = None |
| 131 self.results = None | 138 self.results = None |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 314 'I': lambda: self.information_box.get() == 1, | 321 'I': lambda: self.information_box.get() == 1, |
| 315 'C': lambda: self.convention_box.get() == 1, | 322 'C': lambda: self.convention_box.get() == 1, |
| 316 'R': lambda: self.refactor_box.get() == 1, | 323 'R': lambda: self.refactor_box.get() == 1, |
| 317 'E': lambda: self.error_box.get() == 1, | 324 'E': lambda: self.error_box.get() == 1, |
| 318 'W': lambda: self.warning_box.get() == 1, | 325 'W': lambda: self.warning_box.get() == 1, |
| 319 'F': lambda: self.fatal_box.get() == 1 | 326 'F': lambda: self.fatal_box.get() == 1 |
| 320 } | 327 } |
| 321 self.txt_module.focus_set() | 328 self.txt_module.focus_set() |
| 322 | 329 |
| 323 | 330 |
| 324 def select_recent_file(self, event): | 331 def select_recent_file(self, event): # pylint: disable=unused-argument |
| 325 """adds the selected file in the history listbox to the Module box""" | 332 """adds the selected file in the history listbox to the Module box""" |
| 326 if not self.showhistory.size(): | 333 if not self.showhistory.size(): |
| 327 return | 334 return |
| 328 | 335 |
| 329 selected = self.showhistory.curselection() | 336 selected = self.showhistory.curselection() |
| 330 item = self.showhistory.get(selected) | 337 item = self.showhistory.get(selected) |
| 331 #update module | 338 #update module |
| 332 self.txt_module.delete(0, END) | 339 self.txt_module.delete(0, END) |
| 333 self.txt_module.insert(0, item) | 340 self.txt_module.insert(0, item) |
| 334 | 341 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 345 fg_color = COLORS.get(msg_str[:3], 'black') | 352 fg_color = COLORS.get(msg_str[:3], 'black') |
| 346 self.lb_messages.itemconfigure(END, fg=fg_color) | 353 self.lb_messages.itemconfigure(END, fg=fg_color) |
| 347 | 354 |
| 348 def refresh_results_window(self): | 355 def refresh_results_window(self): |
| 349 """refresh the results window with current output""" | 356 """refresh the results window with current output""" |
| 350 #clear the window | 357 #clear the window |
| 351 self.results.delete(0, END) | 358 self.results.delete(0, END) |
| 352 try: | 359 try: |
| 353 for res in self.tabs[self.box.get()]: | 360 for res in self.tabs[self.box.get()]: |
| 354 self.results.insert(END, res) | 361 self.results.insert(END, res) |
| 355 except: | 362 except KeyError: |
| 356 pass | 363 pass |
| 357 | 364 |
| 358 def process_incoming(self): | 365 def process_incoming(self): |
| 359 """process the incoming messages from running pylint""" | 366 """process the incoming messages from running pylint""" |
| 360 while self.msg_queue.qsize(): | 367 while self.msg_queue.qsize(): |
| 361 try: | 368 try: |
| 362 msg = self.msg_queue.get(0) | 369 msg = self.msg_queue.get(0) |
| 363 if msg == "DONE": | 370 if msg == "DONE": |
| 364 self.report_stream.output_contents() | 371 self.report_stream.output_contents() |
| 365 return False | 372 return False |
| 366 | 373 |
| 367 #adding message to list of msgs | 374 #adding message to list of msgs |
| 368 self.msgs.append(msg) | 375 self.msgs.append(msg) |
| 369 | 376 |
| 370 #displaying msg if message type is selected in check box | 377 #displaying msg if message type is selected in check box |
| 371 if self.msg_type_dict.get(msg.C)(): | 378 if self.msg_type_dict.get(msg.C)(): |
| 372 self.visible_msgs.append(msg) | 379 self.visible_msgs.append(msg) |
| 373 msg_str = convert_to_string(msg) | 380 msg_str = convert_to_string(msg) |
| 374 self.lb_messages.insert(END, msg_str) | 381 self.lb_messages.insert(END, msg_str) |
| 375 fg_color = COLORS.get(msg_str[:3], 'black') | 382 fg_color = COLORS.get(msg_str[:3], 'black') |
| 376 self.lb_messages.itemconfigure(END, fg=fg_color) | 383 self.lb_messages.itemconfigure(END, fg=fg_color) |
| 377 | 384 |
| 378 except Queue.Empty: | 385 except six.moves.queue.Empty: |
| 379 pass | 386 pass |
| 380 return True | 387 return True |
| 381 | 388 |
| 382 def periodic_call(self): | 389 def periodic_call(self): |
| 383 """determine when to unlock the run button""" | 390 """determine when to unlock the run button""" |
| 384 if self.process_incoming(): | 391 if self.process_incoming(): |
| 385 self.root.after(100, self.periodic_call) | 392 self.root.after(100, self.periodic_call) |
| 386 else: | 393 else: |
| 387 #enabling button so it can be run again | 394 #enabling button so it can be run again |
| 388 self.btnRun.config(state=NORMAL) | 395 self.btnRun.config(state=NORMAL) |
| 389 | 396 |
| 390 def mainloop(self): | 397 def mainloop(self): |
| 391 """launch the mainloop of the application""" | 398 """launch the mainloop of the application""" |
| 392 self.root.mainloop() | 399 self.root.mainloop() |
| 393 | 400 |
| 394 def quit(self, _=None): | 401 def quit(self, _=None): |
| 395 """quit the application""" | 402 """quit the application""" |
| 396 self.root.quit() | 403 self.root.quit() |
| 397 | 404 |
| 398 def halt(self): | 405 def halt(self): # pylint: disable=no-self-use |
| 399 """program halt placeholder""" | 406 """program halt placeholder""" |
| 400 return | 407 return |
| 401 | 408 |
| 402 def file_open(self, package=False, _=None): | 409 def file_open(self, package=False, _=None): |
| 403 """launch a file browser""" | 410 """launch a file browser""" |
| 404 if not package: | 411 if not package: |
| 405 filename = askopenfilename(parent=self.root, | 412 filename = askopenfilename(parent=self.root, |
| 406 filetypes=[('pythonfiles', '*.py'), | 413 filetypes=[('pythonfiles', '*.py'), |
| 407 ('allfiles', '*')], | 414 ('allfiles', '*')], |
| 408 title='Select Module') | 415 title='Select Module') |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 469 | 476 |
| 470 # Overwrite the .pylint-gui-history file with all the new recently added
files | 477 # Overwrite the .pylint-gui-history file with all the new recently added
files |
| 471 # in order from filenames but only save last 10 files | 478 # in order from filenames but only save last 10 files |
| 472 write_history = open(HOME+HISTORY, 'w') | 479 write_history = open(HOME+HISTORY, 'w') |
| 473 write_history.writelines(self.filenames) | 480 write_history.writelines(self.filenames) |
| 474 write_history.close() | 481 write_history.close() |
| 475 self.set_history_window() | 482 self.set_history_window() |
| 476 | 483 |
| 477 self.root.configure(cursor='') | 484 self.root.configure(cursor='') |
| 478 | 485 |
| 479 def show_sourcefile(self, event=None): | 486 def show_sourcefile(self, event=None): # pylint: disable=unused-argument |
| 480 selected = self.lb_messages.curselection() | 487 selected = self.lb_messages.curselection() |
| 481 if not selected: | 488 if not selected: |
| 482 return | 489 return |
| 483 | 490 |
| 484 msg = self.visible_msgs[int(selected[0])] | 491 msg = self.visible_msgs[int(selected[0])] |
| 485 scroll = msg.line - 3 | 492 scroll = msg.line - 3 |
| 486 if scroll < 0: | 493 if scroll < 0: |
| 487 scroll = 0 | 494 scroll = 0 |
| 488 | 495 |
| 489 self.tabs["Source File"] = open(msg.path, "r").readlines() | 496 self.tabs["Source File"] = open(msg.path, "r").readlines() |
| 490 self.box.set("Source File") | 497 self.box.set("Source File") |
| 491 self.refresh_results_window() | 498 self.refresh_results_window() |
| 492 self.results.yview(scroll) | 499 self.results.yview(scroll) |
| 493 self.results.select_set(msg.line - 1) | 500 self.results.select_set(msg.line - 1) |
| 494 | 501 |
| 495 | 502 |
| 496 def lint_thread(module, reporter, gui): | 503 def lint_thread(module, reporter, gui): |
| 497 """thread for pylint""" | 504 """thread for pylint""" |
| 498 gui.status.text = "processing module(s)" | 505 gui.status.text = "processing module(s)" |
| 499 pylint.lint.Run(args=[module], reporter=reporter, exit=False) | 506 pylint.lint.Run(args=[module], reporter=reporter, exit=False) |
| 500 gui.msg_queue.put("DONE") | 507 gui.msg_queue.put("DONE") |
| 501 | 508 |
| 502 | 509 |
| 503 def Run(args): | 510 def Run(args): |
| 504 """launch pylint gui from args""" | 511 """launch pylint gui from args""" |
| 505 if args: | 512 if args: |
| 506 print 'USAGE: pylint-gui\n launch a simple pylint gui using Tk' | 513 print('USAGE: pylint-gui\n launch a simple pylint gui using Tk') |
| 507 sys.exit(1) | 514 sys.exit(1) |
| 508 gui = LintGui() | 515 gui = LintGui() |
| 509 gui.mainloop() | 516 gui.mainloop() |
| 510 sys.exit(0) | 517 sys.exit(0) |
| 511 | 518 |
| 512 if __name__ == '__main__': | 519 if __name__ == '__main__': |
| 513 Run(sys.argv[1:]) | 520 Run(sys.argv[1:]) |
| OLD | NEW |