1
2
3
4
5
6
7
8
9
10 """
11 Graphical interface to epydoc. This interface might be useful for
12 systems where it's inconvenient to use the command-line interface
13 (such as Windows). It supports many (but not all) of the features
14 that are supported by the command-line interface. It also supports
15 loading and saving of X{project files}, which store a set of related
16 modules, and the options that should be used to generate the
17 documentation for those modules.
18
19 Usage::
20 epydocgui [OPTIONS] [FILE.prj | MODULES...]
21
22 FILE.prj An epydoc GUI project file.
23 MODULES... A list of Python modules to document.
24 -V, --version Print the version of epydoc.
25 -h, -?, --help, --usage Display this usage message
26 --debug Do not suppress error messages
27
28 @todo: Use ini-style project files, rather than pickles (using the
29 same format as the CLI).
30 """
31 __docformat__ = 'epytext en'
32
33 import sys, os.path, re, glob
34 from Tkinter import *
35 from tkFileDialog import askopenfilename, asksaveasfilename
36 from thread import start_new_thread, exit_thread
37 from pickle import dump, load
38
39
40
41 try: from tkFileDialog import askdirectory
42 except: askdirectory = None
43
44
45 try: import ZODB
46 except: pass
47
48
49
50
51
52 DEBUG = 0
53
54
55 BG_COLOR='#e0e0e0'
56 ACTIVEBG_COLOR='#e0e0e0'
57 TEXT_COLOR='black'
58 ENTRYSELECT_COLOR = ACTIVEBG_COLOR
59 SELECT_COLOR = '#208070'
60 MESSAGE_COLOR = '#000060'
61 ERROR_COLOR = '#600000'
62 GUIERROR_COLOR = '#600000'
63 WARNING_COLOR = '#604000'
64 HEADER_COLOR = '#000000'
65
66
67 COLOR_CONFIG = {'background':BG_COLOR, 'highlightcolor': BG_COLOR,
68 'foreground':TEXT_COLOR, 'highlightbackground': BG_COLOR}
69 ENTRY_CONFIG = {'background':BG_COLOR, 'highlightcolor': BG_COLOR,
70 'foreground':TEXT_COLOR, 'highlightbackground': BG_COLOR,
71 'selectbackground': ENTRYSELECT_COLOR,
72 'selectforeground': TEXT_COLOR}
73 SB_CONFIG = {'troughcolor':BG_COLOR, 'activebackground':BG_COLOR,
74 'background':BG_COLOR, 'highlightbackground':BG_COLOR}
75 LISTBOX_CONFIG = {'highlightcolor': BG_COLOR, 'highlightbackground': BG_COLOR,
76 'foreground':TEXT_COLOR, 'selectforeground': TEXT_COLOR,
77 'selectbackground': ACTIVEBG_COLOR, 'background':BG_COLOR}
78 BUTTON_CONFIG = {'background':BG_COLOR, 'highlightthickness':0, 'padx':4,
79 'highlightbackground': BG_COLOR, 'foreground':TEXT_COLOR,
80 'highlightcolor': BG_COLOR, 'activeforeground': TEXT_COLOR,
81 'activebackground': ACTIVEBG_COLOR, 'pady':0}
82 CBUTTON_CONFIG = {'background':BG_COLOR, 'highlightthickness':0, 'padx':4,
83 'highlightbackground': BG_COLOR, 'foreground':TEXT_COLOR,
84 'highlightcolor': BG_COLOR, 'activeforeground': TEXT_COLOR,
85 'activebackground': ACTIVEBG_COLOR, 'pady':0,
86 'selectcolor': SELECT_COLOR}
87 SHOWMSG_CONFIG = CBUTTON_CONFIG.copy()
88 SHOWMSG_CONFIG['foreground'] = MESSAGE_COLOR
89 SHOWWRN_CONFIG = CBUTTON_CONFIG.copy()
90 SHOWWRN_CONFIG['foreground'] = WARNING_COLOR
91 SHOWERR_CONFIG = CBUTTON_CONFIG.copy()
92 SHOWERR_CONFIG['foreground'] = ERROR_COLOR
93
94
95 PROGRESS_HEIGHT = 16
96 PROGRESS_WIDTH = 200
97 PROGRESS_BG='#305060'
98 PROGRESS_COLOR1 = '#30c070'
99 PROGRESS_COLOR2 = '#60ffa0'
100 PROGRESS_COLOR3 = '#106030'
101
102
103 if sys.platform.lower().startswith('win'):
104 DX = 3; DY = 3
105 DH = 0; DW = 7
106 else:
107 DX = 1; DY = 1
108 DH = 1; DW = 3
109
110
111 IMPORT_PROGRESS = 0.1
112 BUILD_PROGRESS = 0.2
113 WRITE_PROGRESS = 1.0 - BUILD_PROGRESS - IMPORT_PROGRESS
114
115
116
117
118
119 UP_GIF = '''\
120 R0lGODlhCwAMALMAANnZ2QDMmQCZZgBmZgAAAAAzM////////wAAAAAAAAAAAAAAAAAAAAAAAAAA
121 AAAAACH5BAEAAAAALAAAAAALAAwAAAQjEMhJKxCW4gzCIJxXZIEwFGDlDadqsii1sq1U0nA64+ON
122 5xEAOw==
123 '''
124 DOWN_GIF = '''\
125 R0lGODlhCwAMALMAANnZ2QDMmQCZZgBmZgAAAAAzM////////wAAAAAAAAAAAAAAAAAAAAAAAAAA
126 AAAAACH5BAEAAAAALAAAAAALAAwAAAQmEIQxgLVUCsppsVPngVtXEFfIfWk5nBe4xuSL0tKLy/cu
127 7JffJQIAOw==
128 '''
129 LEFT_GIF='''\
130 R0lGODlhDAALAKIAANnZ2QDMmQCZZgBmZgAAAAAzM////////yH5BAEAAAAALAAAAAAMAAsAAAM4
131 CLocgaCrESiDoBshOAoAgBEyMzgAEIGCowsiOLoLgEBVOLoIqlSFo4OgC1RYM4Ogq1RYg6DLVJgA
132 Ow==
133 '''
134 RIGHT_GIF='''\
135 R0lGODlhDAALAKIAANnZ2QDMmQBmZgCZZgAzMwAAAP///////yH5BAEAAAAALAAAAAAMAAsAAAM5
136 GIGgyzIYgaCrIigTgaALIigyEQiqKLoTgaAoujuDgKJLVAgqIoJEBQAIIkKEhaArRFgIukqFoMsJ
137 ADs=
138 '''
139
140
141
142
143
144 from epydoc import log
145 from epydoc.util import wordwrap
147 _STAGES = [40, 7, 1, 3, 30, 1, 2, 100]
148
150 self._progress = progress
151 self._cancel = cancel
152 self.clear()
153
155 self._messages = []
156 self._n = 0
157 self._stage = 0
158 self._message_blocks = []
159
160 - def log(self, level, message):
161 message = wordwrap(str(message)).rstrip() + '\n'
162 if self._message_blocks:
163 self._message_blocks[-1][-1].append( (level, message) )
164 else:
165 self._messages.append( (level, message) )
166
168 self._message_blocks.append( (header, []) )
169
171 header, messages = self._message_blocks.pop()
172 if messages:
173 self._messages.append( ('uline', ' '*75+'\n') )
174 self.log('header', header)
175 self._messages += messages
176 self._messages.append( ('uline', ' '*75+'\n') )
177
179 self.log(log.INFO, header)
180 self._stage += 1
181
184
185 - def progress(self, percent, message=''):
186 if self._cancel[0]: exit_thread()
187 i = self._stage - 1
188 p = ((sum(self._STAGES[:i]) + percent*self._STAGES[i]) /
189 float(sum(self._STAGES)))
190 self._progress[0] = p
191
193 if self._n >= len(self._messages):
194 return None, None
195 else:
196 self._n += 1
197 return self._messages[self._n-1]
198
199
200
201
202
204 """
205 Create the documentation for C{modules}, using the options
206 specified by C{options}. C{document} is designed to be started in
207 its own thread by L{EpydocGUI._go}.
208
209 @param options: The options to use for generating documentation.
210 This includes keyword options that can be given to
211 L{docwriter.html.HTMLWriter}, as well as the option C{target}, which
212 controls where the output is written to.
213 @type options: C{dictionary}
214 """
215 from epydoc.docwriter.html import HTMLWriter
216 from epydoc.docbuilder import build_doc_index
217 import epydoc.docstringparser
218
219
220 docformat = options.get('docformat', 'epytext')
221 epydoc.docstringparser.DEFAULT_DOCFORMAT = docformat
222
223 try:
224 parse = options['introspect_or_parse'] in ('parse', 'both')
225 introspect = options['introspect_or_parse'] in ('introspect', 'both')
226 docindex = build_doc_index(options['modules'], parse, introspect)
227 html_writer = HTMLWriter(docindex, **options)
228 log.start_progress('Writing HTML docs to %r' % options['target'])
229 html_writer.write(options['target'])
230 log.end_progress()
231
232
233 log.warning('Finished!')
234 done[0] = 'done'
235
236 except SystemExit:
237
238 log.error('Cancelled!')
239 done[0] ='cancel'
240 raise
241 except Exception, e:
242
243 log.error('Internal error: %s' % e)
244 done[0] ='cancel'
245 raise
246 except:
247
248 log.error('Internal error!')
249 done[0] ='cancel'
250 raise
251
252
253
254
255
257 """
258 A graphical user interace to epydoc.
259 """
261 self._afterid = 0
262 self._progress = [None]
263 self._cancel = [0]
264 self._filename = None
265 self._init_dir = None
266
267
268
269
270
271
272
273 self._old_modules = sys.modules.keys()
274
275
276 self._root = Tk()
277 self._root['background']=BG_COLOR
278 self._root.bind('<Control-q>', self.destroy)
279 self._root.bind('<Alt-q>', self.destroy)
280 self._root.bind('<Alt-x>', self.destroy)
281 self._root.bind('<Control-x>', self.destroy)
282
283 self._root.title('Epydoc')
284 self._rootframe = Frame(self._root, background=BG_COLOR,
285 border=2, relief='raised')
286 self._rootframe.pack(expand=1, fill='both', padx=2, pady=2)
287
288
289
290 leftframe = Frame(self._rootframe, background=BG_COLOR)
291 leftframe.pack(expand=1, fill='both', side='left')
292 optsframe = Frame(self._rootframe, background=BG_COLOR)
293 mainframe = Frame(leftframe, background=BG_COLOR)
294 mainframe.pack(expand=1, fill='both', side='top')
295 ctrlframe = Frame(mainframe, background=BG_COLOR)
296 ctrlframe.pack(side="bottom", fill='x', expand=0)
297 msgsframe = Frame(leftframe, background=BG_COLOR)
298
299 self._optsframe = optsframe
300 self._msgsframe = msgsframe
301
302
303 self._init_menubar()
304 self._init_progress_bar(mainframe)
305 self._init_module_list(mainframe)
306 self._init_options(optsframe, ctrlframe)
307 self._init_messages(msgsframe, ctrlframe)
308 self._init_bindings()
309
310
311 self._logger = GUILogger(self._progress, self._cancel)
312 log.register_logger(self._logger)
313
314
315 self._messages_toggle()
316
317
318
319
321 menubar = Menu(self._root, borderwidth=2,
322 background=BG_COLOR,
323 activebackground=BG_COLOR)
324 filemenu = Menu(menubar, tearoff=0)
325 filemenu.add_command(label='New Project', underline=0,
326 command=self._new,
327 accelerator='Ctrl-n')
328 filemenu.add_command(label='Open Project', underline=0,
329 command=self._open,
330 accelerator='Ctrl-o')
331 filemenu.add_command(label='Save Project', underline=0,
332 command=self._save,
333 accelerator='Ctrl-s')
334 filemenu.add_command(label='Save As..', underline=5,
335 command=self._saveas,
336 accelerator='Ctrl-a')
337 filemenu.add_separator()
338 filemenu.add_command(label='Exit', underline=1,
339 command=self.destroy,
340 accelerator='Ctrl-x')
341 menubar.add_cascade(label='File', underline=0, menu=filemenu)
342 gomenu = Menu(menubar, tearoff=0)
343 gomenu.add_command(label='Run Epydoc', command=self._open,
344 underline=0, accelerator='Alt-g')
345 menubar.add_cascade(label='Run', menu=gomenu, underline=0)
346 self._root.config(menu=menubar)
347
349 mframe1 = Frame(mainframe, relief='groove', border=2,
350 background=BG_COLOR)
351 mframe1.pack(side="top", fill='both', expand=1, padx=4, pady=3)
352 l = Label(mframe1, text="Modules to document:",
353 justify='left', **COLOR_CONFIG)
354 l.pack(side='top', fill='none', anchor='nw', expand=0)
355 mframe2 = Frame(mframe1, background=BG_COLOR)
356 mframe2.pack(side="top", fill='both', expand=1)
357 mframe3 = Frame(mframe1, background=BG_COLOR)
358 mframe3.pack(side="bottom", fill='x', expand=0)
359 self._module_list = Listbox(mframe2, width=80, height=10,
360 selectmode='multiple',
361 **LISTBOX_CONFIG)
362 self._module_list.pack(side="left", fill='both', expand=1)
363 sb = Scrollbar(mframe2, orient='vertical',**SB_CONFIG)
364 sb['command']=self._module_list.yview
365 sb.pack(side='right', fill='y')
366 self._module_list.config(yscrollcommand=sb.set)
367 Label(mframe3, text="Add:", **COLOR_CONFIG).pack(side='left')
368 self._module_entry = Entry(mframe3, **ENTRY_CONFIG)
369 self._module_entry.pack(side='left', fill='x', expand=1)
370 self._module_entry.bind('<Return>', self._entry_module)
371 self._module_delete = Button(mframe3, text="Remove",
372 command=self._delete_module,
373 **BUTTON_CONFIG)
374 self._module_delete.pack(side='right', expand=0, padx=2)
375 self._module_browse = Button(mframe3, text="Browse",
376 command=self._browse_module,
377 **BUTTON_CONFIG)
378 self._module_browse.pack(side='right', expand=0, padx=2)
379
381 pframe1 = Frame(mainframe, background=BG_COLOR)
382 pframe1.pack(side="bottom", fill='x', expand=0)
383 self._go_button = Button(pframe1, width=4, text='Start',
384 underline=0, command=self._go,
385 **BUTTON_CONFIG)
386 self._go_button.pack(side='left', padx=4)
387 pframe2 = Frame(pframe1, relief='groove', border=2,
388 background=BG_COLOR)
389 pframe2.pack(side="top", fill='x', expand=1, padx=4, pady=3)
390 Label(pframe2, text='Progress:', **COLOR_CONFIG).pack(side='left')
391 H = self._H = PROGRESS_HEIGHT
392 W = self._W = PROGRESS_WIDTH
393 c = self._canvas = Canvas(pframe2, height=H+DH, width=W+DW,
394 background=PROGRESS_BG, border=0,
395 selectborderwidth=0, relief='sunken',
396 insertwidth=0, insertborderwidth=0,
397 highlightbackground=BG_COLOR)
398 self._canvas.pack(side='left', fill='x', expand=1, padx=4)
399 self._r2 = c.create_rectangle(0,0,0,0, outline=PROGRESS_COLOR2)
400 self._r3 = c.create_rectangle(0,0,0,0, outline=PROGRESS_COLOR3)
401 self._r1 = c.create_rectangle(0,0,0,0, fill=PROGRESS_COLOR1,
402 outline='')
403 self._canvas.bind('<Configure>', self._configure)
404
406 self._downImage = PhotoImage(master=self._root, data=DOWN_GIF)
407 self._upImage = PhotoImage(master=self._root, data=UP_GIF)
408
409
410 b1 = Button(ctrlframe, text="Messages", justify='center',
411 command=self._messages_toggle, underline=0,
412 highlightthickness=0, activebackground=BG_COLOR,
413 border=0, relief='flat', padx=2, pady=0, **COLOR_CONFIG)
414 b2 = Button(ctrlframe, image=self._downImage, relief='flat',
415 border=0, command=self._messages_toggle,
416 activebackground=BG_COLOR, **COLOR_CONFIG)
417 self._message_button = b2
418 self._messages_visible = 0
419 b2.pack(side="left")
420 b1.pack(side="left")
421
422 f = Frame(msgsframe, background=BG_COLOR)
423 f.pack(side='top', expand=1, fill='both')
424 messages = Text(f, width=80, height=10, **ENTRY_CONFIG)
425 messages['state'] = 'disabled'
426 messages.pack(fill='both', expand=1, side='left')
427 self._messages = messages
428
429
430 sb = Scrollbar(f, orient='vertical', **SB_CONFIG)
431 sb.pack(fill='y', side='right')
432 sb['command'] = messages.yview
433 messages['yscrollcommand'] = sb.set
434
435
436 messages.tag_config('error', foreground=ERROR_COLOR)
437 messages.tag_config('warning', foreground=WARNING_COLOR)
438 messages.tag_config('guierror', foreground=GUIERROR_COLOR)
439 messages.tag_config('message', foreground=MESSAGE_COLOR)
440 messages.tag_config('header', foreground=HEADER_COLOR)
441 messages.tag_config('uline', underline=1)
442
443
444 self._in_header = 0
445 self._last_tag = 'error'
446
447
448 buttons = Frame(msgsframe, background=BG_COLOR)
449 buttons.pack(side='bottom', fill='x')
450 self._show_errors = IntVar(self._root)
451 self._show_errors.set(1)
452 self._show_warnings = IntVar(self._root)
453 self._show_warnings.set(1)
454 self._show_messages = IntVar(self._root)
455 self._show_messages.set(0)
456 Checkbutton(buttons, text='Show Messages', var=self._show_messages,
457 command=self._update_msg_tags,
458 **SHOWMSG_CONFIG).pack(side='left')
459 Checkbutton(buttons, text='Show Warnings', var=self._show_warnings,
460 command=self._update_msg_tags,
461 **SHOWWRN_CONFIG).pack(side='left')
462 Checkbutton(buttons, text='Show Errors', var=self._show_errors,
463 command=self._update_msg_tags,
464 **SHOWERR_CONFIG).pack(side='left')
465 self._update_msg_tags()
466
477
479 self._leftImage=PhotoImage(master=self._root, data=LEFT_GIF)
480 self._rightImage=PhotoImage(master=self._root, data=RIGHT_GIF)
481
482
483 b1 = Button(ctrlframe, text="Options", justify='center',
484 border=0, relief='flat',
485 command=self._options_toggle, padx=2,
486 underline=0, pady=0, highlightthickness=0,
487 activebackground=BG_COLOR, **COLOR_CONFIG)
488 b2 = Button(ctrlframe, image=self._rightImage, relief='flat',
489 border=0, command=self._options_toggle,
490 activebackground=BG_COLOR, **COLOR_CONFIG)
491 self._option_button = b2
492 self._options_visible = 0
493 b2.pack(side="right")
494 b1.pack(side="right")
495
496 oframe2 = Frame(optsframe, relief='groove', border=2,
497 background=BG_COLOR)
498 oframe2.pack(side="right", fill='both',
499 expand=0, padx=4, pady=3, ipadx=4)
500
501 Label(oframe2, text="Project Options", font='helvetica -16',
502 **COLOR_CONFIG).pack(anchor='w')
503 oframe3 = Frame(oframe2, background=BG_COLOR)
504 oframe3.pack(fill='x')
505 oframe4 = Frame(oframe2, background=BG_COLOR)
506 oframe4.pack(fill='x')
507 oframe7 = Frame(oframe2, background=BG_COLOR)
508 oframe7.pack(fill='x')
509 div = Frame(oframe2, background=BG_COLOR, border=1, relief='sunk')
510 div.pack(ipady=1, fill='x', padx=4, pady=2)
511
512 Label(oframe2, text="Help File", font='helvetica -16',
513 **COLOR_CONFIG).pack(anchor='w')
514 oframe5 = Frame(oframe2, background=BG_COLOR)
515 oframe5.pack(fill='x')
516 div = Frame(oframe2, background=BG_COLOR, border=1, relief='sunk')
517 div.pack(ipady=1, fill='x', padx=4, pady=2)
518
519 Label(oframe2, text="CSS Stylesheet", font='helvetica -16',
520 **COLOR_CONFIG).pack(anchor='w')
521 oframe6 = Frame(oframe2, background=BG_COLOR)
522 oframe6.pack(fill='x')
523
524
525
526 row = 0
527 l = Label(oframe3, text="Project Name:", **COLOR_CONFIG)
528 l.grid(row=row, column=0, sticky='e')
529 self._name_entry = Entry(oframe3, **ENTRY_CONFIG)
530 self._name_entry.grid(row=row, column=1, sticky='ew', columnspan=3)
531
532
533 row += 1
534 l = Label(oframe3, text="Project URL:", **COLOR_CONFIG)
535 l.grid(row=row, column=0, sticky='e')
536 self._url_entry = Entry(oframe3, **ENTRY_CONFIG)
537 self._url_entry.grid(row=row, column=1, sticky='ew', columnspan=3)
538
539
540 row += 1
541 l = Label(oframe3, text="Output Directory:", **COLOR_CONFIG)
542 l.grid(row=row, column=0, sticky='e')
543 self._out_entry = Entry(oframe3, **ENTRY_CONFIG)
544 self._out_entry.grid(row=row, column=1, sticky='ew', columnspan=2)
545 self._out_browse = Button(oframe3, text="Browse",
546 command=self._browse_out,
547 **BUTTON_CONFIG)
548 self._out_browse.grid(row=row, column=3, sticky='ew', padx=2)
549
550
551
552 row = 0
553 self._frames_var = IntVar(self._root)
554 self._frames_var.set(1)
555 l = Label(oframe4, text="Generate a frame-based table of contents",
556 **COLOR_CONFIG)
557 l.grid(row=row, column=1, sticky='w')
558 cb = Checkbutton(oframe4, var=self._frames_var, **CBUTTON_CONFIG)
559 cb.grid(row=row, column=0, sticky='e')
560
561
562 row += 1
563 self._private_var = IntVar(self._root)
564 self._private_var.set(1)
565 l = Label(oframe4, text="Generate documentation for private objects",
566 **COLOR_CONFIG)
567 l.grid(row=row, column=1, sticky='w')
568 cb = Checkbutton(oframe4, var=self._private_var, **CBUTTON_CONFIG)
569 cb.grid(row=row, column=0, sticky='e')
570
571
572 row += 1
573 self._imports_var = IntVar(self._root)
574 self._imports_var.set(0)
575 l = Label(oframe4, text="List imported classes and functions",
576 **COLOR_CONFIG)
577 l.grid(row=row, column=1, sticky='w')
578 cb = Checkbutton(oframe4, var=self._imports_var, **CBUTTON_CONFIG)
579 cb.grid(row=row, column=0, sticky='e')
580
581
582
583 row += 1
584 l = Label(oframe7, text="Default Docformat:", **COLOR_CONFIG)
585 l.grid(row=row, column=0, sticky='e')
586 df_var = self._docformat_var = StringVar(self._root)
587 self._docformat_var.set('epytext')
588 b = Radiobutton(oframe7, var=df_var, text='Epytext',
589 value='epytext', **CBUTTON_CONFIG)
590 b.grid(row=row, column=1, sticky='w')
591 b = Radiobutton(oframe7, var=df_var, text='ReStructuredText',
592 value='restructuredtext', **CBUTTON_CONFIG)
593 b.grid(row=row, column=2, columnspan=2, sticky='w')
594 row += 1
595 b = Radiobutton(oframe7, var=df_var, text='Plaintext',
596 value='plaintext', **CBUTTON_CONFIG)
597 b.grid(row=row, column=1, sticky='w')
598 b = Radiobutton(oframe7, var=df_var, text='Javadoc',
599 value='javadoc', **CBUTTON_CONFIG)
600 b.grid(row=row, column=2, columnspan=2, sticky='w')
601 row += 1
602
603
604 Frame(oframe7, background=BG_COLOR).grid(row=row, column=1, pady=3)
605 row += 1
606
607
608 l = Label(oframe7, text="Inheritance Style:", **COLOR_CONFIG)
609 l.grid(row=row, column=0, sticky='e')
610 inh_var = self._inheritance_var = StringVar(self._root)
611 self._inheritance_var.set('grouped')
612 b = Radiobutton(oframe7, var=inh_var, text='Grouped',
613 value='grouped', **CBUTTON_CONFIG)
614 b.grid(row=row, column=1, sticky='w')
615 b = Radiobutton(oframe7, var=inh_var, text='Listed',
616 value='listed', **CBUTTON_CONFIG)
617 b.grid(row=row, column=2, sticky='w')
618 b = Radiobutton(oframe7, var=inh_var, text='Included',
619 value='included', **CBUTTON_CONFIG)
620 b.grid(row=row, column=3, sticky='w')
621 row += 1
622
623
624 Frame(oframe7, background=BG_COLOR).grid(row=row, column=1, pady=3)
625 row += 1
626
627
628 l = Label(oframe7, text="Get docs from:", **COLOR_CONFIG)
629 l.grid(row=row, column=0, sticky='e')
630 iop_var = self._introspect_or_parse_var = StringVar(self._root)
631 self._introspect_or_parse_var.set('both')
632 b = Radiobutton(oframe7, var=iop_var, text='Parsing',
633 value='parse', **CBUTTON_CONFIG)
634 b.grid(row=row, column=1, sticky='w')
635 b = Radiobutton(oframe7, var=iop_var, text='Introspecting',
636 value='introspect', **CBUTTON_CONFIG)
637 b.grid(row=row, column=2, sticky='w')
638 b = Radiobutton(oframe7, var=iop_var, text='Both',
639 value='both', **CBUTTON_CONFIG)
640 b.grid(row=row, column=3, sticky='w')
641 row += 1
642
643
644
645 row = 0
646 self._help_var = StringVar(self._root)
647 self._help_var.set('default')
648 b = Radiobutton(oframe5, var=self._help_var,
649 text='Default',
650 value='default', **CBUTTON_CONFIG)
651 b.grid(row=row, column=1, sticky='w')
652 row += 1
653 b = Radiobutton(oframe5, var=self._help_var,
654 text='Select File',
655 value='-other-', **CBUTTON_CONFIG)
656 b.grid(row=row, column=1, sticky='w')
657 self._help_entry = Entry(oframe5, **ENTRY_CONFIG)
658 self._help_entry.grid(row=row, column=2, sticky='ew')
659 self._help_browse = Button(oframe5, text='Browse',
660 command=self._browse_help,
661 **BUTTON_CONFIG)
662 self._help_browse.grid(row=row, column=3, sticky='ew', padx=2)
663
664 from epydoc.docwriter.html_css import STYLESHEETS
665 items = STYLESHEETS.items()
666 def _css_sort(css1, css2):
667 if css1[0] == 'default': return -1
668 elif css2[0] == 'default': return 1
669 else: return cmp(css1[0], css2[0])
670 items.sort(_css_sort)
671
672
673
674
675 row = 0
676
677
678
679
680 row += 1
681 css_var = self._css_var = StringVar(self._root)
682 css_var.set('default')
683
684
685 for (name, (sheet, descr)) in items:
686 b = Radiobutton(oframe6, var=css_var, value=name, **CBUTTON_CONFIG)
687 b.grid(row=row, column=0, sticky='e')
688
689
690
691 l = Label(oframe6, text=descr, **COLOR_CONFIG)
692 l.grid(row=row, column=1, sticky='w')
693 row += 1
694 b = Radiobutton(oframe6, var=css_var, value='-other-',
695 **CBUTTON_CONFIG)
696 b.grid(row=row, column=0, sticky='e')
697
698
699
700
701
702 self._css_entry = Entry(oframe6, **ENTRY_CONFIG)
703 self._css_entry.grid(row=row, column=1, sticky='ew')
704 self._css_browse = Button(oframe6, text="Browse",
705 command=self._browse_css,
706 **BUTTON_CONFIG)
707 self._css_browse.grid(row=row, column=2, sticky='ew', padx=2)
708
710 self._root.bind('<Delete>', self._delete_module)
711 self._root.bind('<Alt-o>', self._options_toggle)
712 self._root.bind('<Alt-m>', self._messages_toggle)
713 self._root.bind('<F5>', self._go)
714 self._root.bind('<Alt-s>', self._go)
715
716 self._root.bind('<Control-n>', self._new)
717 self._root.bind('<Control-o>', self._open)
718 self._root.bind('<Control-s>', self._save)
719 self._root.bind('<Control-a>', self._saveas)
720
722 if self._options_visible:
723 self._optsframe.forget()
724 self._option_button['image'] = self._rightImage
725 self._options_visible = 0
726 else:
727 self._optsframe.pack(fill='both', side='right')
728 self._option_button['image'] = self._leftImage
729 self._options_visible = 1
730
732 if self._messages_visible:
733 self._msgsframe.forget()
734 self._message_button['image'] = self._rightImage
735 self._messages_visible = 0
736 else:
737 self._msgsframe.pack(fill='both', side='bottom', expand=1)
738 self._message_button['image'] = self._leftImage
739 self._messages_visible = 1
740
743
745 selection = self._module_list.curselection()
746 if len(selection) != 1: return
747 self._module_list.delete(selection[0])
748
749 - def _entry_module(self, *e):
750 modules = [self._module_entry.get()]
751 if glob.has_magic(modules[0]):
752 modules = glob.glob(modules[0])
753 for name in modules:
754 self.add_module(name, check=1)
755 self._module_entry.delete(0, 'end')
756
758 title = 'Select a module for documentation'
759 ftypes = [('Python module', '.py'),
760 ('Python extension', '.so'),
761 ('All files', '*')]
762 filename = askopenfilename(filetypes=ftypes, title=title,
763 defaultextension='.py',
764 initialdir=self._init_dir)
765 if not filename: return
766 self._init_dir = os.path.dirname(filename)
767 self.add_module(filename, check=1)
768
770 title = 'Select a CSS stylesheet'
771 ftypes = [('CSS Stylesheet', '.css'), ('All files', '*')]
772 filename = askopenfilename(filetypes=ftypes, title=title,
773 defaultextension='.css')
774 if not filename: return
775 self._css_entry.delete(0, 'end')
776 self._css_entry.insert(0, filename)
777
779 title = 'Select a help file'
780 self._help_var.set('-other-')
781 ftypes = [('HTML file', '.html'), ('All files', '*')]
782 filename = askopenfilename(filetypes=ftypes, title=title,
783 defaultextension='.html')
784 if not filename: return
785 self._help_entry.delete(0, 'end')
786 self._help_entry.insert(0, filename)
787
789 ftypes = [('All files', '*')]
790 title = 'Choose the output directory'
791 if askdirectory is not None:
792 filename = askdirectory(mustexist=0, title=title)
793 if not filename: return
794 else:
795
796 filename = asksaveasfilename(filetypes=ftypes, title=title,
797 initialfile='--this directory--')
798 if not filename: return
799 (f1, f2) = os.path.split(filename)
800 if f2 == '--this directory--': filename = f1
801 self._out_entry.delete(0, 'end')
802 self._out_entry.insert(0, filename)
803
805 if self._root is None: return
806
807
808 for m in sys.modules.keys():
809 if m not in self._old_modules: del sys.modules[m]
810 self._root.destroy()
811 self._root = None
812
841
842 - def mainloop(self, *args, **kwargs):
843 self._root.mainloop(*args, **kwargs)
844
846 options = {}
847 options['modules'] = self._module_list.get(0, 'end')
848 options['prj_name'] = self._name_entry.get() or ''
849 options['prj_url'] = self._url_entry.get() or None
850 options['docformat'] = self._docformat_var.get()
851 options['inheritance'] = self._inheritance_var.get()
852 options['introspect_or_parse'] = self._introspect_or_parse_var.get()
853 options['target'] = self._out_entry.get() or 'html'
854 options['frames'] = self._frames_var.get()
855 options['private'] = self._private_var.get()
856 options['show_imports'] = self._imports_var.get()
857 if self._help_var.get() == '-other-':
858 options['help'] = self._help_entry.get() or None
859 else:
860 options['help'] = None
861 if self._css_var.get() == '-other-':
862 options['css'] = self._css_entry.get() or 'default'
863 else:
864 options['css'] = self._css_var.get() or 'default'
865
866
867
868
869 return options
870
872 if len(self._module_list.get(0,'end')) == 0:
873 self._root.bell()
874 return
875
876 if self._progress[0] != None:
877 self._cancel[0] = 1
878 return
879
880
881 opts = self._getopts()
882 self._progress[0] = 0.0
883 self._cancel[0] = 0
884 args = (opts, self._cancel, self._progress)
885
886
887 self._messages['state'] = 'normal'
888 self._messages.delete('0.0', 'end')
889 self._messages['state'] = 'disabled'
890 self._logger.clear()
891
892
893
894 for m in sys.modules.keys():
895 if m not in self._old_modules:
896 del sys.modules[m]
897
898
899
900
901 start_new_thread(document, args)
902
903
904 self._go_button['text'] = 'Stop'
905 self._afterid += 1
906 dt = 300
907 self._update(dt, self._afterid)
908
910 while 1:
911 level, data = self._logger.read()
912 if data is None: break
913 self._messages['state'] = 'normal'
914 if level == 'header':
915 self._messages.insert('end', data, 'header')
916 elif level == 'uline':
917 self._messages.insert('end', data, 'uline header')
918 elif level >= log.ERROR:
919 data= data.rstrip()+'\n\n'
920 self._messages.insert('end', data, 'guierror')
921 elif level >= log.DOCSTRING_WARNING:
922 data= data.rstrip()+'\n\n'
923 self._messages.insert('end', data, 'warning')
924 elif log >= log.INFO:
925 data= data.rstrip()+'\n\n'
926 self._messages.insert('end', data, 'message')
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949 self._messages['state'] = 'disabled'
950 self._messages.yview('end')
951
953 if self._root is None: return
954 if self._progress[0] is None: return
955 if id != self._afterid: return
956
957
958 self._update_messages()
959
960
961 if self._progress[0] == 'done': p = self._W + DX
962 elif self._progress[0] == 'cancel': p = -5
963 else: p = DX + self._W * self._progress[0]
964 self._canvas.coords(self._r1, DX+1, DY+1, p, self._H+1)
965 self._canvas.coords(self._r2, DX, DY, p-1, self._H)
966 self._canvas.coords(self._r3, DX+1, DY+1, p, self._H+1)
967
968
969 if self._progress[0] in ('done', 'cancel'):
970 if self._progress[0] == 'cancel': self._root.bell()
971 self._go_button['text'] = 'Start'
972 self._progress[0] = None
973 return
974
975 self._root.after(dt, self._update, dt, id)
976
977 - def _new(self, *e):
978 self._module_list.delete(0, 'end')
979 self._name_entry.delete(0, 'end')
980 self._url_entry.delete(0, 'end')
981 self._docformat_var.set('epytext')
982 self._inheritance_var.set('grouped')
983 self._introspect_or_parse_var.set('both')
984 self._out_entry.delete(0, 'end')
985 self._module_entry.delete(0, 'end')
986 self._css_entry.delete(0, 'end')
987 self._help_entry.delete(0, 'end')
988 self._frames_var.set(1)
989 self._private_var.set(1)
990 self._imports_var.set(0)
991 self._css_var.set('default')
992
993 self._help_var.set('default')
994 self._filename = None
995 self._init_dir = None
996
998 title = 'Open project'
999 ftypes = [('Project file', '.prj'),
1000 ('All files', '*')]
1001 filename = askopenfilename(filetypes=ftypes, title=title,
1002 defaultextension='.css')
1003 if not filename: return
1004 self.open(filename)
1005
1006 - def open(self, prjfile):
1007 from epydoc.docwriter.html_css import STYLESHEETS
1008 self._filename = prjfile
1009 try:
1010 opts = load(open(prjfile, 'r'))
1011
1012 modnames = list(opts.get('modules', []))
1013 modnames.sort()
1014 self._module_list.delete(0, 'end')
1015 for name in modnames:
1016 self.add_module(name)
1017 self._module_entry.delete(0, 'end')
1018
1019 self._name_entry.delete(0, 'end')
1020 if opts.get('prj_name'):
1021 self._name_entry.insert(0, opts['prj_name'])
1022
1023 self._url_entry.delete(0, 'end')
1024 if opts.get('prj_url'):
1025 self._url_entry.insert(0, opts['prj_url'])
1026
1027 self._docformat_var.set(opts.get('docformat', 'epytext'))
1028 self._inheritance_var.set(opts.get('inheritance', 'grouped'))
1029 self._introspect_or_parse_var.set(
1030 opts.get('introspect_or_parse', 'both'))
1031
1032 self._help_entry.delete(0, 'end')
1033 if opts.get('help') is None:
1034 self._help_var.set('default')
1035 else:
1036 self._help_var.set('-other-')
1037 self._help_entry.insert(0, opts.get('help'))
1038
1039 self._out_entry.delete(0, 'end')
1040 self._out_entry.insert(0, opts.get('target', 'html'))
1041
1042 self._frames_var.set(opts.get('frames', 1))
1043 self._private_var.set(opts.get('private', 1))
1044 self._imports_var.set(opts.get('show_imports', 0))
1045
1046 self._css_entry.delete(0, 'end')
1047 if opts.get('css', 'default') in STYLESHEETS.keys():
1048 self._css_var.set(opts.get('css', 'default'))
1049 else:
1050 self._css_var.set('-other-')
1051 self._css_entry.insert(0, opts.get('css', 'default'))
1052
1053
1054
1055
1056
1057
1058
1059 except Exception, e:
1060 log.error('Error opening %s: %s' % (prjfile, e))
1061 self._root.bell()
1062
1064 if self._filename is None: return self._saveas()
1065 try:
1066 opts = self._getopts()
1067 dump(opts, open(self._filename, 'w'))
1068 except Exception, e:
1069 if self._filename is None:
1070 log.error('Error saving: %s' % e)
1071 else:
1072 log.error('Error saving %s: %s' % (self._filename, e))
1073 self._root.bell()
1074
1076 title = 'Save project as'
1077 ftypes = [('Project file', '.prj'), ('All files', '*')]
1078 filename = asksaveasfilename(filetypes=ftypes, title=title,
1079 defaultextension='.prj')
1080 if not filename: return
1081 self._filename = filename
1082 self._save()
1083
1085 """
1086 Display the version information, and exit.
1087 @rtype: C{None}
1088 """
1089 import epydoc
1090 print "Epydoc version %s" % epydoc.__version__
1091 sys.exit(0)
1092
1093
1094
1095
1097 print
1098 print 'Usage: epydocgui [OPTIONS] [FILE.prj | MODULES...]'
1099 print
1100 print ' FILE.prj An epydoc GUI project file.'
1101 print ' MODULES... A list of Python modules to document.'
1102 print ' -V, --version Print the version of epydoc.'
1103 print ' -h, -?, --help, --usage Display this usage message'
1104 print ' --debug Do not suppress error messages'
1105 print
1106 sys.exit(0)
1107
1109 s = '%s; run "%s -h" for usage' % (s, os.path.basename(sys.argv[0]))
1110 if len(s) > 80:
1111 i = s.rfind(' ', 0, 80)
1112 if i>0: s = s[:i]+'\n'+s[i+1:]
1113 print >>sys.stderr, s
1114 sys.exit(1)
1115
1117 global DEBUG
1118 sys.stderr = sys.__stderr__
1119 projects = []
1120 modules = []
1121 for arg in sys.argv[1:]:
1122 if arg[0] == '-':
1123 arg = arg.lower()
1124 if arg in ('-h', '--help', '-?', '--usage'): _usage()
1125 elif arg in ('-V', '--version'): _version()
1126 elif arg in ('--debug',): DEBUG = 1
1127 else:
1128 _error('Unknown parameter %r' % arg)
1129 elif arg[-4:] == '.prj': projects.append(arg)
1130 else: modules.append(arg)
1131
1132 if len(projects) > 1:
1133 _error('Too many projects')
1134 if len(projects) == 1:
1135 if len(modules) > 0:
1136 _error('You must specify either a project or a list of modules')
1137 if not os.path.exists(projects[0]):
1138 _error('Cannot open project file %s' % projects[0])
1139 gui = EpydocGUI()
1140 gui.open(projects[0])
1141 gui.mainloop()
1142 else:
1143 gui = EpydocGUI()
1144 for module in modules: gui.add_module(module, check=1)
1145 gui.mainloop()
1146
1147 if __name__ == '__main__': gui()
1148