diff options
Diffstat (limited to 'Lib/idlelib/EditorWindow.py')
-rw-r--r-- | Lib/idlelib/EditorWindow.py | 198 |
1 files changed, 92 insertions, 106 deletions
diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py index 4bf1111..b5868be 100644 --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -1,7 +1,8 @@ import importlib import importlib.abc +import importlib.util import os -from platform import python_version +import platform import re import string import sys @@ -12,7 +13,6 @@ import traceback import webbrowser from idlelib.MultiCall import MultiCallCreator -from idlelib import idlever from idlelib import WindowList from idlelib import SearchDialog from idlelib import GrepDialog @@ -21,10 +21,13 @@ from idlelib import PyParse from idlelib.configHandler import idleConf from idlelib import aboutDialog, textView, configDialog from idlelib import macosxSupport +from idlelib import help # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 +_py_version = ' (%s)' % platform.python_version() + def _sphinx_version(): "Format sys.version_info to produce the Sphinx version string used to install the chm docs" major, minor, micro, level, serial = sys.version_info @@ -51,6 +54,11 @@ class HelpDialog(object): near - a Toplevel widget (e.g. EditorWindow or PyShell) to use as a reference for placing the help window """ + import warnings as w + w.warn("EditorWindow.HelpDialog is no longer used by Idle.\n" + "It will be removed in 3.6 or later.\n" + "It has been replaced by private help.HelpWindow\n", + DeprecationWarning, stacklevel=2) if self.dlg is None: self.show_dialog(parent) if near: @@ -77,7 +85,7 @@ class HelpDialog(object): self.dlg = None self.parent = None -helpDialog = HelpDialog() # singleton instance +helpDialog = HelpDialog() # singleton instance, no longer used class EditorWindow(object): @@ -108,8 +116,8 @@ class EditorWindow(object): 'Python%s.chm' % _sphinx_version()) if os.path.isfile(chmfile): dochome = chmfile - elif macosxSupport.runningAsOSXApp(): - # documentation is stored inside the python framework + elif sys.platform == 'darwin': + # documentation may be stored inside a python framework dochome = os.path.join(sys.base_prefix, 'Resources/English.lproj/Documentation/index.html') dochome = os.path.normpath(dochome) @@ -119,8 +127,7 @@ class EditorWindow(object): # Safari requires real file:-URLs EditorWindow.help_url = 'file://' + EditorWindow.help_url else: - EditorWindow.help_url = "http://docs.python.org/%d.%d" % sys.version_info[:2] - currentTheme=idleConf.CurrentTheme() + EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2] self.flist = flist root = root or flist.root self.root = root @@ -149,6 +156,7 @@ class EditorWindow(object): 'name': 'text', 'padx': 5, 'wrap': 'none', + 'highlightthickness': 0, 'width': self.width, 'height': idleConf.GetOption('main', 'EditorWindow', 'height', type='int')} @@ -165,16 +173,16 @@ class EditorWindow(object): self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.bind("<<close-window>>", self.close_event) - if macosxSupport.runningAsOSXApp(): + if macosxSupport.isAquaTk(): # Command-W on editorwindows doesn't work without this. text.bind('<<close-window>>', self.close_event) - # Some OS X systems have only one mouse button, - # so use control-click for pulldown menus there. - # (Note, AquaTk defines <2> as the right button if - # present and the Tk Text widget already binds <2>.) + # Some OS X systems have only one mouse button, so use + # control-click for popup context menus there. For two + # buttons, AquaTk defines <2> as the right button, not <3>. text.bind("<Control-Button-1>",self.right_menu_event) + text.bind("<2>", self.right_menu_event) else: - # Elsewhere, use right-click for pulldown menus. + # Elsewhere, use right-click for popup menus. text.bind("<3>",self.right_menu_event) text.bind("<<cut>>", self.cut) text.bind("<<copy>>", self.copy) @@ -219,18 +227,13 @@ class EditorWindow(object): text.bind("<<close-all-windows>>", self.flist.close_all_callback) text.bind("<<open-class-browser>>", self.open_class_browser) text.bind("<<open-path-browser>>", self.open_path_browser) + text.bind("<<open-turtle-demo>>", self.open_turtle_demo) self.set_status_bar() vbar['command'] = text.yview vbar.pack(side=RIGHT, fill=Y) text['yscrollcommand'] = vbar.set - fontWeight = 'normal' - if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'): - fontWeight='bold' - text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'), - idleConf.GetOption('main', 'EditorWindow', - 'font-size', type='int'), - fontWeight)) + text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow') text_frame.pack(side=LEFT, fill=BOTH, expand=1) text.pack(side=TOP, fill=BOTH, expand=1) text.focus_set() @@ -313,50 +316,20 @@ class EditorWindow(object): self.askinteger = tkSimpleDialog.askinteger self.showerror = tkMessageBox.showerror - self._highlight_workaround() # Fix selection tags on Windows - - def _highlight_workaround(self): - # On Windows, Tk removes painting of the selection - # tags which is different behavior than on Linux and Mac. - # See issue14146 for more information. - if not sys.platform.startswith('win'): - return - - text = self.text - text.event_add("<<Highlight-FocusOut>>", "<FocusOut>") - text.event_add("<<Highlight-FocusIn>>", "<FocusIn>") - def highlight_fix(focus): - sel_range = text.tag_ranges("sel") - if sel_range: - if focus == 'out': - HILITE_CONFIG = idleConf.GetHighlight( - idleConf.CurrentTheme(), 'hilite') - text.tag_config("sel_fix", HILITE_CONFIG) - text.tag_raise("sel_fix") - text.tag_add("sel_fix", *sel_range) - elif focus == 'in': - text.tag_remove("sel_fix", "1.0", "end") - - text.bind("<<Highlight-FocusOut>>", - lambda ev: highlight_fix("out")) - text.bind("<<Highlight-FocusIn>>", - lambda ev: highlight_fix("in")) - - def _filename_to_unicode(self, filename): - """convert filename to unicode in order to display it in Tk""" - if isinstance(filename, str) or not filename: - return filename - else: + """Return filename as BMP unicode so diplayable in Tk.""" + # Decode bytes to unicode. + if isinstance(filename, bytes): try: - return filename.decode(self.filesystemencoding) + filename = filename.decode(self.filesystemencoding) except UnicodeDecodeError: - # XXX try: - return filename.decode(self.encoding) + filename = filename.decode(self.encoding) except UnicodeDecodeError: # byte-to-byte conversion - return filename.decode('iso8859-1') + filename = filename.decode('iso8859-1') + # Replace non-BMP char with diamond questionmark. + return re.sub('[\U00010000-\U0010FFFF]', '\ufffd', filename) def new_callback(self, event): dirname, basename = self.io.defaultfilename() @@ -408,13 +381,15 @@ class EditorWindow(object): def set_status_bar(self): self.status_bar = self.MultiStatusBar(self.top) - if macosxSupport.runningAsOSXApp(): + sep = Frame(self.top, height=1, borderwidth=1, background='grey75') + if sys.platform == "darwin": # Insert some padding to avoid obscuring some of the statusbar # by the resize widget. self.status_bar.set_label('_padding1', ' ', side=RIGHT) self.status_bar.set_label('column', 'Col: ?', side=RIGHT) self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) self.status_bar.pack(side=BOTTOM, fill=X) + sep.pack(side=BOTTOM, fill=X) self.text.bind("<<set-line-and-column>>", self.set_line_and_column) self.text.event_add("<<set-line-and-column>>", "<KeyRelease>", "<ButtonRelease>") @@ -431,27 +406,25 @@ class EditorWindow(object): ("format", "F_ormat"), ("run", "_Run"), ("options", "_Options"), - ("windows", "_Windows"), + ("windows", "_Window"), ("help", "_Help"), ] - if macosxSupport.runningAsOSXApp(): - menu_specs[-2] = ("windows", "_Window") - def createmenubar(self): mbar = self.menubar self.menudict = menudict = {} for name, label in self.menu_specs: underline, label = prepstr(label) - menudict[name] = menu = Menu(mbar, name=name) + menudict[name] = menu = Menu(mbar, name=name, tearoff=0) mbar.add_cascade(label=label, menu=menu, underline=underline) - if macosxSupport.isCarbonAquaTk(self.root): + if macosxSupport.isCarbonTk(): # Insert the application menu - menudict['application'] = menu = Menu(mbar, name='apple') + menudict['application'] = menu = Menu(mbar, name='apple', + tearoff=0) mbar.add_cascade(label='IDLE', menu=menu) self.fill_menus() - self.recent_files_menu = Menu(self.menubar) + self.recent_files_menu = Menu(self.menubar, tearoff=0) self.menudict['file'].insert_cascade(3, label='Recent Files', underline=0, menu=self.recent_files_menu) @@ -533,23 +506,29 @@ class EditorWindow(object): return 'normal' def about_dialog(self, event=None): + "Handle Help 'About IDLE' event." + # Synchronize with macosxSupport.overrideRootMenu.about_dialog. aboutDialog.AboutDialog(self.top,'About IDLE') def config_dialog(self, event=None): + "Handle Options 'Configure IDLE' event." + # Synchronize with macosxSupport.overrideRootMenu.config_dialog. configDialog.ConfigDialog(self.top,'Settings') def help_dialog(self, event=None): + "Handle Help 'IDLE Help' event." + # Synchronize with macosxSupport.overrideRootMenu.help_dialog. if self.root: parent = self.root else: parent = self.top - helpDialog.display(parent, near=self.top) + help.show_idlehelp(parent) def python_docs(self, event=None): if sys.platform[:3] == 'win': try: os.startfile(self.help_url) - except WindowsError as why: + except OSError as why: tkMessageBox.showerror(title='Document Start Failure', message=str(why), parent=self.text) else: @@ -660,20 +639,20 @@ class EditorWindow(object): return # XXX Ought to insert current file's directory in front of path try: - loader = importlib.find_loader(name) + spec = importlib.util.find_spec(name) except (ValueError, ImportError) as msg: tkMessageBox.showerror("Import error", str(msg), parent=self.text) return - if loader is None: + if spec is None: tkMessageBox.showerror("Import error", "module not found", parent=self.text) return - if not isinstance(loader, importlib.abc.SourceLoader): + if not isinstance(spec.loader, importlib.abc.SourceLoader): tkMessageBox.showerror("Import error", "not a source-based module", parent=self.text) return try: - file_path = loader.get_filename(name) + file_path = spec.loader.get_filename(name) except AttributeError: tkMessageBox.showerror("Import error", "loader does not support get_filename", @@ -683,16 +662,15 @@ class EditorWindow(object): self.flist.open(file_path) else: self.io.loadfile(file_path) + return file_path def open_class_browser(self, event=None): filename = self.io.filename - if not filename: - tkMessageBox.showerror( - "No filename", - "This buffer has no associated filename", - master=self.text) - self.text.focus_set() - return None + if not (self.__class__.__name__ == 'PyShellEditorWindow' + and filename): + filename = self.open_module() + if filename is None: + return head, tail = os.path.split(filename) base, ext = os.path.splitext(tail) from idlelib import ClassBrowser @@ -702,6 +680,14 @@ class EditorWindow(object): from idlelib import PathBrowser PathBrowser.PathBrowser(self.flist) + def open_turtle_demo(self, event = None): + import subprocess + + cmd = [sys.executable, + '-c', + 'from turtledemo.__main__ import main; main()'] + subprocess.Popen(cmd, shell=False) + def gotoline(self, lineno): if lineno is not None and lineno > 0: self.text.mark_set("insert", "%d.0" % lineno) @@ -752,11 +738,11 @@ class EditorWindow(object): self.color = None def ResetColorizer(self): - "Update the colour theme" + "Update the color theme" # Called from self.filename_change_hook and from configDialog.py self._rmcolorizer() self._addcolorizer() - theme = idleConf.GetOption('main','Theme','name') + theme = idleConf.CurrentTheme() normal_colors = idleConf.GetHighlight(theme, 'normal') cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') select_colors = idleConf.GetHighlight(theme, 'hilite') @@ -767,6 +753,9 @@ class EditorWindow(object): selectforeground=select_colors['foreground'], selectbackground=select_colors['background'], ) + if TkVersion >= 8.5: + self.text.config( + inactiveselectbackground=select_colors['background']) IDENTCHARS = string.ascii_letters + string.digits + "_" @@ -784,13 +773,8 @@ class EditorWindow(object): def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configDialog.py - fontWeight='normal' - if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'): - fontWeight='bold' - self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'), - idleConf.GetOption('main','EditorWindow','font-size', - type='int'), - fontWeight)) + + self.text['font'] = idleConf.GetFont(self.root, 'main','EditorWindow') def RemoveKeybindings(self): "Remove the keybindings before they are changed." @@ -872,7 +856,7 @@ class EditorWindow(object): if sys.platform[:3] == 'win': try: os.startfile(helpfile) - except WindowsError as why: + except OSError as why: tkMessageBox.showerror(title='Document Start Failure', message=str(why), parent=self.text) else: @@ -906,9 +890,11 @@ class EditorWindow(object): except OSError as err: if not getattr(self.root, "recentfilelist_error_displayed", False): self.root.recentfilelist_error_displayed = True - tkMessageBox.showerror(title='IDLE Error', - message='Unable to update Recent Files list:\n%s' - % str(err), + tkMessageBox.showwarning(title='IDLE Warning', + message="Cannot update File menu Recent Files list. " + "Your operating system says:\n%s\n" + "Select OK and IDLE will continue without updating." + % self._filename_to_unicode(str(err)), parent=self.text) # for each edit window instance, construct the recent files menu for instance in self.top.instance_dict: @@ -932,7 +918,7 @@ class EditorWindow(object): short = self.short_title() long = self.long_title() if short and long: - title = short + " - " + long + title = short + " - " + long + _py_version elif short: title = short elif long: @@ -956,14 +942,13 @@ class EditorWindow(object): self.undo.reset_undo() def short_title(self): - pyversion = "Python " + python_version() + ": " filename = self.io.filename if filename: filename = os.path.basename(filename) else: filename = "Untitled" # return unicode string to display non-ASCII chars correctly - return pyversion + self._filename_to_unicode(filename) + return self._filename_to_unicode(filename) def long_title(self): # return unicode string to display non-ASCII chars correctly @@ -1063,7 +1048,7 @@ class EditorWindow(object): try: try: mod = importlib.import_module('.' + name, package=__package__) - except ImportError: + except (ImportError, TypeError): mod = importlib.import_module(name) except ImportError: print("\nFailed to import extension: ", name) @@ -1397,7 +1382,7 @@ class EditorWindow(object): text.see("insert") text.undo_block_stop() - # Our editwin provides a is_char_in_string function that works + # Our editwin provides an is_char_in_string function that works # with a Tk text index, but PyParse only knows about offsets into # a string. This builds a function for PyParse that accepts an # offset. @@ -1672,7 +1657,7 @@ def get_accelerator(keydefs, eventname): keylist = keydefs.get(eventname) # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5 # if not keylist: - if (not keylist) or (macosxSupport.runningAsOSXApp() and eventname in { + if (not keylist) or (macosxSupport.isCocoaTk() and eventname in { "<<open-module>>", "<<goto-line>>", "<<change-indentwidth>>"}): @@ -1699,19 +1684,20 @@ def fixwordbreaks(root): tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]') -def test(): - root = Tk() +def _editor_window(parent): # htest # + # error if close master window first - timer event, after script + root = parent fixwordbreaks(root) - root.withdraw() if sys.argv[1:]: filename = sys.argv[1] else: filename = None + macosxSupport.setupApp(root, None) edit = EditorWindow(root=root, filename=filename) - edit.set_close_hook(root.quit) edit.text.bind("<<close-all-windows>>", edit.close_event) - root.mainloop() - root.destroy() + # Does not stop error, neither does following + # edit.text.bind("<<close-window>>", edit.close_event) if __name__ == '__main__': - test() + from idlelib.idle_test.htest import run + run(_editor_window) |