blob: 52aa3999163975a742aa474757d2cad726723d3d [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001# changes by dscherer@cmu.edu
2# - created format and run menus
3# - added silly advice dialog (apologies to Douglas Adams)
4# - made Python Documentation work on Windows (requires win32api to
5# do a ShellExecute(); other ways of starting a web browser are awkward)
6
7import sys
8import os
9import string
10import re
11import imp
12from Tkinter import *
13import tkSimpleDialog
14import tkMessageBox
Kurt B. Kaiserfd182cd2001-07-14 03:58:25 +000015
16import webbrowser
David Scherer7aced172000-08-15 01:13:23 +000017import idlever
18import WindowList
19from IdleConf import idleconf
Steven M. Gavab9d07b52001-07-31 11:11:38 +000020import aboutDialog, textView
David Scherer7aced172000-08-15 01:13:23 +000021
22# The default tab setting for a Text widget, in average-width characters.
23TK_TABWIDTH_DEFAULT = 8
24
25# File menu
26
27#$ event <<open-module>>
28#$ win <Alt-m>
29#$ unix <Control-x><Control-m>
30
31#$ event <<open-class-browser>>
32#$ win <Alt-c>
33#$ unix <Control-x><Control-b>
34
35#$ event <<open-path-browser>>
36
37#$ event <<close-window>>
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +000038
David Scherer7aced172000-08-15 01:13:23 +000039#$ unix <Control-x><Control-0>
40#$ unix <Control-x><Key-0>
41#$ win <Alt-F4>
42
43# Edit menu
44
45#$ event <<Copy>>
46#$ win <Control-c>
47#$ unix <Alt-w>
48
49#$ event <<Cut>>
50#$ win <Control-x>
51#$ unix <Control-w>
52
53#$ event <<Paste>>
54#$ win <Control-v>
55#$ unix <Control-y>
56
57#$ event <<select-all>>
58#$ win <Alt-a>
59#$ unix <Alt-a>
60
61# Help menu
62
63#$ event <<help>>
64#$ win <F1>
65#$ unix <F1>
66
67#$ event <<about-idle>>
68
69# Events without menu entries
70
71#$ event <<remove-selection>>
72#$ win <Escape>
73
74#$ event <<center-insert>>
75#$ win <Control-l>
76#$ unix <Control-l>
77
78#$ event <<do-nothing>>
79#$ unix <Control-x>
80
David Scherer7aced172000-08-15 01:13:23 +000081class EditorWindow:
82
83 from Percolator import Percolator
84 from ColorDelegator import ColorDelegator
85 from UndoDelegator import UndoDelegator
86 from IOBinding import IOBinding
87 import Bindings
88 from Tkinter import Toplevel
89 from MultiStatusBar import MultiStatusBar
90
David Scherer7aced172000-08-15 01:13:23 +000091 vars = {}
92
93 def __init__(self, flist=None, filename=None, key=None, root=None):
94 edconf = idleconf.getsection('EditorWindow')
95 coconf = idleconf.getsection('Colors')
96 self.flist = flist
97 root = root or flist.root
98 self.root = root
99 if flist:
100 self.vars = flist.vars
101 self.menubar = Menu(root)
102 self.top = top = self.Toplevel(root, menu=self.menubar)
103 self.vbar = vbar = Scrollbar(top, name='vbar')
104 self.text_frame = text_frame = Frame(top)
105 self.text = text = Text(text_frame, name='text', padx=5,
106 foreground=coconf.getdef('normal-foreground'),
107 background=coconf.getdef('normal-background'),
108 highlightcolor=coconf.getdef('hilite-foreground'),
109 highlightbackground=coconf.getdef('hilite-background'),
110 insertbackground=coconf.getdef('cursor-background'),
111 width=edconf.getint('width'),
112 height=edconf.getint('height'),
113 wrap="none")
114
115 self.createmenubar()
116 self.apply_bindings()
117
118 self.top.protocol("WM_DELETE_WINDOW", self.close)
119 self.top.bind("<<close-window>>", self.close_event)
120 text.bind("<<center-insert>>", self.center_insert_event)
121 text.bind("<<help>>", self.help_dialog)
122 text.bind("<<good-advice>>", self.good_advice)
Steven M. Gavaabdfc412001-08-11 07:46:26 +0000123 text.bind("<<view-readme>>", self.view_readme)
David Scherer7aced172000-08-15 01:13:23 +0000124 text.bind("<<python-docs>>", self.python_docs)
125 text.bind("<<about-idle>>", self.about_dialog)
126 text.bind("<<open-module>>", self.open_module)
127 text.bind("<<do-nothing>>", lambda event: "break")
128 text.bind("<<select-all>>", self.select_all)
129 text.bind("<<remove-selection>>", self.remove_selection)
130 text.bind("<3>", self.right_menu_event)
131 if flist:
132 flist.inversedict[self] = key
133 if key:
134 flist.dict[key] = self
135 text.bind("<<open-new-window>>", self.flist.new_callback)
136 text.bind("<<close-all-windows>>", self.flist.close_all_callback)
137 text.bind("<<open-class-browser>>", self.open_class_browser)
138 text.bind("<<open-path-browser>>", self.open_path_browser)
139
140 vbar['command'] = text.yview
141 vbar.pack(side=RIGHT, fill=Y)
142
143 text['yscrollcommand'] = vbar.set
144 text['font'] = edconf.get('font-name'), edconf.get('font-size')
145 text_frame.pack(side=LEFT, fill=BOTH, expand=1)
146 text.pack(side=TOP, fill=BOTH, expand=1)
147 text.focus_set()
148
149 self.per = per = self.Percolator(text)
150 if self.ispythonsource(filename):
151 self.color = color = self.ColorDelegator(); per.insertfilter(color)
152 ##print "Initial colorizer"
153 else:
154 ##print "No initial colorizer"
155 self.color = None
156 self.undo = undo = self.UndoDelegator(); per.insertfilter(undo)
157 self.io = io = self.IOBinding(self)
158
159 text.undo_block_start = undo.undo_block_start
160 text.undo_block_stop = undo.undo_block_stop
161 undo.set_saved_change_hook(self.saved_change_hook)
162 io.set_filename_change_hook(self.filename_change_hook)
163
164 if filename:
165 if os.path.exists(filename):
166 io.loadfile(filename)
167 else:
168 io.set_filename(filename)
169
170 self.saved_change_hook()
171
172 self.load_extensions()
173
174 menu = self.menudict.get('windows')
175 if menu:
176 end = menu.index("end")
177 if end is None:
178 end = -1
179 if end >= 0:
180 menu.add_separator()
181 end = end + 1
182 self.wmenu_end = end
183 WindowList.register_callback(self.postwindowsmenu)
184
185 # Some abstractions so IDLE extensions are cross-IDE
186 self.askyesno = tkMessageBox.askyesno
187 self.askinteger = tkSimpleDialog.askinteger
188 self.showerror = tkMessageBox.showerror
189
190 if self.extensions.has_key('AutoIndent'):
191 self.extensions['AutoIndent'].set_indentation_params(
192 self.ispythonsource(filename))
193 self.set_status_bar()
194
195 def set_status_bar(self):
196 self.status_bar = self.MultiStatusBar(self.text_frame)
197 self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
198 self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
199 self.status_bar.pack(side=BOTTOM, fill=X)
200 self.text.bind('<KeyRelease>', self.set_line_and_column)
201 self.text.bind('<ButtonRelease>', self.set_line_and_column)
202 self.text.after_idle(self.set_line_and_column)
203
204 def set_line_and_column(self, event=None):
205 line, column = string.split(self.text.index(INSERT), '.')
206 self.status_bar.set_label('column', 'Col: %s' % column)
207 self.status_bar.set_label('line', 'Ln: %s' % line)
208
209 def wakeup(self):
210 if self.top.wm_state() == "iconic":
211 self.top.wm_deiconify()
212 else:
213 self.top.tkraise()
214 self.text.focus_set()
215
216 menu_specs = [
217 ("file", "_File"),
218 ("edit", "_Edit"),
219 ("format", "F_ormat"),
220 ("run", "_Run"),
221 ("windows", "_Windows"),
222 ("help", "_Help"),
223 ]
224
225 def createmenubar(self):
226 mbar = self.menubar
227 self.menudict = menudict = {}
228 for name, label in self.menu_specs:
229 underline, label = prepstr(label)
230 menudict[name] = menu = Menu(mbar, name=name)
231 mbar.add_cascade(label=label, menu=menu, underline=underline)
232 self.fill_menus()
233
234 def postwindowsmenu(self):
235 # Only called when Windows menu exists
236 # XXX Actually, this Just-In-Time updating interferes badly
237 # XXX with the tear-off feature. It would be better to update
238 # XXX all Windows menus whenever the list of windows changes.
239 menu = self.menudict['windows']
240 end = menu.index("end")
241 if end is None:
242 end = -1
243 if end > self.wmenu_end:
244 menu.delete(self.wmenu_end+1, end)
245 WindowList.add_windows_to_menu(menu)
246
247 rmenu = None
248
249 def right_menu_event(self, event):
250 self.text.tag_remove("sel", "1.0", "end")
251 self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
252 if not self.rmenu:
253 self.make_rmenu()
254 rmenu = self.rmenu
255 self.event = event
256 iswin = sys.platform[:3] == 'win'
257 if iswin:
258 self.text.config(cursor="arrow")
259 rmenu.tk_popup(event.x_root, event.y_root)
260 if iswin:
261 self.text.config(cursor="ibeam")
262
263 rmenu_specs = [
264 # ("Label", "<<virtual-event>>"), ...
265 ("Close", "<<close-window>>"), # Example
266 ]
267
268 def make_rmenu(self):
269 rmenu = Menu(self.text, tearoff=0)
270 for label, eventname in self.rmenu_specs:
271 def command(text=self.text, eventname=eventname):
272 text.event_generate(eventname)
273 rmenu.add_command(label=label, command=command)
274 self.rmenu = rmenu
275
276 def about_dialog(self, event=None):
Steven M. Gava7d9ed722001-07-31 07:01:47 +0000277 aboutDialog.AboutDialog(self.top,'About IDLEfork')
278
David Scherer7aced172000-08-15 01:13:23 +0000279 def good_advice(self, event=None):
280 tkMessageBox.showinfo('Advice', "Don't Panic!", master=self.text)
281
Steven M. Gavaabdfc412001-08-11 07:46:26 +0000282 def view_readme(self, event=None):
283 fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'README.txt')
284 textView.TextViewer(self.top,'IDLEfork - README',fn)
285
David Scherer7aced172000-08-15 01:13:23 +0000286 def help_dialog(self, event=None):
Steven M. Gavab9d07b52001-07-31 11:11:38 +0000287 fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
288 textView.TextViewer(self.top,'Help',fn)
289
David Scherer7aced172000-08-15 01:13:23 +0000290 help_url = "http://www.python.org/doc/current/"
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +0000291 if sys.platform[:3] == "win":
292 fn = os.path.dirname(__file__)
Kurt B. Kaiserfd182cd2001-07-14 03:58:25 +0000293 fn = os.path.join(fn, os.pardir, os.pardir, "Doc", "index.html")
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +0000294 fn = os.path.normpath(fn)
295 if os.path.isfile(fn):
296 help_url = fn
297 del fn
David Scherer7aced172000-08-15 01:13:23 +0000298
299 def python_docs(self, event=None):
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +0000300 webbrowser.open(self.help_url)
David Scherer7aced172000-08-15 01:13:23 +0000301
302 def select_all(self, event=None):
303 self.text.tag_add("sel", "1.0", "end-1c")
304 self.text.mark_set("insert", "1.0")
305 self.text.see("insert")
306 return "break"
307
308 def remove_selection(self, event=None):
309 self.text.tag_remove("sel", "1.0", "end")
310 self.text.see("insert")
311
312 def open_module(self, event=None):
313 # XXX Shouldn't this be in IOBinding or in FileList?
314 try:
315 name = self.text.get("sel.first", "sel.last")
316 except TclError:
317 name = ""
318 else:
319 name = string.strip(name)
320 if not name:
321 name = tkSimpleDialog.askstring("Module",
322 "Enter the name of a Python module\n"
323 "to search on sys.path and open:",
324 parent=self.text)
325 if name:
326 name = string.strip(name)
327 if not name:
328 return
329 # XXX Ought to support package syntax
330 # XXX Ought to insert current file's directory in front of path
331 try:
332 (f, file, (suffix, mode, type)) = imp.find_module(name)
333 except (NameError, ImportError), msg:
334 tkMessageBox.showerror("Import error", str(msg), parent=self.text)
335 return
336 if type != imp.PY_SOURCE:
337 tkMessageBox.showerror("Unsupported type",
338 "%s is not a source module" % name, parent=self.text)
339 return
340 if f:
341 f.close()
342 if self.flist:
343 self.flist.open(file)
344 else:
345 self.io.loadfile(file)
346
347 def open_class_browser(self, event=None):
348 filename = self.io.filename
349 if not filename:
350 tkMessageBox.showerror(
351 "No filename",
352 "This buffer has no associated filename",
353 master=self.text)
354 self.text.focus_set()
355 return None
356 head, tail = os.path.split(filename)
357 base, ext = os.path.splitext(tail)
358 import ClassBrowser
359 ClassBrowser.ClassBrowser(self.flist, base, [head])
360
361 def open_path_browser(self, event=None):
362 import PathBrowser
363 PathBrowser.PathBrowser(self.flist)
364
365 def gotoline(self, lineno):
366 if lineno is not None and lineno > 0:
367 self.text.mark_set("insert", "%d.0" % lineno)
368 self.text.tag_remove("sel", "1.0", "end")
369 self.text.tag_add("sel", "insert", "insert +1l")
370 self.center()
371
372 def ispythonsource(self, filename):
373 if not filename:
374 return 1
375 base, ext = os.path.splitext(os.path.basename(filename))
376 if os.path.normcase(ext) in (".py", ".pyw"):
377 return 1
378 try:
379 f = open(filename)
380 line = f.readline()
381 f.close()
382 except IOError:
383 return 0
384 return line[:2] == '#!' and string.find(line, 'python') >= 0
385
386 def close_hook(self):
387 if self.flist:
388 self.flist.close_edit(self)
389
390 def set_close_hook(self, close_hook):
391 self.close_hook = close_hook
392
393 def filename_change_hook(self):
394 if self.flist:
395 self.flist.filename_changed_edit(self)
396 self.saved_change_hook()
397 if self.ispythonsource(self.io.filename):
398 self.addcolorizer()
399 else:
400 self.rmcolorizer()
401
402 def addcolorizer(self):
403 if self.color:
404 return
405 ##print "Add colorizer"
406 self.per.removefilter(self.undo)
407 self.color = self.ColorDelegator()
408 self.per.insertfilter(self.color)
409 self.per.insertfilter(self.undo)
410
411 def rmcolorizer(self):
412 if not self.color:
413 return
414 ##print "Remove colorizer"
415 self.per.removefilter(self.undo)
416 self.per.removefilter(self.color)
417 self.color = None
418 self.per.insertfilter(self.undo)
419
420 def saved_change_hook(self):
421 short = self.short_title()
422 long = self.long_title()
423 if short and long:
424 title = short + " - " + long
425 elif short:
426 title = short
427 elif long:
428 title = long
429 else:
430 title = "Untitled"
431 icon = short or long or title
432 if not self.get_saved():
433 title = "*%s*" % title
434 icon = "*%s" % icon
435 self.top.wm_title(title)
436 self.top.wm_iconname(icon)
437
438 def get_saved(self):
439 return self.undo.get_saved()
440
441 def set_saved(self, flag):
442 self.undo.set_saved(flag)
443
444 def reset_undo(self):
445 self.undo.reset_undo()
446
447 def short_title(self):
448 filename = self.io.filename
449 if filename:
450 filename = os.path.basename(filename)
451 return filename
452
453 def long_title(self):
454 return self.io.filename or ""
455
456 def center_insert_event(self, event):
457 self.center()
458
459 def center(self, mark="insert"):
460 text = self.text
461 top, bot = self.getwindowlines()
462 lineno = self.getlineno(mark)
463 height = bot - top
464 newtop = max(1, lineno - height/2)
465 text.yview(float(newtop))
466
467 def getwindowlines(self):
468 text = self.text
469 top = self.getlineno("@0,0")
470 bot = self.getlineno("@0,65535")
471 if top == bot and text.winfo_height() == 1:
472 # Geometry manager hasn't run yet
473 height = int(text['height'])
474 bot = top + height - 1
475 return top, bot
476
477 def getlineno(self, mark="insert"):
478 text = self.text
479 return int(float(text.index(mark)))
480
481 def close_event(self, event):
482 self.close()
483
484 def maybesave(self):
485 if self.io:
486 return self.io.maybesave()
487
488 def close(self):
489 self.top.wm_deiconify()
490 self.top.tkraise()
491 reply = self.maybesave()
492 if reply != "cancel":
493 self._close()
494 return reply
495
496 def _close(self):
497 WindowList.unregister_callback(self.postwindowsmenu)
498 if self.close_hook:
499 self.close_hook()
500 self.flist = None
501 colorizing = 0
502 self.unload_extensions()
503 self.io.close(); self.io = None
504 self.undo = None # XXX
505 if self.color:
506 colorizing = self.color.colorizing
507 doh = colorizing and self.top
508 self.color.close(doh) # Cancel colorization
509 self.text = None
510 self.vars = None
511 self.per.close(); self.per = None
512 if not colorizing:
513 self.top.destroy()
514
515 def load_extensions(self):
516 self.extensions = {}
517 self.load_standard_extensions()
518
519 def unload_extensions(self):
520 for ins in self.extensions.values():
521 if hasattr(ins, "close"):
522 ins.close()
523 self.extensions = {}
524
525 def load_standard_extensions(self):
526 for name in self.get_standard_extension_names():
527 try:
528 self.load_extension(name)
529 except:
530 print "Failed to load extension", `name`
531 import traceback
532 traceback.print_exc()
533
534 def get_standard_extension_names(self):
535 return idleconf.getextensions()
536
537 def load_extension(self, name):
538 mod = __import__(name, globals(), locals(), [])
539 cls = getattr(mod, name)
540 ins = cls(self)
541 self.extensions[name] = ins
542 kdnames = ["keydefs"]
543 if sys.platform == 'win32':
544 kdnames.append("windows_keydefs")
545 elif sys.platform == 'mac':
546 kdnames.append("mac_keydefs")
547 else:
548 kdnames.append("unix_keydefs")
549 keydefs = {}
550 for kdname in kdnames:
551 if hasattr(ins, kdname):
552 keydefs.update(getattr(ins, kdname))
553 if keydefs:
554 self.apply_bindings(keydefs)
555 for vevent in keydefs.keys():
556 methodname = string.replace(vevent, "-", "_")
557 while methodname[:1] == '<':
558 methodname = methodname[1:]
559 while methodname[-1:] == '>':
560 methodname = methodname[:-1]
561 methodname = methodname + "_event"
562 if hasattr(ins, methodname):
563 self.text.bind(vevent, getattr(ins, methodname))
564 if hasattr(ins, "menudefs"):
565 self.fill_menus(ins.menudefs, keydefs)
566 return ins
567
568 def apply_bindings(self, keydefs=None):
569 if keydefs is None:
570 keydefs = self.Bindings.default_keydefs
571 text = self.text
572 text.keydefs = keydefs
573 for event, keylist in keydefs.items():
574 if keylist:
575 apply(text.event_add, (event,) + tuple(keylist))
576
577 def fill_menus(self, defs=None, keydefs=None):
578 # Fill the menus. Menus that are absent or None in
579 # self.menudict are ignored.
580 if defs is None:
581 defs = self.Bindings.menudefs
582 if keydefs is None:
583 keydefs = self.Bindings.default_keydefs
584 menudict = self.menudict
585 text = self.text
586 for mname, itemlist in defs:
587 menu = menudict.get(mname)
588 if not menu:
589 continue
590 for item in itemlist:
591 if not item:
592 menu.add_separator()
593 else:
594 label, event = item
595 checkbutton = (label[:1] == '!')
596 if checkbutton:
597 label = label[1:]
598 underline, label = prepstr(label)
599 accelerator = get_accelerator(keydefs, event)
600 def command(text=text, event=event):
601 text.event_generate(event)
602 if checkbutton:
603 var = self.getrawvar(event, BooleanVar)
604 menu.add_checkbutton(label=label, underline=underline,
605 command=command, accelerator=accelerator,
606 variable=var)
607 else:
608 menu.add_command(label=label, underline=underline,
609 command=command, accelerator=accelerator)
610
611 def getvar(self, name):
612 var = self.getrawvar(name)
613 if var:
614 return var.get()
615
616 def setvar(self, name, value, vartype=None):
617 var = self.getrawvar(name, vartype)
618 if var:
619 var.set(value)
620
621 def getrawvar(self, name, vartype=None):
622 var = self.vars.get(name)
623 if not var and vartype:
624 self.vars[name] = var = vartype(self.text)
625 return var
626
627 # Tk implementations of "virtual text methods" -- each platform
628 # reusing IDLE's support code needs to define these for its GUI's
629 # flavor of widget.
630
631 # Is character at text_index in a Python string? Return 0 for
632 # "guaranteed no", true for anything else. This info is expensive
633 # to compute ab initio, but is probably already known by the
634 # platform's colorizer.
635
636 def is_char_in_string(self, text_index):
637 if self.color:
638 # Return true iff colorizer hasn't (re)gotten this far
639 # yet, or the character is tagged as being in a string
640 return self.text.tag_prevrange("TODO", text_index) or \
641 "STRING" in self.text.tag_names(text_index)
642 else:
643 # The colorizer is missing: assume the worst
644 return 1
645
646 # If a selection is defined in the text widget, return (start,
647 # end) as Tkinter text indices, otherwise return (None, None)
648 def get_selection_indices(self):
649 try:
650 first = self.text.index("sel.first")
651 last = self.text.index("sel.last")
652 return first, last
653 except TclError:
654 return None, None
655
656 # Return the text widget's current view of what a tab stop means
657 # (equivalent width in spaces).
658
659 def get_tabwidth(self):
660 current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
661 return int(current)
662
663 # Set the text widget's current view of what a tab stop means.
664
665 def set_tabwidth(self, newtabwidth):
666 text = self.text
667 if self.get_tabwidth() != newtabwidth:
668 pixels = text.tk.call("font", "measure", text["font"],
669 "-displayof", text.master,
Kurt B. Kaiserafdf71b2001-07-13 03:35:32 +0000670 "n" * newtabwidth)
David Scherer7aced172000-08-15 01:13:23 +0000671 text.configure(tabs=pixels)
672
673def prepstr(s):
674 # Helper to extract the underscore from a string, e.g.
675 # prepstr("Co_py") returns (2, "Copy").
676 i = string.find(s, '_')
677 if i >= 0:
678 s = s[:i] + s[i+1:]
679 return i, s
680
681
682keynames = {
683 'bracketleft': '[',
684 'bracketright': ']',
685 'slash': '/',
686}
687
688def get_accelerator(keydefs, event):
689 keylist = keydefs.get(event)
690 if not keylist:
691 return ""
692 s = keylist[0]
693 s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s)
694 s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
695 s = re.sub("Key-", "", s)
696 s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu
697 s = re.sub("Control-", "Ctrl-", s)
698 s = re.sub("-", "+", s)
699 s = re.sub("><", " ", s)
700 s = re.sub("<", "", s)
701 s = re.sub(">", "", s)
702 return s
703
704
705def fixwordbreaks(root):
706 # Make sure that Tk's double-click and next/previous word
707 # operations use our definition of a word (i.e. an identifier)
708 tk = root.tk
709 tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
710 tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]')
711 tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
712
713
714def test():
715 root = Tk()
716 fixwordbreaks(root)
717 root.withdraw()
718 if sys.argv[1:]:
719 filename = sys.argv[1]
720 else:
721 filename = None
722 edit = EditorWindow(root=root, filename=filename)
723 edit.set_close_hook(root.quit)
724 root.mainloop()
725 root.destroy()
726
727if __name__ == '__main__':
728 test()