summaryrefslogtreecommitdiffstats
path: root/Lib/idlelib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/idlelib')
-rw-r--r--Lib/idlelib/AutoComplete.py5
-rw-r--r--Lib/idlelib/AutoExpand.py21
-rw-r--r--Lib/idlelib/Bindings.py35
-rw-r--r--Lib/idlelib/CallTipWindow.py57
-rw-r--r--Lib/idlelib/ClassBrowser.py22
-rw-r--r--Lib/idlelib/ColorDelegator.py33
-rw-r--r--Lib/idlelib/Debugger.py3
-rw-r--r--Lib/idlelib/EditorWindow.py64
-rw-r--r--Lib/idlelib/FileList.py2
-rw-r--r--Lib/idlelib/FormatParagraph.py14
-rw-r--r--Lib/idlelib/GrepDialog.py73
-rw-r--r--Lib/idlelib/HyperParser.py249
-rw-r--r--Lib/idlelib/IOBinding.py18
-rw-r--r--Lib/idlelib/IdleHistory.py2
-rw-r--r--Lib/idlelib/MultiCall.py48
-rw-r--r--Lib/idlelib/MultiStatusBar.py35
-rw-r--r--Lib/idlelib/NEWS.txt68
-rw-r--r--Lib/idlelib/ObjectBrowser.py11
-rw-r--r--Lib/idlelib/ParenMatch.py14
-rw-r--r--Lib/idlelib/PathBrowser.py22
-rw-r--r--Lib/idlelib/Percolator.py50
-rw-r--r--Lib/idlelib/PyParse.py80
-rwxr-xr-xLib/idlelib/PyShell.py37
-rw-r--r--Lib/idlelib/ReplaceDialog.py33
-rw-r--r--Lib/idlelib/ScriptBinding.py4
-rw-r--r--Lib/idlelib/ScrolledList.py19
-rw-r--r--Lib/idlelib/SearchDialog.py22
-rw-r--r--Lib/idlelib/SearchDialogBase.py199
-rw-r--r--Lib/idlelib/SearchEngine.py3
-rw-r--r--Lib/idlelib/StackViewer.py30
-rw-r--r--Lib/idlelib/ToolTip.py22
-rw-r--r--Lib/idlelib/TreeWidget.py35
-rw-r--r--Lib/idlelib/UndoDelegator.py21
-rw-r--r--Lib/idlelib/WidgetRedirector.py110
-rw-r--r--Lib/idlelib/ZoomHeight.py2
-rw-r--r--Lib/idlelib/aboutDialog.py20
-rw-r--r--Lib/idlelib/config-keys.def28
-rw-r--r--Lib/idlelib/config-main.def2
-rw-r--r--Lib/idlelib/configDialog.py1227
-rw-r--r--Lib/idlelib/configHandler.py42
-rw-r--r--Lib/idlelib/configHelpSourceEdit.py27
-rw-r--r--Lib/idlelib/configSectionNameDialog.py28
-rw-r--r--Lib/idlelib/dynOptionMenuWidget.py26
-rw-r--r--Lib/idlelib/help.txt556
-rw-r--r--Lib/idlelib/idle_test/README.txt37
-rw-r--r--Lib/idlelib/idle_test/htest.py369
-rw-r--r--Lib/idlelib/idle_test/mock_idle.py28
-rw-r--r--Lib/idlelib/idle_test/mock_tk.py27
-rw-r--r--Lib/idlelib/idle_test/test_autocomplete.py143
-rw-r--r--Lib/idlelib/idle_test/test_autoexpand.py141
-rw-r--r--Lib/idlelib/idle_test/test_calltips.py7
-rw-r--r--Lib/idlelib/idle_test/test_configdialog.py32
-rw-r--r--Lib/idlelib/idle_test/test_formatparagraph.py10
-rw-r--r--Lib/idlelib/idle_test/test_hyperparser.py273
-rw-r--r--Lib/idlelib/idle_test/test_parenmatch.py109
-rw-r--r--Lib/idlelib/idle_test/test_searchdialogbase.py165
-rw-r--r--Lib/idlelib/idle_test/test_textview.py97
-rw-r--r--Lib/idlelib/idle_test/test_widgetredir.py122
-rw-r--r--Lib/idlelib/idlever.py2
-rw-r--r--Lib/idlelib/keybindingDialog.py31
-rw-r--r--Lib/idlelib/macosxSupport.py136
-rw-r--r--Lib/idlelib/rpc.py8
-rw-r--r--Lib/idlelib/run.py4
-rw-r--r--Lib/idlelib/tabbedpages.py10
-rw-r--r--Lib/idlelib/textView.py39
65 files changed, 3615 insertions, 1594 deletions
diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py
index f366030..b20512d 100644
--- a/Lib/idlelib/AutoComplete.py
+++ b/Lib/idlelib/AutoComplete.py
@@ -226,3 +226,8 @@ class AutoComplete:
namespace = sys.modules.copy()
namespace.update(__main__.__dict__)
return eval(name, namespace)
+
+
+if __name__ == '__main__':
+ from unittest import main
+ main('idlelib.idle_test.test_autocomplete', verbosity=2)
diff --git a/Lib/idlelib/AutoExpand.py b/Lib/idlelib/AutoExpand.py
index 9e93d57..7059054 100644
--- a/Lib/idlelib/AutoExpand.py
+++ b/Lib/idlelib/AutoExpand.py
@@ -1,3 +1,17 @@
+'''Complete the current word before the cursor with words in the editor.
+
+Each menu selection or shortcut key selection replaces the word with a
+different word with the same prefix. The search for matches begins
+before the target and moves toward the top of the editor. It then starts
+after the cursor and moves down. It then returns to the original word and
+the cycle starts again.
+
+Changing the current text line or leaving the cursor in a different
+place before requesting the next selection causes AutoExpand to reset
+its state.
+
+This is an extension file and there is only one instance of AutoExpand.
+'''
import string
import re
@@ -20,6 +34,7 @@ class AutoExpand:
self.state = None
def expand_word_event(self, event):
+ "Replace the current word with the next expansion."
curinsert = self.text.index("insert")
curline = self.text.get("insert linestart", "insert lineend")
if not self.state:
@@ -46,6 +61,7 @@ class AutoExpand:
return "break"
def getwords(self):
+ "Return a list of words that match the prefix before the cursor."
word = self.getprevword()
if not word:
return []
@@ -76,8 +92,13 @@ class AutoExpand:
return words
def getprevword(self):
+ "Return the word prefix before the cursor."
line = self.text.get("insert linestart", "insert")
i = len(line)
while i > 0 and line[i-1] in self.wordchars:
i = i-1
return line[i:]
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2)
diff --git a/Lib/idlelib/Bindings.py b/Lib/idlelib/Bindings.py
index 65c0317..c9bef21 100644
--- a/Lib/idlelib/Bindings.py
+++ b/Lib/idlelib/Bindings.py
@@ -8,9 +8,16 @@ the PythonShell window, and a Format menu which is only present in the Editor
windows.
"""
-import sys
+from importlib.util import find_spec
+
from idlelib.configHandler import idleConf
-from idlelib import macosxSupport
+
+# Warning: menudefs is altered in macosxSupport.overrideRootMenu()
+# after it is determined that an OS X Aqua Tk is in use,
+# which cannot be done until after Tk() is first called.
+# Do not alter the 'file', 'options', or 'help' cascades here
+# without altering overrideRootMenu() as well.
+# TODO: Make this more robust
menudefs = [
# underscore prefixes character to underscore
@@ -81,27 +88,7 @@ menudefs = [
]),
]
-if macosxSupport.runningAsOSXApp():
- # Running as a proper MacOS application bundle. This block restructures
- # the menus a little to make them conform better to the HIG.
-
- quitItem = menudefs[0][1][-1]
- closeItem = menudefs[0][1][-2]
-
- # Remove the last 3 items of the file menu: a separator, close window and
- # quit. Close window will be reinserted just above the save item, where
- # it should be according to the HIG. Quit is in the application menu.
- del menudefs[0][1][-3:]
- menudefs[0][1].insert(6, closeItem)
-
- # Remove the 'About' entry from the help menu, it is in the application
- # menu
- del menudefs[-1][1][0:2]
-
- # Remove the 'Configure' entry from the options menu, it is in the
- # application menu as 'Preferences'
- del menudefs[-2][1][0:2]
+if find_spec('turtledemo'):
+ menudefs[-1][1].append(('Turtle Demo', '<<open-turtle-demo>>'))
default_keydefs = idleConf.GetCurrentKeySet()
-
-del sys
diff --git a/Lib/idlelib/CallTipWindow.py b/Lib/idlelib/CallTipWindow.py
index 8e29dab..84d22fd 100644
--- a/Lib/idlelib/CallTipWindow.py
+++ b/Lib/idlelib/CallTipWindow.py
@@ -2,9 +2,8 @@
After ToolTip.py, which uses ideas gleaned from PySol
Used by the CallTips IDLE extension.
-
"""
-from tkinter import *
+from tkinter import Toplevel, Label, LEFT, SOLID, TclError
HIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-hide>>"
HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>")
@@ -133,37 +132,39 @@ class CallTip:
return bool(self.tipwindow)
+def _calltip_window(parent): # htest #
+ import re
+ from tkinter import Tk, Text, LEFT, BOTH
+
+ root = Tk()
+ root.title("Test calltips")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
-###############################
-#
-# Test Code
-#
-class container: # Conceptually an editor_window
- def __init__(self):
- root = Tk()
- text = self.text = Text(root)
- text.pack(side=LEFT, fill=BOTH, expand=1)
- text.insert("insert", "string.split")
- root.update()
- self.calltip = CallTip(text)
+ class MyEditWin: # conceptually an editor_window
+ def __init__(self):
+ text = self.text = Text(root)
+ text.pack(side=LEFT, fill=BOTH, expand=1)
+ text.insert("insert", "string.split")
+ root.update()
+ self.calltip = CallTip(text)
- text.event_add("<<calltip-show>>", "(")
- text.event_add("<<calltip-hide>>", ")")
- text.bind("<<calltip-show>>", self.calltip_show)
- text.bind("<<calltip-hide>>", self.calltip_hide)
+ text.event_add("<<calltip-show>>", "(")
+ text.event_add("<<calltip-hide>>", ")")
+ text.bind("<<calltip-show>>", self.calltip_show)
+ text.bind("<<calltip-hide>>", self.calltip_hide)
- text.focus_set()
- root.mainloop()
+ text.focus_set()
+ root.mainloop()
- def calltip_show(self, event):
- self.calltip.showtip("Hello world")
+ def calltip_show(self, event):
+ self.calltip.showtip("Hello world", "insert", "end")
- def calltip_hide(self, event):
- self.calltip.hidetip()
+ def calltip_hide(self, event):
+ self.calltip.hidetip()
-def main():
- # Test code
- c=container()
+ MyEditWin()
if __name__=='__main__':
- main()
+ from idlelib.idle_test.htest import run
+ run(_calltip_window)
diff --git a/Lib/idlelib/ClassBrowser.py b/Lib/idlelib/ClassBrowser.py
index 71176cd..a0e782e 100644
--- a/Lib/idlelib/ClassBrowser.py
+++ b/Lib/idlelib/ClassBrowser.py
@@ -21,11 +21,15 @@ from idlelib.configHandler import idleConf
class ClassBrowser:
- def __init__(self, flist, name, path):
+ def __init__(self, flist, name, path, _htest=False):
# XXX This API should change, if the file doesn't end in ".py"
# XXX the code here is bogus!
+ """
+ _htest - bool, change box when location running htest.
+ """
self.name = name
self.file = os.path.join(path[0], self.name + ".py")
+ self._htest = _htest
self.init(flist)
def close(self, event=None):
@@ -40,6 +44,9 @@ class ClassBrowser:
self.top = top = ListedToplevel(flist.root)
top.protocol("WM_DELETE_WINDOW", self.close)
top.bind("<Escape>", self.close)
+ if self._htest: # place dialog below parent if running htest
+ top.geometry("+%d+%d" %
+ (flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200))
self.settitle()
top.focus_set()
# create scrolled canvas
@@ -94,7 +101,7 @@ class ModuleBrowserTreeItem(TreeItem):
return []
try:
dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
- except ImportError as msg:
+ except ImportError:
return []
items = []
self.classes = {}
@@ -202,7 +209,7 @@ class MethodBrowserTreeItem(TreeItem):
edit = PyShell.flist.open(self.file)
edit.gotoline(self.cl.methods[self.name])
-def main():
+def _class_browser(parent): #Wrapper for htest
try:
file = __file__
except NameError:
@@ -213,9 +220,10 @@ def main():
file = sys.argv[0]
dir, file = os.path.split(file)
name = os.path.splitext(file)[0]
- ClassBrowser(PyShell.flist, name, [dir])
- if sys.stdin is sys.__stdin__:
- mainloop()
+ flist = PyShell.PyShellFileList(parent)
+ ClassBrowser(flist, name, [dir], _htest=True)
+ parent.mainloop()
if __name__ == "__main__":
- main()
+ from idlelib.idle_test.htest import run
+ run(_class_browser)
diff --git a/Lib/idlelib/ColorDelegator.py b/Lib/idlelib/ColorDelegator.py
index 61e2be4..22bb22f 100644
--- a/Lib/idlelib/ColorDelegator.py
+++ b/Lib/idlelib/ColorDelegator.py
@@ -32,7 +32,6 @@ def make_pat():
prog = re.compile(make_pat(), re.S)
idprog = re.compile(r"\s+(\w+)", re.S)
-asprog = re.compile(r".*?\b(as)\b")
class ColorDelegator(Delegator):
@@ -40,7 +39,6 @@ class ColorDelegator(Delegator):
Delegator.__init__(self)
self.prog = prog
self.idprog = idprog
- self.asprog = asprog
self.LoadTagDefs()
def setdelegate(self, delegate):
@@ -72,7 +70,6 @@ class ColorDelegator(Delegator):
"DEFINITION": idleConf.GetHighlight(theme, "definition"),
"SYNC": {'background':None,'foreground':None},
"TODO": {'background':None,'foreground':None},
- "BREAK": idleConf.GetHighlight(theme, "break"),
"ERROR": idleConf.GetHighlight(theme, "error"),
# The following is used by ReplaceDialog:
"hit": idleConf.GetHighlight(theme, "hit"),
@@ -214,22 +211,6 @@ class ColorDelegator(Delegator):
self.tag_add("DEFINITION",
head + "+%dc" % a,
head + "+%dc" % b)
- elif value == "import":
- # color all the "as" words on same line, except
- # if in a comment; cheap approximation to the
- # truth
- if '#' in chars:
- endpos = chars.index('#')
- else:
- endpos = len(chars)
- while True:
- m1 = self.asprog.match(chars, b, endpos)
- if not m1:
- break
- a, b = m1.span(1)
- self.tag_add("KEYWORD",
- head + "+%dc" % a,
- head + "+%dc" % b)
m = self.prog.search(chars, m.end())
if "SYNC" in self.tag_names(next + "-1c"):
head = next
@@ -253,17 +234,21 @@ class ColorDelegator(Delegator):
for tag in self.tagdefs:
self.tag_remove(tag, "1.0", "end")
-def main():
+def _color_delegator(parent):
from idlelib.Percolator import Percolator
root = Tk()
- root.wm_protocol("WM_DELETE_WINDOW", root.quit)
- text = Text(background="white")
+ root.title("Test ColorDelegator")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ source = "if somename: x = 'abc' # comment\nprint"
+ text = Text(root, background="white")
+ text.insert("insert", source)
text.pack(expand=1, fill="both")
- text.focus_set()
p = Percolator(text)
d = ColorDelegator()
p.insertfilter(d)
root.mainloop()
if __name__ == "__main__":
- main()
+ from idlelib.idle_test.htest import run
+ run(_color_delegator)
diff --git a/Lib/idlelib/Debugger.py b/Lib/idlelib/Debugger.py
index d4872ed..6875197 100644
--- a/Lib/idlelib/Debugger.py
+++ b/Lib/idlelib/Debugger.py
@@ -1,6 +1,5 @@
import os
import bdb
-import types
from tkinter import *
from idlelib.WindowList import ListedToplevel
from idlelib.ScrolledList import ScrolledList
@@ -322,7 +321,7 @@ class Debugger:
class StackViewer(ScrolledList):
def __init__(self, master, flist, gui):
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isAquaTk():
# At least on with the stock AquaTk version on OSX 10.4 you'll
# get an shaking GUI that eventually kills IDLE if the width
# argument is specified.
diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py
index 4bf1111..31336a3 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
@@ -25,6 +26,8 @@ from idlelib import macosxSupport
# 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
@@ -78,6 +81,8 @@ class HelpDialog(object):
self.parent = None
helpDialog = HelpDialog() # singleton instance
+def _help_dialog(parent): # wrapper for htest
+ helpDialog.show_dialog(parent)
class EditorWindow(object):
@@ -108,8 +113,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,7 +124,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]
+ EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2]
currentTheme=idleConf.CurrentTheme()
self.flist = flist
root = root or flist.root
@@ -165,7 +170,7 @@ 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,
@@ -219,6 +224,7 @@ 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
@@ -408,7 +414,7 @@ class EditorWindow(object):
def set_status_bar(self):
self.status_bar = self.MultiStatusBar(self.top)
- if macosxSupport.runningAsOSXApp():
+ 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)
@@ -435,7 +441,7 @@ class EditorWindow(object):
("help", "_Help"),
]
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
menu_specs[-2] = ("windows", "_Window")
@@ -446,7 +452,7 @@ class EditorWindow(object):
underline, label = prepstr(label)
menudict[name] = menu = Menu(mbar, name=name)
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')
mbar.add_cascade(label='IDLE', menu=menu)
@@ -549,7 +555,7 @@ class EditorWindow(object):
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 +666,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",
@@ -702,6 +708,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()']
+ p = 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)
@@ -872,7 +886,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:
@@ -932,7 +946,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 +970,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 +1076,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)
@@ -1672,7 +1685,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 +1712,18 @@ def fixwordbreaks(root):
tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]')
-def test():
- root = Tk()
+def _editor_window(parent):
+ 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()
+ parent.mainloop()
if __name__ == '__main__':
- test()
+ from idlelib.idle_test.htest import run
+ run(_help_dialog, _editor_window)
diff --git a/Lib/idlelib/FileList.py b/Lib/idlelib/FileList.py
index 37a337e..a9989a8 100644
--- a/Lib/idlelib/FileList.py
+++ b/Lib/idlelib/FileList.py
@@ -103,7 +103,7 @@ class FileList:
if not os.path.isabs(filename):
try:
pwd = os.getcwd()
- except os.error:
+ except OSError:
pass
else:
filename = os.path.join(pwd, filename)
diff --git a/Lib/idlelib/FormatParagraph.py b/Lib/idlelib/FormatParagraph.py
index ae4e6e7..9b10c0a 100644
--- a/Lib/idlelib/FormatParagraph.py
+++ b/Lib/idlelib/FormatParagraph.py
@@ -32,7 +32,7 @@ class FormatParagraph:
def close(self):
self.editwin = None
- def format_paragraph_event(self, event):
+ def format_paragraph_event(self, event, limit=None):
"""Formats paragraph to a max width specified in idleConf.
If text is selected, format_paragraph_event will start breaking lines
@@ -41,9 +41,12 @@ class FormatParagraph:
If no text is selected, format_paragraph_event uses the current
cursor location to determine the paragraph (lines of text surrounded
by blank lines) and formats it.
+
+ The length limit parameter is for testing with a known value.
"""
- maxformatwidth = idleConf.GetOption(
- 'main', 'FormatParagraph', 'paragraph', type='int')
+ if limit == None:
+ limit = idleConf.GetOption(
+ 'main', 'FormatParagraph', 'paragraph', type='int')
text = self.editwin.text
first, last = self.editwin.get_selection_indices()
if first and last:
@@ -53,9 +56,9 @@ class FormatParagraph:
first, last, comment_header, data = \
find_paragraph(text, text.index("insert"))
if comment_header:
- newdata = reformat_comment(data, maxformatwidth, comment_header)
+ newdata = reformat_comment(data, limit, comment_header)
else:
- newdata = reformat_paragraph(data, maxformatwidth)
+ newdata = reformat_paragraph(data, limit)
text.tag_remove("sel", "1.0", "end")
if newdata != data:
@@ -185,7 +188,6 @@ def get_comment_header(line):
return m.group(1)
if __name__ == "__main__":
- from test import support; support.use_resources = ['gui']
import unittest
unittest.main('idlelib.idle_test.test_formatparagraph',
verbosity=2, exit=False)
diff --git a/Lib/idlelib/GrepDialog.py b/Lib/idlelib/GrepDialog.py
index c359074..5d22a9e 100644
--- a/Lib/idlelib/GrepDialog.py
+++ b/Lib/idlelib/GrepDialog.py
@@ -1,9 +1,14 @@
import os
import fnmatch
+import re # for htest
import sys
-from tkinter import *
+from tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog
+from tkinter import Tk, Text, Button, SEL, END # for htest
from idlelib import SearchEngine
+import itertools
from idlelib.SearchDialogBase import SearchDialogBase
+# Importing OutputWindow fails due to import loop
+# EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow
def grep(text, io=None, flist=None):
root = text._root()
@@ -40,10 +45,10 @@ class GrepDialog(SearchDialogBase):
def create_entries(self):
SearchDialogBase.create_entries(self)
- self.globent = self.make_entry("In files:", self.globvar)
+ self.globent = self.make_entry("In files:", self.globvar)[0]
def create_other_buttons(self):
- f = self.make_frame()
+ f = self.make_frame()[0]
btn = Checkbutton(f, anchor="w",
variable=self.recvar,
@@ -63,7 +68,7 @@ class GrepDialog(SearchDialogBase):
if not path:
self.top.bell()
return
- from idlelib.OutputWindow import OutputWindow
+ from idlelib.OutputWindow import OutputWindow # leave here!
save = sys.stdout
try:
sys.stdout = OutputWindow(self.flist)
@@ -79,21 +84,26 @@ class GrepDialog(SearchDialogBase):
pat = self.engine.getpat()
print("Searching %r in %s ..." % (pat, path))
hits = 0
- for fn in list:
- try:
- with open(fn, errors='replace') as f:
- for lineno, line in enumerate(f, 1):
- if line[-1:] == '\n':
- line = line[:-1]
- if prog.search(line):
- sys.stdout.write("%s: %s: %s\n" %
- (fn, lineno, line))
- hits += 1
- except OSError as msg:
- print(msg)
- print(("Hits found: %s\n"
- "(Hint: right-click to open locations.)"
- % hits) if hits else "No hits.")
+ try:
+ for fn in list:
+ try:
+ with open(fn, errors='replace') as f:
+ for lineno, line in enumerate(f, 1):
+ if line[-1:] == '\n':
+ line = line[:-1]
+ if prog.search(line):
+ sys.stdout.write("%s: %s: %s\n" %
+ (fn, lineno, line))
+ hits += 1
+ except OSError as msg:
+ print(msg)
+ print(("Hits found: %s\n"
+ "(Hint: right-click to open locations.)"
+ % hits) if hits else "No hits.")
+ except AttributeError:
+ # Tk window has been closed, OutputWindow.text = None,
+ # so in OW.write, OW.text.insert fails.
+ pass
def findfiles(self, dir, base, rec):
try:
@@ -120,9 +130,30 @@ class GrepDialog(SearchDialogBase):
self.top.grab_release()
self.top.withdraw()
+
+def _grep_dialog(parent): # for htest
+ from idlelib.PyShell import PyShellFileList
+ root = Tk()
+ root.title("Test GrepDialog")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+
+ flist = PyShellFileList(root)
+ text = Text(root, height=5)
+ text.pack()
+
+ def show_grep_dialog():
+ text.tag_add(SEL, "1.0", END)
+ grep(text, flist=flist)
+ text.tag_remove(SEL, "1.0", END)
+
+ button = Button(root, text="Show GrepDialog", command=show_grep_dialog)
+ button.pack()
+ root.mainloop()
+
if __name__ == "__main__":
- # A human test is a bit tricky since EditorWindow() imports this module.
- # Hence Idle must be restarted after editing this file for a live test.
import unittest
unittest.main('idlelib.idle_test.test_grep', verbosity=2, exit=False)
+ from idlelib.idle_test.htest import run
+ run(_grep_dialog)
diff --git a/Lib/idlelib/HyperParser.py b/Lib/idlelib/HyperParser.py
index 4af4b08..77cb057 100644
--- a/Lib/idlelib/HyperParser.py
+++ b/Lib/idlelib/HyperParser.py
@@ -1,23 +1,31 @@
-"""
-HyperParser
-===========
-This module defines the HyperParser class, which provides advanced parsing
-abilities for the ParenMatch and other extensions.
-The HyperParser uses PyParser. PyParser is intended mostly to give information
-on the proper indentation of code. HyperParser gives some information on the
-structure of code, used by extensions to help the user.
+"""Provide advanced parsing abilities for ParenMatch and other extensions.
+
+HyperParser uses PyParser. PyParser mostly gives information on the
+proper indentation of code. HyperParser gives additional information on
+the structure of code.
"""
import string
-import keyword
+from keyword import iskeyword
from idlelib import PyParse
-class HyperParser:
+# all ASCII chars that may be in an identifier
+_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
+# all ASCII chars that may be the first char of an identifier
+_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
+
+# lookup table for whether 7-bit ASCII chars are valid in a Python identifier
+_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
+# lookup table for whether 7-bit ASCII chars are valid as the first
+# char in a Python identifier
+_IS_ASCII_ID_FIRST_CHAR = \
+ [(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]
+
+
+class HyperParser:
def __init__(self, editwin, index):
- """Initialize the HyperParser to analyze the surroundings of the given
- index.
- """
+ "To initialize, analyze the surroundings of the given index."
self.editwin = editwin
self.text = text = editwin.text
@@ -33,9 +41,10 @@ class HyperParser:
startat = max(lno - context, 1)
startatindex = repr(startat) + ".0"
stopatindex = "%d.end" % lno
- # We add the newline because PyParse requires a newline at end.
- # We add a space so that index won't be at end of line, so that
- # its status will be the same as the char before it, if should.
+ # We add the newline because PyParse requires a newline
+ # at end. We add a space so that index won't be at end
+ # of line, so that its status will be the same as the
+ # char before it, if should.
parser.set_str(text.get(startatindex, stopatindex)+' \n')
bod = parser.find_good_parse_start(
editwin._build_char_in_string_func(startatindex))
@@ -49,122 +58,175 @@ class HyperParser:
else:
startatindex = "1.0"
stopatindex = "%d.end" % lno
- # We add the newline because PyParse requires a newline at end.
- # We add a space so that index won't be at end of line, so that
- # its status will be the same as the char before it, if should.
+ # We add the newline because PyParse requires it. We add a
+ # space so that index won't be at end of line, so that its
+ # status will be the same as the char before it, if should.
parser.set_str(text.get(startatindex, stopatindex)+' \n')
parser.set_lo(0)
- # We want what the parser has, except for the last newline and space.
+ # We want what the parser has, minus the last newline and space.
self.rawtext = parser.str[:-2]
- # As far as I can see, parser.str preserves the statement we are in,
- # so that stopatindex can be used to synchronize the string with the
- # text box indices.
+ # Parser.str apparently preserves the statement we are in, so
+ # that stopatindex can be used to synchronize the string with
+ # the text box indices.
self.stopatindex = stopatindex
self.bracketing = parser.get_last_stmt_bracketing()
- # find which pairs of bracketing are openers. These always correspond
- # to a character of rawtext.
- self.isopener = [i>0 and self.bracketing[i][1] > self.bracketing[i-1][1]
+ # find which pairs of bracketing are openers. These always
+ # correspond to a character of rawtext.
+ self.isopener = [i>0 and self.bracketing[i][1] >
+ self.bracketing[i-1][1]
for i in range(len(self.bracketing))]
self.set_index(index)
def set_index(self, index):
- """Set the index to which the functions relate. Note that it must be
- in the same statement.
+ """Set the index to which the functions relate.
+
+ The index must be in the same statement.
"""
- indexinrawtext = \
- len(self.rawtext) - len(self.text.get(index, self.stopatindex))
+ indexinrawtext = (len(self.rawtext) -
+ len(self.text.get(index, self.stopatindex)))
if indexinrawtext < 0:
- raise ValueError("The index given is before the analyzed statement")
+ raise ValueError("Index %s precedes the analyzed statement"
+ % index)
self.indexinrawtext = indexinrawtext
# find the rightmost bracket to which index belongs
self.indexbracket = 0
- while self.indexbracket < len(self.bracketing)-1 and \
- self.bracketing[self.indexbracket+1][0] < self.indexinrawtext:
+ while (self.indexbracket < len(self.bracketing)-1 and
+ self.bracketing[self.indexbracket+1][0] < self.indexinrawtext):
self.indexbracket += 1
- if self.indexbracket < len(self.bracketing)-1 and \
- self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and \
- not self.isopener[self.indexbracket+1]:
+ if (self.indexbracket < len(self.bracketing)-1 and
+ self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and
+ not self.isopener[self.indexbracket+1]):
self.indexbracket += 1
def is_in_string(self):
- """Is the index given to the HyperParser is in a string?"""
+ """Is the index given to the HyperParser in a string?"""
# The bracket to which we belong should be an opener.
# If it's an opener, it has to have a character.
- return self.isopener[self.indexbracket] and \
- self.rawtext[self.bracketing[self.indexbracket][0]] in ('"', "'")
+ return (self.isopener[self.indexbracket] and
+ self.rawtext[self.bracketing[self.indexbracket][0]]
+ in ('"', "'"))
def is_in_code(self):
- """Is the index given to the HyperParser is in a normal code?"""
- return not self.isopener[self.indexbracket] or \
- self.rawtext[self.bracketing[self.indexbracket][0]] not in \
- ('#', '"', "'")
+ """Is the index given to the HyperParser in normal code?"""
+ return (not self.isopener[self.indexbracket] or
+ self.rawtext[self.bracketing[self.indexbracket][0]]
+ not in ('#', '"', "'"))
def get_surrounding_brackets(self, openers='([{', mustclose=False):
- """If the index given to the HyperParser is surrounded by a bracket
- defined in openers (or at least has one before it), return the
- indices of the opening bracket and the closing bracket (or the
- end of line, whichever comes first).
- If it is not surrounded by brackets, or the end of line comes before
- the closing bracket and mustclose is True, returns None.
+ """Return bracket indexes or None.
+
+ If the index given to the HyperParser is surrounded by a
+ bracket defined in openers (or at least has one before it),
+ return the indices of the opening bracket and the closing
+ bracket (or the end of line, whichever comes first).
+
+ If it is not surrounded by brackets, or the end of line comes
+ before the closing bracket and mustclose is True, returns None.
"""
+
bracketinglevel = self.bracketing[self.indexbracket][1]
before = self.indexbracket
- while not self.isopener[before] or \
- self.rawtext[self.bracketing[before][0]] not in openers or \
- self.bracketing[before][1] > bracketinglevel:
+ while (not self.isopener[before] or
+ self.rawtext[self.bracketing[before][0]] not in openers or
+ self.bracketing[before][1] > bracketinglevel):
before -= 1
if before < 0:
return None
bracketinglevel = min(bracketinglevel, self.bracketing[before][1])
after = self.indexbracket + 1
- while after < len(self.bracketing) and \
- self.bracketing[after][1] >= bracketinglevel:
+ while (after < len(self.bracketing) and
+ self.bracketing[after][1] >= bracketinglevel):
after += 1
beforeindex = self.text.index("%s-%dc" %
(self.stopatindex, len(self.rawtext)-self.bracketing[before][0]))
- if after >= len(self.bracketing) or \
- self.bracketing[after][0] > len(self.rawtext):
+ if (after >= len(self.bracketing) or
+ self.bracketing[after][0] > len(self.rawtext)):
if mustclose:
return None
afterindex = self.stopatindex
else:
- # We are after a real char, so it is a ')' and we give the index
- # before it.
- afterindex = self.text.index("%s-%dc" %
- (self.stopatindex,
+ # We are after a real char, so it is a ')' and we give the
+ # index before it.
+ afterindex = self.text.index(
+ "%s-%dc" % (self.stopatindex,
len(self.rawtext)-(self.bracketing[after][0]-1)))
return beforeindex, afterindex
- # This string includes all chars that may be in a white space
- _whitespace_chars = " \t\n\\"
- # This string includes all chars that may be in an identifier
- _id_chars = string.ascii_letters + string.digits + "_"
- # This string includes all chars that may be the first char of an identifier
- _id_first_chars = string.ascii_letters + "_"
-
- # Given a string and pos, return the number of chars in the identifier
- # which ends at pos, or 0 if there is no such one. Saved words are not
- # identifiers.
- def _eat_identifier(self, str, limit, pos):
+ # the set of built-in identifiers which are also keywords,
+ # i.e. keyword.iskeyword() returns True for them
+ _ID_KEYWORDS = frozenset({"True", "False", "None"})
+
+ @classmethod
+ def _eat_identifier(cls, str, limit, pos):
+ """Given a string and pos, return the number of chars in the
+ identifier which ends at pos, or 0 if there is no such one.
+
+ This ignores non-identifier eywords are not identifiers.
+ """
+ is_ascii_id_char = _IS_ASCII_ID_CHAR
+
+ # Start at the end (pos) and work backwards.
i = pos
- while i > limit and str[i-1] in self._id_chars:
+
+ # Go backwards as long as the characters are valid ASCII
+ # identifier characters. This is an optimization, since it
+ # is faster in the common case where most of the characters
+ # are ASCII.
+ while i > limit and (
+ ord(str[i - 1]) < 128 and
+ is_ascii_id_char[ord(str[i - 1])]
+ ):
i -= 1
- if i < pos and (str[i] not in self._id_first_chars or \
- keyword.iskeyword(str[i:pos])):
- i = pos
+
+ # If the above loop ended due to reaching a non-ASCII
+ # character, continue going backwards using the most generic
+ # test for whether a string contains only valid identifier
+ # characters.
+ if i > limit and ord(str[i - 1]) >= 128:
+ while i - 4 >= limit and ('a' + str[i - 4:pos]).isidentifier():
+ i -= 4
+ if i - 2 >= limit and ('a' + str[i - 2:pos]).isidentifier():
+ i -= 2
+ if i - 1 >= limit and ('a' + str[i - 1:pos]).isidentifier():
+ i -= 1
+
+ # The identifier candidate starts here. If it isn't a valid
+ # identifier, don't eat anything. At this point that is only
+ # possible if the first character isn't a valid first
+ # character for an identifier.
+ if not str[i:pos].isidentifier():
+ return 0
+ elif i < pos:
+ # All characters in str[i:pos] are valid ASCII identifier
+ # characters, so it is enough to check that the first is
+ # valid as the first character of an identifier.
+ if not _IS_ASCII_ID_FIRST_CHAR[ord(str[i])]:
+ return 0
+
+ # All keywords are valid identifiers, but should not be
+ # considered identifiers here, except for True, False and None.
+ if i < pos and (
+ iskeyword(str[i:pos]) and
+ str[i:pos] not in cls._ID_KEYWORDS
+ ):
+ return 0
+
return pos - i
+ # This string includes all chars that may be in a white space
+ _whitespace_chars = " \t\n\\"
+
def get_expression(self):
- """Return a string with the Python expression which ends at the given
- index, which is empty if there is no real one.
+ """Return a string with the Python expression which ends at the
+ given index, which is empty if there is no real one.
"""
if not self.is_in_code():
- raise ValueError("get_expression should only be called if index "\
- "is inside a code.")
+ raise ValueError("get_expression should only be called"
+ "if index is inside a code.")
rawtext = self.rawtext
bracketing = self.bracketing
@@ -177,20 +239,20 @@ class HyperParser:
postdot_phase = True
while 1:
- # Eat whitespaces, comments, and if postdot_phase is False - one dot
+ # Eat whitespaces, comments, and if postdot_phase is False - a dot
while 1:
if pos>brck_limit and rawtext[pos-1] in self._whitespace_chars:
# Eat a whitespace
pos -= 1
- elif not postdot_phase and \
- pos > brck_limit and rawtext[pos-1] == '.':
+ elif (not postdot_phase and
+ pos > brck_limit and rawtext[pos-1] == '.'):
# Eat a dot
pos -= 1
postdot_phase = True
- # The next line will fail if we are *inside* a comment, but we
- # shouldn't be.
- elif pos == brck_limit and brck_index > 0 and \
- rawtext[bracketing[brck_index-1][0]] == '#':
+ # The next line will fail if we are *inside* a comment,
+ # but we shouldn't be.
+ elif (pos == brck_limit and brck_index > 0 and
+ rawtext[bracketing[brck_index-1][0]] == '#'):
# Eat a comment
brck_index -= 2
brck_limit = bracketing[brck_index][0]
@@ -200,8 +262,8 @@ class HyperParser:
break
if not postdot_phase:
- # We didn't find a dot, so the expression end at the last
- # identifier pos.
+ # We didn't find a dot, so the expression end at the
+ # last identifier pos.
break
ret = self._eat_identifier(rawtext, brck_limit, pos)
@@ -209,13 +271,13 @@ class HyperParser:
# There is an identifier to eat
pos = pos - ret
last_identifier_pos = pos
- # Now, in order to continue the search, we must find a dot.
+ # Now, to continue the search, we must find a dot.
postdot_phase = False
# (the loop continues now)
elif pos == brck_limit:
- # We are at a bracketing limit. If it is a closing bracket,
- # eat the bracket, otherwise, stop the search.
+ # We are at a bracketing limit. If it is a closing
+ # bracket, eat the bracket, otherwise, stop the search.
level = bracketing[brck_index][1]
while brck_index > 0 and bracketing[brck_index-1][1] > level:
brck_index -= 1
@@ -244,3 +306,8 @@ class HyperParser:
break
return rawtext[last_identifier_pos:self.indexinrawtext]
+
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_hyperparser', verbosity=2)
diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py
index f008b46..841a308 100644
--- a/Lib/idlelib/IOBinding.py
+++ b/Lib/idlelib/IOBinding.py
@@ -506,7 +506,7 @@ class IOBinding:
else:
try:
pwd = os.getcwd()
- except os.error:
+ except OSError:
pwd = ""
return pwd, ""
@@ -525,16 +525,17 @@ class IOBinding:
if self.editwin.flist:
self.editwin.update_recent_files_list(filename)
-def test():
+def _io_binding(parent):
root = Tk()
+ root.title("Test IOBinding")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
class MyEditWin:
def __init__(self, text):
self.text = text
self.flist = None
self.text.bind("<Control-o>", self.open)
self.text.bind("<Control-s>", self.save)
- self.text.bind("<Alt-s>", self.save_as)
- self.text.bind("<Alt-z>", self.save_a_copy)
def get_saved(self): return 0
def set_saved(self, flag): pass
def reset_undo(self): pass
@@ -542,16 +543,13 @@ def test():
self.text.event_generate("<<open-window-from-file>>")
def save(self, event):
self.text.event_generate("<<save-window>>")
- def save_as(self, event):
- self.text.event_generate("<<save-window-as-file>>")
- def save_a_copy(self, event):
- self.text.event_generate("<<save-copy-of-window-as-file>>")
+
text = Text(root)
text.pack()
text.focus_set()
editwin = MyEditWin(text)
io = IOBinding(editwin)
- root.mainloop()
if __name__ == "__main__":
- test()
+ from idlelib.idle_test.htest import run
+ run(_io_binding)
diff --git a/Lib/idlelib/IdleHistory.py b/Lib/idlelib/IdleHistory.py
index d6cb162..078af29 100644
--- a/Lib/idlelib/IdleHistory.py
+++ b/Lib/idlelib/IdleHistory.py
@@ -100,7 +100,5 @@ class History:
self.prefix = None
if __name__ == "__main__":
- from test import support
- support.use_resources = ['gui']
from unittest import main
main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False)
diff --git a/Lib/idlelib/MultiCall.py b/Lib/idlelib/MultiCall.py
index 64729ea..ee31f30 100644
--- a/Lib/idlelib/MultiCall.py
+++ b/Lib/idlelib/MultiCall.py
@@ -32,7 +32,6 @@ Each function will be called at most once for each event.
import sys
import re
import tkinter
-from idlelib import macosxSupport
# the event type constants, which define the meaning of mc_type
MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
@@ -45,7 +44,7 @@ MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
MC_OPTION = 1<<6; MC_COMMAND = 1<<7
# define the list of modifiers, to be used in complex event types.
-if macosxSupport.runningAsOSXApp():
+if sys.platform == "darwin":
_modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
_modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
else:
@@ -57,6 +56,13 @@ _modifier_names = dict([(name, number)
for number in range(len(_modifiers))
for name in _modifiers[number]])
+# In 3.4, if no shell window is ever open, the underlying Tk widget is
+# destroyed before .__del__ methods here are called. The following
+# is used to selectively ignore shutdown exceptions to avoid
+# 'Exception ignored' messages. See http://bugs.python.org/issue20167
+APPLICATION_GONE = '''\
+can't invoke "bind" command: application has been destroyed'''
+
# A binder is a class which binds functions to one type of event. It has two
# methods: bind and unbind, which get a function and a parsed sequence, as
# returned by _parse_sequence(). There are two types of binders:
@@ -98,7 +104,14 @@ class _SimpleBinder:
def __del__(self):
if self.handlerid:
- self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
+ try:
+ self.widget.unbind(self.widgetinst, self.sequence,
+ self.handlerid)
+ except tkinter.TclError as e:
+ if e.args[0] == APPLICATION_GONE:
+ pass
+ else:
+ raise
# An int in range(1 << len(_modifiers)) represents a combination of modifiers
# (if the least significent bit is on, _modifiers[0] is on, and so on).
@@ -227,7 +240,13 @@ class _ComplexBinder:
def __del__(self):
for seq, id in self.handlerids:
- self.widget.unbind(self.widgetinst, seq, id)
+ try:
+ self.widget.unbind(self.widgetinst, seq, id)
+ except tkinter.TclError as e:
+ if e.args[0] == APPLICATION_GONE:
+ break
+ else:
+ raise
# define the list of event types to be handled by MultiEvent. the order is
# compatible with the definition of event type constants.
@@ -390,15 +409,23 @@ def MultiCallCreator(widget):
func, triplets = self.__eventinfo[virtual]
if func:
for triplet in triplets:
- self.__binders[triplet[1]].unbind(triplet, func)
-
+ try:
+ self.__binders[triplet[1]].unbind(triplet, func)
+ except tkinter.TclError as e:
+ if e.args[0] == APPLICATION_GONE:
+ break
+ else:
+ raise
_multicall_dict[widget] = MultiCall
return MultiCall
-if __name__ == "__main__":
- # Test
+
+def _multi_call(parent):
root = tkinter.Tk()
+ root.title("Test MultiCall")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
text = MultiCallCreator(tkinter.Text)(root)
text.pack()
def bindseq(seq, n=[0]):
@@ -414,8 +441,13 @@ if __name__ == "__main__":
bindseq("<Alt-Control-Key-a>")
bindseq("<Key-b>")
bindseq("<Control-Button-1>")
+ bindseq("<Button-2>")
bindseq("<Alt-Button-1>")
bindseq("<FocusOut>")
bindseq("<Enter>")
bindseq("<Leave>")
root.mainloop()
+
+if __name__ == "__main__":
+ from idlelib.idle_test.htest import run
+ run(_multi_call)
diff --git a/Lib/idlelib/MultiStatusBar.py b/Lib/idlelib/MultiStatusBar.py
index 4fc8dcf..f44b6a8 100644
--- a/Lib/idlelib/MultiStatusBar.py
+++ b/Lib/idlelib/MultiStatusBar.py
@@ -17,16 +17,29 @@ class MultiStatusBar(Frame):
label = self.labels[name]
label.config(text=text)
-def _test():
- b = Frame()
- c = Text(b)
- c.pack(side=TOP)
- a = MultiStatusBar(b)
- a.set_label("one", "hello")
- a.set_label("two", "world")
- a.pack(side=BOTTOM, fill=X)
- b.pack()
- b.mainloop()
+def _multistatus_bar(parent):
+ root = Tk()
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d" %(x, y + 150))
+ root.title("Test multistatus bar")
+ frame = Frame(root)
+ text = Text(frame)
+ text.pack()
+ msb = MultiStatusBar(frame)
+ msb.set_label("one", "hello")
+ msb.set_label("two", "world")
+ msb.pack(side=BOTTOM, fill=X)
+
+ def change():
+ msb.set_label("one", "foo")
+ msb.set_label("two", "bar")
+
+ button = Button(root, text="Update status", command=change)
+ button.pack(side=BOTTOM)
+ frame.pack()
+ frame.mainloop()
+ root.mainloop()
if __name__ == '__main__':
- _test()
+ from idlelib.idle_test.htest import run
+ run(_multistatus_bar)
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 6388d0d..953a38d 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -1,74 +1,10 @@
-What's New in IDLE 3.3.4?
-=========================
-
-- Issue #17390: Add Python version to Idle editor window title bar.
- Original patches by Edmond Burnett and Kent Johnson.
-
-- Issue #18960: IDLE now ignores the source encoding declaration on the second
- line if the first line contains anything except a comment.
-
-- Issue #20058: sys.stdin.readline() in IDLE now always returns only one line.
-
-- Issue #19481: print() of string subclass instance in IDLE no longer hangs.
-
-- Issue #18270: Prevent possible IDLE AttributeError on OS X when no initial
- shell window is present.
-
-
-What's New in IDLE 3.3.3?
-=========================
-
-- Issue #18873: IDLE now detects Python source code encoding only in comment
- lines.
-
-- Issue #18988: The "Tab" key now works when a word is already autocompleted.
-
-- Issue #18489: Add tests for SearchEngine. Original patch by Phil Webster.
-
-- Issue #18429: Format / Format Paragraph, now works when comment blocks
- are selected. As with text blocks, this works best when the selection
- only includes complete lines.
-
-- Issue #18226: Add docstrings and unittests for FormatParagraph.py.
- Original patches by Todd Rovito and Phil Webster.
-
-- Issue #18279: Format - Strip trailing whitespace no longer marks a file as
- changed when it has not been changed. This fix followed the addition of a
- test file originally written by Phil Webster (the issue's main goal).
-
-- Issue #7136: In the Idle File menu, "New Window" is renamed "New File".
- Patch by Tal Einat, Roget Serwy, and Todd Rovito.
-
-- Remove dead imports of imp.
-
-- Issue #18196: Avoid displaying spurious SystemExit tracebacks.
-
-- Issue #5492: Avoid traceback when exiting IDLE caused by a race condition.
-
-- Issue #17511: Keep IDLE find dialog open after clicking "Find Next".
- Original patch by Sarah K.
-
-- Issue #18055: Move IDLE off of imp and on to importlib.
-
-- Issue #15392: Create a unittest framework for IDLE.
- Initial patch by Rajagopalasarma Jayakrishnan.
- See Lib/idlelib/idle_test/README.txt for how to run Idle tests.
-
-- Issue #14146: Highlight source line while debugging on Windows.
-
-- Issue #17532: Always include Options menu for IDLE on OS X.
- Patch by Guilherme Simões.
-
-
-What's New in IDLE 3.3.2?
+What's New in IDLE 3.4.0?
=========================
- Issue #17390: Display Python version on Idle title bar.
Initial patch by Edmond Burnett.
-
-What's New in IDLE 3.3.1?
-=========================
+- Issue #5066: Update IDLE docs. Patch by Todd Rovito.
- Issue #17625: Close the replace dialog after it is used.
diff --git a/Lib/idlelib/ObjectBrowser.py b/Lib/idlelib/ObjectBrowser.py
index b359efc..7b57aa4 100644
--- a/Lib/idlelib/ObjectBrowser.py
+++ b/Lib/idlelib/ObjectBrowser.py
@@ -9,6 +9,8 @@
# XXX TO DO:
# - for classes/modules, add "open source" to object browser
+import re
+
from idlelib.TreeWidget import TreeItem, TreeNode, ScrolledCanvas
from reprlib import Repr
@@ -119,12 +121,14 @@ def make_objecttreeitem(labeltext, object, setfunction=None):
c = ObjectTreeItem
return c(labeltext, object, setfunction)
-# Test script
-def _test():
+def _object_browser(parent):
import sys
from tkinter import Tk
root = Tk()
+ root.title("Test ObjectBrowser")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
root.configure(bd=0, bg="yellow")
root.focus_set()
sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
@@ -135,4 +139,5 @@ def _test():
root.mainloop()
if __name__ == '__main__':
- _test()
+ from idlelib.idle_test.htest import run
+ run(_object_browser)
diff --git a/Lib/idlelib/ParenMatch.py b/Lib/idlelib/ParenMatch.py
index 6d91b39..19bad8c 100644
--- a/Lib/idlelib/ParenMatch.py
+++ b/Lib/idlelib/ParenMatch.py
@@ -90,7 +90,8 @@ class ParenMatch:
self.set_timeout = self.set_timeout_none
def flash_paren_event(self, event):
- indices = HyperParser(self.editwin, "insert").get_surrounding_brackets()
+ indices = (HyperParser(self.editwin, "insert")
+ .get_surrounding_brackets())
if indices is None:
self.warn_mismatched()
return
@@ -167,6 +168,11 @@ class ParenMatch:
# associate a counter with an event; only disable the "paren"
# tag if the event is for the most recent timer.
self.counter += 1
- self.editwin.text_frame.after(self.FLASH_DELAY,
- lambda self=self, c=self.counter: \
- self.handle_restore_timer(c))
+ self.editwin.text_frame.after(
+ self.FLASH_DELAY,
+ lambda self=self, c=self.counter: self.handle_restore_timer(c))
+
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2)
diff --git a/Lib/idlelib/PathBrowser.py b/Lib/idlelib/PathBrowser.py
index ba40719..58ff830 100644
--- a/Lib/idlelib/PathBrowser.py
+++ b/Lib/idlelib/PathBrowser.py
@@ -4,10 +4,16 @@ import importlib.machinery
from idlelib.TreeWidget import TreeItem
from idlelib.ClassBrowser import ClassBrowser, ModuleBrowserTreeItem
+from idlelib.PyShell import PyShellFileList
+
class PathBrowser(ClassBrowser):
- def __init__(self, flist):
+ def __init__(self, flist, _htest=False):
+ """
+ _htest - bool, change box location when running htest
+ """
+ self._htest = _htest
self.init(flist)
def settitle(self):
@@ -44,7 +50,7 @@ class DirBrowserTreeItem(TreeItem):
def GetSubList(self):
try:
names = os.listdir(self.dir or os.curdir)
- except os.error:
+ except OSError:
return []
packages = []
for name in names:
@@ -87,12 +93,14 @@ class DirBrowserTreeItem(TreeItem):
sorted.sort()
return sorted
-def main():
- from idlelib import PyShell
- PathBrowser(PyShell.flist)
- if sys.stdin is sys.__stdin__:
- mainloop()
+def _path_browser(parent):
+ flist = PyShellFileList(parent)
+ PathBrowser(flist, _htest=True)
+ parent.mainloop()
if __name__ == "__main__":
from unittest import main
main('idlelib.idle_test.test_pathbrowser', verbosity=2, exit=False)
+
+ from idlelib.idle_test.htest import run
+ run(_path_browser)
diff --git a/Lib/idlelib/Percolator.py b/Lib/idlelib/Percolator.py
index c91de38..9e93319 100644
--- a/Lib/idlelib/Percolator.py
+++ b/Lib/idlelib/Percolator.py
@@ -51,8 +51,9 @@ class Percolator:
f.setdelegate(filter.delegate)
filter.setdelegate(None)
-def main():
- import tkinter as Tk
+def _percolator(parent):
+ import tkinter as tk
+ import re
class Tracer(Delegator):
def __init__(self, name):
self.name = name
@@ -63,22 +64,41 @@ def main():
def delete(self, *args):
print(self.name, ": delete", args)
self.delegate.delete(*args)
- root = Tk.Tk()
- root.wm_protocol("WM_DELETE_WINDOW", root.quit)
- text = Tk.Text()
- text.pack()
- text.focus_set()
+ root = tk.Tk()
+ root.title("Test Percolator")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ text = tk.Text(root)
p = Percolator(text)
t1 = Tracer("t1")
t2 = Tracer("t2")
- p.insertfilter(t1)
- p.insertfilter(t2)
- root.mainloop() # click close widget to continue...
- p.removefilter(t2)
- root.mainloop()
- p.insertfilter(t2)
- p.removefilter(t1)
+
+ def toggle1():
+ if var1.get() == 0:
+ var1.set(1)
+ p.insertfilter(t1)
+ elif var1.get() == 1:
+ var1.set(0)
+ p.removefilter(t1)
+
+ def toggle2():
+ if var2.get() == 0:
+ var2.set(1)
+ p.insertfilter(t2)
+ elif var2.get() == 1:
+ var2.set(0)
+ p.removefilter(t2)
+
+ text.pack()
+ var1 = tk.IntVar()
+ cb1 = tk.Checkbutton(root, text="Tracer1", command=toggle1, variable=var1)
+ cb1.pack()
+ var2 = tk.IntVar()
+ cb2 = tk.Checkbutton(root, text="Tracer2", command=toggle2, variable=var2)
+ cb2.pack()
+
root.mainloop()
if __name__ == "__main__":
- main()
+ from idlelib.idle_test.htest import run
+ run(_percolator)
diff --git a/Lib/idlelib/PyParse.py b/Lib/idlelib/PyParse.py
index 61a0003..3e501ca 100644
--- a/Lib/idlelib/PyParse.py
+++ b/Lib/idlelib/PyParse.py
@@ -1,5 +1,7 @@
import re
import sys
+from collections import Mapping
+from functools import partial
# Reason last stmt is continued (or C_NONE if it's not).
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE,
@@ -91,19 +93,48 @@ _chew_ordinaryre = re.compile(r"""
[^[\](){}#'"\\]+
""", re.VERBOSE).match
-# Build translation table to map uninteresting chars to "x", open
-# brackets to "(", and close brackets to ")".
-_tran = {}
-for i in range(256):
- _tran[i] = 'x'
-for ch in "({[":
- _tran[ord(ch)] = '('
-for ch in ")}]":
- _tran[ord(ch)] = ')'
-for ch in "\"'\\\n#":
- _tran[ord(ch)] = ch
-del i, ch
+class StringTranslatePseudoMapping(Mapping):
+ r"""Utility class to be used with str.translate()
+
+ This Mapping class wraps a given dict. When a value for a key is
+ requested via __getitem__() or get(), the key is looked up in the
+ given dict. If found there, the value from the dict is returned.
+ Otherwise, the default value given upon initialization is returned.
+
+ This allows using str.translate() to make some replacements, and to
+ replace all characters for which no replacement was specified with
+ a given character instead of leaving them as-is.
+
+ For example, to replace everything except whitespace with 'x':
+
+ >>> whitespace_chars = ' \t\n\r'
+ >>> preserve_dict = {ord(c): ord(c) for c in whitespace_chars}
+ >>> mapping = StringTranslatePseudoMapping(preserve_dict, ord('x'))
+ >>> text = "a + b\tc\nd"
+ >>> text.translate(mapping)
+ 'x x x\tx\nx'
+ """
+ def __init__(self, non_defaults, default_value):
+ self._non_defaults = non_defaults
+ self._default_value = default_value
+
+ def _get(key, _get=non_defaults.get, _default=default_value):
+ return _get(key, _default)
+ self._get = _get
+
+ def __getitem__(self, item):
+ return self._get(item)
+
+ def __len__(self):
+ return len(self._non_defaults)
+
+ def __iter__(self):
+ return iter(self._non_defaults)
+
+ def get(self, key, default=None):
+ return self._get(key)
+
class Parser:
@@ -113,19 +144,6 @@ class Parser:
def set_str(self, s):
assert len(s) == 0 or s[-1] == '\n'
- if isinstance(s, str):
- # The parse functions have no idea what to do with Unicode, so
- # replace all Unicode characters with "x". This is "safe"
- # so long as the only characters germane to parsing the structure
- # of Python are 7-bit ASCII. It's *necessary* because Unicode
- # strings don't have a .translate() method that supports
- # deletechars.
- uniphooey = s
- s = []
- push = s.append
- for raw in map(ord, uniphooey):
- push(raw < 127 and chr(raw) or "x")
- s = "".join(s)
self.str = s
self.study_level = 0
@@ -197,6 +215,16 @@ class Parser:
if lo > 0:
self.str = self.str[lo:]
+ # Build a translation table to map uninteresting chars to 'x', open
+ # brackets to '(', close brackets to ')' while preserving quotes,
+ # backslashes, newlines and hashes. This is to be passed to
+ # str.translate() in _study1().
+ _tran = {}
+ _tran.update((ord(c), ord('(')) for c in "({[")
+ _tran.update((ord(c), ord(')')) for c in ")}]")
+ _tran.update((ord(c), ord(c)) for c in "\"'\\\n#")
+ _tran = StringTranslatePseudoMapping(_tran, default_value=ord('x'))
+
# As quickly as humanly possible <wink>, find the line numbers (0-
# based) of the non-continuation lines.
# Creates self.{goodlines, continuation}.
@@ -211,7 +239,7 @@ class Parser:
# uninteresting characters. This can cut the number of chars
# by a factor of 10-40, and so greatly speed the following loop.
str = self.str
- str = str.translate(_tran)
+ str = str.translate(self._tran)
str = str.replace('xxxxxxxx', 'x')
str = str.replace('xxxx', 'x')
str = str.replace('xx', 'x')
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index 2e5ebb2..7eab8d0 100755
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -21,7 +21,7 @@ from platform import python_version, system
try:
from tkinter import *
except ImportError:
- print("** IDLE can't import Tkinter. " \
+ print("** IDLE can't import Tkinter.\n"
"Your Python may not be configured for Tk. **", file=sys.__stderr__)
sys.exit(1)
import tkinter.messagebox as tkMessageBox
@@ -138,6 +138,7 @@ class PyShellEditorWindow(EditorWindow):
self.io.set_filename_change_hook(filename_changed_hook)
if self.io.filename:
self.restore_file_breaks()
+ self.color_breakpoint_text()
rmenu_specs = [
("Cut", "<<cut>>", "rmenu_check_cut"),
@@ -148,6 +149,15 @@ class PyShellEditorWindow(EditorWindow):
("Clear Breakpoint", "<<clear-breakpoint-here>>", None)
]
+ def color_breakpoint_text(self, color=True):
+ "Turn colorizing of breakpoint text on or off"
+ if color:
+ theme = idleConf.GetOption('main','Theme','name')
+ cfg = idleConf.GetHighlight(theme, "break")
+ else:
+ cfg = {'foreground': '', 'background': ''}
+ self.text.tag_config('BREAK', cfg)
+
def set_breakpoint(self, lineno):
text = self.text
filename = self.io.filename
@@ -419,7 +429,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
try:
self.rpcclt = MyRPCClient(addr)
break
- except socket.error as err:
+ except OSError as err:
pass
else:
self.display_port_binding_error()
@@ -641,9 +651,9 @@ class ModifiedInterpreter(InteractiveInterpreter):
code = compile(source, filename, "exec")
except (OverflowError, SyntaxError):
self.tkconsole.resetoutput()
- tkerr = self.tkconsole.stderr
- print('*** Error in script or command!\n', file=tkerr)
- print('Traceback (most recent call last):', file=tkerr)
+ print('*** Error in script or command!\n'
+ 'Traceback (most recent call last):',
+ file=self.tkconsole.stderr)
InteractiveInterpreter.showsyntaxerror(self, filename)
self.tkconsole.showprompt()
else:
@@ -844,7 +854,7 @@ class PyShell(OutputWindow):
("help", "_Help"),
]
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
menu_specs[-2] = ("windows", "_Window")
@@ -1034,7 +1044,10 @@ class PyShell(OutputWindow):
self.close()
return False
else:
- nosub = "==== No Subprocess ===="
+ nosub = ("==== No Subprocess ====\n\n" +
+ "WARNING: Running IDLE without a Subprocess is deprecated\n" +
+ "and will be removed in a later version. See Help/IDLE Help\n" +
+ "for details.\n\n")
sys.displayhook = rpc.displayhook
self.write("Python %s on %s\n%s\n%s" %
@@ -1398,7 +1411,8 @@ USAGE: idle [-deins] [-t title] [file]*
idle [-dns] [-t title] - [arg]*
-h print this help message and exit
- -n run IDLE without a subprocess (see Help/IDLE Help for details)
+ -n run IDLE without a subprocess (DEPRECATED,
+ see Help/IDLE Help for details)
The following options will override the IDLE 'settings' configuration:
@@ -1458,8 +1472,7 @@ def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
except getopt.error as msg:
- sys.stderr.write("Error: %s\n" % str(msg))
- sys.stderr.write(usage_msg)
+ print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr)
sys.exit(2)
for o, a in opts:
if o == '-c':
@@ -1476,6 +1489,8 @@ def main():
if o == '-i':
enable_shell = True
if o == '-n':
+ print(" Warning: running IDLE without a subprocess is deprecated.",
+ file=sys.stderr)
use_subprocess = False
if o == '-r':
script = a
@@ -1554,7 +1569,7 @@ def main():
shell = flist.open_shell()
if not shell:
return # couldn't open shell
- if macosxSupport.runningAsOSXApp() and flist.dict:
+ if macosxSupport.isAquaTk() and flist.dict:
# On OSX: when the user has double-clicked on a file that causes
# IDLE to be launched the shell window will open just in front of
# the file she wants to see. Lower the interpreter window when
diff --git a/Lib/idlelib/ReplaceDialog.py b/Lib/idlelib/ReplaceDialog.py
index e73f2c5..fc8b80f 100644
--- a/Lib/idlelib/ReplaceDialog.py
+++ b/Lib/idlelib/ReplaceDialog.py
@@ -40,7 +40,7 @@ class ReplaceDialog(SearchDialogBase):
def create_entries(self):
SearchDialogBase.create_entries(self)
- self.replent = self.make_entry("Replace with:", self.replvar)
+ self.replent = self.make_entry("Replace with:", self.replvar)[0]
def create_command_buttons(self):
SearchDialogBase.create_command_buttons(self)
@@ -188,3 +188,34 @@ class ReplaceDialog(SearchDialogBase):
def close(self, event=None):
SearchDialogBase.close(self, event)
self.text.tag_remove("hit", "1.0", "end")
+
+def _replace_dialog(parent):
+ root = Tk()
+ root.title("Test ReplaceDialog")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+
+ # mock undo delegator methods
+ def undo_block_start():
+ pass
+
+ def undo_block_stop():
+ pass
+
+ text = Text(root)
+ text.undo_block_start = undo_block_start
+ text.undo_block_stop = undo_block_stop
+ text.pack()
+ text.insert("insert","This is a sample string.\n"*10)
+
+ def show_replace():
+ text.tag_add(SEL, "1.0", END)
+ replace(text)
+ text.tag_remove(SEL, "1.0", END)
+
+ button = Button(root, text="Replace", command=show_replace)
+ button.pack()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_replace_dialog)
diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py
index 6bfe128..b783637 100644
--- a/Lib/idlelib/ScriptBinding.py
+++ b/Lib/idlelib/ScriptBinding.py
@@ -53,7 +53,7 @@ class ScriptBinding:
self.flist = self.editwin.flist
self.root = self.editwin.root
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isCocoaTk():
self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
def check_module_event(self, event):
@@ -114,7 +114,7 @@ class ScriptBinding:
shell.set_warning_stream(saved_stream)
def run_module_event(self, event):
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isCocoaTk():
# Tk-Cocoa in MacOSX is broken until at least
# Tk 8.5.9, and without this rather
# crude workaround IDLE would hang when a user
diff --git a/Lib/idlelib/ScrolledList.py b/Lib/idlelib/ScrolledList.py
index 0255a0a..71ec547 100644
--- a/Lib/idlelib/ScrolledList.py
+++ b/Lib/idlelib/ScrolledList.py
@@ -119,21 +119,22 @@ class ScrolledList:
pass
-def test():
+def _scrolled_list(parent):
root = Tk()
- root.protocol("WM_DELETE_WINDOW", root.destroy)
+ root.title("Test ScrolledList")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
class MyScrolledList(ScrolledList):
- def fill_menu(self): self.menu.add_command(label="pass")
+ def fill_menu(self): self.menu.add_command(label="right click")
def on_select(self, index): print("select", self.get(index))
def on_double(self, index): print("double", self.get(index))
- s = MyScrolledList(root)
+
+ scrolled_list = MyScrolledList(root)
for i in range(30):
- s.append("item %02d" % i)
- return root
+ scrolled_list.append("Item %02d" % i)
-def main():
- root = test()
root.mainloop()
if __name__ == '__main__':
- main()
+ from idlelib.idle_test.htest import run
+ run(_scrolled_list)
diff --git a/Lib/idlelib/SearchDialog.py b/Lib/idlelib/SearchDialog.py
index bf76c41..38408b8 100644
--- a/Lib/idlelib/SearchDialog.py
+++ b/Lib/idlelib/SearchDialog.py
@@ -65,3 +65,25 @@ class SearchDialog(SearchDialogBase):
if pat:
self.engine.setcookedpat(pat)
return self.find_again(text)
+
+def _search_dialog(parent):
+ root = Tk()
+ root.title("Test SearchDialog")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ text = Text(root)
+ text.pack()
+ text.insert("insert","This is a sample string.\n"*10)
+
+ def show_find():
+ text.tag_add(SEL, "1.0", END)
+ s = _setup(text)
+ s.open(text)
+ text.tag_remove(SEL, "1.0", END)
+
+ button = Button(root, text="Search", command=show_find)
+ button.pack()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_search_dialog)
diff --git a/Lib/idlelib/SearchDialogBase.py b/Lib/idlelib/SearchDialogBase.py
index b8b49b2..5fa84e2 100644
--- a/Lib/idlelib/SearchDialogBase.py
+++ b/Lib/idlelib/SearchDialogBase.py
@@ -1,34 +1,51 @@
'''Define SearchDialogBase used by Search, Replace, and Grep dialogs.'''
-from tkinter import *
+
+from tkinter import (Toplevel, Frame, Entry, Label, Button,
+ Checkbutton, Radiobutton)
class SearchDialogBase:
- '''Create most of a modal search dialog (make_frame, create_widgets).
+ '''Create most of a 3 or 4 row, 3 column search dialog.
- The wide left column contains:
- 1 or 2 text entry lines (create_entries, make_entry);
- a row of standard radiobuttons (create_option_buttons);
- a row of dialog specific radiobuttons (create_other_buttons).
+ The left and wide middle column contain:
+ 1 or 2 labeled text entry lines (make_entry, create_entries);
+ a row of standard Checkbuttons (make_frame, create_option_buttons),
+ each of which corresponds to a search engine Variable;
+ a row of dialog-specific Check/Radiobuttons (create_other_buttons).
The narrow right column contains command buttons
- (create_command_buttons, make_button).
+ (make_button, create_command_buttons).
These are bound to functions that execute the command.
- Except for command buttons, this base class is not limited to
- items common to all three subclasses. Rather, it is the Find dialog
- minus the "Find Next" command and its execution function.
- The other dialogs override methods to replace and add widgets.
+ Except for command buttons, this base class is not limited to items
+ common to all three subclasses. Rather, it is the Find dialog minus
+ the "Find Next" command, its execution function, and the
+ default_command attribute needed in create_widgets. The other
+ dialogs override attributes and methods, the latter to replace and
+ add widgets.
'''
- title = "Search Dialog"
+ title = "Search Dialog" # replace in subclasses
icon = "Search"
- needwrapbutton = 1
+ needwrapbutton = 1 # not in Find in Files
def __init__(self, root, engine):
+ '''Initialize root, engine, and top attributes.
+
+ top (level widget): set in create_widgets() called from open().
+ text (Text searched): set in open(), only used in subclasses().
+ ent (ry): created in make_entry() called from create_entry().
+ row (of grid): 0 in create_widgets(), +1 in make_entry/frame().
+ default_command: set in subclasses, used in create_widgers().
+
+ title (of dialog): class attribute, override in subclasses.
+ icon (of dialog): ditto, use unclear if cannot minimize dialog.
+ '''
self.root = root
self.engine = engine
self.top = None
def open(self, text, searchphrase=None):
+ "Make dialog visible on top of others and ready to use."
self.text = text
if not self.top:
self.create_widgets()
@@ -44,11 +61,17 @@ class SearchDialogBase:
self.top.grab_set()
def close(self, event=None):
+ "Put dialog away for later use."
if self.top:
self.top.grab_release()
self.top.withdraw()
def create_widgets(self):
+ '''Create basic 3 row x 3 col search (find) dialog.
+
+ Other dialogs override subsidiary create_x methods as needed.
+ Replace and Find-in-Files add another entry row.
+ '''
top = Toplevel(self.root)
top.bind("<Return>", self.default_command)
top.bind("<Escape>", self.close)
@@ -61,29 +84,84 @@ class SearchDialogBase:
self.top.grid_columnconfigure(0, pad=2, weight=0)
self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
- self.create_entries()
- self.create_option_buttons()
- self.create_other_buttons()
- return self.create_command_buttons()
-
- def make_entry(self, label, var):
- l = Label(self.top, text=label)
- l.grid(row=self.row, column=0, sticky="nw")
- e = Entry(self.top, textvariable=var, exportselection=0)
- e.grid(row=self.row, column=1, sticky="nwe")
+ self.create_entries() # row 0 (and maybe 1), cols 0, 1
+ self.create_option_buttons() # next row, cols 0, 1
+ self.create_other_buttons() # next row, cols 0, 1
+ self.create_command_buttons() # col 2, all rows
+
+ def make_entry(self, label_text, var):
+ '''Return (entry, label), .
+
+ entry - gridded labeled Entry for text entry.
+ label - Label widget, returned for testing.
+ '''
+ label = Label(self.top, text=label_text)
+ label.grid(row=self.row, column=0, sticky="nw")
+ entry = Entry(self.top, textvariable=var, exportselection=0)
+ entry.grid(row=self.row, column=1, sticky="nwe")
self.row = self.row + 1
- return e
+ return entry, label
+
+ def create_entries(self):
+ "Create one or more entry lines with make_entry."
+ self.ent = self.make_entry("Find:", self.engine.patvar)[0]
def make_frame(self,labeltext=None):
+ '''Return (frame, label).
+
+ frame - gridded labeled Frame for option or other buttons.
+ label - Label widget, returned for testing.
+ '''
if labeltext:
- l = Label(self.top, text=labeltext)
- l.grid(row=self.row, column=0, sticky="nw")
- f = Frame(self.top)
- f.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
+ label = Label(self.top, text=labeltext)
+ label.grid(row=self.row, column=0, sticky="nw")
+ else:
+ label = ''
+ frame = Frame(self.top)
+ frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
self.row = self.row + 1
- return f
+ return frame, label
+
+ def create_option_buttons(self):
+ '''Return (filled frame, options) for testing.
+
+ Options is a list of SearchEngine booleanvar, label pairs.
+ A gridded frame from make_frame is filled with a Checkbutton
+ for each pair, bound to the var, with the corresponding label.
+ '''
+ frame = self.make_frame("Options")[0]
+ engine = self.engine
+ options = [(engine.revar, "Regular expression"),
+ (engine.casevar, "Match case"),
+ (engine.wordvar, "Whole word")]
+ if self.needwrapbutton:
+ options.append((engine.wrapvar, "Wrap around"))
+ for var, label in options:
+ btn = Checkbutton(frame, anchor="w", variable=var, text=label)
+ btn.pack(side="left", fill="both")
+ if var.get():
+ btn.select()
+ return frame, options
+
+ def create_other_buttons(self):
+ '''Return (frame, others) for testing.
+
+ Others is a list of value, label pairs.
+ A gridded frame from make_frame is filled with radio buttons.
+ '''
+ frame = self.make_frame("Direction")[0]
+ var = self.engine.backvar
+ others = [(1, 'Up'), (0, 'Down')]
+ for val, label in others:
+ btn = Radiobutton(frame, anchor="w",
+ variable=var, value=val, text=label)
+ btn.pack(side="left", fill="both")
+ if var.get() == val:
+ btn.select()
+ return frame, others
def make_button(self, label, command, isdef=0):
+ "Return command button gridded in command frame."
b = Button(self.buttonframe,
text=label, command=command,
default=isdef and "active" or "normal")
@@ -92,66 +170,15 @@ class SearchDialogBase:
self.buttonframe.grid(rowspan=rows+1)
return b
- def create_entries(self):
- self.ent = self.make_entry("Find:", self.engine.patvar)
-
- def create_option_buttons(self):
- f = self.make_frame("Options")
-
- btn = Checkbutton(f, anchor="w",
- variable=self.engine.revar,
- text="Regular expression")
- btn.pack(side="left", fill="both")
- if self.engine.isre():
- btn.select()
-
- btn = Checkbutton(f, anchor="w",
- variable=self.engine.casevar,
- text="Match case")
- btn.pack(side="left", fill="both")
- if self.engine.iscase():
- btn.select()
-
- btn = Checkbutton(f, anchor="w",
- variable=self.engine.wordvar,
- text="Whole word")
- btn.pack(side="left", fill="both")
- if self.engine.isword():
- btn.select()
-
- if self.needwrapbutton:
- btn = Checkbutton(f, anchor="w",
- variable=self.engine.wrapvar,
- text="Wrap around")
- btn.pack(side="left", fill="both")
- if self.engine.iswrap():
- btn.select()
-
- def create_other_buttons(self):
- f = self.make_frame("Direction")
-
- #lbl = Label(f, text="Direction: ")
- #lbl.pack(side="left")
-
- btn = Radiobutton(f, anchor="w",
- variable=self.engine.backvar, value=1,
- text="Up")
- btn.pack(side="left", fill="both")
- if self.engine.isback():
- btn.select()
-
- btn = Radiobutton(f, anchor="w",
- variable=self.engine.backvar, value=0,
- text="Down")
- btn.pack(side="left", fill="both")
- if not self.engine.isback():
- btn.select()
-
def create_command_buttons(self):
- #
- # place button frame on the right
+ "Place buttons in vertical command frame gridded on right."
f = self.buttonframe = Frame(self.top)
f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
b = self.make_button("close", self.close)
b.lower()
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main(
+ 'idlelib.idle_test.test_searchdialogbase', verbosity=2)
diff --git a/Lib/idlelib/SearchEngine.py b/Lib/idlelib/SearchEngine.py
index 9d3c4cb..099cb09 100644
--- a/Lib/idlelib/SearchEngine.py
+++ b/Lib/idlelib/SearchEngine.py
@@ -85,7 +85,7 @@ class SearchEngine:
except re.error as what:
args = what.args
msg = args[0]
- col = arg[1] if len(args) >= 2 else -1
+ col = args[1] if len(args) >= 2 else -1
self.report_error(pat, msg, col)
return None
return prog
@@ -229,6 +229,5 @@ def get_line_col(index):
return line, col
if __name__ == "__main__":
- from test import support; support.use_resources = ['gui']
import unittest
unittest.main('idlelib.idle_test.test_searchengine', verbosity=2, exit=False)
diff --git a/Lib/idlelib/StackViewer.py b/Lib/idlelib/StackViewer.py
index 4ef2d31..b1e5e26 100644
--- a/Lib/idlelib/StackViewer.py
+++ b/Lib/idlelib/StackViewer.py
@@ -1,9 +1,12 @@
import os
import sys
import linecache
+import re
+import tkinter as tk
from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
from idlelib.ObjectBrowser import ObjectTreeItem, make_objecttreeitem
+from idlelib.PyShell import PyShellFileList
def StackBrowser(root, flist=None, tb=None, top=None):
if top is None:
@@ -120,3 +123,30 @@ class VariablesTreeItem(ObjectTreeItem):
item = make_objecttreeitem(key + " =", value, setfunction)
sublist.append(item)
return sublist
+
+def _stack_viewer(parent):
+ root = tk.Tk()
+ root.title("Test StackViewer")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ flist = PyShellFileList(root)
+ try: # to obtain a traceback object
+ intentional_name_error
+ except NameError:
+ exc_type, exc_value, exc_tb = sys.exc_info()
+
+ # inject stack trace to sys
+ sys.last_type = exc_type
+ sys.last_value = exc_value
+ sys.last_traceback = exc_tb
+
+ StackBrowser(root, flist=flist, top=root, tb=exc_tb)
+
+ # restore sys to original state
+ del sys.last_type
+ del sys.last_value
+ del sys.last_traceback
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_stack_viewer)
diff --git a/Lib/idlelib/ToolTip.py b/Lib/idlelib/ToolTip.py
index b178803..964107e 100644
--- a/Lib/idlelib/ToolTip.py
+++ b/Lib/idlelib/ToolTip.py
@@ -76,14 +76,22 @@ class ListboxToolTip(ToolTipBase):
for item in self.items:
listbox.insert(END, item)
-def main():
- # Test code
+def _tooltip(parent):
root = Tk()
- b = Button(root, text="Hello", command=root.destroy)
- b.pack()
- root.update()
- tip = ListboxToolTip(b, ["Hello", "world"])
+ root.title("Test tooltip")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ label = Label(root, text="Place your mouse over buttons")
+ label.pack()
+ button1 = Button(root, text="Button 1")
+ button2 = Button(root, text="Button 2")
+ button1.pack()
+ button2.pack()
+ ToolTip(button1, "This is tooltip text for button1.")
+ ListboxToolTip(button2, ["This is","multiple line",
+ "tooltip text","for button2"])
root.mainloop()
if __name__ == '__main__':
- main()
+ from idlelib.idle_test.htest import run
+ run(_tooltip)
diff --git a/Lib/idlelib/TreeWidget.py b/Lib/idlelib/TreeWidget.py
index 25bae48..f05011d 100644
--- a/Lib/idlelib/TreeWidget.py
+++ b/Lib/idlelib/TreeWidget.py
@@ -381,7 +381,7 @@ class FileTreeItem(TreeItem):
try:
os.rename(self.path, newpath)
self.path = newpath
- except os.error:
+ except OSError:
pass
def GetIconName(self):
@@ -394,7 +394,7 @@ class FileTreeItem(TreeItem):
def GetSubList(self):
try:
names = os.listdir(self.path)
- except os.error:
+ except OSError:
return []
names.sort(key = os.path.normcase)
sublist = []
@@ -448,29 +448,18 @@ class ScrolledCanvas:
return "break"
-# Testing functions
-
-def test():
- from idlelib import PyShell
- root = Toplevel(PyShell.root)
- root.configure(bd=0, bg="yellow")
- root.focus_set()
+def _tree_widget(parent):
+ root = Tk()
+ root.title("Test TreeWidget")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1)
- sc.frame.pack(expand=1, fill="both")
- item = FileTreeItem("C:/windows/desktop")
+ sc.frame.pack(expand=1, fill="both", side=LEFT)
+ item = FileTreeItem(os.getcwd())
node = TreeNode(sc.canvas, None, item)
node.expand()
-
-def test2():
- # test w/o scrolling canvas
- root = Tk()
- root.configure(bd=0)
- canvas = Canvas(root, bg="white", highlightthickness=0)
- canvas.pack(expand=1, fill="both")
- item = FileTreeItem(os.curdir)
- node = TreeNode(canvas, None, item)
- node.update()
- canvas.focus_set()
+ root.mainloop()
if __name__ == '__main__':
- test()
+ from idlelib.idle_test.htest import run
+ run(_tree_widget)
diff --git a/Lib/idlelib/UndoDelegator.py b/Lib/idlelib/UndoDelegator.py
index d2ef638..04c1cf5 100644
--- a/Lib/idlelib/UndoDelegator.py
+++ b/Lib/idlelib/UndoDelegator.py
@@ -336,17 +336,30 @@ class CommandSequence(Command):
self.depth = self.depth + incr
return self.depth
-def main():
+def _undo_delegator(parent):
from idlelib.Percolator import Percolator
root = Tk()
- root.wm_protocol("WM_DELETE_WINDOW", root.quit)
- text = Text()
+ root.title("Test UndoDelegator")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+
+ text = Text(root)
+ text.config(height=10)
text.pack()
text.focus_set()
p = Percolator(text)
d = UndoDelegator()
p.insertfilter(d)
+
+ undo = Button(root, text="Undo", command=lambda:d.undo_event(None))
+ undo.pack(side='left')
+ redo = Button(root, text="Redo", command=lambda:d.redo_event(None))
+ redo.pack(side='left')
+ dump = Button(root, text="Dump", command=lambda:d.dump_event(None))
+ dump.pack(side='left')
+
root.mainloop()
if __name__ == "__main__":
- main()
+ from idlelib.idle_test.htest import run
+ run(_undo_delegator)
diff --git a/Lib/idlelib/WidgetRedirector.py b/Lib/idlelib/WidgetRedirector.py
index ba5251f..b3d7bfa 100644
--- a/Lib/idlelib/WidgetRedirector.py
+++ b/Lib/idlelib/WidgetRedirector.py
@@ -1,29 +1,40 @@
-from tkinter import *
+from tkinter import TclError
class WidgetRedirector:
-
"""Support for redirecting arbitrary widget subcommands.
- Some Tk operations don't normally pass through Tkinter. For example, if a
+ Some Tk operations don't normally pass through tkinter. For example, if a
character is inserted into a Text widget by pressing a key, a default Tk
binding to the widget's 'insert' operation is activated, and the Tk library
- processes the insert without calling back into Tkinter.
+ processes the insert without calling back into tkinter.
- Although a binding to <Key> could be made via Tkinter, what we really want
- to do is to hook the Tk 'insert' operation itself.
+ Although a binding to <Key> could be made via tkinter, what we really want
+ to do is to hook the Tk 'insert' operation itself. For one thing, we want
+ a text.insert call in idle code to have the same effect as a key press.
When a widget is instantiated, a Tcl command is created whose name is the
same as the pathname widget._w. This command is used to invoke the various
widget operations, e.g. insert (for a Text widget). We are going to hook
this command and provide a facility ('register') to intercept the widget
- operation.
-
- In IDLE, the function being registered provides access to the top of a
- Percolator chain. At the bottom of the chain is a call to the original
- Tk widget operation.
+ operation. We will also intercept method calls on the tkinter class
+ instance that represents the tk widget.
+ In IDLE, WidgetRedirector is used in Percolator to intercept Text
+ commands. The function being registered provides access to the top
+ of a Percolator chain. At the bottom of the chain is a call to the
+ original Tk widget operation.
"""
def __init__(self, widget):
+ '''Initialize attributes and setup redirection.
+
+ _operations: dict mapping operation name to new function.
+ widget: the widget whose tcl command is to be intercepted.
+ tk: widget.tk, a convenience attribute, probably not needed.
+ orig: new name of the original tcl command.
+
+ Since renaming to orig fails with TclError when orig already
+ exists, only one WidgetDirector can exist for a given widget.
+ '''
self._operations = {}
self.widget = widget # widget instance
self.tk = tk = widget.tk # widget's root
@@ -40,27 +51,45 @@ class WidgetRedirector:
self.widget._w)
def close(self):
+ "Unregister operations and revert redirection created by .__init__."
for operation in list(self._operations):
self.unregister(operation)
- widget = self.widget; del self.widget
- orig = self.orig; del self.orig
+ widget = self.widget
tk = widget.tk
w = widget._w
+ # Restore the original widget Tcl command.
tk.deletecommand(w)
- # restore the original widget Tcl command:
- tk.call("rename", orig, w)
+ tk.call("rename", self.orig, w)
+ del self.widget, self.tk # Should not be needed
+ # if instance is deleted after close, as in Percolator.
def register(self, operation, function):
+ '''Return OriginalCommand(operation) after registering function.
+
+ Registration adds an operation: function pair to ._operations.
+ It also adds an widget function attribute that masks the tkinter
+ class instance method. Method masking operates independently
+ from command dispatch.
+
+ If a second function is registered for the same operation, the
+ first function is replaced in both places.
+ '''
self._operations[operation] = function
setattr(self.widget, operation, function)
return OriginalCommand(self, operation)
def unregister(self, operation):
+ '''Return the function for the operation, or None.
+
+ Deleting the instance attribute unmasks the class attribute.
+ '''
if operation in self._operations:
function = self._operations[operation]
del self._operations[operation]
- if hasattr(self.widget, operation):
+ try:
delattr(self.widget, operation)
+ except AttributeError:
+ pass
return function
else:
return None
@@ -88,14 +117,29 @@ class WidgetRedirector:
class OriginalCommand:
+ '''Callable for original tk command that has been redirected.
+
+ Returned by .register; can be used in the function registered.
+ redir = WidgetRedirector(text)
+ def my_insert(*args):
+ print("insert", args)
+ original_insert(*args)
+ original_insert = redir.register("insert", my_insert)
+ '''
def __init__(self, redir, operation):
+ '''Create .tk_call and .orig_and_operation for .__call__ method.
+
+ .redir and .operation store the input args for __repr__.
+ .tk and .orig copy attributes of .redir (probably not needed).
+ '''
self.redir = redir
self.operation = operation
- self.tk = redir.tk
- self.orig = redir.orig
- self.tk_call = self.tk.call
- self.orig_and_operation = (self.orig, self.operation)
+ self.tk = redir.tk # redundant with self.redir
+ self.orig = redir.orig # redundant with self.redir
+ # These two could be deleted after checking recipient code.
+ self.tk_call = redir.tk.call
+ self.orig_and_operation = (redir.orig, operation)
def __repr__(self):
return "OriginalCommand(%r, %r)" % (self.redir, self.operation)
@@ -104,23 +148,27 @@ class OriginalCommand:
return self.tk_call(self.orig_and_operation + args)
-def main():
+def _widget_redirector(parent): # htest #
+ from tkinter import Tk, Text
+ import re
+
root = Tk()
- root.wm_protocol("WM_DELETE_WINDOW", root.quit)
- text = Text()
+ root.title("Test WidgetRedirector")
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ text = Text(root)
text.pack()
text.focus_set()
redir = WidgetRedirector(text)
- global previous_tcl_fcn
def my_insert(*args):
print("insert", args)
- previous_tcl_fcn(*args)
- previous_tcl_fcn = redir.register("insert", my_insert)
- root.mainloop()
- redir.unregister("insert") # runs after first 'close window'
- redir.close()
+ original_insert(*args)
+ original_insert = redir.register("insert", my_insert)
root.mainloop()
- root.destroy()
if __name__ == "__main__":
- main()
+ import unittest
+ unittest.main('idlelib.idle_test.test_widgetredir',
+ verbosity=2, exit=False)
+ from idlelib.idle_test.htest import run
+ run(_widget_redirector)
diff --git a/Lib/idlelib/ZoomHeight.py b/Lib/idlelib/ZoomHeight.py
index e8d1710..a5d679e 100644
--- a/Lib/idlelib/ZoomHeight.py
+++ b/Lib/idlelib/ZoomHeight.py
@@ -32,7 +32,7 @@ def zoom_height(top):
newy = 0
newheight = newheight - 72
- elif macosxSupport.runningAsOSXApp():
+ elif macosxSupport.isAquaTk():
# The '88' below is a magic number that avoids placing the bottom
# of the window below the panel on my machine. I don't know how
# to calculate the correct value for this with tkinter.
diff --git a/Lib/idlelib/aboutDialog.py b/Lib/idlelib/aboutDialog.py
index 7fe1ab8..e264295 100644
--- a/Lib/idlelib/aboutDialog.py
+++ b/Lib/idlelib/aboutDialog.py
@@ -12,11 +12,16 @@ class AboutDialog(Toplevel):
"""Modal about dialog for idle
"""
- def __init__(self,parent,title):
+ def __init__(self, parent, title, _htest=False):
+ """
+ _htest - bool, change box location when running htest
+ """
Toplevel.__init__(self, parent)
self.configure(borderwidth=5)
- self.geometry("+%d+%d" % (parent.winfo_rootx()+30,
- parent.winfo_rooty()+30))
+ # place dialog below parent if running htest
+ self.geometry("+%d+%d" % (
+ parent.winfo_rootx()+30,
+ parent.winfo_rooty()+(30 if not _htest else 100)))
self.bg = "#707070"
self.fg = "#ffffff"
self.CreateWidgets()
@@ -136,10 +141,5 @@ class AboutDialog(Toplevel):
self.destroy()
if __name__ == '__main__':
- # test the dialog
- root = Tk()
- def run():
- from idlelib import aboutDialog
- aboutDialog.AboutDialog(root, 'About')
- Button(root, text='Dialog', command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(AboutDialog)
diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def
index fdc35ba..3bfcb69 100644
--- a/Lib/idlelib/config-keys.def
+++ b/Lib/idlelib/config-keys.def
@@ -13,37 +13,37 @@ cut=<Control-Key-x> <Control-Key-X>
paste=<Control-Key-v> <Control-Key-V>
beginning-of-line= <Key-Home>
center-insert=<Control-Key-l> <Control-Key-L>
-close-all-windows=<Control-Key-q>
+close-all-windows=<Control-Key-q> <Control-Key-Q>
close-window=<Alt-Key-F4> <Meta-Key-F4>
do-nothing=<Control-Key-F12>
end-of-file=<Control-Key-d> <Control-Key-D>
python-docs=<Key-F1>
python-context-help=<Shift-Key-F1>
-history-next=<Alt-Key-n> <Meta-Key-n>
-history-previous=<Alt-Key-p> <Meta-Key-p>
+history-next=<Alt-Key-n> <Meta-Key-n> <Alt-Key-N> <Meta-Key-N>
+history-previous=<Alt-Key-p> <Meta-Key-p> <Alt-Key-P> <Meta-Key-P>
interrupt-execution=<Control-Key-c> <Control-Key-C>
view-restart=<Key-F6>
restart-shell=<Control-Key-F6>
-open-class-browser=<Alt-Key-c> <Meta-Key-c> <Alt-Key-C>
-open-module=<Alt-Key-m> <Meta-Key-m> <Alt-Key-M>
+open-class-browser=<Alt-Key-c> <Meta-Key-c> <Alt-Key-C> <Meta-Key-C>
+open-module=<Alt-Key-m> <Meta-Key-m> <Alt-Key-M> <Meta-Key-M>
open-new-window=<Control-Key-n> <Control-Key-N>
open-window-from-file=<Control-Key-o> <Control-Key-O>
plain-newline-and-indent=<Control-Key-j> <Control-Key-J>
print-window=<Control-Key-p> <Control-Key-P>
-redo=<Control-Shift-Key-Z>
+redo=<Control-Shift-Key-Z> <Control-Shift-Key-z>
remove-selection=<Key-Escape>
-save-copy-of-window-as-file=<Alt-Shift-Key-S>
-save-window-as-file=<Control-Shift-Key-S>
-save-window=<Control-Key-s>
-select-all=<Control-Key-a>
+save-copy-of-window-as-file=<Alt-Shift-Key-S> <Alt-Shift-Key-s>
+save-window-as-file=<Control-Shift-Key-S> <Control-Shift-Key-s>
+save-window=<Control-Key-s> <Control-Key-S>
+select-all=<Control-Key-a> <Control-Key-A>
toggle-auto-coloring=<Control-Key-slash>
undo=<Control-Key-z> <Control-Key-Z>
find=<Control-Key-f> <Control-Key-F>
-find-again=<Control-Key-g> <Key-F3>
+find-again=<Control-Key-g> <Key-F3> <Control-Key-G>
find-in-files=<Alt-Key-F3> <Meta-Key-F3>
find-selection=<Control-Key-F3>
replace=<Control-Key-h> <Control-Key-H>
-goto-line=<Alt-Key-g> <Meta-Key-g>
+goto-line=<Alt-Key-g> <Meta-Key-g> <Alt-Key-G> <Meta-Key-G>
smart-backspace=<Key-BackSpace>
newline-and-indent=<Key-Return> <Key-KP_Enter>
smart-indent=<Key-Tab>
@@ -53,8 +53,8 @@ comment-region=<Alt-Key-3> <Meta-Key-3>
uncomment-region=<Alt-Key-4> <Meta-Key-4>
tabify-region=<Alt-Key-5> <Meta-Key-5>
untabify-region=<Alt-Key-6> <Meta-Key-6>
-toggle-tabs=<Alt-Key-t> <Meta-Key-t> <Alt-Key-T>
-change-indentwidth=<Alt-Key-u> <Meta-Key-u> <Alt-Key-U>
+toggle-tabs=<Alt-Key-t> <Meta-Key-t> <Alt-Key-T> <Meta-Key-T>
+change-indentwidth=<Alt-Key-u> <Meta-Key-u> <Alt-Key-U> <Meta-Key-U>
del-word-left=<Control-Key-BackSpace>
del-word-right=<Control-Key-Delete>
diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def
index 9546e2b..8f0fe76 100644
--- a/Lib/idlelib/config-main.def
+++ b/Lib/idlelib/config-main.def
@@ -59,7 +59,7 @@ font-bold= 0
encoding= none
[FormatParagraph]
-paragraph=70
+paragraph=72
[Indent]
use-spaces= 1
diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py
index efe5c43..70b0b3b 100644
--- a/Lib/idlelib/configDialog.py
+++ b/Lib/idlelib/configDialog.py
@@ -13,7 +13,6 @@ from tkinter import *
import tkinter.messagebox as tkMessageBox
import tkinter.colorchooser as tkColorChooser
import tkinter.font as tkFont
-import copy
from idlelib.configHandler import idleConf
from idlelib.dynOptionMenuWidget import DynOptionMenu
@@ -25,522 +24,571 @@ from idlelib import macosxSupport
class ConfigDialog(Toplevel):
- def __init__(self,parent,title):
+ def __init__(self, parent, title, _htest=False, _utest=False):
+ """
+ _htest - bool, change box location when running htest
+ _utest - bool, don't wait_window when running unittest
+ """
Toplevel.__init__(self, parent)
+ self.parent = parent
+ if _htest:
+ parent.instance_dict = {}
self.wm_withdraw()
self.configure(borderwidth=5)
self.title('IDLE Preferences')
- self.geometry("+%d+%d" % (parent.winfo_rootx()+20,
- parent.winfo_rooty()+30))
+ self.geometry(
+ "+%d+%d" % (parent.winfo_rootx() + 20,
+ parent.winfo_rooty() + (30 if not _htest else 150)))
#Theme Elements. Each theme element key is its display name.
#The first value of the tuple is the sample area tag name.
#The second value is the display name list sort index.
- self.themeElements={'Normal Text':('normal','00'),
- 'Python Keywords':('keyword','01'),
- 'Python Definitions':('definition','02'),
+ self.themeElements={
+ 'Normal Text':('normal', '00'),
+ 'Python Keywords':('keyword', '01'),
+ 'Python Definitions':('definition', '02'),
'Python Builtins':('builtin', '03'),
- 'Python Comments':('comment','04'),
- 'Python Strings':('string','05'),
- 'Selected Text':('hilite','06'),
- 'Found Text':('hit','07'),
- 'Cursor':('cursor','08'),
- 'Error Text':('error','09'),
- 'Shell Normal Text':('console','10'),
- 'Shell Stdout Text':('stdout','11'),
- 'Shell Stderr Text':('stderr','12'),
+ 'Python Comments':('comment', '04'),
+ 'Python Strings':('string', '05'),
+ 'Selected Text':('hilite', '06'),
+ 'Found Text':('hit', '07'),
+ 'Cursor':('cursor', '08'),
+ 'Error Text':('error', '09'),
+ 'Shell Normal Text':('console', '10'),
+ 'Shell Stdout Text':('stdout', '11'),
+ 'Shell Stderr Text':('stderr', '12'),
}
self.ResetChangedItems() #load initial values in changed items dict
self.CreateWidgets()
- self.resizable(height=FALSE,width=FALSE)
+ self.resizable(height=FALSE, width=FALSE)
self.transient(parent)
self.grab_set()
self.protocol("WM_DELETE_WINDOW", self.Cancel)
- self.parent = parent
self.tabPages.focus_set()
#key bindings for this dialog
- #self.bind('<Escape>',self.Cancel) #dismiss dialog, no save
- #self.bind('<Alt-a>',self.Apply) #apply changes, save
- #self.bind('<F1>',self.Help) #context help
+ #self.bind('<Escape>', self.Cancel) #dismiss dialog, no save
+ #self.bind('<Alt-a>', self.Apply) #apply changes, save
+ #self.bind('<F1>', self.Help) #context help
self.LoadConfigs()
self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
- self.wm_deiconify()
- self.wait_window()
+ if not _utest:
+ self.wm_deiconify()
+ self.wait_window()
def CreateWidgets(self):
self.tabPages = TabbedPageSet(self,
- page_names=['Fonts/Tabs','Highlighting','Keys','General'])
- frameActionButtons = Frame(self,pady=2)
+ page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General'])
+ frameActionButtons = Frame(self, pady=2)
#action buttons
-
- if macosxSupport.runningAsOSXApp():
- # Surpress the padx and pady arguments when
- # running as IDLE.app, otherwise the text
- # on these buttons will not be readable.
- extraKwds={}
+ if macosxSupport.isAquaTk():
+ # Changing the default padding on OSX results in unreadable
+ # text in the buttons
+ paddingArgs = {}
else:
- extraKwds=dict(padx=6, pady=3)
+ paddingArgs = {'padx':6, 'pady':3}
# Comment out button creation and packing until implement self.Help
-## self.buttonHelp = Button(frameActionButtons,text='Help',
-## command=self.Help,takefocus=FALSE,
-## **extraKwds)
- self.buttonOk = Button(frameActionButtons,text='Ok',
- command=self.Ok,takefocus=FALSE,
- **extraKwds)
- self.buttonApply = Button(frameActionButtons,text='Apply',
- command=self.Apply,takefocus=FALSE,
- **extraKwds)
- self.buttonCancel = Button(frameActionButtons,text='Cancel',
- command=self.Cancel,takefocus=FALSE,
- **extraKwds)
+## self.buttonHelp = Button(frameActionButtons, text='Help',
+## command=self.Help, takefocus=FALSE,
+## **paddingArgs)
+ self.buttonOk = Button(
+ frameActionButtons, text='Ok',
+ command=self.Ok, takefocus=FALSE, **paddingArgs)
+ self.buttonApply = Button(
+ frameActionButtons, text='Apply',
+ command=self.Apply, takefocus=FALSE, **paddingArgs)
+ self.buttonCancel = Button(
+ frameActionButtons, text='Cancel',
+ command=self.Cancel, takefocus=FALSE, **paddingArgs)
self.CreatePageFontTab()
self.CreatePageHighlight()
self.CreatePageKeys()
self.CreatePageGeneral()
-## self.buttonHelp.pack(side=RIGHT,padx=5)
- self.buttonOk.pack(side=LEFT,padx=5)
- self.buttonApply.pack(side=LEFT,padx=5)
- self.buttonCancel.pack(side=LEFT,padx=5)
+## self.buttonHelp.pack(side=RIGHT, padx=5)
+ self.buttonOk.pack(side=LEFT, padx=5)
+ self.buttonApply.pack(side=LEFT, padx=5)
+ self.buttonCancel.pack(side=LEFT, padx=5)
frameActionButtons.pack(side=BOTTOM)
Frame(self, height=2, borderwidth=0).pack(side=BOTTOM)
- self.tabPages.pack(side=TOP,expand=TRUE,fill=BOTH)
+ self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH)
def CreatePageFontTab(self):
- #tkVars
- self.fontSize=StringVar(self)
- self.fontBold=BooleanVar(self)
- self.fontName=StringVar(self)
- self.spaceNum=IntVar(self)
- self.editFont=tkFont.Font(self,('courier',10,'normal'))
+ parent = self.parent
+ self.fontSize = StringVar(parent)
+ self.fontBold = BooleanVar(parent)
+ self.fontName = StringVar(parent)
+ self.spaceNum = IntVar(parent)
+ self.editFont = tkFont.Font(parent, ('courier', 10, 'normal'))
+
##widget creation
#body frame
- frame=self.tabPages.pages['Fonts/Tabs'].frame
+ frame = self.tabPages.pages['Fonts/Tabs'].frame
#body section frames
- frameFont=LabelFrame(frame,borderwidth=2,relief=GROOVE,
- text=' Base Editor Font ')
- frameIndent=LabelFrame(frame,borderwidth=2,relief=GROOVE,
- text=' Indentation Width ')
+ frameFont = LabelFrame(
+ frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ')
+ frameIndent = LabelFrame(
+ frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ')
#frameFont
- frameFontName=Frame(frameFont)
- frameFontParam=Frame(frameFont)
- labelFontNameTitle=Label(frameFontName,justify=LEFT,
- text='Font Face :')
- self.listFontName=Listbox(frameFontName,height=5,takefocus=FALSE,
- exportselection=FALSE)
- self.listFontName.bind('<ButtonRelease-1>',self.OnListFontButtonRelease)
- scrollFont=Scrollbar(frameFontName)
+ frameFontName = Frame(frameFont)
+ frameFontParam = Frame(frameFont)
+ labelFontNameTitle = Label(
+ frameFontName, justify=LEFT, text='Font Face :')
+ self.listFontName = Listbox(
+ frameFontName, height=5, takefocus=FALSE, exportselection=FALSE)
+ self.listFontName.bind(
+ '<ButtonRelease-1>', self.OnListFontButtonRelease)
+ scrollFont = Scrollbar(frameFontName)
scrollFont.config(command=self.listFontName.yview)
self.listFontName.config(yscrollcommand=scrollFont.set)
- labelFontSizeTitle=Label(frameFontParam,text='Size :')
- self.optMenuFontSize=DynOptionMenu(frameFontParam,self.fontSize,None,
- command=self.SetFontSample)
- checkFontBold=Checkbutton(frameFontParam,variable=self.fontBold,
- onvalue=1,offvalue=0,text='Bold',command=self.SetFontSample)
- frameFontSample=Frame(frameFont,relief=SOLID,borderwidth=1)
- self.labelFontSample=Label(frameFontSample,
- text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]',
- justify=LEFT,font=self.editFont)
+ labelFontSizeTitle = Label(frameFontParam, text='Size :')
+ self.optMenuFontSize = DynOptionMenu(
+ frameFontParam, self.fontSize, None, command=self.SetFontSample)
+ checkFontBold = Checkbutton(
+ frameFontParam, variable=self.fontBold, onvalue=1,
+ offvalue=0, text='Bold', command=self.SetFontSample)
+ frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1)
+ self.labelFontSample = Label(
+ frameFontSample, justify=LEFT, font=self.editFont,
+ text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]')
#frameIndent
- frameIndentSize=Frame(frameIndent)
- labelSpaceNumTitle=Label(frameIndentSize, justify=LEFT,
- text='Python Standard: 4 Spaces!')
- self.scaleSpaceNum=Scale(frameIndentSize, variable=self.spaceNum,
- orient='horizontal',
- tickinterval=2, from_=2, to=16)
+ frameIndentSize = Frame(frameIndent)
+ labelSpaceNumTitle = Label(
+ frameIndentSize, justify=LEFT,
+ text='Python Standard: 4 Spaces!')
+ self.scaleSpaceNum = Scale(
+ frameIndentSize, variable=self.spaceNum,
+ orient='horizontal', tickinterval=2, from_=2, to=16)
+
#widget packing
#body
- frameFont.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
- frameIndent.pack(side=LEFT,padx=5,pady=5,fill=Y)
+ frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y)
#frameFont
- frameFontName.pack(side=TOP,padx=5,pady=5,fill=X)
- frameFontParam.pack(side=TOP,padx=5,pady=5,fill=X)
- labelFontNameTitle.pack(side=TOP,anchor=W)
- self.listFontName.pack(side=LEFT,expand=TRUE,fill=X)
- scrollFont.pack(side=LEFT,fill=Y)
- labelFontSizeTitle.pack(side=LEFT,anchor=W)
- self.optMenuFontSize.pack(side=LEFT,anchor=W)
- checkFontBold.pack(side=LEFT,anchor=W,padx=20)
- frameFontSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
- self.labelFontSample.pack(expand=TRUE,fill=BOTH)
+ frameFontName.pack(side=TOP, padx=5, pady=5, fill=X)
+ frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X)
+ labelFontNameTitle.pack(side=TOP, anchor=W)
+ self.listFontName.pack(side=LEFT, expand=TRUE, fill=X)
+ scrollFont.pack(side=LEFT, fill=Y)
+ labelFontSizeTitle.pack(side=LEFT, anchor=W)
+ self.optMenuFontSize.pack(side=LEFT, anchor=W)
+ checkFontBold.pack(side=LEFT, anchor=W, padx=20)
+ frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ self.labelFontSample.pack(expand=TRUE, fill=BOTH)
#frameIndent
- frameIndentSize.pack(side=TOP,fill=X)
- labelSpaceNumTitle.pack(side=TOP,anchor=W,padx=5)
- self.scaleSpaceNum.pack(side=TOP,padx=5,fill=X)
+ frameIndentSize.pack(side=TOP, fill=X)
+ labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5)
+ self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X)
return frame
def CreatePageHighlight(self):
- self.builtinTheme=StringVar(self)
- self.customTheme=StringVar(self)
- self.fgHilite=BooleanVar(self)
- self.colour=StringVar(self)
- self.fontName=StringVar(self)
- self.themeIsBuiltin=BooleanVar(self)
- self.highlightTarget=StringVar(self)
+ parent = self.parent
+ self.builtinTheme = StringVar(parent)
+ self.customTheme = StringVar(parent)
+ self.fgHilite = BooleanVar(parent)
+ self.colour = StringVar(parent)
+ self.fontName = StringVar(parent)
+ self.themeIsBuiltin = BooleanVar(parent)
+ self.highlightTarget = StringVar(parent)
+
##widget creation
#body frame
- frame=self.tabPages.pages['Highlighting'].frame
+ frame = self.tabPages.pages['Highlighting'].frame
#body section frames
- frameCustom=LabelFrame(frame,borderwidth=2,relief=GROOVE,
- text=' Custom Highlighting ')
- frameTheme=LabelFrame(frame,borderwidth=2,relief=GROOVE,
- text=' Highlighting Theme ')
+ frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
+ text=' Custom Highlighting ')
+ frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
+ text=' Highlighting Theme ')
#frameCustom
- self.textHighlightSample=Text(frameCustom,relief=SOLID,borderwidth=1,
- font=('courier',12,''),cursor='hand2',width=21,height=11,
- takefocus=FALSE,highlightthickness=0,wrap=NONE)
+ self.textHighlightSample=Text(
+ frameCustom, relief=SOLID, borderwidth=1,
+ font=('courier', 12, ''), cursor='hand2', width=21, height=11,
+ takefocus=FALSE, highlightthickness=0, wrap=NONE)
text=self.textHighlightSample
- text.bind('<Double-Button-1>',lambda e: 'break')
- text.bind('<B1-Motion>',lambda e: 'break')
- textAndTags=(('#you can click here','comment'),('\n','normal'),
- ('#to choose items','comment'),('\n','normal'),('def','keyword'),
- (' ','normal'),('func','definition'),('(param):','normal'),
- ('\n ','normal'),('"""string"""','string'),('\n var0 = ','normal'),
- ("'string'",'string'),('\n var1 = ','normal'),("'selected'",'hilite'),
- ('\n var2 = ','normal'),("'found'",'hit'),
- ('\n var3 = ','normal'),('list', 'builtin'), ('(','normal'),
- ('None', 'keyword'),(')\n\n','normal'),
- (' error ','error'),(' ','normal'),('cursor |','cursor'),
- ('\n ','normal'),('shell','console'),(' ','normal'),('stdout','stdout'),
- (' ','normal'),('stderr','stderr'),('\n','normal'))
+ text.bind('<Double-Button-1>', lambda e: 'break')
+ text.bind('<B1-Motion>', lambda e: 'break')
+ textAndTags=(
+ ('#you can click here', 'comment'), ('\n', 'normal'),
+ ('#to choose items', 'comment'), ('\n', 'normal'),
+ ('def', 'keyword'), (' ', 'normal'),
+ ('func', 'definition'), ('(param):\n ', 'normal'),
+ ('"""string"""', 'string'), ('\n var0 = ', 'normal'),
+ ("'string'", 'string'), ('\n var1 = ', 'normal'),
+ ("'selected'", 'hilite'), ('\n var2 = ', 'normal'),
+ ("'found'", 'hit'), ('\n var3 = ', 'normal'),
+ ('list', 'builtin'), ('(', 'normal'),
+ ('None', 'keyword'), (')\n\n', 'normal'),
+ (' error ', 'error'), (' ', 'normal'),
+ ('cursor |', 'cursor'), ('\n ', 'normal'),
+ ('shell', 'console'), (' ', 'normal'),
+ ('stdout', 'stdout'), (' ', 'normal'),
+ ('stderr', 'stderr'), ('\n', 'normal'))
for txTa in textAndTags:
- text.insert(END,txTa[0],txTa[1])
+ text.insert(END, txTa[0], txTa[1])
for element in self.themeElements:
- text.tag_bind(self.themeElements[element][0],'<ButtonPress-1>',
- lambda event,elem=element: event.widget.winfo_toplevel()
- .highlightTarget.set(elem))
+ def tem(event, elem=element):
+ event.widget.winfo_toplevel().highlightTarget.set(elem)
+ text.tag_bind(
+ self.themeElements[element][0], '<ButtonPress-1>', tem)
text.config(state=DISABLED)
- self.frameColourSet=Frame(frameCustom,relief=SOLID,borderwidth=1)
- frameFgBg=Frame(frameCustom)
- buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :',
- command=self.GetColour,highlightthickness=0)
- self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet,
- self.highlightTarget,None,highlightthickness=0)#,command=self.SetHighlightTargetBinding
- self.radioFg=Radiobutton(frameFgBg,variable=self.fgHilite,
- value=1,text='Foreground',command=self.SetColourSampleBinding)
- self.radioBg=Radiobutton(frameFgBg,variable=self.fgHilite,
- value=0,text='Background',command=self.SetColourSampleBinding)
+ self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1)
+ frameFgBg = Frame(frameCustom)
+ buttonSetColour = Button(
+ self.frameColourSet, text='Choose Colour for :',
+ command=self.GetColour, highlightthickness=0)
+ self.optMenuHighlightTarget = DynOptionMenu(
+ self.frameColourSet, self.highlightTarget, None,
+ highlightthickness=0) #, command=self.SetHighlightTargetBinding
+ self.radioFg = Radiobutton(
+ frameFgBg, variable=self.fgHilite, value=1,
+ text='Foreground', command=self.SetColourSampleBinding)
+ self.radioBg=Radiobutton(
+ frameFgBg, variable=self.fgHilite, value=0,
+ text='Background', command=self.SetColourSampleBinding)
self.fgHilite.set(1)
- buttonSaveCustomTheme=Button(frameCustom,
- text='Save as New Custom Theme',command=self.SaveAsNewTheme)
+ buttonSaveCustomTheme = Button(
+ frameCustom, text='Save as New Custom Theme',
+ command=self.SaveAsNewTheme)
#frameTheme
- labelTypeTitle=Label(frameTheme,text='Select : ')
- self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
- value=1,command=self.SetThemeType,text='a Built-in Theme')
- self.radioThemeCustom=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
- value=0,command=self.SetThemeType,text='a Custom Theme')
- self.optMenuThemeBuiltin=DynOptionMenu(frameTheme,
- self.builtinTheme,None,command=None)
- self.optMenuThemeCustom=DynOptionMenu(frameTheme,
- self.customTheme,None,command=None)
- self.buttonDeleteCustomTheme=Button(frameTheme,text='Delete Custom Theme',
+ labelTypeTitle = Label(frameTheme, text='Select : ')
+ self.radioThemeBuiltin = Radiobutton(
+ frameTheme, variable=self.themeIsBuiltin, value=1,
+ command=self.SetThemeType, text='a Built-in Theme')
+ self.radioThemeCustom = Radiobutton(
+ frameTheme, variable=self.themeIsBuiltin, value=0,
+ command=self.SetThemeType, text='a Custom Theme')
+ self.optMenuThemeBuiltin = DynOptionMenu(
+ frameTheme, self.builtinTheme, None, command=None)
+ self.optMenuThemeCustom=DynOptionMenu(
+ frameTheme, self.customTheme, None, command=None)
+ self.buttonDeleteCustomTheme=Button(
+ frameTheme, text='Delete Custom Theme',
command=self.DeleteCustomTheme)
+
##widget packing
#body
- frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
- frameTheme.pack(side=LEFT,padx=5,pady=5,fill=Y)
+ frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y)
#frameCustom
- self.frameColourSet.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=X)
- frameFgBg.pack(side=TOP,padx=5,pady=0)
- self.textHighlightSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,
- fill=BOTH)
- buttonSetColour.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=4)
- self.optMenuHighlightTarget.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=3)
- self.radioFg.pack(side=LEFT,anchor=E)
- self.radioBg.pack(side=RIGHT,anchor=W)
- buttonSaveCustomTheme.pack(side=BOTTOM,fill=X,padx=5,pady=5)
+ self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X)
+ frameFgBg.pack(side=TOP, padx=5, pady=0)
+ self.textHighlightSample.pack(
+ side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4)
+ self.optMenuHighlightTarget.pack(
+ side=TOP, expand=TRUE, fill=X, padx=8, pady=3)
+ self.radioFg.pack(side=LEFT, anchor=E)
+ self.radioBg.pack(side=RIGHT, anchor=W)
+ buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
#frameTheme
- labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
- self.radioThemeBuiltin.pack(side=TOP,anchor=W,padx=5)
- self.radioThemeCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
- self.optMenuThemeBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
- self.optMenuThemeCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
- self.buttonDeleteCustomTheme.pack(side=TOP,fill=X,padx=5,pady=5)
+ labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5)
+ self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5)
+ self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2)
+ self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5)
+ self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
+ self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5)
return frame
def CreatePageKeys(self):
- #tkVars
- self.bindingTarget=StringVar(self)
- self.builtinKeys=StringVar(self)
- self.customKeys=StringVar(self)
- self.keysAreBuiltin=BooleanVar(self)
- self.keyBinding=StringVar(self)
+ parent = self.parent
+ self.bindingTarget = StringVar(parent)
+ self.builtinKeys = StringVar(parent)
+ self.customKeys = StringVar(parent)
+ self.keysAreBuiltin = BooleanVar(parent)
+ self.keyBinding = StringVar(parent)
+
##widget creation
#body frame
- frame=self.tabPages.pages['Keys'].frame
+ frame = self.tabPages.pages['Keys'].frame
#body section frames
- frameCustom=LabelFrame(frame,borderwidth=2,relief=GROOVE,
- text=' Custom Key Bindings ')
- frameKeySets=LabelFrame(frame,borderwidth=2,relief=GROOVE,
- text=' Key Set ')
+ frameCustom = LabelFrame(
+ frame, borderwidth=2, relief=GROOVE,
+ text=' Custom Key Bindings ')
+ frameKeySets = LabelFrame(
+ frame, borderwidth=2, relief=GROOVE, text=' Key Set ')
#frameCustom
- frameTarget=Frame(frameCustom)
- labelTargetTitle=Label(frameTarget,text='Action - Key(s)')
- scrollTargetY=Scrollbar(frameTarget)
- scrollTargetX=Scrollbar(frameTarget,orient=HORIZONTAL)
- self.listBindings=Listbox(frameTarget,takefocus=FALSE,
- exportselection=FALSE)
- self.listBindings.bind('<ButtonRelease-1>',self.KeyBindingSelected)
+ frameTarget = Frame(frameCustom)
+ labelTargetTitle = Label(frameTarget, text='Action - Key(s)')
+ scrollTargetY = Scrollbar(frameTarget)
+ scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL)
+ self.listBindings = Listbox(
+ frameTarget, takefocus=FALSE, exportselection=FALSE)
+ self.listBindings.bind('<ButtonRelease-1>', self.KeyBindingSelected)
scrollTargetY.config(command=self.listBindings.yview)
scrollTargetX.config(command=self.listBindings.xview)
self.listBindings.config(yscrollcommand=scrollTargetY.set)
self.listBindings.config(xscrollcommand=scrollTargetX.set)
- self.buttonNewKeys=Button(frameCustom,text='Get New Keys for Selection',
- command=self.GetNewKeys,state=DISABLED)
+ self.buttonNewKeys = Button(
+ frameCustom, text='Get New Keys for Selection',
+ command=self.GetNewKeys, state=DISABLED)
#frameKeySets
frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0)
for i in range(2)]
- self.radioKeysBuiltin=Radiobutton(frames[0],variable=self.keysAreBuiltin,
- value=1,command=self.SetKeysType,text='Use a Built-in Key Set')
- self.radioKeysCustom=Radiobutton(frames[0],variable=self.keysAreBuiltin,
- value=0,command=self.SetKeysType,text='Use a Custom Key Set')
- self.optMenuKeysBuiltin=DynOptionMenu(frames[0],
- self.builtinKeys,None,command=None)
- self.optMenuKeysCustom=DynOptionMenu(frames[0],
- self.customKeys,None,command=None)
- self.buttonDeleteCustomKeys=Button(frames[1],text='Delete Custom Key Set',
+ self.radioKeysBuiltin = Radiobutton(
+ frames[0], variable=self.keysAreBuiltin, value=1,
+ command=self.SetKeysType, text='Use a Built-in Key Set')
+ self.radioKeysCustom = Radiobutton(
+ frames[0], variable=self.keysAreBuiltin, value=0,
+ command=self.SetKeysType, text='Use a Custom Key Set')
+ self.optMenuKeysBuiltin = DynOptionMenu(
+ frames[0], self.builtinKeys, None, command=None)
+ self.optMenuKeysCustom = DynOptionMenu(
+ frames[0], self.customKeys, None, command=None)
+ self.buttonDeleteCustomKeys = Button(
+ frames[1], text='Delete Custom Key Set',
command=self.DeleteCustomKeys)
- buttonSaveCustomKeys=Button(frames[1],
- text='Save as New Custom Key Set',command=self.SaveAsNewKeySet)
+ buttonSaveCustomKeys = Button(
+ frames[1], text='Save as New Custom Key Set',
+ command=self.SaveAsNewKeySet)
+
##widget packing
#body
- frameCustom.pack(side=BOTTOM,padx=5,pady=5,expand=TRUE,fill=BOTH)
- frameKeySets.pack(side=BOTTOM,padx=5,pady=5,fill=BOTH)
+ frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH)
#frameCustom
- self.buttonNewKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
- frameTarget.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
+ self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5)
+ frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH)
#frame target
- frameTarget.columnconfigure(0,weight=1)
- frameTarget.rowconfigure(1,weight=1)
- labelTargetTitle.grid(row=0,column=0,columnspan=2,sticky=W)
- self.listBindings.grid(row=1,column=0,sticky=NSEW)
- scrollTargetY.grid(row=1,column=1,sticky=NS)
- scrollTargetX.grid(row=2,column=0,sticky=EW)
+ frameTarget.columnconfigure(0, weight=1)
+ frameTarget.rowconfigure(1, weight=1)
+ labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W)
+ self.listBindings.grid(row=1, column=0, sticky=NSEW)
+ scrollTargetY.grid(row=1, column=1, sticky=NS)
+ scrollTargetX.grid(row=2, column=0, sticky=EW)
#frameKeySets
self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS)
self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS)
self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW)
self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW)
- self.buttonDeleteCustomKeys.pack(side=LEFT,fill=X,expand=True,padx=2)
- buttonSaveCustomKeys.pack(side=LEFT,fill=X,expand=True,padx=2)
+ self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
+ buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2)
frames[0].pack(side=TOP, fill=BOTH, expand=True)
frames[1].pack(side=TOP, fill=X, expand=True, pady=2)
return frame
def CreatePageGeneral(self):
- #tkVars
- self.winWidth=StringVar(self)
- self.winHeight=StringVar(self)
- self.paraWidth=StringVar(self)
- self.startupEdit=IntVar(self)
- self.autoSave=IntVar(self)
- self.encoding=StringVar(self)
- self.userHelpBrowser=BooleanVar(self)
- self.helpBrowser=StringVar(self)
+ parent = self.parent
+ self.winWidth = StringVar(parent)
+ self.winHeight = StringVar(parent)
+ self.paraWidth = StringVar(parent)
+ self.startupEdit = IntVar(parent)
+ self.autoSave = IntVar(parent)
+ self.encoding = StringVar(parent)
+ self.userHelpBrowser = BooleanVar(parent)
+ self.helpBrowser = StringVar(parent)
+
#widget creation
#body
- frame=self.tabPages.pages['General'].frame
+ frame = self.tabPages.pages['General'].frame
#body section frames
- frameRun=LabelFrame(frame,borderwidth=2,relief=GROOVE,
- text=' Startup Preferences ')
- frameSave=LabelFrame(frame,borderwidth=2,relief=GROOVE,
- text=' Autosave Preferences ')
- frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE)
- frameParaSize=Frame(frame,borderwidth=2,relief=GROOVE)
- frameHelp=LabelFrame(frame,borderwidth=2,relief=GROOVE,
- text=' Additional Help Sources ')
+ frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE,
+ text=' Startup Preferences ')
+ frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE,
+ text=' Autosave Preferences ')
+ frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE)
+ frameParaSize = Frame(frame, borderwidth=2, relief=GROOVE)
+ frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE,
+ text=' Additional Help Sources ')
#frameRun
- labelRunChoiceTitle=Label(frameRun,text='At Startup')
- radioStartupEdit=Radiobutton(frameRun,variable=self.startupEdit,
- value=1,command=self.SetKeysType,text="Open Edit Window")
- radioStartupShell=Radiobutton(frameRun,variable=self.startupEdit,
- value=0,command=self.SetKeysType,text='Open Shell Window')
+ labelRunChoiceTitle = Label(frameRun, text='At Startup')
+ radioStartupEdit = Radiobutton(
+ frameRun, variable=self.startupEdit, value=1,
+ command=self.SetKeysType, text="Open Edit Window")
+ radioStartupShell = Radiobutton(
+ frameRun, variable=self.startupEdit, value=0,
+ command=self.SetKeysType, text='Open Shell Window')
#frameSave
- labelRunSaveTitle=Label(frameSave,text='At Start of Run (F5) ')
- radioSaveAsk=Radiobutton(frameSave,variable=self.autoSave,
- value=0,command=self.SetKeysType,text="Prompt to Save")
- radioSaveAuto=Radiobutton(frameSave,variable=self.autoSave,
- value=1,command=self.SetKeysType,text='No Prompt')
+ labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ')
+ radioSaveAsk = Radiobutton(
+ frameSave, variable=self.autoSave, value=0,
+ command=self.SetKeysType, text="Prompt to Save")
+ radioSaveAuto = Radiobutton(
+ frameSave, variable=self.autoSave, value=1,
+ command=self.SetKeysType, text='No Prompt')
#frameWinSize
- labelWinSizeTitle=Label(frameWinSize,text='Initial Window Size'+
- ' (in characters)')
- labelWinWidthTitle=Label(frameWinSize,text='Width')
- entryWinWidth=Entry(frameWinSize,textvariable=self.winWidth,
- width=3)
- labelWinHeightTitle=Label(frameWinSize,text='Height')
- entryWinHeight=Entry(frameWinSize,textvariable=self.winHeight,
- width=3)
+ labelWinSizeTitle = Label(
+ frameWinSize, text='Initial Window Size (in characters)')
+ labelWinWidthTitle = Label(frameWinSize, text='Width')
+ entryWinWidth = Entry(
+ frameWinSize, textvariable=self.winWidth, width=3)
+ labelWinHeightTitle = Label(frameWinSize, text='Height')
+ entryWinHeight = Entry(
+ frameWinSize, textvariable=self.winHeight, width=3)
#paragraphFormatWidth
- labelParaWidthTitle=Label(frameParaSize,text='Paragraph reformat'+
- ' width (in characters)')
- entryParaWidth=Entry(frameParaSize,textvariable=self.paraWidth,
- width=3)
+ labelParaWidthTitle = Label(
+ frameParaSize, text='Paragraph reformat width (in characters)')
+ entryParaWidth = Entry(
+ frameParaSize, textvariable=self.paraWidth, width=3)
#frameHelp
- frameHelpList=Frame(frameHelp)
- frameHelpListButtons=Frame(frameHelpList)
- scrollHelpList=Scrollbar(frameHelpList)
- self.listHelp=Listbox(frameHelpList,height=5,takefocus=FALSE,
+ frameHelpList = Frame(frameHelp)
+ frameHelpListButtons = Frame(frameHelpList)
+ scrollHelpList = Scrollbar(frameHelpList)
+ self.listHelp = Listbox(
+ frameHelpList, height=5, takefocus=FALSE,
exportselection=FALSE)
scrollHelpList.config(command=self.listHelp.yview)
self.listHelp.config(yscrollcommand=scrollHelpList.set)
- self.listHelp.bind('<ButtonRelease-1>',self.HelpSourceSelected)
- self.buttonHelpListEdit=Button(frameHelpListButtons,text='Edit',
- state=DISABLED,width=8,command=self.HelpListItemEdit)
- self.buttonHelpListAdd=Button(frameHelpListButtons,text='Add',
- width=8,command=self.HelpListItemAdd)
- self.buttonHelpListRemove=Button(frameHelpListButtons,text='Remove',
- state=DISABLED,width=8,command=self.HelpListItemRemove)
+ self.listHelp.bind('<ButtonRelease-1>', self.HelpSourceSelected)
+ self.buttonHelpListEdit = Button(
+ frameHelpListButtons, text='Edit', state=DISABLED,
+ width=8, command=self.HelpListItemEdit)
+ self.buttonHelpListAdd = Button(
+ frameHelpListButtons, text='Add',
+ width=8, command=self.HelpListItemAdd)
+ self.buttonHelpListRemove = Button(
+ frameHelpListButtons, text='Remove', state=DISABLED,
+ width=8, command=self.HelpListItemRemove)
+
#widget packing
#body
- frameRun.pack(side=TOP,padx=5,pady=5,fill=X)
- frameSave.pack(side=TOP,padx=5,pady=5,fill=X)
- frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X)
- frameParaSize.pack(side=TOP,padx=5,pady=5,fill=X)
- frameHelp.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
+ frameRun.pack(side=TOP, padx=5, pady=5, fill=X)
+ frameSave.pack(side=TOP, padx=5, pady=5, fill=X)
+ frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X)
+ frameParaSize.pack(side=TOP, padx=5, pady=5, fill=X)
+ frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
#frameRun
- labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
- radioStartupShell.pack(side=RIGHT,anchor=W,padx=5,pady=5)
- radioStartupEdit.pack(side=RIGHT,anchor=W,padx=5,pady=5)
+ labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
+ radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+ radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5)
#frameSave
- labelRunSaveTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
- radioSaveAuto.pack(side=RIGHT,anchor=W,padx=5,pady=5)
- radioSaveAsk.pack(side=RIGHT,anchor=W,padx=5,pady=5)
+ labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
+ radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5)
+ radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5)
#frameWinSize
- labelWinSizeTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
- entryWinHeight.pack(side=RIGHT,anchor=E,padx=10,pady=5)
- labelWinHeightTitle.pack(side=RIGHT,anchor=E,pady=5)
- entryWinWidth.pack(side=RIGHT,anchor=E,padx=10,pady=5)
- labelWinWidthTitle.pack(side=RIGHT,anchor=E,pady=5)
+ labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
+ entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5)
+ labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5)
+ entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
+ labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5)
#paragraphFormatWidth
- labelParaWidthTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
- entryParaWidth.pack(side=RIGHT,anchor=E,padx=10,pady=5)
+ labelParaWidthTitle.pack(side=LEFT, anchor=W, padx=5, pady=5)
+ entryParaWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5)
#frameHelp
- frameHelpListButtons.pack(side=RIGHT,padx=5,pady=5,fill=Y)
- frameHelpList.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
- scrollHelpList.pack(side=RIGHT,anchor=W,fill=Y)
- self.listHelp.pack(side=LEFT,anchor=E,expand=TRUE,fill=BOTH)
- self.buttonHelpListEdit.pack(side=TOP,anchor=W,pady=5)
- self.buttonHelpListAdd.pack(side=TOP,anchor=W)
- self.buttonHelpListRemove.pack(side=TOP,anchor=W,pady=5)
+ frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y)
+ frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH)
+ scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y)
+ self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH)
+ self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5)
+ self.buttonHelpListAdd.pack(side=TOP, anchor=W)
+ self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5)
return frame
def AttachVarCallbacks(self):
- self.fontSize.trace_variable('w',self.VarChanged_fontSize)
- self.fontName.trace_variable('w',self.VarChanged_fontName)
- self.fontBold.trace_variable('w',self.VarChanged_fontBold)
- self.spaceNum.trace_variable('w',self.VarChanged_spaceNum)
- self.colour.trace_variable('w',self.VarChanged_colour)
- self.builtinTheme.trace_variable('w',self.VarChanged_builtinTheme)
- self.customTheme.trace_variable('w',self.VarChanged_customTheme)
- self.themeIsBuiltin.trace_variable('w',self.VarChanged_themeIsBuiltin)
- self.highlightTarget.trace_variable('w',self.VarChanged_highlightTarget)
- self.keyBinding.trace_variable('w',self.VarChanged_keyBinding)
- self.builtinKeys.trace_variable('w',self.VarChanged_builtinKeys)
- self.customKeys.trace_variable('w',self.VarChanged_customKeys)
- self.keysAreBuiltin.trace_variable('w',self.VarChanged_keysAreBuiltin)
- self.winWidth.trace_variable('w',self.VarChanged_winWidth)
- self.winHeight.trace_variable('w',self.VarChanged_winHeight)
- self.paraWidth.trace_variable('w',self.VarChanged_paraWidth)
- self.startupEdit.trace_variable('w',self.VarChanged_startupEdit)
- self.autoSave.trace_variable('w',self.VarChanged_autoSave)
- self.encoding.trace_variable('w',self.VarChanged_encoding)
-
- def VarChanged_fontSize(self,*params):
- value=self.fontSize.get()
- self.AddChangedItem('main','EditorWindow','font-size',value)
-
- def VarChanged_fontName(self,*params):
- value=self.fontName.get()
- self.AddChangedItem('main','EditorWindow','font',value)
-
- def VarChanged_fontBold(self,*params):
- value=self.fontBold.get()
- self.AddChangedItem('main','EditorWindow','font-bold',value)
-
- def VarChanged_spaceNum(self,*params):
- value=self.spaceNum.get()
- self.AddChangedItem('main','Indent','num-spaces',value)
-
- def VarChanged_colour(self,*params):
+ self.fontSize.trace_variable('w', self.VarChanged_fontSize)
+ self.fontName.trace_variable('w', self.VarChanged_fontName)
+ self.fontBold.trace_variable('w', self.VarChanged_fontBold)
+ self.spaceNum.trace_variable('w', self.VarChanged_spaceNum)
+ self.colour.trace_variable('w', self.VarChanged_colour)
+ self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme)
+ self.customTheme.trace_variable('w', self.VarChanged_customTheme)
+ self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin)
+ self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget)
+ self.keyBinding.trace_variable('w', self.VarChanged_keyBinding)
+ self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys)
+ self.customKeys.trace_variable('w', self.VarChanged_customKeys)
+ self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin)
+ self.winWidth.trace_variable('w', self.VarChanged_winWidth)
+ self.winHeight.trace_variable('w', self.VarChanged_winHeight)
+ self.paraWidth.trace_variable('w', self.VarChanged_paraWidth)
+ self.startupEdit.trace_variable('w', self.VarChanged_startupEdit)
+ self.autoSave.trace_variable('w', self.VarChanged_autoSave)
+ self.encoding.trace_variable('w', self.VarChanged_encoding)
+
+ def VarChanged_fontSize(self, *params):
+ value = self.fontSize.get()
+ self.AddChangedItem('main', 'EditorWindow', 'font-size', value)
+
+ def VarChanged_fontName(self, *params):
+ value = self.fontName.get()
+ self.AddChangedItem('main', 'EditorWindow', 'font', value)
+
+ def VarChanged_fontBold(self, *params):
+ value = self.fontBold.get()
+ self.AddChangedItem('main', 'EditorWindow', 'font-bold', value)
+
+ def VarChanged_spaceNum(self, *params):
+ value = self.spaceNum.get()
+ self.AddChangedItem('main', 'Indent', 'num-spaces', value)
+
+ def VarChanged_colour(self, *params):
self.OnNewColourSet()
- def VarChanged_builtinTheme(self,*params):
- value=self.builtinTheme.get()
- self.AddChangedItem('main','Theme','name',value)
+ def VarChanged_builtinTheme(self, *params):
+ value = self.builtinTheme.get()
+ self.AddChangedItem('main', 'Theme', 'name', value)
self.PaintThemeSample()
- def VarChanged_customTheme(self,*params):
- value=self.customTheme.get()
+ def VarChanged_customTheme(self, *params):
+ value = self.customTheme.get()
if value != '- no custom themes -':
- self.AddChangedItem('main','Theme','name',value)
+ self.AddChangedItem('main', 'Theme', 'name', value)
self.PaintThemeSample()
- def VarChanged_themeIsBuiltin(self,*params):
- value=self.themeIsBuiltin.get()
- self.AddChangedItem('main','Theme','default',value)
+ def VarChanged_themeIsBuiltin(self, *params):
+ value = self.themeIsBuiltin.get()
+ self.AddChangedItem('main', 'Theme', 'default', value)
if value:
self.VarChanged_builtinTheme()
else:
self.VarChanged_customTheme()
- def VarChanged_highlightTarget(self,*params):
+ def VarChanged_highlightTarget(self, *params):
self.SetHighlightTarget()
- def VarChanged_keyBinding(self,*params):
- value=self.keyBinding.get()
- keySet=self.customKeys.get()
- event=self.listBindings.get(ANCHOR).split()[0]
+ def VarChanged_keyBinding(self, *params):
+ value = self.keyBinding.get()
+ keySet = self.customKeys.get()
+ event = self.listBindings.get(ANCHOR).split()[0]
if idleConf.IsCoreBinding(event):
#this is a core keybinding
- self.AddChangedItem('keys',keySet,event,value)
+ self.AddChangedItem('keys', keySet, event, value)
else: #this is an extension key binding
- extName=idleConf.GetExtnNameForEvent(event)
- extKeybindSection=extName+'_cfgBindings'
- self.AddChangedItem('extensions',extKeybindSection,event,value)
+ extName = idleConf.GetExtnNameForEvent(event)
+ extKeybindSection = extName + '_cfgBindings'
+ self.AddChangedItem('extensions', extKeybindSection, event, value)
- def VarChanged_builtinKeys(self,*params):
- value=self.builtinKeys.get()
- self.AddChangedItem('main','Keys','name',value)
+ def VarChanged_builtinKeys(self, *params):
+ value = self.builtinKeys.get()
+ self.AddChangedItem('main', 'Keys', 'name', value)
self.LoadKeysList(value)
- def VarChanged_customKeys(self,*params):
- value=self.customKeys.get()
+ def VarChanged_customKeys(self, *params):
+ value = self.customKeys.get()
if value != '- no custom keys -':
- self.AddChangedItem('main','Keys','name',value)
+ self.AddChangedItem('main', 'Keys', 'name', value)
self.LoadKeysList(value)
- def VarChanged_keysAreBuiltin(self,*params):
- value=self.keysAreBuiltin.get()
- self.AddChangedItem('main','Keys','default',value)
+ def VarChanged_keysAreBuiltin(self, *params):
+ value = self.keysAreBuiltin.get()
+ self.AddChangedItem('main', 'Keys', 'default', value)
if value:
self.VarChanged_builtinKeys()
else:
self.VarChanged_customKeys()
- def VarChanged_winWidth(self,*params):
- value=self.winWidth.get()
- self.AddChangedItem('main','EditorWindow','width',value)
+ def VarChanged_winWidth(self, *params):
+ value = self.winWidth.get()
+ self.AddChangedItem('main', 'EditorWindow', 'width', value)
- def VarChanged_winHeight(self,*params):
- value=self.winHeight.get()
- self.AddChangedItem('main','EditorWindow','height',value)
+ def VarChanged_winHeight(self, *params):
+ value = self.winHeight.get()
+ self.AddChangedItem('main', 'EditorWindow', 'height', value)
- def VarChanged_paraWidth(self,*params):
- value=self.paraWidth.get()
- self.AddChangedItem('main','FormatParagraph','paragraph',value)
+ def VarChanged_paraWidth(self, *params):
+ value = self.paraWidth.get()
+ self.AddChangedItem('main', 'FormatParagraph', 'paragraph', value)
- def VarChanged_startupEdit(self,*params):
- value=self.startupEdit.get()
- self.AddChangedItem('main','General','editor-on-startup',value)
+ def VarChanged_startupEdit(self, *params):
+ value = self.startupEdit.get()
+ self.AddChangedItem('main', 'General', 'editor-on-startup', value)
- def VarChanged_autoSave(self,*params):
- value=self.autoSave.get()
- self.AddChangedItem('main','General','autosave',value)
+ def VarChanged_autoSave(self, *params):
+ value = self.autoSave.get()
+ self.AddChangedItem('main', 'General', 'autosave', value)
- def VarChanged_encoding(self,*params):
- value=self.encoding.get()
- self.AddChangedItem('main','EditorWindow','encoding',value)
+ def VarChanged_encoding(self, *params):
+ value = self.encoding.get()
+ self.AddChangedItem('main', 'EditorWindow', 'encoding', value)
def ResetChangedItems(self):
#When any config item is changed in this dialog, an entry
@@ -548,24 +596,25 @@ class ConfigDialog(Toplevel):
#dictionary. The key should be the config file section name and the
#value a dictionary, whose key:value pairs are item=value pairs for
#that config file section.
- self.changedItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
+ self.changedItems = {'main':{}, 'highlight':{}, 'keys':{},
+ 'extensions':{}}
- def AddChangedItem(self,type,section,item,value):
- value=str(value) #make sure we use a string
- if section not in self.changedItems[type]:
- self.changedItems[type][section]={}
- self.changedItems[type][section][item]=value
+ def AddChangedItem(self, typ, section, item, value):
+ value = str(value) #make sure we use a string
+ if section not in self.changedItems[typ]:
+ self.changedItems[typ][section] = {}
+ self.changedItems[typ][section][item] = value
def GetDefaultItems(self):
- dItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
+ dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
for configType in dItems:
- sections=idleConf.GetSectionList('default',configType)
+ sections = idleConf.GetSectionList('default', configType)
for section in sections:
- dItems[configType][section]={}
- options=idleConf.defaultCfg[configType].GetOptionList(section)
+ dItems[configType][section] = {}
+ options = idleConf.defaultCfg[configType].GetOptionList(section)
for option in options:
- dItems[configType][section][option]=(
- idleConf.defaultCfg[configType].Get(section,option))
+ dItems[configType][section][option] = (
+ idleConf.defaultCfg[configType].Get(section, option))
return dItems
def SetThemeType(self):
@@ -591,26 +640,26 @@ class ConfigDialog(Toplevel):
self.buttonDeleteCustomKeys.config(state=NORMAL)
def GetNewKeys(self):
- listIndex=self.listBindings.index(ANCHOR)
- binding=self.listBindings.get(listIndex)
- bindName=binding.split()[0] #first part, up to first space
+ listIndex = self.listBindings.index(ANCHOR)
+ binding = self.listBindings.get(listIndex)
+ bindName = binding.split()[0] #first part, up to first space
if self.keysAreBuiltin.get():
- currentKeySetName=self.builtinKeys.get()
+ currentKeySetName = self.builtinKeys.get()
else:
- currentKeySetName=self.customKeys.get()
- currentBindings=idleConf.GetCurrentKeySet()
+ currentKeySetName = self.customKeys.get()
+ currentBindings = idleConf.GetCurrentKeySet()
if currentKeySetName in self.changedItems['keys']: #unsaved changes
- keySetChanges=self.changedItems['keys'][currentKeySetName]
+ keySetChanges = self.changedItems['keys'][currentKeySetName]
for event in keySetChanges:
- currentBindings[event]=keySetChanges[event].split()
+ currentBindings[event] = keySetChanges[event].split()
currentKeySequences = list(currentBindings.values())
- newKeys=GetKeysDialog(self,'Get New Keys',bindName,
+ newKeys = GetKeysDialog(self, 'Get New Keys', bindName,
currentKeySequences).result
if newKeys: #new keys were specified
if self.keysAreBuiltin.get(): #current key set is a built-in
- message=('Your changes will be saved as a new Custom Key Set. '+
- 'Enter a name for your new Custom Key Set below.')
- newKeySet=self.GetNewKeysName(message)
+ message = ('Your changes will be saved as a new Custom Key Set.'
+ ' Enter a name for your new Custom Key Set below.')
+ newKeySet = self.GetNewKeysName(message)
if not newKeySet: #user cancelled custom key set creation
self.listBindings.select_set(listIndex)
self.listBindings.select_anchor(listIndex)
@@ -618,7 +667,7 @@ class ConfigDialog(Toplevel):
else: #create new custom key set based on previously active key set
self.CreateNewKeySet(newKeySet)
self.listBindings.delete(listIndex)
- self.listBindings.insert(listIndex,bindName+' - '+newKeys)
+ self.listBindings.insert(listIndex, bindName+' - '+newKeys)
self.listBindings.select_set(listIndex)
self.listBindings.select_anchor(listIndex)
self.keyBinding.set(newKeys)
@@ -626,65 +675,65 @@ class ConfigDialog(Toplevel):
self.listBindings.select_set(listIndex)
self.listBindings.select_anchor(listIndex)
- def GetNewKeysName(self,message):
- usedNames=(idleConf.GetSectionList('user','keys')+
- idleConf.GetSectionList('default','keys'))
- newKeySet=GetCfgSectionNameDialog(self,'New Custom Key Set',
- message,usedNames).result
+ def GetNewKeysName(self, message):
+ usedNames = (idleConf.GetSectionList('user', 'keys') +
+ idleConf.GetSectionList('default', 'keys'))
+ newKeySet = GetCfgSectionNameDialog(
+ self, 'New Custom Key Set', message, usedNames).result
return newKeySet
def SaveAsNewKeySet(self):
- newKeysName=self.GetNewKeysName('New Key Set Name:')
+ newKeysName = self.GetNewKeysName('New Key Set Name:')
if newKeysName:
self.CreateNewKeySet(newKeysName)
- def KeyBindingSelected(self,event):
+ def KeyBindingSelected(self, event):
self.buttonNewKeys.config(state=NORMAL)
- def CreateNewKeySet(self,newKeySetName):
+ def CreateNewKeySet(self, newKeySetName):
#creates new custom key set based on the previously active key set,
#and makes the new key set active
if self.keysAreBuiltin.get():
- prevKeySetName=self.builtinKeys.get()
+ prevKeySetName = self.builtinKeys.get()
else:
- prevKeySetName=self.customKeys.get()
- prevKeys=idleConf.GetCoreKeys(prevKeySetName)
- newKeys={}
+ prevKeySetName = self.customKeys.get()
+ prevKeys = idleConf.GetCoreKeys(prevKeySetName)
+ newKeys = {}
for event in prevKeys: #add key set to changed items
- eventName=event[2:-2] #trim off the angle brackets
- binding=' '.join(prevKeys[event])
- newKeys[eventName]=binding
+ eventName = event[2:-2] #trim off the angle brackets
+ binding = ' '.join(prevKeys[event])
+ newKeys[eventName] = binding
#handle any unsaved changes to prev key set
if prevKeySetName in self.changedItems['keys']:
- keySetChanges=self.changedItems['keys'][prevKeySetName]
+ keySetChanges = self.changedItems['keys'][prevKeySetName]
for event in keySetChanges:
- newKeys[event]=keySetChanges[event]
+ newKeys[event] = keySetChanges[event]
#save the new theme
- self.SaveNewKeySet(newKeySetName,newKeys)
+ self.SaveNewKeySet(newKeySetName, newKeys)
#change gui over to the new key set
- customKeyList=idleConf.GetSectionList('user','keys')
+ customKeyList = idleConf.GetSectionList('user', 'keys')
customKeyList.sort()
- self.optMenuKeysCustom.SetMenu(customKeyList,newKeySetName)
+ self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName)
self.keysAreBuiltin.set(0)
self.SetKeysType()
- def LoadKeysList(self,keySetName):
- reselect=0
- newKeySet=0
+ def LoadKeysList(self, keySetName):
+ reselect = 0
+ newKeySet = 0
if self.listBindings.curselection():
- reselect=1
- listIndex=self.listBindings.index(ANCHOR)
- keySet=idleConf.GetKeySet(keySetName)
+ reselect = 1
+ listIndex = self.listBindings.index(ANCHOR)
+ keySet = idleConf.GetKeySet(keySetName)
bindNames = list(keySet.keys())
bindNames.sort()
- self.listBindings.delete(0,END)
+ self.listBindings.delete(0, END)
for bindName in bindNames:
- key=' '.join(keySet[bindName]) #make key(s) into a string
- bindName=bindName[2:-2] #trim off the angle brackets
+ key = ' '.join(keySet[bindName]) #make key(s) into a string
+ bindName = bindName[2:-2] #trim off the angle brackets
if keySetName in self.changedItems['keys']:
#handle any unsaved changes to this key set
if bindName in self.changedItems['keys'][keySetName]:
- key=self.changedItems['keys'][keySetName][bindName]
+ key = self.changedItems['keys'][keySetName][bindName]
self.listBindings.insert(END, bindName+' - '+key)
if reselect:
self.listBindings.see(listIndex)
@@ -693,9 +742,9 @@ class ConfigDialog(Toplevel):
def DeleteCustomKeys(self):
keySetName=self.customKeys.get()
- if not tkMessageBox.askyesno('Delete Key Set','Are you sure you wish '+
- 'to delete the key set %r ?' % (keySetName),
- parent=self):
+ delmsg = 'Are you sure you wish to delete the key set %r ?'
+ if not tkMessageBox.askyesno(
+ 'Delete Key Set', delmsg % keySetName, parent=self):
return
#remove key set from config
idleConf.userCfg['keys'].remove_section(keySetName)
@@ -704,25 +753,25 @@ class ConfigDialog(Toplevel):
#write changes
idleConf.userCfg['keys'].Save()
#reload user key set list
- itemList=idleConf.GetSectionList('user','keys')
+ itemList = idleConf.GetSectionList('user', 'keys')
itemList.sort()
if not itemList:
self.radioKeysCustom.config(state=DISABLED)
- self.optMenuKeysCustom.SetMenu(itemList,'- no custom keys -')
+ self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -')
else:
- self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
+ self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
#revert to default key set
- self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default'))
- self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name'))
+ self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default'))
+ self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name'))
#user can't back out of these changes, they must be applied now
self.Apply()
self.SetKeysType()
def DeleteCustomTheme(self):
- themeName=self.customTheme.get()
- if not tkMessageBox.askyesno('Delete Theme','Are you sure you wish '+
- 'to delete the theme %r ?' % (themeName,),
- parent=self):
+ themeName = self.customTheme.get()
+ delmsg = 'Are you sure you wish to delete the theme %r ?'
+ if not tkMessageBox.askyesno(
+ 'Delete Theme', delmsg % themeName, parent=self):
return
#remove theme from config
idleConf.userCfg['highlight'].remove_section(themeName)
@@ -731,153 +780,149 @@ class ConfigDialog(Toplevel):
#write changes
idleConf.userCfg['highlight'].Save()
#reload user theme list
- itemList=idleConf.GetSectionList('user','highlight')
+ itemList = idleConf.GetSectionList('user', 'highlight')
itemList.sort()
if not itemList:
self.radioThemeCustom.config(state=DISABLED)
- self.optMenuThemeCustom.SetMenu(itemList,'- no custom themes -')
+ self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -')
else:
- self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
+ self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
#revert to default theme
- self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme','default'))
- self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme','name'))
+ self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default'))
+ self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
#user can't back out of these changes, they must be applied now
self.Apply()
self.SetThemeType()
def GetColour(self):
- target=self.highlightTarget.get()
- prevColour=self.frameColourSet.cget('bg')
- rgbTuplet, colourString = tkColorChooser.askcolor(parent=self,
- title='Pick new colour for : '+target,initialcolor=prevColour)
- if colourString and (colourString!=prevColour):
+ target = self.highlightTarget.get()
+ prevColour = self.frameColourSet.cget('bg')
+ rgbTuplet, colourString = tkColorChooser.askcolor(
+ parent=self, title='Pick new colour for : '+target,
+ initialcolor=prevColour)
+ if colourString and (colourString != prevColour):
#user didn't cancel, and they chose a new colour
- if self.themeIsBuiltin.get(): #current theme is a built-in
- message=('Your changes will be saved as a new Custom Theme. '+
- 'Enter a name for your new Custom Theme below.')
- newTheme=self.GetNewThemeName(message)
- if not newTheme: #user cancelled custom theme creation
+ if self.themeIsBuiltin.get(): #current theme is a built-in
+ message = ('Your changes will be saved as a new Custom Theme. '
+ 'Enter a name for your new Custom Theme below.')
+ newTheme = self.GetNewThemeName(message)
+ if not newTheme: #user cancelled custom theme creation
return
- else: #create new custom theme based on previously active theme
+ else: #create new custom theme based on previously active theme
self.CreateNewTheme(newTheme)
self.colour.set(colourString)
- else: #current theme is user defined
+ else: #current theme is user defined
self.colour.set(colourString)
def OnNewColourSet(self):
newColour=self.colour.get()
- self.frameColourSet.config(bg=newColour)#set sample
- if self.fgHilite.get(): plane='foreground'
- else: plane='background'
- sampleElement=self.themeElements[self.highlightTarget.get()][0]
+ self.frameColourSet.config(bg=newColour) #set sample
+ plane ='foreground' if self.fgHilite.get() else 'background'
+ sampleElement = self.themeElements[self.highlightTarget.get()][0]
self.textHighlightSample.tag_config(sampleElement, **{plane:newColour})
- theme=self.customTheme.get()
- themeElement=sampleElement+'-'+plane
- self.AddChangedItem('highlight',theme,themeElement,newColour)
-
- def GetNewThemeName(self,message):
- usedNames=(idleConf.GetSectionList('user','highlight')+
- idleConf.GetSectionList('default','highlight'))
- newTheme=GetCfgSectionNameDialog(self,'New Custom Theme',
- message,usedNames).result
+ theme = self.customTheme.get()
+ themeElement = sampleElement + '-' + plane
+ self.AddChangedItem('highlight', theme, themeElement, newColour)
+
+ def GetNewThemeName(self, message):
+ usedNames = (idleConf.GetSectionList('user', 'highlight') +
+ idleConf.GetSectionList('default', 'highlight'))
+ newTheme = GetCfgSectionNameDialog(
+ self, 'New Custom Theme', message, usedNames).result
return newTheme
def SaveAsNewTheme(self):
- newThemeName=self.GetNewThemeName('New Theme Name:')
+ newThemeName = self.GetNewThemeName('New Theme Name:')
if newThemeName:
self.CreateNewTheme(newThemeName)
- def CreateNewTheme(self,newThemeName):
+ def CreateNewTheme(self, newThemeName):
#creates new custom theme based on the previously active theme,
#and makes the new theme active
if self.themeIsBuiltin.get():
- themeType='default'
- themeName=self.builtinTheme.get()
+ themeType = 'default'
+ themeName = self.builtinTheme.get()
else:
- themeType='user'
- themeName=self.customTheme.get()
- newTheme=idleConf.GetThemeDict(themeType,themeName)
+ themeType = 'user'
+ themeName = self.customTheme.get()
+ newTheme = idleConf.GetThemeDict(themeType, themeName)
#apply any of the old theme's unsaved changes to the new theme
if themeName in self.changedItems['highlight']:
- themeChanges=self.changedItems['highlight'][themeName]
+ themeChanges = self.changedItems['highlight'][themeName]
for element in themeChanges:
- newTheme[element]=themeChanges[element]
+ newTheme[element] = themeChanges[element]
#save the new theme
- self.SaveNewTheme(newThemeName,newTheme)
+ self.SaveNewTheme(newThemeName, newTheme)
#change gui over to the new theme
- customThemeList=idleConf.GetSectionList('user','highlight')
+ customThemeList = idleConf.GetSectionList('user', 'highlight')
customThemeList.sort()
- self.optMenuThemeCustom.SetMenu(customThemeList,newThemeName)
+ self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName)
self.themeIsBuiltin.set(0)
self.SetThemeType()
- def OnListFontButtonRelease(self,event):
+ def OnListFontButtonRelease(self, event):
font = self.listFontName.get(ANCHOR)
self.fontName.set(font.lower())
self.SetFontSample()
- def SetFontSample(self,event=None):
- fontName=self.fontName.get()
- if self.fontBold.get():
- fontWeight=tkFont.BOLD
- else:
- fontWeight=tkFont.NORMAL
+ def SetFontSample(self, event=None):
+ fontName = self.fontName.get()
+ fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL
newFont = (fontName, self.fontSize.get(), fontWeight)
self.labelFontSample.config(font=newFont)
self.textHighlightSample.configure(font=newFont)
def SetHighlightTarget(self):
- if self.highlightTarget.get()=='Cursor': #bg not possible
+ if self.highlightTarget.get() == 'Cursor': #bg not possible
self.radioFg.config(state=DISABLED)
self.radioBg.config(state=DISABLED)
self.fgHilite.set(1)
- else: #both fg and bg can be set
+ else: #both fg and bg can be set
self.radioFg.config(state=NORMAL)
self.radioBg.config(state=NORMAL)
self.fgHilite.set(1)
self.SetColourSample()
- def SetColourSampleBinding(self,*args):
+ def SetColourSampleBinding(self, *args):
self.SetColourSample()
def SetColourSample(self):
#set the colour smaple area
- tag=self.themeElements[self.highlightTarget.get()][0]
- if self.fgHilite.get(): plane='foreground'
- else: plane='background'
- colour=self.textHighlightSample.tag_cget(tag,plane)
+ tag = self.themeElements[self.highlightTarget.get()][0]
+ plane = 'foreground' if self.fgHilite.get() else 'background'
+ colour = self.textHighlightSample.tag_cget(tag, plane)
self.frameColourSet.config(bg=colour)
def PaintThemeSample(self):
- if self.themeIsBuiltin.get(): #a default theme
- theme=self.builtinTheme.get()
- else: #a user theme
- theme=self.customTheme.get()
+ if self.themeIsBuiltin.get(): #a default theme
+ theme = self.builtinTheme.get()
+ else: #a user theme
+ theme = self.customTheme.get()
for elementTitle in self.themeElements:
- element=self.themeElements[elementTitle][0]
- colours=idleConf.GetHighlight(theme,element)
- if element=='cursor': #cursor sample needs special painting
- colours['background']=idleConf.GetHighlight(theme,
- 'normal', fgBg='bg')
+ element = self.themeElements[elementTitle][0]
+ colours = idleConf.GetHighlight(theme, element)
+ if element == 'cursor': #cursor sample needs special painting
+ colours['background'] = idleConf.GetHighlight(
+ theme, 'normal', fgBg='bg')
#handle any unsaved changes to this theme
if theme in self.changedItems['highlight']:
- themeDict=self.changedItems['highlight'][theme]
- if element+'-foreground' in themeDict:
- colours['foreground']=themeDict[element+'-foreground']
- if element+'-background' in themeDict:
- colours['background']=themeDict[element+'-background']
+ themeDict = self.changedItems['highlight'][theme]
+ if element + '-foreground' in themeDict:
+ colours['foreground'] = themeDict[element + '-foreground']
+ if element + '-background' in themeDict:
+ colours['background'] = themeDict[element + '-background']
self.textHighlightSample.tag_config(element, **colours)
self.SetColourSample()
- def HelpSourceSelected(self,event):
+ def HelpSourceSelected(self, event):
self.SetHelpListButtonStates()
def SetHelpListButtonStates(self):
- if self.listHelp.size()<1: #no entries in list
+ if self.listHelp.size() < 1: #no entries in list
self.buttonHelpListEdit.config(state=DISABLED)
self.buttonHelpListRemove.config(state=DISABLED)
else: #there are some entries
- if self.listHelp.curselection(): #there currently is a selection
+ if self.listHelp.curselection(): #there currently is a selection
self.buttonHelpListEdit.config(state=NORMAL)
self.buttonHelpListRemove.config(state=NORMAL)
else: #there currently is not a selection
@@ -885,28 +930,29 @@ class ConfigDialog(Toplevel):
self.buttonHelpListRemove.config(state=DISABLED)
def HelpListItemAdd(self):
- helpSource=GetHelpSourceDialog(self,'New Help Source').result
+ helpSource = GetHelpSourceDialog(self, 'New Help Source').result
if helpSource:
- self.userHelpList.append( (helpSource[0],helpSource[1]) )
- self.listHelp.insert(END,helpSource[0])
+ self.userHelpList.append((helpSource[0], helpSource[1]))
+ self.listHelp.insert(END, helpSource[0])
self.UpdateUserHelpChangedItems()
self.SetHelpListButtonStates()
def HelpListItemEdit(self):
- itemIndex=self.listHelp.index(ANCHOR)
- helpSource=self.userHelpList[itemIndex]
- newHelpSource=GetHelpSourceDialog(self,'Edit Help Source',
- menuItem=helpSource[0],filePath=helpSource[1]).result
- if (not newHelpSource) or (newHelpSource==helpSource):
+ itemIndex = self.listHelp.index(ANCHOR)
+ helpSource = self.userHelpList[itemIndex]
+ newHelpSource = GetHelpSourceDialog(
+ self, 'Edit Help Source', menuItem=helpSource[0],
+ filePath=helpSource[1]).result
+ if (not newHelpSource) or (newHelpSource == helpSource):
return #no changes
- self.userHelpList[itemIndex]=newHelpSource
+ self.userHelpList[itemIndex] = newHelpSource
self.listHelp.delete(itemIndex)
- self.listHelp.insert(itemIndex,newHelpSource[0])
+ self.listHelp.insert(itemIndex, newHelpSource[0])
self.UpdateUserHelpChangedItems()
self.SetHelpListButtonStates()
def HelpListItemRemove(self):
- itemIndex=self.listHelp.index(ANCHOR)
+ itemIndex = self.listHelp.index(ANCHOR)
del(self.userHelpList[itemIndex])
self.listHelp.delete(itemIndex)
self.UpdateUserHelpChangedItems()
@@ -915,18 +961,19 @@ class ConfigDialog(Toplevel):
def UpdateUserHelpChangedItems(self):
"Clear and rebuild the HelpFiles section in self.changedItems"
self.changedItems['main']['HelpFiles'] = {}
- for num in range(1,len(self.userHelpList)+1):
- self.AddChangedItem('main','HelpFiles',str(num),
+ for num in range(1, len(self.userHelpList) + 1):
+ self.AddChangedItem(
+ 'main', 'HelpFiles', str(num),
';'.join(self.userHelpList[num-1][:2]))
def LoadFontCfg(self):
##base editor font selection list
- fonts=list(tkFont.families(self))
+ fonts = list(tkFont.families(self))
fonts.sort()
for font in fonts:
- self.listFontName.insert(END,font)
- configuredFont=idleConf.GetOption('main','EditorWindow','font',
- default='courier')
+ self.listFontName.insert(END, font)
+ configuredFont = idleConf.GetOption(
+ 'main', 'EditorWindow', 'font', default='courier')
lc_configuredFont = configuredFont.lower()
self.fontName.set(lc_configuredFont)
lc_fonts = [s.lower() for s in fonts]
@@ -936,107 +983,107 @@ class ConfigDialog(Toplevel):
self.listFontName.select_set(currentFontIndex)
self.listFontName.select_anchor(currentFontIndex)
##font size dropdown
- fontSize=idleConf.GetOption('main', 'EditorWindow', 'font-size',
- type='int', default='10')
- self.optMenuFontSize.SetMenu(('7','8','9','10','11','12','13','14',
- '16','18','20','22'), fontSize )
+ fontSize = idleConf.GetOption(
+ 'main', 'EditorWindow', 'font-size', type='int', default='10')
+ self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13',
+ '14', '16', '18', '20', '22'), fontSize )
##fontWeight
- self.fontBold.set(idleConf.GetOption('main','EditorWindow',
- 'font-bold',default=0,type='bool'))
+ self.fontBold.set(idleConf.GetOption(
+ 'main', 'EditorWindow', 'font-bold', default=0, type='bool'))
##font sample
self.SetFontSample()
def LoadTabCfg(self):
##indent sizes
- spaceNum=idleConf.GetOption('main','Indent','num-spaces',
- default=4,type='int')
+ spaceNum = idleConf.GetOption(
+ 'main', 'Indent', 'num-spaces', default=4, type='int')
self.spaceNum.set(spaceNum)
def LoadThemeCfg(self):
##current theme type radiobutton
- self.themeIsBuiltin.set(idleConf.GetOption('main','Theme','default',
- type='bool',default=1))
+ self.themeIsBuiltin.set(idleConf.GetOption(
+ 'main', 'Theme', 'default', type='bool', default=1))
##currently set theme
- currentOption=idleConf.CurrentTheme()
+ currentOption = idleConf.CurrentTheme()
##load available theme option menus
if self.themeIsBuiltin.get(): #default theme selected
- itemList=idleConf.GetSectionList('default','highlight')
+ itemList = idleConf.GetSectionList('default', 'highlight')
itemList.sort()
- self.optMenuThemeBuiltin.SetMenu(itemList,currentOption)
- itemList=idleConf.GetSectionList('user','highlight')
+ self.optMenuThemeBuiltin.SetMenu(itemList, currentOption)
+ itemList = idleConf.GetSectionList('user', 'highlight')
itemList.sort()
if not itemList:
self.radioThemeCustom.config(state=DISABLED)
self.customTheme.set('- no custom themes -')
else:
- self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
+ self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
else: #user theme selected
- itemList=idleConf.GetSectionList('user','highlight')
+ itemList = idleConf.GetSectionList('user', 'highlight')
itemList.sort()
- self.optMenuThemeCustom.SetMenu(itemList,currentOption)
- itemList=idleConf.GetSectionList('default','highlight')
+ self.optMenuThemeCustom.SetMenu(itemList, currentOption)
+ itemList = idleConf.GetSectionList('default', 'highlight')
itemList.sort()
- self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0])
+ self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0])
self.SetThemeType()
##load theme element option menu
themeNames = list(self.themeElements.keys())
themeNames.sort(key=lambda x: self.themeElements[x][1])
- self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0])
+ self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0])
self.PaintThemeSample()
self.SetHighlightTarget()
def LoadKeyCfg(self):
##current keys type radiobutton
- self.keysAreBuiltin.set(idleConf.GetOption('main','Keys','default',
- type='bool',default=1))
+ self.keysAreBuiltin.set(idleConf.GetOption(
+ 'main', 'Keys', 'default', type='bool', default=1))
##currently set keys
- currentOption=idleConf.CurrentKeys()
+ currentOption = idleConf.CurrentKeys()
##load available keyset option menus
if self.keysAreBuiltin.get(): #default theme selected
- itemList=idleConf.GetSectionList('default','keys')
+ itemList = idleConf.GetSectionList('default', 'keys')
itemList.sort()
- self.optMenuKeysBuiltin.SetMenu(itemList,currentOption)
- itemList=idleConf.GetSectionList('user','keys')
+ self.optMenuKeysBuiltin.SetMenu(itemList, currentOption)
+ itemList = idleConf.GetSectionList('user', 'keys')
itemList.sort()
if not itemList:
self.radioKeysCustom.config(state=DISABLED)
self.customKeys.set('- no custom keys -')
else:
- self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
+ self.optMenuKeysCustom.SetMenu(itemList, itemList[0])
else: #user key set selected
- itemList=idleConf.GetSectionList('user','keys')
+ itemList = idleConf.GetSectionList('user', 'keys')
itemList.sort()
- self.optMenuKeysCustom.SetMenu(itemList,currentOption)
- itemList=idleConf.GetSectionList('default','keys')
+ self.optMenuKeysCustom.SetMenu(itemList, currentOption)
+ itemList = idleConf.GetSectionList('default', 'keys')
itemList.sort()
- self.optMenuKeysBuiltin.SetMenu(itemList,itemList[0])
+ self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0])
self.SetKeysType()
##load keyset element list
- keySetName=idleConf.CurrentKeys()
+ keySetName = idleConf.CurrentKeys()
self.LoadKeysList(keySetName)
def LoadGeneralCfg(self):
#startup state
- self.startupEdit.set(idleConf.GetOption('main','General',
- 'editor-on-startup',default=1,type='bool'))
+ self.startupEdit.set(idleConf.GetOption(
+ 'main', 'General', 'editor-on-startup', default=1, type='bool'))
#autosave state
- self.autoSave.set(idleConf.GetOption('main', 'General', 'autosave',
- default=0, type='bool'))
+ self.autoSave.set(idleConf.GetOption(
+ 'main', 'General', 'autosave', default=0, type='bool'))
#initial window size
- self.winWidth.set(idleConf.GetOption('main','EditorWindow','width',
- type='int'))
- self.winHeight.set(idleConf.GetOption('main','EditorWindow','height',
- type='int'))
+ self.winWidth.set(idleConf.GetOption(
+ 'main', 'EditorWindow', 'width', type='int'))
+ self.winHeight.set(idleConf.GetOption(
+ 'main', 'EditorWindow', 'height', type='int'))
#initial paragraph reformat size
- self.paraWidth.set(idleConf.GetOption('main','FormatParagraph','paragraph',
- type='int'))
+ self.paraWidth.set(idleConf.GetOption(
+ 'main', 'FormatParagraph', 'paragraph', type='int'))
# default source encoding
- self.encoding.set(idleConf.GetOption('main', 'EditorWindow',
- 'encoding', default='none'))
+ self.encoding.set(idleConf.GetOption(
+ 'main', 'EditorWindow', 'encoding', default='none'))
# additional help sources
self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
for helpItem in self.userHelpList:
- self.listHelp.insert(END,helpItem[0])
+ self.listHelp.insert(END, helpItem[0])
self.SetHelpListButtonStates()
def LoadConfigs(self):
@@ -1054,7 +1101,7 @@ class ConfigDialog(Toplevel):
### general page
self.LoadGeneralCfg()
- def SaveNewKeySet(self,keySetName,keySet):
+ def SaveNewKeySet(self, keySetName, keySet):
"""
save a newly created core key set.
keySetName - string, the name of the new key set
@@ -1063,10 +1110,10 @@ class ConfigDialog(Toplevel):
if not idleConf.userCfg['keys'].has_section(keySetName):
idleConf.userCfg['keys'].add_section(keySetName)
for event in keySet:
- value=keySet[event]
- idleConf.userCfg['keys'].SetOption(keySetName,event,value)
+ value = keySet[event]
+ idleConf.userCfg['keys'].SetOption(keySetName, event, value)
- def SaveNewTheme(self,themeName,theme):
+ def SaveNewTheme(self, themeName, theme):
"""
save a newly created theme.
themeName - string, the name of the new theme
@@ -1075,16 +1122,16 @@ class ConfigDialog(Toplevel):
if not idleConf.userCfg['highlight'].has_section(themeName):
idleConf.userCfg['highlight'].add_section(themeName)
for element in theme:
- value=theme[element]
- idleConf.userCfg['highlight'].SetOption(themeName,element,value)
+ value = theme[element]
+ idleConf.userCfg['highlight'].SetOption(themeName, element, value)
- def SetUserValue(self,configType,section,item,value):
- if idleConf.defaultCfg[configType].has_option(section,item):
- if idleConf.defaultCfg[configType].Get(section,item)==value:
+ def SetUserValue(self, configType, section, item, value):
+ if idleConf.defaultCfg[configType].has_option(section, item):
+ if idleConf.defaultCfg[configType].Get(section, item) == value:
#the setting equals a default setting, remove it from user cfg
- return idleConf.userCfg[configType].RemoveOption(section,item)
+ return idleConf.userCfg[configType].RemoveOption(section, item)
#if we got here set the option
- return idleConf.userCfg[configType].SetOption(section,item,value)
+ return idleConf.userCfg[configType].SetOption(section, item, value)
def SaveAllChangedConfigs(self):
"Save configuration changes to the user config file."
@@ -1098,7 +1145,7 @@ class ConfigDialog(Toplevel):
cfgTypeHasChanges = True
for item in self.changedItems[configType][section]:
value = self.changedItems[configType][section][item]
- if self.SetUserValue(configType,section,item,value):
+ if self.SetUserValue(configType, section, item, value):
cfgTypeHasChanges = True
if cfgTypeHasChanges:
idleConf.userCfg[configType].Save()
@@ -1140,9 +1187,9 @@ class ConfigDialog(Toplevel):
pass
if __name__ == '__main__':
- #test the dialog
- root=Tk()
- Button(root,text='Dialog',
- command=lambda:ConfigDialog(root,'Settings')).pack()
- root.instance_dict={}
- root.mainloop()
+ import unittest
+ unittest.main('idlelib.idle_test.test_configdialog',
+ verbosity=2, exit=False)
+
+ from idlelib.idle_test.htest import run
+ run(ConfigDialog)
diff --git a/Lib/idlelib/configHandler.py b/Lib/idlelib/configHandler.py
index a974d54..a9957c5 100644
--- a/Lib/idlelib/configHandler.py
+++ b/Lib/idlelib/configHandler.py
@@ -20,8 +20,7 @@ configuration problem notification and resolution.
import os
import sys
-from idlelib import macosxSupport
-from configparser import ConfigParser, NoOptionError, NoSectionError
+from configparser import ConfigParser
class InvalidConfigType(Exception): pass
class InvalidConfigSet(Exception): pass
@@ -204,9 +203,9 @@ class IdleConf:
if userDir != '~': # expanduser() found user home dir
if not os.path.exists(userDir):
warn = ('\n Warning: os.path.expanduser("~") points to\n '+
- userDir+',\n but the path does not exist.\n')
+ userDir+',\n but the path does not exist.')
try:
- sys.stderr.write(warn)
+ print(warn, file=sys.stderr)
except OSError:
pass
userDir = '~'
@@ -219,8 +218,8 @@ class IdleConf:
os.mkdir(userDir)
except OSError:
warn = ('\n Warning: unable to create user config directory\n'+
- userDir+'\n Check path and permissions.\n Exiting!\n\n')
- sys.stderr.write(warn)
+ userDir+'\n Check path and permissions.\n Exiting!\n')
+ print(warn, file=sys.stderr)
raise SystemExit
return userDir
@@ -245,12 +244,12 @@ class IdleConf:
except ValueError:
warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
' invalid %r value for configuration option %r\n'
- ' from section %r: %r\n' %
+ ' from section %r: %r' %
(type, option, section,
self.userCfg[configType].Get(section, option,
raw=raw)))
try:
- sys.stderr.write(warning)
+ print(warning, file=sys.stderr)
except OSError:
pass
try:
@@ -264,15 +263,17 @@ class IdleConf:
warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
' problem retrieving configuration option %r\n'
' from section %r.\n'
- ' returning default value: %r\n' %
+ ' returning default value: %r' %
(option, section, default))
try:
- sys.stderr.write(warning)
+ print(warning, file=sys.stderr)
except OSError:
pass
return default
+
def SetOption(self, configType, section, option, value):
"""In user's config file, set section's option to value.
+
"""
self.userCfg[configType].SetOption(section, option, value)
@@ -374,10 +375,10 @@ class IdleConf:
warning=('\n Warning: configHandler.py - IdleConf.GetThemeDict'
' -\n problem retrieving theme element %r'
'\n from theme %r.\n'
- ' returning default value: %r\n' %
+ ' returning default value: %r' %
(element, themeName, theme[element]))
try:
- sys.stderr.write(warning)
+ print(warning, file=sys.stderr)
except OSError:
pass
colour=cfgParser.Get(themeName,element,default=theme[element])
@@ -525,10 +526,13 @@ class IdleConf:
def GetCurrentKeySet(self):
result = self.GetKeySet(self.CurrentKeys())
- if macosxSupport.runningAsOSXApp():
- # We're using AquaTk, replace all keybingings that use the
- # Alt key by ones that use the Option key because the former
- # don't work reliably.
+ if sys.platform == "darwin":
+ # OS X Tk variants do not support the "Alt" keyboard modifier.
+ # So replace all keybingings that use "Alt" with ones that
+ # use the "Option" keyboard modifier.
+ # TO DO: the "Option" modifier does not work properly for
+ # Cocoa Tk and XQuartz Tk so we should not use it
+ # in default OS X KeySets.
for k, v in result.items():
v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
if v != v2:
@@ -631,15 +635,17 @@ class IdleConf:
warning=('\n Warning: configHandler.py - IdleConf.GetCoreKeys'
' -\n problem retrieving key binding for event %r'
'\n from key set %r.\n'
- ' returning default value: %r\n' %
+ ' returning default value: %r' %
(event, keySetName, keyBindings[event]))
try:
- sys.stderr.write(warning)
+ print(warning, file=sys.stderr)
except OSError:
pass
return keyBindings
+
def GetExtraHelpSourceList(self,configSet):
"""Fetch list of extra help sources from a given configSet.
+
Valid configSets are 'user' or 'default'. Return a list of tuples of
the form (menu_item , path_to_help_file , option), or return the empty
list. 'option' is the sequence number of the help resource. 'option'
diff --git a/Lib/idlelib/configHelpSourceEdit.py b/Lib/idlelib/configHelpSourceEdit.py
index 2ccb400..242b08d 100644
--- a/Lib/idlelib/configHelpSourceEdit.py
+++ b/Lib/idlelib/configHelpSourceEdit.py
@@ -8,13 +8,14 @@ import tkinter.messagebox as tkMessageBox
import tkinter.filedialog as tkFileDialog
class GetHelpSourceDialog(Toplevel):
- def __init__(self, parent, title, menuItem='', filePath=''):
+ def __init__(self, parent, title, menuItem='', filePath='', _htest=False):
"""Get menu entry and url/ local file location for Additional Help
User selects a name for the Help resource and provides a web url
or a local file as its source. The user can enter a url or browse
for the file.
+ _htest - bool, change box location when running htest
"""
Toplevel.__init__(self, parent)
self.configure(borderwidth=5)
@@ -31,12 +32,14 @@ class GetHelpSourceDialog(Toplevel):
self.withdraw() #hide while setting geometry
#needs to be done here so that the winfo_reqwidth is valid
self.update_idletasks()
- #centre dialog over parent:
- self.geometry("+%d+%d" %
- ((parent.winfo_rootx() + ((parent.winfo_width()/2)
- -(self.winfo_reqwidth()/2)),
- parent.winfo_rooty() + ((parent.winfo_height()/2)
- -(self.winfo_reqheight()/2)))))
+ #centre dialog over parent. below parent if running htest.
+ self.geometry(
+ "+%d+%d" % (
+ parent.winfo_rootx() +
+ (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
+ parent.winfo_rooty() +
+ ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
+ if not _htest else 150)))
self.deiconify() #geometry set, unhide
self.bind('<Return>', self.Ok)
self.wait_window()
@@ -159,11 +162,5 @@ class GetHelpSourceDialog(Toplevel):
self.destroy()
if __name__ == '__main__':
- #test the dialog
- root = Tk()
- def run():
- keySeq = ''
- dlg = GetHelpSourceDialog(root, 'Get Help Source')
- print(dlg.result)
- Button(root,text='Dialog', command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(GetHelpSourceDialog)
diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py
index b05e38e..5137836 100644
--- a/Lib/idlelib/configSectionNameDialog.py
+++ b/Lib/idlelib/configSectionNameDialog.py
@@ -8,10 +8,11 @@ from tkinter import *
import tkinter.messagebox as tkMessageBox
class GetCfgSectionNameDialog(Toplevel):
- def __init__(self, parent, title, message, used_names):
+ def __init__(self, parent, title, message, used_names, _htest=False):
"""
message - string, informational message to display
used_names - string collection, names already in use for validity check
+ _htest - bool, change box location when running htest
"""
Toplevel.__init__(self, parent)
self.configure(borderwidth=5)
@@ -30,11 +31,12 @@ class GetCfgSectionNameDialog(Toplevel):
self.messageInfo.config(width=self.frameMain.winfo_reqwidth())
self.geometry(
"+%d+%d" % (
- parent.winfo_rootx() +
- (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
- parent.winfo_rooty() +
- (parent.winfo_height()/2 - self.winfo_reqheight()/2)
- ) ) #centre dialog over parent
+ parent.winfo_rootx() +
+ (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
+ parent.winfo_rooty() +
+ ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
+ if not _htest else 100)
+ ) ) #centre dialog over parent (or below htest box)
self.deiconify() #geometry set, unhide
self.wait_window()
@@ -92,15 +94,5 @@ if __name__ == '__main__':
import unittest
unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False)
- # also human test the dialog
- root = Tk()
- def run():
- dlg=GetCfgSectionNameDialog(root,'Get Name',
- "After the text entered with [Ok] is stripped, <nothing>, "
- "'abc', or more that 30 chars are errors. "
- "Close with a valid entry (printed), [Cancel], or [X]",
- {'abc'})
- print(dlg.result)
- Message(root, text='').pack() # will be needed for oher dialog tests
- Button(root, text='Click to begin dialog test', command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(GetCfgSectionNameDialog)
diff --git a/Lib/idlelib/dynOptionMenuWidget.py b/Lib/idlelib/dynOptionMenuWidget.py
index 922de96..877ca1b 100644
--- a/Lib/idlelib/dynOptionMenuWidget.py
+++ b/Lib/idlelib/dynOptionMenuWidget.py
@@ -2,9 +2,10 @@
OptionMenu widget modified to allow dynamic menu reconfiguration
and setting of highlightthickness
"""
-from tkinter import OptionMenu
-from tkinter import _setit
+from tkinter import OptionMenu, _setit, Tk, StringVar, Button
+
import copy
+import re
class DynOptionMenu(OptionMenu):
"""
@@ -33,3 +34,24 @@ class DynOptionMenu(OptionMenu):
command=_setit(self.variable,item,self.command))
if value:
self.variable.set(value)
+
+def _dyn_option_menu(parent):
+ root = Tk()
+ root.title("Tets dynamic option menu")
+ var = StringVar(root)
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 150))
+ var.set("Old option set") #Set the default value
+ dyn = DynOptionMenu(root,var, "old1","old2","old3","old4")
+ dyn.pack()
+
+ def update():
+ dyn.SetMenu(["new1","new2","new3","new4"],value="new option set")
+
+ button = Button(root, text="Change option set", command=update)
+ button.pack()
+ root.mainloop()
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_dyn_option_menu)
diff --git a/Lib/idlelib/help.txt b/Lib/idlelib/help.txt
index ff786c5..6378a2e 100644
--- a/Lib/idlelib/help.txt
+++ b/Lib/idlelib/help.txt
@@ -1,142 +1,185 @@
[See the end of this file for ** TIPS ** on using IDLE !!]
-Click on the dotted line at the top of a menu to "tear it off": a
-separate window containing the menu is created.
-
-File Menu:
-
- New File -- Create a new file editing window
- Open... -- Open an existing file
- Recent Files... -- Open a list of recent files
- Open Module... -- Open an existing module (searches sys.path)
- Class Browser -- Show classes and methods in current file
- Path Browser -- Show sys.path directories, modules, classes
+IDLE is the Python IDE built with the tkinter GUI toolkit.
+
+IDLE has the following features:
+-coded in 100% pure Python, using the tkinter GUI toolkit
+-cross-platform: works on Windows, Unix, and OS X
+-multi-window text editor with multiple undo, Python colorizing, smart indent,
+call tips, and many other features
+-Python shell window (a.k.a interactive interpreter)
+-debugger (not complete, but you can set breakpoints, view and step)
+
+Menus:
+
+IDLE has two window types the Shell window and the Editor window. It is
+possible to have multiple editor windows simultaneously. IDLE's
+menus dynamically change based on which window is currently selected. Each menu
+documented below indicates which window type it is associated with. Click on
+the dotted line at the top of a menu to "tear it off": a separate window
+containing the menu is created (for Unix and Windows only).
+
+File Menu (Shell and Editor):
+
+ New File -- Create a new file editing window
+ Open... -- Open an existing file
+ Open Module... -- Open an existing module (searches sys.path)
+ Recent Files... -- Open a list of recent files
+ Class Browser -- Show classes and methods in current file
+ Path Browser -- Show sys.path directories, modules, classes,
and methods
- ---
- Save -- Save current window to the associated file (unsaved
- windows have a * before and after the window title)
-
- Save As... -- Save current window to new file, which becomes
- the associated file
- Save Copy As... -- Save current window to different file
- without changing the associated file
- ---
- Print Window -- Print the current window
- ---
- Close -- Close current window (asks to save if unsaved)
- Exit -- Close all windows, quit (asks to save if unsaved)
-
-Edit Menu:
-
- Undo -- Undo last change to current window
- (A maximum of 1000 changes may be undone)
- Redo -- Redo last undone change to current window
- ---
- Cut -- Copy a selection into system-wide clipboard,
+ ---
+ Save -- Save current window to the associated file (unsaved
+ windows have a * before and after the window title)
+
+ Save As... -- Save current window to new file, which becomes
+ the associated file
+ Save Copy As... -- Save current window to different file
+ without changing the associated file
+ ---
+ Print Window -- Print the current window
+ ---
+ Close -- Close current window (asks to save if unsaved)
+ Exit -- Close all windows, quit (asks to save if unsaved)
+
+Edit Menu (Shell and Editor):
+
+ Undo -- Undo last change to current window
+ (a maximum of 1000 changes may be undone)
+ Redo -- Redo last undone change to current window
+ ---
+ Cut -- Copy a selection into system-wide clipboard,
then delete the selection
- Copy -- Copy selection into system-wide clipboard
- Paste -- Insert system-wide clipboard into window
- Select All -- Select the entire contents of the edit buffer
- ---
- Find... -- Open a search dialog box with many options
- Find Again -- Repeat last search
- Find Selection -- Search for the string in the selection
- Find in Files... -- Open a search dialog box for searching files
- Replace... -- Open a search-and-replace dialog box
- Go to Line -- Ask for a line number and show that line
- Show Calltip -- Open a small window with function param hints
- Show Completions -- Open a scroll window allowing selection keywords
- and attributes. (see '*TIPS*', below)
- Show Parens -- Highlight the surrounding parenthesis
- Expand Word -- Expand the word you have typed to match another
- word in the same buffer; repeat to get a
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ Select All -- Select the entire contents of the edit buffer
+ ---
+ Find... -- Open a search dialog box with many options
+ Find Again -- Repeat last search
+ Find Selection -- Search for the string in the selection
+ Find in Files... -- Open a search dialog box for searching files
+ Replace... -- Open a search-and-replace dialog box
+ Go to Line -- Ask for a line number and show that line
+ Expand Word -- Expand the word you have typed to match another
+ word in the same buffer; repeat to get a
different expansion
-
-Format Menu (only in Edit window):
-
- Indent Region -- Shift selected lines right 4 spaces
- Dedent Region -- Shift selected lines left 4 spaces
- Comment Out Region -- Insert ## in front of selected lines
- Uncomment Region -- Remove leading # or ## from selected lines
- Tabify Region -- Turns *leading* stretches of spaces into tabs
- (Note: We recommend using 4 space blocks to indent Python code.)
- Untabify Region -- Turn *all* tabs into the right number of spaces
- New Indent Width... -- Open dialog to change indent width
- Format Paragraph -- Reformat the current blank-line-separated
- paragraph
-
-Run Menu (only in Edit window):
-
- Python Shell -- Open or wake up the Python shell window
- ---
- Check Module -- Run a syntax check on the module
- Run Module -- Execute the current file in the __main__ namespace
-
-Shell Menu (only in Shell window):
-
- View Last Restart -- Scroll the shell window to the last restart
- Restart Shell -- Restart the interpreter with a fresh environment
-
-Debug Menu (only in Shell window):
-
- Go to File/Line -- look around the insert point for a filename
- and line number, open the file, and show the line
- Debugger (toggle) -- Run commands in the shell under the debugger
- Stack Viewer -- Show the stack traceback of the last exception
- Auto-open Stack Viewer (toggle) -- Open stack viewer on traceback
-
-Options Menu:
-
- Configure IDLE -- Open a configuration dialog. Fonts, indentation,
+ Show Calltip -- After an unclosed parenthesis for a function, open
+ a small window with function parameter hints
+ Show Parens -- Highlight the surrounding parenthesis
+ Show Completions -- Open a scroll window allowing selection keywords
+ and attributes. (see '*TIPS*', below)
+
+Format Menu (Editor window only):
+
+ Indent Region -- Shift selected lines right by the indent width
+ (default 4 spaces)
+ Dedent Region -- Shift selected lines left by the indent width
+ (default 4 spaces)
+ Comment Out Region -- Insert ## in front of selected lines
+ Uncomment Region -- Remove leading # or ## from selected lines
+ Tabify Region -- Turns *leading* stretches of spaces into tabs.
+ (Note: We recommend using 4 space blocks to indent Python code.)
+ Untabify Region -- Turn *all* tabs into the corrent number of spaces
+ Toggle tabs -- Open a dialog to switch between indenting with
+ spaces and tabs.
+ New Indent Width... -- Open a dialog to change indent width. The
+ accepted default by the Python community is 4
+ spaces.
+ Format Paragraph -- Reformat the current blank-line-separated
+ paragraph. All lines in the paragraph will be
+ formatted to less than 80 columns.
+ ---
+ Strip trailing whitespace -- Removed any space characters after the end
+ of the last non-space character
+
+Run Menu (Editor window only):
+
+ Python Shell -- Open or wake up the Python shell window
+ ---
+ Check Module -- Check the syntax of the module currently open in the
+ Editor window. If the module has not been saved IDLE
+ will prompt the user to save the code.
+ Run Module -- Restart the shell to clean the environment, then
+ execute the currently open module. If the module has
+ not been saved IDLE will prompt the user to save the
+ code.
+
+Shell Menu (Shell window only):
+
+ View Last Restart -- Scroll the shell window to the last Shell restart
+ Restart Shell -- Restart the shell to clean the environment
+
+Debug Menu (Shell window only):
+
+ Go to File/Line -- Look around the insert point for a filename
+ and line number, open the file, and show the line.
+ Useful to view the source lines referenced in an
+ exception traceback. Available in the context
+ menu of the Shell window.
+ Debugger (toggle) -- This feature is not complete and considered
+ experimental. Run commands in the shell under the
+ debugger.
+ Stack Viewer -- Show the stack traceback of the last exception
+ Auto-open Stack Viewer (toggle) -- Toggle automatically opening the
+ stack viewer on unhandled
+ exception
+
+Options Menu (Shell and Editor):
+
+ Configure IDLE -- Open a configuration dialog. Fonts, indentation,
keybindings, and color themes may be altered.
- Startup Preferences may be set, and Additional Help
- Sources can be specified.
-
- On OS X this menu is not present, use
- menu 'IDLE -> Preferences...' instead.
- ---
- Code Context -- Open a pane at the top of the edit window which
- shows the block context of the section of code
- which is scrolling off the top or the window.
- (Not present in Shell window.)
-
-Windows Menu:
-
- Zoom Height -- toggles the window between configured size
- and maximum height.
- ---
- The rest of this menu lists the names of all open windows;
- select one to bring it to the foreground (deiconifying it if
- necessary).
+ Startup Preferences may be set, and additional Help
+ sources can be specified.
+
+ ---
+ Code Context (toggle) -- Open a pane at the top of the edit window
+ which shows the block context of the section
+ of code which is scrolling off the top or the
+ window. This is not present in the Shell
+ window only the Editor window.
+
+Windows Menu (Shell and Editor):
+
+ Zoom Height -- Toggles the window between normal size (40x80 initial
+ setting) and maximum height. The initial size is in the Configure
+ IDLE dialog under the general tab.
+ ---
+ The rest of this menu lists the names of all open windows;
+ select one to bring it to the foreground (deiconifying it if
+ necessary).
Help Menu:
- About IDLE -- Version, copyright, license, credits
- IDLE Readme -- Background discussion and change details
- ---
- IDLE Help -- Display this file
- Python Docs -- Access local Python documentation, if
- installed. Otherwise, access www.python.org.
- ---
- (Additional Help Sources may be added here)
-
-Edit context menu (Right-click / Control-click on OS X in Edit window):
-
- Cut -- Copy a selection into system-wide clipboard,
+ About IDLE -- Version, copyright, license, credits
+ ---
+ IDLE Help -- Display this file which is a help file for IDLE
+ detailing the menu options, basic editing and navigation,
+ and other tips.
+ Python Docs -- Access local Python documentation, if
+ installed. Or will start a web browser and open
+ docs.python.org showing the latest Python documentation.
+ ---
+ Additional help sources may be added here with the Configure IDLE
+ dialog under the General tab.
+
+Editor context menu (Right-click / Control-click on OS X in Edit window):
+
+ Cut -- Copy a selection into system-wide clipboard,
then delete the selection
- Copy -- Copy selection into system-wide clipboard
- Paste -- Insert system-wide clipboard into window
- Set Breakpoint -- Sets a breakpoint (when debugger open)
- Clear Breakpoint -- Clears the breakpoint on that line
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ Set Breakpoint -- Sets a breakpoint. Breakpoints are only enabled
+ when the debugger is open.
+ Clear Breakpoint -- Clears the breakpoint on that line
Shell context menu (Right-click / Control-click on OS X in Shell window):
- Cut -- Copy a selection into system-wide clipboard,
+ Cut -- Copy a selection into system-wide clipboard,
then delete the selection
- Copy -- Copy selection into system-wide clipboard
- Paste -- Insert system-wide clipboard into window
- ---
- Go to file/line -- Same as in Debug menu
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ ---
+ Go to file/line -- Same as in Debug menu
** TIPS **
@@ -144,159 +187,182 @@ Shell context menu (Right-click / Control-click on OS X in Shell window):
Additional Help Sources:
- Windows users can Google on zopeshelf.chm to access Zope help files in
- the Windows help format. The Additional Help Sources feature of the
- configuration GUI supports .chm, along with any other filetypes
- supported by your browser. Supply a Menu Item title, and enter the
- location in the Help File Path slot of the New Help Source dialog. Use
- http:// and/or www. to identify external URLs, or download the file and
- browse for its path on your machine using the Browse button.
+ Windows users can Google on zopeshelf.chm to access Zope help files in
+ the Windows help format. The Additional Help Sources feature of the
+ configuration GUI supports .chm, along with any other filetypes
+ supported by your browser. Supply a Menu Item title, and enter the
+ location in the Help File Path slot of the New Help Source dialog. Use
+ http:// and/or www. to identify external URLs, or download the file and
+ browse for its path on your machine using the Browse button.
- All users can access the extensive sources of help, including
- tutorials, available at www.python.org/doc. Selected URLs can be added
- or removed from the Help menu at any time using Configure IDLE.
+ All users can access the extensive sources of help, including
+ tutorials, available at docs.python.org. Selected URLs can be added
+ or removed from the Help menu at any time using Configure IDLE.
Basic editing and navigation:
- Backspace deletes char to the left; DEL deletes char to the right.
- Control-backspace deletes word left, Control-DEL deletes word right.
- Arrow keys and Page Up/Down move around.
- Control-left/right Arrow moves by words in a strange but useful way.
- Home/End go to begin/end of line.
- Control-Home/End go to begin/end of file.
- Some useful Emacs bindings are inherited from Tcl/Tk:
- Control-a beginning of line
- Control-e end of line
- Control-k kill line (but doesn't put it in clipboard)
- Control-l center window around the insertion point
- Standard Windows bindings may work on that platform.
- Keybindings are selected in the Settings Dialog, look there.
+ Backspace deletes char to the left; DEL deletes char to the right.
+ Control-backspace deletes word left, Control-DEL deletes word right.
+ Arrow keys and Page Up/Down move around.
+ Control-left/right Arrow moves by words in a strange but useful way.
+ Home/End go to begin/end of line.
+ Control-Home/End go to begin/end of file.
+ Some useful Emacs bindings are inherited from Tcl/Tk:
+ Control-a beginning of line
+ Control-e end of line
+ Control-k kill line (but doesn't put it in clipboard)
+ Control-l center window around the insertion point
+ Standard keybindings (like Control-c to copy and Control-v to
+ paste) may work. Keybindings are selected in the Configure IDLE
+ dialog.
Automatic indentation:
- After a block-opening statement, the next line is indented by 4 spaces
- (in the Python Shell window by one tab). After certain keywords
- (break, return etc.) the next line is dedented. In leading
- indentation, Backspace deletes up to 4 spaces if they are there. Tab
- inserts spaces (in the Python Shell window one tab), number depends on
- Indent Width. (N.B. Currently tabs are restricted to four spaces due
- to Tcl/Tk issues.)
+ After a block-opening statement, the next line is indented by 4 spaces
+ (in the Python Shell window by one tab). After certain keywords
+ (break, return etc.) the next line is dedented. In leading
+ indentation, Backspace deletes up to 4 spaces if they are there. Tab
+ inserts spaces (in the Python Shell window one tab), number depends on
+ Indent Width. Currently tabs are restricted to four spaces due
+ to Tcl/Tk limitations.
See also the indent/dedent region commands in the edit menu.
Completions:
- Completions are supplied for functions, classes, and attributes of
- classes, both built-in and user-defined. Completions are also provided
- for filenames.
-
- The AutoCompleteWindow (ACW) will open after a predefined delay
- (default is two seconds) after a '.' or (in a string) an os.sep is
- typed. If after one of those characters (plus zero or more other
- characters) you type a Tab the ACW will open immediately if a possible
- continuation is found.
-
- If there is only one possible completion for the characters entered, a
- Tab will supply that completion without opening the ACW.
-
- 'Show Completions' will force open a completions window. In an empty
- string, this will contain the files in the current directory. On a
- blank line, it will contain the built-in and user-defined functions and
- classes in the current name spaces, plus any modules imported. If some
- characters have been entered, the ACW will attempt to be more specific.
-
- If string of characters is typed, the ACW selection will jump to the
- entry most closely matching those characters. Entering a Tab will cause
- the longest non-ambiguous match to be entered in the Edit window or
- Shell. Two Tabs in a row will supply the current ACW selection, as
- will Return or a double click. Cursor keys, Page Up/Down, mouse
- selection, and the scrollwheel all operate on the ACW.
-
- 'Hidden' attributes can be accessed by typing the beginning of hidden
- name after a '.'. e.g. '_'. This allows access to modules with
- '__all__' set, or to class-private attributes.
-
- Completions and the 'Expand Word' facility can save a lot of typing!
-
- Completions are currently limited to those in the namespaces. Names in
- an Edit window which are not via __main__ or sys.modules will not be
- found. Run the module once with your imports to correct this
- situation. Note that IDLE itself places quite a few modules in
- sys.modules, so much can be found by default, e.g. the re module.
-
- If you don't like the ACW popping up unbidden, simply make the delay
- longer or disable the extension. OTOH, you could make the delay zero.
-
- You could also switch off the CallTips extension. (We will be adding
- a delay to the call tip window.)
+ Completions are supplied for functions, classes, and attributes of
+ classes, both built-in and user-defined. Completions are also provided
+ for filenames.
+
+ The AutoCompleteWindow (ACW) will open after a predefined delay
+ (default is two seconds) after a '.' or (in a string) an os.sep is
+ typed. If after one of those characters (plus zero or more other
+ characters) a tab is typed the ACW will open immediately if a possible
+ continuation is found.
+
+ If there is only one possible completion for the characters entered, a
+ tab will supply that completion without opening the ACW.
+
+ 'Show Completions' will force open a completions window, by default the
+ Control-space keys will open a completions window. In an empty
+ string, this will contain the files in the current directory. On a
+ blank line, it will contain the built-in and user-defined functions and
+ classes in the current name spaces, plus any modules imported. If some
+ characters have been entered, the ACW will attempt to be more specific.
+
+ If string of characters is typed, the ACW selection will jump to the
+ entry most closely matching those characters. Entering a tab will cause
+ the longest non-ambiguous match to be entered in the Edit window or
+ Shell. Two tabs in a row will supply the current ACW selection, as
+ will return or a double click. Cursor keys, Page Up/Down, mouse
+ selection, and the scroll wheel all operate on the ACW.
+
+ "Hidden" attributes can be accessed by typing the beginning of hidden
+ name after a '.', e.g. '_'. This allows access to modules with
+ '__all__' set, or to class-private attributes.
+
+ Completions and the 'Expand Word' facility can save a lot of typing!
+
+ Completions are currently limited to those in the namespaces. Names in
+ an Editor window which are not via __main__ or sys.modules will not be
+ found. Run the module once with your imports to correct this
+ situation. Note that IDLE itself places quite a few modules in
+ sys.modules, so much can be found by default, e.g. the re module.
+
+ If you don't like the ACW popping up unbidden, simply make the delay
+ longer or disable the extension. Or another option is the delay could
+ be set to zero. Another alternative to preventing ACW popups is to
+ disable the call tips extension.
Python Shell window:
- Control-c interrupts executing command.
- Control-d sends end-of-file; closes window if typed at >>> prompt.
+ Control-c interrupts executing command.
+ Control-d sends end-of-file; closes window if typed at >>> prompt.
+ Alt-/ expand word is also useful to reduce typing.
Command history:
- Alt-p retrieves previous command matching what you have typed.
- Alt-n retrieves next.
- (These are Control-p, Control-n on OS X)
- Return while cursor is on a previous command retrieves that command.
- Expand word is also useful to reduce typing.
+ Alt-p retrieves previous command matching what you have typed. On OS X
+ use Control-p.
+ Alt-n retrieves next. On OS X use Control-n.
+ Return while cursor is on a previous command retrieves that command.
Syntax colors:
- The coloring is applied in a background "thread", so you may
- occasionally see uncolorized text. To change the color
- scheme, use the Configure IDLE / Highlighting dialog.
+ The coloring is applied in a background "thread", so you may
+ occasionally see uncolorized text. To change the color
+ scheme, use the Configure IDLE / Highlighting dialog.
Python default syntax colors:
- Keywords orange
- Builtins royal purple
- Strings green
- Comments red
- Definitions blue
+ Keywords orange
+ Builtins royal purple
+ Strings green
+ Comments red
+ Definitions blue
Shell default colors:
- Console output brown
- stdout blue
- stderr red
- stdin black
+ Console output brown
+ stdout blue
+ stderr red
+ stdin black
Other preferences:
- The font preferences, keybinding, and startup preferences can
- be changed using the Settings dialog.
+ The font preferences, highlighting, keys, and general preferences can
+ be changed via the Configure IDLE menu option. Be sure to note that
+ keys can be user defined, IDLE ships with four built in key sets. In
+ addition a user can create a custom key set in the Configure IDLE
+ dialog under the keys tab.
Command line usage:
- Enter idle -h at the command prompt to get a usage message.
-
-Running without a subprocess:
-
- If IDLE is started with the -n command line switch it will run in a
- single process and will not create the subprocess which runs the RPC
- Python execution server. This can be useful if Python cannot create
- the subprocess or the RPC socket interface on your platform. However,
- in this mode user code is not isolated from IDLE itself. Also, the
- environment is not restarted when Run/Run Module (F5) is selected. If
- your code has been modified, you must reload() the affected modules and
- re-import any specific items (e.g. from foo import baz) if the changes
- are to take effect. For these reasons, it is preferable to run IDLE
- with the default subprocess if at all possible.
+ Enter idle -h at the command prompt to get a usage message.
+
+ idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
+
+ -c command run this command
+ -d enable debugger
+ -e edit mode; arguments are files to be edited
+ -s run $IDLESTARTUP or $PYTHONSTARTUP first
+ -t title set title of shell window
+
+ If there are arguments:
+ 1. If -e is used, arguments are files opened for editing and sys.argv
+ reflects the arguments passed to IDLE itself.
+ 2. Otherwise, if -c is used, all arguments are placed in
+ sys.argv[1:...], with sys.argv[0] set to -c.
+ 3. Otherwise, if neither -e nor -c is used, the first argument is a
+ script which is executed with the remaining arguments in
+ sys.argv[1:...] and sys.argv[0] set to the script name. If the
+ script name is -, no script is executed but an interactive Python
+ session is started; the arguments are still available in sys.argv.
+
+Running without a subprocess: (DEPRECATED in Python 3.4 see Issue 16123)
+
+ If IDLE is started with the -n command line switch it will run in a
+ single process and will not create the subprocess which runs the RPC
+ Python execution server. This can be useful if Python cannot create
+ the subprocess or the RPC socket interface on your platform. However,
+ in this mode user code is not isolated from IDLE itself. Also, the
+ environment is not restarted when Run/Run Module (F5) is selected. If
+ your code has been modified, you must reload() the affected modules and
+ re-import any specific items (e.g. from foo import baz) if the changes
+ are to take effect. For these reasons, it is preferable to run IDLE
+ with the default subprocess if at all possible.
Extensions:
- IDLE contains an extension facility. See the beginning of
- config-extensions.def in the idlelib directory for further information.
- The default extensions are currently:
-
- FormatParagraph
- AutoExpand
- ZoomHeight
- ScriptBinding
- CallTips
- ParenMatch
- AutoComplete
- CodeContext
+ IDLE contains an extension facility. See the beginning of
+ config-extensions.def in the idlelib directory for further information.
+ The default extensions are currently:
+
+ FormatParagraph
+ AutoExpand
+ ZoomHeight
+ ScriptBinding
+ CallTips
+ ParenMatch
+ AutoComplete
+ CodeContext
diff --git a/Lib/idlelib/idle_test/README.txt b/Lib/idlelib/idle_test/README.txt
index 6b92483..f6b6a21 100644
--- a/Lib/idlelib/idle_test/README.txt
+++ b/Lib/idlelib/idle_test/README.txt
@@ -26,7 +26,6 @@ Once test_xyy is written, the following should go at the end of xyy.py,
with xyz (lowercased) added after 'test_'.
---
if __name__ == "__main__":
- from test import support; support.use_resources = ['gui']
import unittest
unittest.main('idlelib.idle_test.test_', verbosity=2, exit=False)
---
@@ -34,12 +33,12 @@ if __name__ == "__main__":
2. Gui Tests
-Gui tests need 'requires' and 'use_resources' from test.support
-(test.test_support in 2.7). A test is a gui test if it creates a Tk root or
-master object either directly or indirectly by instantiating a tkinter or
-idle class. For the benefit of buildbot machines that do not have a graphics
-screen, gui tests must be 'guarded' by "requires('gui')" in a setUp
-function or method. This will typically be setUpClass.
+Gui tests need 'requires' from test.support (test.test_support in 2.7). A
+test is a gui test if it creates a Tk root or master object either directly
+or indirectly by instantiating a tkinter or idle class. For the benefit of
+test processes that either have no graphical environment available or are not
+allowed to use it, gui tests must be 'guarded' by "requires('gui')" in a
+setUp function or method. This will typically be setUpClass.
To avoid interfering with other gui tests, all gui objects must be destroyed
and deleted by the end of the test. If a widget, such as a Tk root, is created
@@ -57,11 +56,17 @@ and class attributes, also delete the widget.
del cls.root
---
-Support.requires('gui') returns true if it is either called in a main module
-(which never happens on buildbots) or if use_resources contains 'gui'.
-Use_resources is set by test.regrtest but not by unittest. So when running
-tests in another module with unittest, we set it ourselves, as in the xyz.py
-template above.
+Support.requires('gui') causes the test(s) it guards to be skipped if any of
+a few conditions are met:
+ - The tests are being run by regrtest.py, and it was started without
+ enabling the "gui" resource with the "-u" command line option.
+ - The tests are being run on Windows by a service that is not allowed to
+ interact with the graphical environment.
+ - The tests are being run on Mac OSX in a process that cannot make a window
+ manager connection.
+ - tkinter.Tk cannot be successfully instantiated for some reason.
+ - test.support.use_resources has been set by something other than
+ regrtest.py and does not contain "gui".
Since non-gui tests always run, but gui tests only sometimes, tests of non-gui
operations should best avoid needing a gui. Methods that make incidental use of
@@ -88,8 +93,8 @@ python -m idlelib.idle_test.test_xyz
To run all idle_test/test_*.py tests, either interactively
('>>>', with unittest imported) or from a command line, use one of the
-following. (Notes: unittest does not run gui tests; in 2.7, 'test ' (with the
-space) is 'test.regrtest '; where present, -v and -ugui can be omitted.)
+following. (Notes: in 2.7, 'test ' (with the space) is 'test.regrtest ';
+where present, -v and -ugui can be omitted.)
>>> unittest.main('idlelib.idle_test', verbosity=2, exit=False)
python -m unittest -v idlelib.idle_test
@@ -98,13 +103,13 @@ python -m test.test_idle
The idle tests are 'discovered' by idlelib.idle_test.__init__.load_tests,
which is also imported into test.test_idle. Normally, neither file should be
-changed when working on individual test modules. The third command runs runs
+changed when working on individual test modules. The third command runs
unittest indirectly through regrtest. The same happens when the entire test
suite is run with 'python -m test'. So that command must work for buildbots
to stay green. Idle tests must not disturb the environment in a way that
makes other tests fail (issue 18081).
To run an individual Testcase or test method, extend the dotted name given to
-unittest on the command line. (But gui tests will not this way.)
+unittest on the command line.
python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth
diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py
new file mode 100644
index 0000000..d0faf44
--- /dev/null
+++ b/Lib/idlelib/idle_test/htest.py
@@ -0,0 +1,369 @@
+'''Run human tests of Idle's window, dialog, and popup widgets.
+
+run(*tests)
+Run each callable in tests after finding the matching test spec in this file.
+If there are none, run an htest for each spec dict in this file after finding
+the matching callable in the module named in the spec.
+
+In a tested module, let X be a global name bound to a widget callable.
+End the module with
+
+if __name__ == '__main__':
+ <unittest, if there is one>
+ from idlelib.idle_test.htest import run
+ run(X)
+
+The X object must have a .__name__ attribute and a 'parent' parameter.
+X will often be a widget class, but a callable instance with .__name__
+or a wrapper function also work. The name of wrapper functions, like
+'_editor_window', should start with '_'.
+
+This file must contain a matching instance of the following template,
+with X.__name__ prepended, as in '_editor_window_spec ...'.
+
+_spec = {
+ 'file': '',
+ 'kwds': {'title': ''},
+ 'msg': ""
+ }
+
+file (no .py): used in run() to import the file and get X.
+kwds: passed to X (**kwds), after 'parent' is added, to initialize X.
+title: an example; used for some widgets, delete if not.
+msg: displayed in a master window. Hints as to how the user might
+ test the widget. Close the window to skip or end the test.
+
+Modules not being tested at the moment:
+PyShell.PyShellEditorWindow
+Debugger.Debugger
+AutoCompleteWindow.AutoCompleteWindow
+OutputWindow.OutputWindow (indirectly being tested with grep test)
+'''
+from importlib import import_module
+from idlelib.macosxSupport import _initializeTkVariantTests
+import tkinter as tk
+
+AboutDialog_spec = {
+ 'file': 'aboutDialog',
+ 'kwds': {'title': 'aboutDialog test',
+ '_htest': True,
+ },
+ 'msg': "Test every button. Ensure Python, TK and IDLE versions "
+ "are correctly displayed.\n [Close] to exit.",
+ }
+
+_calltip_window_spec = {
+ 'file': 'CallTipWindow',
+ 'kwds': {},
+ 'msg': "Typing '(' should display a calltip.\n"
+ "Typing ') should hide the calltip.\n"
+ }
+
+_class_browser_spec = {
+ 'file': 'ClassBrowser',
+ 'kwds': {},
+ 'msg': "Inspect names of module, class(with superclass if "
+ "applicable), methods and functions.\nToggle nested items.\n"
+ "Double clicking on items prints a traceback for an exception "
+ "that is ignored."
+ }
+
+_color_delegator_spec = {
+ 'file': 'ColorDelegator',
+ 'kwds': {},
+ 'msg': "The text is sample Python code.\n"
+ "Ensure components like comments, keywords, builtins,\n"
+ "string, definitions, and break are correctly colored.\n"
+ "The default color scheme is in idlelib/config-highlight.def"
+ }
+
+ConfigDialog_spec = {
+ 'file': 'configDialog',
+ 'kwds': {'title': 'Settings',
+ '_htest': True,},
+ 'msg': "IDLE preferences dialog.\n"
+ "In the 'Fonts/Tabs' tab, changing font face, should update the "
+ "font face of the text in the area below it.\nIn the "
+ "'Highlighting' tab, try different color schemes. Clicking "
+ "items in the sample program should update the choices above it."
+ "\nIn the 'Keys' and 'General' tab, test settings of interest."
+ "\n[Ok] to close the dialog.[Apply] to apply the settings and "
+ "and [Cancel] to revert all changes.\nRe-run the test to ensure "
+ "changes made have persisted."
+ }
+
+_dyn_option_menu_spec = {
+ 'file': 'dynOptionMenuWidget',
+ 'kwds': {},
+ 'msg': "Select one of the many options in the 'old option set'.\n"
+ "Click the button to change the option set.\n"
+ "Select one of the many options in the 'new option set'."
+ }
+
+_editor_window_spec = {
+ 'file': 'EditorWindow',
+ 'kwds': {},
+ 'msg': "Test editor functions of interest."
+ }
+
+GetCfgSectionNameDialog_spec = {
+ 'file': 'configSectionNameDialog',
+ 'kwds': {'title':'Get Name',
+ 'message':'Enter something',
+ 'used_names': {'abc'},
+ '_htest': True},
+ 'msg': "After the text entered with [Ok] is stripped, <nothing>, "
+ "'abc', or more that 30 chars are errors.\n"
+ "Close 'Get Name' with a valid entry (printed to Shell), "
+ "[Cancel], or [X]",
+ }
+
+GetHelpSourceDialog_spec = {
+ 'file': 'configHelpSourceEdit',
+ 'kwds': {'title': 'Get helpsource',
+ '_htest': True},
+ 'msg': "Enter menu item name and help file path\n "
+ "<nothing> and more than 30 chars are invalid menu item names.\n"
+ "<nothing>, file does not exist are invalid path items.\n"
+ "Test for incomplete web address for help file path.\n"
+ "A valid entry will be printed to shell with [0k].\n"
+ "[Cancel] will print None to shell",
+ }
+
+# Update once issue21519 is resolved.
+GetKeysDialog_spec = {
+ 'file': 'keybindingDialog',
+ 'kwds': {'title': 'Test keybindings',
+ 'action': 'find-again',
+ 'currentKeySequences': [''] ,
+ '_htest': True,
+ },
+ 'msg': "Test for different key modifier sequences.\n"
+ "<nothing> is invalid.\n"
+ "No modifier key is invalid.\n"
+ "Shift key with [a-z],[0-9], function key, move key, tab, space"
+ "is invalid.\nNo validitity checking if advanced key binding "
+ "entry is used."
+ }
+
+_grep_dialog_spec = {
+ 'file': 'GrepDialog',
+ 'kwds': {},
+ 'msg': "Click the 'Show GrepDialog' button.\n"
+ "Test the various 'Find-in-files' functions.\n"
+ "The results should be displayed in a new '*Output*' window.\n"
+ "'Right-click'->'Goto file/line' anywhere in the search results "
+ "should open that file \nin a new EditorWindow."
+ }
+
+_help_dialog_spec = {
+ 'file': 'EditorWindow',
+ 'kwds': {},
+ 'msg': "If the help text displays, this works.\n"
+ "Text is selectable. Window is scrollable."
+ }
+
+_io_binding_spec = {
+ 'file': 'IOBinding',
+ 'kwds': {},
+ 'msg': "Test the following bindings\n"
+ "<Control-o> to display open window from file dialog.\n"
+ "<Control-s> to save the file\n"
+ }
+
+_multi_call_spec = {
+ 'file': 'MultiCall',
+ 'kwds': {},
+ 'msg': "The following actions should trigger a print to console or IDLE"
+ " Shell.\nEntering and leaving the text area, key entry, "
+ "<Control-Key>,\n<Alt-Key-a>, <Control-Key-a>, "
+ "<Alt-Control-Key-a>, \n<Control-Button-1>, <Alt-Button-1> and "
+ "focusing out of the window\nare sequences to be tested."
+ }
+
+_multistatus_bar_spec = {
+ 'file': 'MultiStatusBar',
+ 'kwds': {},
+ 'msg': "Ensure presence of multi-status bar below text area.\n"
+ "Click 'Update Status' to change the multi-status text"
+ }
+
+_object_browser_spec = {
+ 'file': 'ObjectBrowser',
+ 'kwds': {},
+ 'msg': "Double click on items upto the lowest level.\n"
+ "Attributes of the objects and related information "
+ "will be displayed side-by-side at each level."
+ }
+
+_path_browser_spec = {
+ 'file': 'PathBrowser',
+ 'kwds': {},
+ 'msg': "Test for correct display of all paths in sys.path.\n"
+ "Toggle nested items upto the lowest level.\n"
+ "Double clicking on an item prints a traceback\n"
+ "for an exception that is ignored."
+ }
+
+_percolator_spec = {
+ 'file': 'Percolator',
+ 'kwds': {},
+ 'msg': "There are two tracers which can be toggled using a checkbox.\n"
+ "Toggling a tracer 'on' by checking it should print tracer"
+ "output to the console or to the IDLE shell.\n"
+ "If both the tracers are 'on', the output from the tracer which "
+ "was switched 'on' later, should be printed first\n"
+ "Test for actions like text entry, and removal."
+ }
+
+_replace_dialog_spec = {
+ 'file': 'ReplaceDialog',
+ 'kwds': {},
+ 'msg': "Click the 'Replace' button.\n"
+ "Test various replace options in the 'Replace dialog'.\n"
+ "Click [Close] or [X] to close the 'Replace Dialog'."
+ }
+
+_search_dialog_spec = {
+ 'file': 'SearchDialog',
+ 'kwds': {},
+ 'msg': "Click the 'Search' button.\n"
+ "Test various search options in the 'Search dialog'.\n"
+ "Click [Close] or [X] to close the 'Search Dialog'."
+ }
+
+_scrolled_list_spec = {
+ 'file': 'ScrolledList',
+ 'kwds': {},
+ 'msg': "You should see a scrollable list of items\n"
+ "Selecting (clicking) or double clicking an item "
+ "prints the name to the console or Idle shell.\n"
+ "Right clicking an item will display a popup."
+ }
+
+_stack_viewer_spec = {
+ 'file': 'StackViewer',
+ 'kwds': {},
+ 'msg': "A stacktrace for a NameError exception.\n"
+ "Expand 'idlelib ...' and '<locals>'.\n"
+ "Check that exc_value, exc_tb, and exc_type are correct.\n"
+ }
+
+_tabbed_pages_spec = {
+ 'file': 'tabbedpages',
+ 'kwds': {},
+ 'msg': "Toggle between the two tabs 'foo' and 'bar'\n"
+ "Add a tab by entering a suitable name for it.\n"
+ "Remove an existing tab by entering its name.\n"
+ "Remove all existing tabs.\n"
+ "<nothing> is an invalid add page and remove page name.\n"
+ }
+
+TextViewer_spec = {
+ 'file': 'textView',
+ 'kwds': {'title': 'Test textView',
+ 'text':'The quick brown fox jumps over the lazy dog.\n'*35,
+ '_htest': True},
+ 'msg': "Test for read-only property of text.\n"
+ "Text is selectable. Window is scrollable.",
+ }
+
+_tooltip_spec = {
+ 'file': 'ToolTip',
+ 'kwds': {},
+ 'msg': "Place mouse cursor over both the buttons\n"
+ "A tooltip should appear with some text."
+ }
+
+_tree_widget_spec = {
+ 'file': 'TreeWidget',
+ 'kwds': {},
+ 'msg': "The canvas is scrollable.\n"
+ "Click on folders upto to the lowest level."
+ }
+
+_undo_delegator_spec = {
+ 'file': 'UndoDelegator',
+ 'kwds': {},
+ 'msg': "Click [Undo] to undo any action.\n"
+ "Click [Redo] to redo any action.\n"
+ "Click [Dump] to dump the current state "
+ "by printing to the console or the IDLE shell.\n"
+ }
+
+_widget_redirector_spec = {
+ 'file': 'WidgetRedirector',
+ 'kwds': {},
+ 'msg': "Every text insert should be printed to the console."
+ "or the IDLE shell."
+ }
+
+def run(*tests):
+ root = tk.Tk()
+ root.title('IDLE htest')
+ root.resizable(0, 0)
+ _initializeTkVariantTests(root)
+
+ # a scrollable Label like constant width text widget.
+ frameLabel = tk.Frame(root, padx=10)
+ frameLabel.pack()
+ text = tk.Text(frameLabel, wrap='word')
+ text.configure(bg=root.cget('bg'), relief='flat', height=4, width=70)
+ scrollbar = tk.Scrollbar(frameLabel, command=text.yview)
+ text.config(yscrollcommand=scrollbar.set)
+ scrollbar.pack(side='right', fill='y', expand=False)
+ text.pack(side='left', fill='both', expand=True)
+
+ test_list = [] # List of tuples of the form (spec, callable widget)
+ if tests:
+ for test in tests:
+ test_spec = globals()[test.__name__ + '_spec']
+ test_spec['name'] = test.__name__
+ test_list.append((test_spec, test))
+ else:
+ for k, d in globals().items():
+ if k.endswith('_spec'):
+ test_name = k[:-5]
+ test_spec = d
+ test_spec['name'] = test_name
+ mod = import_module('idlelib.' + test_spec['file'])
+ test = getattr(mod, test_name)
+ test_list.append((test_spec, test))
+
+ test_name = tk.StringVar('')
+ callable_object = None
+ test_kwds = None
+
+ def next():
+
+ nonlocal test_name, callable_object, test_kwds
+ if len(test_list) == 1:
+ next_button.pack_forget()
+ test_spec, callable_object = test_list.pop()
+ test_kwds = test_spec['kwds']
+ test_kwds['parent'] = root
+ test_name.set('Test ' + test_spec['name'])
+
+ text.configure(state='normal') # enable text editing
+ text.delete('1.0','end')
+ text.insert("1.0",test_spec['msg'])
+ text.configure(state='disabled') # preserve read-only property
+
+ def run_test():
+ widget = callable_object(**test_kwds)
+ try:
+ print(widget.result)
+ except AttributeError:
+ pass
+
+ button = tk.Button(root, textvariable=test_name, command=run_test)
+ button.pack()
+ next_button = tk.Button(root, text="Next", command=next)
+ next_button.pack()
+
+ next()
+
+ root.mainloop()
+
+if __name__ == '__main__':
+ run()
diff --git a/Lib/idlelib/idle_test/mock_idle.py b/Lib/idlelib/idle_test/mock_idle.py
index c364a24..1672a34 100644
--- a/Lib/idlelib/idle_test/mock_idle.py
+++ b/Lib/idlelib/idle_test/mock_idle.py
@@ -5,6 +5,33 @@ Attributes and methods will be added as needed for tests.
from idlelib.idle_test.mock_tk import Text
+class Func:
+ '''Mock function captures args and returns result set by test.
+
+ Attributes:
+ self.called - records call even if no args, kwds passed.
+ self.result - set by init, returned by call.
+ self.args - captures positional arguments.
+ self.kwds - captures keyword arguments.
+
+ Most common use will probably be to mock methods.
+ Mock_tk.Var and Mbox_func are special variants of this.
+ '''
+ def __init__(self, result=None):
+ self.called = False
+ self.result = result
+ self.args = None
+ self.kwds = None
+ def __call__(self, *args, **kwds):
+ self.called = True
+ self.args = args
+ self.kwds = kwds
+ if isinstance(self.result, BaseException):
+ raise self.result
+ else:
+ return self.result
+
+
class Editor:
'''Minimally imitate EditorWindow.EditorWindow class.
'''
@@ -17,6 +44,7 @@ class Editor:
last = self.text.index('end')
return first, last
+
class UndoDelegator:
'''Minimally imitate UndoDelegator,UndoDelegator class.
'''
diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py
index 762bbc9..b9fd521 100644
--- a/Lib/idlelib/idle_test/mock_tk.py
+++ b/Lib/idlelib/idle_test/mock_tk.py
@@ -1,9 +1,27 @@
"""Classes that replace tkinter gui objects used by an object being tested.
-A gui object is anything with a master or parent paramenter, which is typically
-required in spite of what the doc strings say.
+A gui object is anything with a master or parent paramenter, which is
+typically required in spite of what the doc strings say.
"""
+class Event:
+ '''Minimal mock with attributes for testing event handlers.
+
+ This is not a gui object, but is used as an argument for callbacks
+ that access attributes of the event passed. If a callback ignores
+ the event, other than the fact that is happened, pass 'event'.
+
+ Keyboard, mouse, window, and other sources generate Event instances.
+ Event instances have the following attributes: serial (number of
+ event), time (of event), type (of event as number), widget (in which
+ event occurred), and x,y (position of mouse). There are other
+ attributes for specific events, such as keycode for key events.
+ tkinter.Event.__doc__ has more but is still not complete.
+ '''
+ def __init__(self, **kwds):
+ "Create event with attributes needed for test"
+ self.__dict__.update(kwds)
+
class Var:
"Use for String/Int/BooleanVar: incomplete"
def __init__(self, master=None, value=None, name=None):
@@ -20,9 +38,10 @@ class Mbox_func:
Instead of displaying a message box, the mock's call method saves the
arguments as instance attributes, which test functions can then examime.
+ The test can set the result returned to ask function
"""
- def __init__(self):
- self.result = None # The return for all show funcs
+ def __init__(self, result=None):
+ self.result = result # Return None for all show funcs
def __call__(self, title, message, *args, **kwds):
# Save all args for possible examination by tester
self.title = title
diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py
new file mode 100644
index 0000000..bcc853c
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_autocomplete.py
@@ -0,0 +1,143 @@
+import unittest
+from test.support import requires
+from tkinter import Tk, Text, TclError
+
+import idlelib.AutoComplete as ac
+import idlelib.AutoCompleteWindow as acw
+import idlelib.macosxSupport as mac
+from idlelib.idle_test.mock_idle import Func
+from idlelib.idle_test.mock_tk import Event
+
+class AutoCompleteWindow:
+ def complete():
+ return
+
+class DummyEditwin:
+ def __init__(self, root, text):
+ self.root = root
+ self.text = text
+ self.indentwidth = 8
+ self.tabwidth = 8
+ self.context_use_ps1 = True
+
+
+class AutoCompleteTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ mac.setupApp(cls.root, None)
+ cls.text = Text(cls.root)
+ cls.editor = DummyEditwin(cls.root, cls.text)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.text
+ del cls.editor
+ del cls.root
+
+ def setUp(self):
+ self.editor.text.delete('1.0', 'end')
+ self.autocomplete = ac.AutoComplete(self.editor)
+
+ def test_init(self):
+ self.assertEqual(self.autocomplete.editwin, self.editor)
+
+ def test_make_autocomplete_window(self):
+ testwin = self.autocomplete._make_autocomplete_window()
+ self.assertIsInstance(testwin, acw.AutoCompleteWindow)
+
+ def test_remove_autocomplete_window(self):
+ self.autocomplete.autocompletewindow = (
+ self.autocomplete._make_autocomplete_window())
+ self.autocomplete._remove_autocomplete_window()
+ self.assertIsNone(self.autocomplete.autocompletewindow)
+
+ def test_force_open_completions_event(self):
+ # Test that force_open_completions_event calls _open_completions
+ o_cs = Func()
+ self.autocomplete.open_completions = o_cs
+ self.autocomplete.force_open_completions_event('event')
+ self.assertEqual(o_cs.args, (True, False, True))
+
+ def test_try_open_completions_event(self):
+ Equal = self.assertEqual
+ autocomplete = self.autocomplete
+ trycompletions = self.autocomplete.try_open_completions_event
+ o_c_l = Func()
+ autocomplete._open_completions_later = o_c_l
+
+ # _open_completions_later should not be called with no text in editor
+ trycompletions('event')
+ Equal(o_c_l.args, None)
+
+ # _open_completions_later should be called with COMPLETE_ATTRIBUTES (1)
+ self.text.insert('1.0', 're.')
+ trycompletions('event')
+ Equal(o_c_l.args, (False, False, False, 1))
+
+ # _open_completions_later should be called with COMPLETE_FILES (2)
+ self.text.delete('1.0', 'end')
+ self.text.insert('1.0', '"./Lib/')
+ trycompletions('event')
+ Equal(o_c_l.args, (False, False, False, 2))
+
+ def test_autocomplete_event(self):
+ Equal = self.assertEqual
+ autocomplete = self.autocomplete
+
+ # Test that the autocomplete event is ignored if user is pressing a
+ # modifier key in addition to the tab key
+ ev = Event(mc_state=True)
+ self.assertIsNone(autocomplete.autocomplete_event(ev))
+ del ev.mc_state
+
+ # If autocomplete window is open, complete() method is called
+ testwin = self.autocomplete._make_autocomplete_window()
+ self.text.insert('1.0', 're.')
+ Equal(self.autocomplete.autocomplete_event(ev), 'break')
+
+ # If autocomplete window is not active or does not exist,
+ # open_completions is called. Return depends on its return.
+ autocomplete._remove_autocomplete_window()
+ o_cs = Func() # .result = None
+ autocomplete.open_completions = o_cs
+ Equal(self.autocomplete.autocomplete_event(ev), None)
+ Equal(o_cs.args, (False, True, True))
+ o_cs.result = True
+ Equal(self.autocomplete.autocomplete_event(ev), 'break')
+ Equal(o_cs.args, (False, True, True))
+
+ def test_open_completions_later(self):
+ # Test that autocomplete._delayed_completion_id is set
+ pass
+
+ def test_delayed_open_completions(self):
+ # Test that autocomplete._delayed_completion_id set to None and that
+ # open_completions only called if insertion index is the same as
+ # _delayed_completion_index
+ pass
+
+ def test_open_completions(self):
+ # Test completions of files and attributes as well as non-completion
+ # of errors
+ pass
+
+ def test_fetch_completions(self):
+ # Test that fetch_completions returns 2 lists:
+ # For attribute completion, a large list containing all variables, and
+ # a small list containing non-private variables.
+ # For file completion, a large list containing all files in the path,
+ # and a small list containing files that do not start with '.'
+ pass
+
+ def test_get_entity(self):
+ # Test that a name is in the namespace of sys.modules and
+ # __main__.__dict__
+ pass
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py
new file mode 100644
index 0000000..7f112a0
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_autoexpand.py
@@ -0,0 +1,141 @@
+"""Unit tests for idlelib.AutoExpand"""
+import unittest
+from test.support import requires
+from tkinter import Text, Tk
+#from idlelib.idle_test.mock_tk import Text
+from idlelib.AutoExpand import AutoExpand
+
+
+class Dummy_Editwin:
+ # AutoExpand.__init__ only needs .text
+ def __init__(self, text):
+ self.text = text
+
+class AutoExpandTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ if 'tkinter' in str(Text):
+ requires('gui')
+ cls.tk = Tk()
+ cls.text = Text(cls.tk)
+ else:
+ cls.text = Text()
+ cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text))
+
+ @classmethod
+ def tearDownClass(cls):
+ if hasattr(cls, 'tk'):
+ cls.tk.destroy()
+ del cls.tk
+ del cls.text, cls.auto_expand
+
+ def tearDown(self):
+ self.text.delete('1.0', 'end')
+
+ def test_get_prevword(self):
+ text = self.text
+ previous = self.auto_expand.getprevword
+ equal = self.assertEqual
+
+ equal(previous(), '')
+
+ text.insert('insert', 't')
+ equal(previous(), 't')
+
+ text.insert('insert', 'his')
+ equal(previous(), 'this')
+
+ text.insert('insert', ' ')
+ equal(previous(), '')
+
+ text.insert('insert', 'is')
+ equal(previous(), 'is')
+
+ text.insert('insert', '\nsample\nstring')
+ equal(previous(), 'string')
+
+ text.delete('3.0', 'insert')
+ equal(previous(), '')
+
+ text.delete('1.0', 'end')
+ equal(previous(), '')
+
+ def test_before_only(self):
+ previous = self.auto_expand.getprevword
+ expand = self.auto_expand.expand_word_event
+ equal = self.assertEqual
+
+ self.text.insert('insert', 'ab ac bx ad ab a')
+ equal(self.auto_expand.getwords(), ['ab', 'ad', 'ac', 'a'])
+ expand('event')
+ equal(previous(), 'ab')
+ expand('event')
+ equal(previous(), 'ad')
+ expand('event')
+ equal(previous(), 'ac')
+ expand('event')
+ equal(previous(), 'a')
+
+ def test_after_only(self):
+ # Also add punctuation 'noise' that shoud be ignored.
+ text = self.text
+ previous = self.auto_expand.getprevword
+ expand = self.auto_expand.expand_word_event
+ equal = self.assertEqual
+
+ text.insert('insert', 'a, [ab] ac: () bx"" cd ac= ad ya')
+ text.mark_set('insert', '1.1')
+ equal(self.auto_expand.getwords(), ['ab', 'ac', 'ad', 'a'])
+ expand('event')
+ equal(previous(), 'ab')
+ expand('event')
+ equal(previous(), 'ac')
+ expand('event')
+ equal(previous(), 'ad')
+ expand('event')
+ equal(previous(), 'a')
+
+ def test_both_before_after(self):
+ text = self.text
+ previous = self.auto_expand.getprevword
+ expand = self.auto_expand.expand_word_event
+ equal = self.assertEqual
+
+ text.insert('insert', 'ab xy yz\n')
+ text.insert('insert', 'a ac by ac')
+
+ text.mark_set('insert', '2.1')
+ equal(self.auto_expand.getwords(), ['ab', 'ac', 'a'])
+ expand('event')
+ equal(previous(), 'ab')
+ expand('event')
+ equal(previous(), 'ac')
+ expand('event')
+ equal(previous(), 'a')
+
+ def test_other_expand_cases(self):
+ text = self.text
+ expand = self.auto_expand.expand_word_event
+ equal = self.assertEqual
+
+ # no expansion candidate found
+ equal(self.auto_expand.getwords(), [])
+ equal(expand('event'), 'break')
+
+ text.insert('insert', 'bx cy dz a')
+ equal(self.auto_expand.getwords(), [])
+
+ # reset state by successfully expanding once
+ # move cursor to another position and expand again
+ text.insert('insert', 'ac xy a ac ad a')
+ text.mark_set('insert', '1.7')
+ expand('event')
+ initial_state = self.auto_expand.state
+ text.mark_set('insert', '1.end')
+ expand('event')
+ new_state = self.auto_expand.state
+ self.assertNotEqual(initial_state, new_state)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_calltips.py b/Lib/idlelib/idle_test/test_calltips.py
index f363764..4ee15ae 100644
--- a/Lib/idlelib/idle_test/test_calltips.py
+++ b/Lib/idlelib/idle_test/test_calltips.py
@@ -54,9 +54,9 @@ class Get_signatureTest(unittest.TestCase):
gtest(List, List.__doc__)
gtest(list.__new__,
- 'T.__new__(S, ...) -> a new object with type S, a subtype of T')
+ 'Create and return a new object. See help(type) for accurate signature.')
gtest(list.__init__,
- 'x.__init__(...) initializes x; see help(type(x)) for signature')
+ 'Initialize self. See help(type(self)) for accurate signature.')
append_doc = "L.append(object) -> None -- append object to end"
gtest(list.append, append_doc)
gtest([].append, append_doc)
@@ -69,7 +69,8 @@ class Get_signatureTest(unittest.TestCase):
self.assertEqual(signature(textwrap.TextWrapper), '''\
(width=70, initial_indent='', subsequent_indent='', expand_tabs=True,
replace_whitespace=True, fix_sentence_endings=False, break_long_words=True,
- drop_whitespace=True, break_on_hyphens=True, tabsize=8)''')
+ drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None,
+ placeholder=' [...]')''')
def test_docline_truncation(self):
def f(): pass
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
new file mode 100644
index 0000000..6883123
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -0,0 +1,32 @@
+'''Unittests for idlelib/configHandler.py
+
+Coverage: 46% just by creating dialog. The other half is change code.
+
+'''
+import unittest
+from test.support import requires
+from tkinter import Tk
+from idlelib.configDialog import ConfigDialog
+from idlelib.macosxSupport import _initializeTkVariantTests
+
+
+class ConfigDialogTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ _initializeTkVariantTests(cls.root)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def test_dialog(self):
+ d=ConfigDialog(self.root, 'Test', _utest=True)
+ d.destroy()
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_formatparagraph.py b/Lib/idlelib/idle_test/test_formatparagraph.py
index f4a7c2d..690c936 100644
--- a/Lib/idlelib/idle_test/test_formatparagraph.py
+++ b/Lib/idlelib/idle_test/test_formatparagraph.py
@@ -293,7 +293,7 @@ class FormatEventTest(unittest.TestCase):
# Set cursor ('insert' mark) to '1.0', within text.
text.insert('1.0', self.test_string)
text.mark_set('insert', '1.0')
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
# find function includes \n
expected = (
@@ -305,7 +305,7 @@ class FormatEventTest(unittest.TestCase):
# Select from 1.11 to line end.
text.insert('1.0', self.test_string)
text.tag_add('sel', '1.11', '1.end')
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
# selection excludes \n
expected = (
@@ -319,7 +319,7 @@ class FormatEventTest(unittest.TestCase):
# Select 2 long lines.
text.insert('1.0', self.multiline_test_string)
text.tag_add('sel', '2.0', '4.0')
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('2.0', 'insert')
expected = (
" The second line's length is way over the max width. It goes on and\n"
@@ -334,7 +334,7 @@ class FormatEventTest(unittest.TestCase):
# Set cursor ('insert') to '1.0', within block.
text.insert('1.0', self.multiline_test_comment)
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
expected = (
"# The first line is under the max width. The second line's length is\n"
@@ -348,7 +348,7 @@ class FormatEventTest(unittest.TestCase):
# Select line 2, verify line 1 unaffected.
text.insert('1.0', self.multiline_test_comment)
text.tag_add('sel', '2.0', '3.0')
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
expected = (
"# The first line is under the max width.\n"
diff --git a/Lib/idlelib/idle_test/test_hyperparser.py b/Lib/idlelib/idle_test/test_hyperparser.py
new file mode 100644
index 0000000..edfc783
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_hyperparser.py
@@ -0,0 +1,273 @@
+"""Unittest for idlelib.HyperParser"""
+import unittest
+from test.support import requires
+from tkinter import Tk, Text
+from idlelib.EditorWindow import EditorWindow
+from idlelib.HyperParser import HyperParser
+
+class DummyEditwin:
+ def __init__(self, text):
+ self.text = text
+ self.indentwidth = 8
+ self.tabwidth = 8
+ self.context_use_ps1 = True
+ self.num_context_lines = 50, 500, 1000
+
+ _build_char_in_string_func = EditorWindow._build_char_in_string_func
+ is_char_in_string = EditorWindow.is_char_in_string
+
+
+class HyperParserTest(unittest.TestCase):
+ code = (
+ '"""This is a module docstring"""\n'
+ '# this line is a comment\n'
+ 'x = "this is a string"\n'
+ "y = 'this is also a string'\n"
+ 'l = [i for i in range(10)]\n'
+ 'm = [py*py for # comment\n'
+ ' py in l]\n'
+ 'x.__len__\n'
+ "z = ((r'asdf')+('a')))\n"
+ '[x for x in\n'
+ 'for = False\n'
+ 'cliché = "this is a string with unicode, what a cliché"'
+ )
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.text = Text(cls.root)
+ cls.editwin = DummyEditwin(cls.text)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text, cls.editwin
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.text.insert('insert', self.code)
+
+ def tearDown(self):
+ self.text.delete('1.0', 'end')
+ self.editwin.context_use_ps1 = True
+
+ def get_parser(self, index):
+ """
+ Return a parser object with index at 'index'
+ """
+ return HyperParser(self.editwin, index)
+
+ def test_init(self):
+ """
+ test corner cases in the init method
+ """
+ with self.assertRaises(ValueError) as ve:
+ self.text.tag_add('console', '1.0', '1.end')
+ p = self.get_parser('1.5')
+ self.assertIn('precedes', str(ve.exception))
+
+ # test without ps1
+ self.editwin.context_use_ps1 = False
+
+ # number of lines lesser than 50
+ p = self.get_parser('end')
+ self.assertEqual(p.rawtext, self.text.get('1.0', 'end'))
+
+ # number of lines greater than 50
+ self.text.insert('end', self.text.get('1.0', 'end')*4)
+ p = self.get_parser('54.5')
+
+ def test_is_in_string(self):
+ get = self.get_parser
+
+ p = get('1.0')
+ self.assertFalse(p.is_in_string())
+ p = get('1.4')
+ self.assertTrue(p.is_in_string())
+ p = get('2.3')
+ self.assertFalse(p.is_in_string())
+ p = get('3.3')
+ self.assertFalse(p.is_in_string())
+ p = get('3.7')
+ self.assertTrue(p.is_in_string())
+ p = get('4.6')
+ self.assertTrue(p.is_in_string())
+ p = get('12.54')
+ self.assertTrue(p.is_in_string())
+
+ def test_is_in_code(self):
+ get = self.get_parser
+
+ p = get('1.0')
+ self.assertTrue(p.is_in_code())
+ p = get('1.1')
+ self.assertFalse(p.is_in_code())
+ p = get('2.5')
+ self.assertFalse(p.is_in_code())
+ p = get('3.4')
+ self.assertTrue(p.is_in_code())
+ p = get('3.6')
+ self.assertFalse(p.is_in_code())
+ p = get('4.14')
+ self.assertFalse(p.is_in_code())
+
+ def test_get_surrounding_bracket(self):
+ get = self.get_parser
+
+ def without_mustclose(parser):
+ # a utility function to get surrounding bracket
+ # with mustclose=False
+ return parser.get_surrounding_brackets(mustclose=False)
+
+ def with_mustclose(parser):
+ # a utility function to get surrounding bracket
+ # with mustclose=True
+ return parser.get_surrounding_brackets(mustclose=True)
+
+ p = get('3.2')
+ self.assertIsNone(with_mustclose(p))
+ self.assertIsNone(without_mustclose(p))
+
+ p = get('5.6')
+ self.assertTupleEqual(without_mustclose(p), ('5.4', '5.25'))
+ self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
+
+ p = get('5.23')
+ self.assertTupleEqual(without_mustclose(p), ('5.21', '5.24'))
+ self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
+
+ p = get('6.15')
+ self.assertTupleEqual(without_mustclose(p), ('6.4', '6.end'))
+ self.assertIsNone(with_mustclose(p))
+
+ p = get('9.end')
+ self.assertIsNone(with_mustclose(p))
+ self.assertIsNone(without_mustclose(p))
+
+ def test_get_expression(self):
+ get = self.get_parser
+
+ p = get('4.2')
+ self.assertEqual(p.get_expression(), 'y ')
+
+ p = get('4.7')
+ with self.assertRaises(ValueError) as ve:
+ p.get_expression()
+ self.assertIn('is inside a code', str(ve.exception))
+
+ p = get('5.25')
+ self.assertEqual(p.get_expression(), 'range(10)')
+
+ p = get('6.7')
+ self.assertEqual(p.get_expression(), 'py')
+
+ p = get('6.8')
+ self.assertEqual(p.get_expression(), '')
+
+ p = get('7.9')
+ self.assertEqual(p.get_expression(), 'py')
+
+ p = get('8.end')
+ self.assertEqual(p.get_expression(), 'x.__len__')
+
+ p = get('9.13')
+ self.assertEqual(p.get_expression(), "r'asdf'")
+
+ p = get('9.17')
+ with self.assertRaises(ValueError) as ve:
+ p.get_expression()
+ self.assertIn('is inside a code', str(ve.exception))
+
+ p = get('10.0')
+ self.assertEqual(p.get_expression(), '')
+
+ p = get('10.6')
+ self.assertEqual(p.get_expression(), '')
+
+ p = get('10.11')
+ self.assertEqual(p.get_expression(), '')
+
+ p = get('11.3')
+ self.assertEqual(p.get_expression(), '')
+
+ p = get('11.11')
+ self.assertEqual(p.get_expression(), 'False')
+
+ p = get('12.6')
+ self.assertEqual(p.get_expression(), 'cliché')
+
+ def test_eat_identifier(self):
+ def is_valid_id(candidate):
+ result = HyperParser._eat_identifier(candidate, 0, len(candidate))
+ if result == len(candidate):
+ return True
+ elif result == 0:
+ return False
+ else:
+ err_msg = "Unexpected result: {} (expected 0 or {}".format(
+ result, len(candidate)
+ )
+ raise Exception(err_msg)
+
+ # invalid first character which is valid elsewhere in an identifier
+ self.assertFalse(is_valid_id('2notid'))
+
+ # ASCII-only valid identifiers
+ self.assertTrue(is_valid_id('valid_id'))
+ self.assertTrue(is_valid_id('_valid_id'))
+ self.assertTrue(is_valid_id('valid_id_'))
+ self.assertTrue(is_valid_id('_2valid_id'))
+
+ # keywords which should be "eaten"
+ self.assertTrue(is_valid_id('True'))
+ self.assertTrue(is_valid_id('False'))
+ self.assertTrue(is_valid_id('None'))
+
+ # keywords which should not be "eaten"
+ self.assertFalse(is_valid_id('for'))
+ self.assertFalse(is_valid_id('import'))
+ self.assertFalse(is_valid_id('return'))
+
+ # valid unicode identifiers
+ self.assertTrue(is_valid_id('cliche'))
+ self.assertTrue(is_valid_id('cliché'))
+ self.assertTrue(is_valid_id('a٢'))
+
+ # invalid unicode identifiers
+ self.assertFalse(is_valid_id('2a'))
+ self.assertFalse(is_valid_id('٢a'))
+ self.assertFalse(is_valid_id('a²'))
+
+ # valid identifier after "punctuation"
+ self.assertEqual(HyperParser._eat_identifier('+ var', 0, 5), len('var'))
+ self.assertEqual(HyperParser._eat_identifier('+var', 0, 4), len('var'))
+ self.assertEqual(HyperParser._eat_identifier('.var', 0, 4), len('var'))
+
+ # invalid identifiers
+ self.assertFalse(is_valid_id('+'))
+ self.assertFalse(is_valid_id(' '))
+ self.assertFalse(is_valid_id(':'))
+ self.assertFalse(is_valid_id('?'))
+ self.assertFalse(is_valid_id('^'))
+ self.assertFalse(is_valid_id('\\'))
+ self.assertFalse(is_valid_id('"'))
+ self.assertFalse(is_valid_id('"a string"'))
+
+ def test_eat_identifier_various_lengths(self):
+ eat_id = HyperParser._eat_identifier
+
+ for length in range(1, 21):
+ self.assertEqual(eat_id('a' * length, 0, length), length)
+ self.assertEqual(eat_id('é' * length, 0, length), length)
+ self.assertEqual(eat_id('a' + '2' * (length - 1), 0, length), length)
+ self.assertEqual(eat_id('é' + '2' * (length - 1), 0, length), length)
+ self.assertEqual(eat_id('é' + 'a' * (length - 1), 0, length), length)
+ self.assertEqual(eat_id('é' * (length - 1) + 'a', 0, length), length)
+ self.assertEqual(eat_id('+' * length, 0, length), 0)
+ self.assertEqual(eat_id('2' + 'a' * (length - 1), 0, length), 0)
+ self.assertEqual(eat_id('2' + 'é' * (length - 1), 0, length), 0)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_parenmatch.py b/Lib/idlelib/idle_test/test_parenmatch.py
new file mode 100644
index 0000000..9aba4be
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_parenmatch.py
@@ -0,0 +1,109 @@
+"""Test idlelib.ParenMatch."""
+# This must currently be a gui test because ParenMatch methods use
+# several text methods not defined on idlelib.idle_test.mock_tk.Text.
+
+import unittest
+from unittest.mock import Mock
+from test.support import requires
+from tkinter import Tk, Text
+from idlelib.ParenMatch import ParenMatch
+
+class DummyEditwin:
+ def __init__(self, text):
+ self.text = text
+ self.indentwidth = 8
+ self.tabwidth = 8
+ self.context_use_ps1 = True
+
+
+class ParenMatchTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.text = Text(cls.root)
+ cls.editwin = DummyEditwin(cls.text)
+ cls.editwin.text_frame = Mock()
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text, cls.editwin
+ cls.root.destroy()
+ del cls.root
+
+ def tearDown(self):
+ self.text.delete('1.0', 'end')
+
+ def test_paren_expression(self):
+ """
+ Test ParenMatch with 'expression' style.
+ """
+ text = self.text
+ pm = ParenMatch(self.editwin)
+ pm.set_style('expression')
+
+ text.insert('insert', 'def foobar(a, b')
+ pm.flash_paren_event('event')
+ self.assertIn('<<parenmatch-check-restore>>', text.event_info())
+ self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
+ ('1.10', '1.15'))
+ text.insert('insert', ')')
+ pm.restore_event()
+ self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
+ self.assertEqual(text.tag_prevrange('paren', 'end'), ())
+
+ # paren_closed_event can only be tested as below
+ pm.paren_closed_event('event')
+ self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
+ ('1.10', '1.16'))
+
+ def test_paren_default(self):
+ """
+ Test ParenMatch with 'default' style.
+ """
+ text = self.text
+ pm = ParenMatch(self.editwin)
+ pm.set_style('default')
+
+ text.insert('insert', 'def foobar(a, b')
+ pm.flash_paren_event('event')
+ self.assertIn('<<parenmatch-check-restore>>', text.event_info())
+ self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
+ ('1.10', '1.11'))
+ text.insert('insert', ')')
+ pm.restore_event()
+ self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
+ self.assertEqual(text.tag_prevrange('paren', 'end'), ())
+
+ def test_paren_corner(self):
+ """
+ Test corner cases in flash_paren_event and paren_closed_event.
+
+ These cases force conditional expression and alternate paths.
+ """
+ text = self.text
+ pm = ParenMatch(self.editwin)
+
+ text.insert('insert', '# this is a commen)')
+ self.assertIsNone(pm.paren_closed_event('event'))
+
+ text.insert('insert', '\ndef')
+ self.assertIsNone(pm.flash_paren_event('event'))
+ self.assertIsNone(pm.paren_closed_event('event'))
+
+ text.insert('insert', ' a, *arg)')
+ self.assertIsNone(pm.paren_closed_event('event'))
+
+ def test_handle_restore_timer(self):
+ pm = ParenMatch(self.editwin)
+ pm.restore_event = Mock()
+ pm.handle_restore_timer(0)
+ self.assertTrue(pm.restore_event.called)
+ pm.restore_event.reset_mock()
+ pm.handle_restore_timer(1)
+ self.assertFalse(pm.restore_event.called)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_searchdialogbase.py b/Lib/idlelib/idle_test/test_searchdialogbase.py
new file mode 100644
index 0000000..eccdd47
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_searchdialogbase.py
@@ -0,0 +1,165 @@
+'''Unittests for idlelib/SearchDialogBase.py
+
+Coverage: 99%. The only thing not covered is inconsequential --
+testing skipping of suite when self.needwrapbutton is false.
+
+'''
+import unittest
+from test.support import requires
+from tkinter import Tk, Toplevel, Frame, Label, BooleanVar, StringVar
+from idlelib import SearchEngine as se
+from idlelib import SearchDialogBase as sdb
+from idlelib.idle_test.mock_idle import Func
+from idlelib.idle_test.mock_tk import Var, Mbox
+
+# The following could help make some tests gui-free.
+# However, they currently make radiobutton tests fail.
+##def setUpModule():
+## # Replace tk objects used to initialize se.SearchEngine.
+## se.BooleanVar = Var
+## se.StringVar = Var
+##
+##def tearDownModule():
+## se.BooleanVar = BooleanVar
+## se.StringVar = StringVar
+
+class SearchDialogBaseTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.engine = se.SearchEngine(self.root) # None also seems to work
+ self.dialog = sdb.SearchDialogBase(root=self.root, engine=self.engine)
+
+ def tearDown(self):
+ self.dialog.close()
+
+ def test_open_and_close(self):
+ # open calls create_widgets, which needs default_command
+ self.dialog.default_command = None
+
+ # Since text parameter of .open is not used in base class,
+ # pass dummy 'text' instead of tk.Text().
+ self.dialog.open('text')
+ self.assertEqual(self.dialog.top.state(), 'normal')
+ self.dialog.close()
+ self.assertEqual(self.dialog.top.state(), 'withdrawn')
+
+ self.dialog.open('text', searchphrase="hello")
+ self.assertEqual(self.dialog.ent.get(), 'hello')
+ self.dialog.close()
+
+ def test_create_widgets(self):
+ self.dialog.create_entries = Func()
+ self.dialog.create_option_buttons = Func()
+ self.dialog.create_other_buttons = Func()
+ self.dialog.create_command_buttons = Func()
+
+ self.dialog.default_command = None
+ self.dialog.create_widgets()
+
+ self.assertTrue(self.dialog.create_entries.called)
+ self.assertTrue(self.dialog.create_option_buttons.called)
+ self.assertTrue(self.dialog.create_other_buttons.called)
+ self.assertTrue(self.dialog.create_command_buttons.called)
+
+ def test_make_entry(self):
+ equal = self.assertEqual
+ self.dialog.row = 0
+ self.dialog.top = Toplevel(self.root)
+ entry, label = self.dialog.make_entry("Test:", 'hello')
+ equal(label['text'], 'Test:')
+
+ self.assertIn(entry.get(), 'hello')
+ egi = entry.grid_info()
+ equal(int(egi['row']), 0)
+ equal(int(egi['column']), 1)
+ equal(int(egi['rowspan']), 1)
+ equal(int(egi['columnspan']), 1)
+ equal(self.dialog.row, 1)
+
+ def test_create_entries(self):
+ self.dialog.row = 0
+ self.engine.setpat('hello')
+ self.dialog.create_entries()
+ self.assertIn(self.dialog.ent.get(), 'hello')
+
+ def test_make_frame(self):
+ self.dialog.row = 0
+ self.dialog.top = Toplevel(self.root)
+ frame, label = self.dialog.make_frame()
+ self.assertEqual(label, '')
+ self.assertIsInstance(frame, Frame)
+
+ frame, label = self.dialog.make_frame('testlabel')
+ self.assertEqual(label['text'], 'testlabel')
+ self.assertIsInstance(frame, Frame)
+
+ def btn_test_setup(self, meth):
+ self.dialog.top = Toplevel(self.root)
+ self.dialog.row = 0
+ return meth()
+
+ def test_create_option_buttons(self):
+ e = self.engine
+ for state in (0, 1):
+ for var in (e.revar, e.casevar, e.wordvar, e.wrapvar):
+ var.set(state)
+ frame, options = self.btn_test_setup(
+ self.dialog.create_option_buttons)
+ for spec, button in zip (options, frame.pack_slaves()):
+ var, label = spec
+ self.assertEqual(button['text'], label)
+ self.assertEqual(var.get(), state)
+ if state == 1:
+ button.deselect()
+ else:
+ button.select()
+ self.assertEqual(var.get(), 1 - state)
+
+ def test_create_other_buttons(self):
+ for state in (False, True):
+ var = self.engine.backvar
+ var.set(state)
+ frame, others = self.btn_test_setup(
+ self.dialog.create_other_buttons)
+ buttons = frame.pack_slaves()
+ for spec, button in zip(others, buttons):
+ val, label = spec
+ self.assertEqual(button['text'], label)
+ if val == state:
+ # hit other button, then this one
+ # indexes depend on button order
+ self.assertEqual(var.get(), state)
+ buttons[val].select()
+ self.assertEqual(var.get(), 1 - state)
+ buttons[1-val].select()
+ self.assertEqual(var.get(), state)
+
+ def test_make_button(self):
+ self.dialog.top = Toplevel(self.root)
+ self.dialog.buttonframe = Frame(self.dialog.top)
+ btn = self.dialog.make_button('Test', self.dialog.close)
+ self.assertEqual(btn['text'], 'Test')
+
+ def test_create_command_buttons(self):
+ self.dialog.create_command_buttons()
+ # Look for close button command in buttonframe
+ closebuttoncommand = ''
+ for child in self.dialog.buttonframe.winfo_children():
+ if child['text'] == 'close':
+ closebuttoncommand = child['command']
+ self.assertIn('close', closebuttoncommand)
+
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=2)
diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py
new file mode 100644
index 0000000..68e5b82
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_textview.py
@@ -0,0 +1,97 @@
+'''Test the functions and main class method of textView.py.
+
+Since all methods and functions create (or destroy) a TextViewer, which
+is a widget containing multiple widgets, all tests must be gui tests.
+Using mock Text would not change this. Other mocks are used to retrieve
+information about calls.
+
+The coverage is essentially 100%.
+'''
+from test.support import requires
+requires('gui')
+
+import unittest
+import os
+from tkinter import Tk
+from idlelib import textView as tv
+from idlelib.idle_test.mock_idle import Func
+from idlelib.idle_test.mock_tk import Mbox
+
+def setUpModule():
+ global root
+ root = Tk()
+
+def tearDownModule():
+ global root
+ root.destroy() # pyflakes falsely sees root as undefined
+ del root
+
+
+class TV(tv.TextViewer): # used by TextViewTest
+ transient = Func()
+ grab_set = Func()
+ wait_window = Func()
+
+class TextViewTest(unittest.TestCase):
+
+ def setUp(self):
+ TV.transient.__init__()
+ TV.grab_set.__init__()
+ TV.wait_window.__init__()
+
+ def test_init_modal(self):
+ view = TV(root, 'Title', 'test text')
+ self.assertTrue(TV.transient.called)
+ self.assertTrue(TV.grab_set.called)
+ self.assertTrue(TV.wait_window.called)
+ view.Ok()
+
+ def test_init_nonmodal(self):
+ view = TV(root, 'Title', 'test text', modal=False)
+ self.assertFalse(TV.transient.called)
+ self.assertFalse(TV.grab_set.called)
+ self.assertFalse(TV.wait_window.called)
+ view.Ok()
+
+ def test_ok(self):
+ view = TV(root, 'Title', 'test text', modal=False)
+ view.destroy = Func()
+ view.Ok()
+ self.assertTrue(view.destroy.called)
+ del view.destroy # unmask real function
+ view.destroy
+
+
+class textviewTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.orig_mbox = tv.tkMessageBox
+ tv.tkMessageBox = Mbox
+
+ @classmethod
+ def tearDownClass(cls):
+ tv.tkMessageBox = cls.orig_mbox
+ del cls.orig_mbox
+
+ def test_view_text(self):
+ # If modal True, tkinter will error with 'can't invoke "event" command'
+ view = tv.view_text(root, 'Title', 'test text', modal=False)
+ self.assertIsInstance(view, tv.TextViewer)
+
+ def test_view_file(self):
+ test_dir = os.path.dirname(__file__)
+ testfile = os.path.join(test_dir, 'test_textview.py')
+ view = tv.view_file(root, 'Title', testfile, modal=False)
+ self.assertIsInstance(view, tv.TextViewer)
+ self.assertIn('Test', view.textView.get('1.0', '1.end'))
+ view.Ok()
+
+ # Mock messagebox will be used and view_file will not return anything
+ testfile = os.path.join(test_dir, '../notthere.py')
+ view = tv.view_file(root, 'Title', testfile, modal=False)
+ self.assertIsNone(view)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_widgetredir.py b/Lib/idlelib/idle_test/test_widgetredir.py
new file mode 100644
index 0000000..6440561
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_widgetredir.py
@@ -0,0 +1,122 @@
+"""Unittest for idlelib.WidgetRedirector
+
+100% coverage
+"""
+from test.support import requires
+import unittest
+from idlelib.idle_test.mock_idle import Func
+from tkinter import Tk, Text, TclError
+from idlelib.WidgetRedirector import WidgetRedirector
+
+
+class InitCloseTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.tk = Tk()
+ cls.text = Text(cls.tk)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.text.destroy()
+ cls.tk.destroy()
+ del cls.text, cls.tk
+
+ def test_init(self):
+ redir = WidgetRedirector(self.text)
+ self.assertEqual(redir.widget, self.text)
+ self.assertEqual(redir.tk, self.text.tk)
+ self.assertRaises(TclError, WidgetRedirector, self.text)
+ redir.close() # restore self.tk, self.text
+
+ def test_close(self):
+ redir = WidgetRedirector(self.text)
+ redir.register('insert', Func)
+ redir.close()
+ self.assertEqual(redir._operations, {})
+ self.assertFalse(hasattr(self.text, 'widget'))
+
+
+class WidgetRedirectorTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.tk = Tk()
+ cls.text = Text(cls.tk)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.text.destroy()
+ cls.tk.destroy()
+ del cls.text, cls.tk
+
+ def setUp(self):
+ self.redir = WidgetRedirector(self.text)
+ self.func = Func()
+ self.orig_insert = self.redir.register('insert', self.func)
+ self.text.insert('insert', 'asdf') # leaves self.text empty
+
+ def tearDown(self):
+ self.text.delete('1.0', 'end')
+ self.redir.close()
+
+ def test_repr(self): # partly for 100% coverage
+ self.assertIn('Redirector', repr(self.redir))
+ self.assertIn('Original', repr(self.orig_insert))
+
+ def test_register(self):
+ self.assertEqual(self.text.get('1.0', 'end'), '\n')
+ self.assertEqual(self.func.args, ('insert', 'asdf'))
+ self.assertIn('insert', self.redir._operations)
+ self.assertIn('insert', self.text.__dict__)
+ self.assertEqual(self.text.insert, self.func)
+
+ def test_original_command(self):
+ self.assertEqual(self.orig_insert.operation, 'insert')
+ self.assertEqual(self.orig_insert.tk_call, self.text.tk.call)
+ self.orig_insert('insert', 'asdf')
+ self.assertEqual(self.text.get('1.0', 'end'), 'asdf\n')
+
+ def test_unregister(self):
+ self.assertIsNone(self.redir.unregister('invalid operation name'))
+ self.assertEqual(self.redir.unregister('insert'), self.func)
+ self.assertNotIn('insert', self.redir._operations)
+ self.assertNotIn('insert', self.text.__dict__)
+
+ def test_unregister_no_attribute(self):
+ del self.text.insert
+ self.assertEqual(self.redir.unregister('insert'), self.func)
+
+ def test_dispatch_intercept(self):
+ self.func.__init__(True)
+ self.assertTrue(self.redir.dispatch('insert', False))
+ self.assertFalse(self.func.args[0])
+
+ def test_dispatch_bypass(self):
+ self.orig_insert('insert', 'asdf')
+ # tk.call returns '' where Python would return None
+ self.assertEqual(self.redir.dispatch('delete', '1.0', 'end'), '')
+ self.assertEqual(self.text.get('1.0', 'end'), '\n')
+
+ def test_dispatch_error(self):
+ self.func.__init__(TclError())
+ self.assertEqual(self.redir.dispatch('insert', False), '')
+ self.assertEqual(self.redir.dispatch('invalid'), '')
+
+ def test_command_dispatch(self):
+ # Test that .__init__ causes redirection of tk calls
+ # through redir.dispatch
+ self.tk.call(self.text._w, 'insert', 'hello')
+ self.assertEqual(self.func.args, ('hello',))
+ self.assertEqual(self.text.get('1.0', 'end'), '\n')
+ # Ensure that called through redir .dispatch and not through
+ # self.text.insert by having mock raise TclError.
+ self.func.__init__(TclError())
+ self.assertEqual(self.tk.call(self.text._w, 'insert', 'boo'), '')
+
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py
index 044bef9..3f42fda 100644
--- a/Lib/idlelib/idlever.py
+++ b/Lib/idlelib/idlever.py
@@ -1 +1 @@
-IDLE_VERSION = "3.3.5"
+IDLE_VERSION = "3.4.2rc1"
diff --git a/Lib/idlelib/keybindingDialog.py b/Lib/idlelib/keybindingDialog.py
index 0f0da8c..e6438bf 100644
--- a/Lib/idlelib/keybindingDialog.py
+++ b/Lib/idlelib/keybindingDialog.py
@@ -4,15 +4,16 @@ Dialog for building Tkinter accelerator key bindings
from tkinter import *
import tkinter.messagebox as tkMessageBox
import string
-from idlelib import macosxSupport
+import sys
class GetKeysDialog(Toplevel):
- def __init__(self,parent,title,action,currentKeySequences):
+ def __init__(self,parent,title,action,currentKeySequences,_htest=False):
"""
action - string, the name of the virtual event these keys will be
mapped to
currentKeys - list, a list of all key sequence lists currently mapped
to virtual events, for overlap checking
+ _htest - bool, change box location when running htest
"""
Toplevel.__init__(self, parent)
self.configure(borderwidth=5)
@@ -38,11 +39,14 @@ class GetKeysDialog(Toplevel):
self.LoadFinalKeyList()
self.withdraw() #hide while setting geometry
self.update_idletasks()
- self.geometry("+%d+%d" %
- ((parent.winfo_rootx()+((parent.winfo_width()/2)
- -(self.winfo_reqwidth()/2)),
- parent.winfo_rooty()+((parent.winfo_height()/2)
- -(self.winfo_reqheight()/2)) )) ) #centre dialog over parent
+ self.geometry(
+ "+%d+%d" % (
+ parent.winfo_rootx() +
+ (parent.winfo_width()/2 - self.winfo_reqwidth()/2),
+ parent.winfo_rooty() +
+ ((parent.winfo_height()/2 - self.winfo_reqheight()/2)
+ if not _htest else 150)
+ ) ) #centre dialog over parent (or below htest box)
self.deiconify() #geometry set, unhide
self.wait_window()
@@ -133,8 +137,7 @@ class GetKeysDialog(Toplevel):
order is also important: key binding equality depends on it, so
config-keys.def must use the same ordering.
"""
- import sys
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
self.modifiers = ['Shift', 'Control', 'Option', 'Command']
else:
self.modifiers = ['Control', 'Alt', 'Shift']
@@ -259,11 +262,5 @@ class GetKeysDialog(Toplevel):
return keysOK
if __name__ == '__main__':
- #test the dialog
- root=Tk()
- def run():
- keySeq=''
- dlg=GetKeysDialog(root,'Get Keys','find-again',[])
- print(dlg.result)
- Button(root,text='Dialog',command=run).pack()
- root.mainloop()
+ from idlelib.idle_test.htest import run
+ run(GetKeysDialog)
diff --git a/Lib/idlelib/macosxSupport.py b/Lib/idlelib/macosxSupport.py
index 67069fa..b6488f8 100644
--- a/Lib/idlelib/macosxSupport.py
+++ b/Lib/idlelib/macosxSupport.py
@@ -1,48 +1,70 @@
"""
-A number of function that enhance IDLE on MacOSX when it used as a normal
-GUI application (as opposed to an X11 application).
+A number of functions that enhance IDLE on Mac OSX.
"""
import sys
import tkinter
from os import path
+import warnings
+def runningAsOSXApp():
+ warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
+ DeprecationWarning, stacklevel=2)
+ return isAquaTk()
-_appbundle = None
+def isCarbonAquaTk(root):
+ warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
+ DeprecationWarning, stacklevel=2)
+ return isCarbonTk()
-def runningAsOSXApp():
+_tk_type = None
+
+def _initializeTkVariantTests(root):
+ """
+ Initializes OS X Tk variant values for
+ isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
"""
- Returns True if Python is running from within an app on OSX.
- If so, the various OS X customizations will be triggered later (menu
- fixup, et al). (Originally, this test was supposed to condition
- behavior on whether IDLE was running under Aqua Tk rather than
- under X11 Tk but that does not work since a framework build
- could be linked with X11. For several releases, this test actually
- differentiates between whether IDLE is running from a framework or
- not. As a future enhancement, it should be considered whether there
- should be a difference based on framework and any needed X11 adaptions
- should be made dependent on a new function that actually tests for X11.)
- """
- global _appbundle
- if _appbundle is None:
- _appbundle = sys.platform == 'darwin'
- if _appbundle:
- import sysconfig
- _appbundle = bool(sysconfig.get_config_var('PYTHONFRAMEWORK'))
- return _appbundle
-
-_carbonaquatk = None
+ global _tk_type
+ if sys.platform == 'darwin':
+ ws = root.tk.call('tk', 'windowingsystem')
+ if 'x11' in ws:
+ _tk_type = "xquartz"
+ elif 'aqua' not in ws:
+ _tk_type = "other"
+ elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
+ _tk_type = "cocoa"
+ else:
+ _tk_type = "carbon"
+ else:
+ _tk_type = "other"
-def isCarbonAquaTk(root):
+def isAquaTk():
+ """
+ Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
+ """
+ assert _tk_type is not None
+ return _tk_type == "cocoa" or _tk_type == "carbon"
+
+def isCarbonTk():
"""
Returns True if IDLE is using a Carbon Aqua Tk (instead of the
newer Cocoa Aqua Tk).
"""
- global _carbonaquatk
- if _carbonaquatk is None:
- _carbonaquatk = (runningAsOSXApp() and
- 'aqua' in root.tk.call('tk', 'windowingsystem') and
- 'AppKit' not in root.tk.call('winfo', 'server', '.'))
- return _carbonaquatk
+ assert _tk_type is not None
+ return _tk_type == "carbon"
+
+def isCocoaTk():
+ """
+ Returns True if IDLE is using a Cocoa Aqua Tk.
+ """
+ assert _tk_type is not None
+ return _tk_type == "cocoa"
+
+def isXQuartz():
+ """
+ Returns True if IDLE is using an OS X X11 Tk.
+ """
+ assert _tk_type is not None
+ return _tk_type == "xquartz"
def tkVersionWarning(root):
"""
@@ -53,8 +75,7 @@ def tkVersionWarning(root):
can still crash unexpectedly.
"""
- if (runningAsOSXApp() and
- ('AppKit' in root.tk.call('winfo', 'server', '.')) ):
+ if isCocoaTk():
patchlevel = root.tk.call('info', 'patchlevel')
if patchlevel not in ('8.5.7', '8.5.9'):
return False
@@ -88,8 +109,8 @@ def hideTkConsole(root):
def overrideRootMenu(root, flist):
"""
- Replace the Tk root menu by something that's more appropriate for
- IDLE.
+ Replace the Tk root menu by something that is more appropriate for
+ IDLE with an Aqua Tk.
"""
# The menu that is attached to the Tk root (".") is also used by AquaTk for
# all windows that don't specify a menu of their own. The default menubar
@@ -108,6 +129,22 @@ def overrideRootMenu(root, flist):
from idlelib import WindowList
from idlelib.MultiCall import MultiCallCreator
+ closeItem = Bindings.menudefs[0][1][-2]
+
+ # Remove the last 3 items of the file menu: a separator, close window and
+ # quit. Close window will be reinserted just above the save item, where
+ # it should be according to the HIG. Quit is in the application menu.
+ del Bindings.menudefs[0][1][-3:]
+ Bindings.menudefs[0][1].insert(6, closeItem)
+
+ # Remove the 'About' entry from the help menu, it is in the application
+ # menu
+ del Bindings.menudefs[-1][1][0:2]
+
+ # Remove the 'Configure' entry from the options menu, it is in the
+ # application menu as 'Preferences'
+ del Bindings.menudefs[-2][1][0:2]
+
menubar = Menu(root)
root.configure(menu=menubar)
menudict = {}
@@ -156,7 +193,7 @@ def overrideRootMenu(root, flist):
# right thing for now.
root.createcommand('exit', flist.close_all_callback)
- if isCarbonAquaTk(root):
+ if isCarbonTk():
# for Carbon AquaTk, replace the default Tk apple menu
menudict['application'] = menu = Menu(menubar, name='apple')
menubar.add_cascade(label='IDLE', menu=menu)
@@ -171,8 +208,7 @@ def overrideRootMenu(root, flist):
Bindings.menudefs[0][1].append(
('_Preferences....', '<<open-config-dialog>>'),
)
- else:
- # assume Cocoa AquaTk
+ if isCocoaTk():
# replace default About dialog with About IDLE one
root.createcommand('tkAboutDialog', about_dialog)
# replace default "Help" item in Help menu
@@ -182,10 +218,22 @@ def overrideRootMenu(root, flist):
def setupApp(root, flist):
"""
- Perform setup for the OSX application bundle.
+ Perform initial OS X customizations if needed.
+ Called from PyShell.main() after initial calls to Tk()
+
+ There are currently three major versions of Tk in use on OS X:
+ 1. Aqua Cocoa Tk (native default since OS X 10.6)
+ 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
+ 3. X11 (supported by some third-party distributors, deprecated)
+ There are various differences among the three that affect IDLE
+ behavior, primarily with menus, mouse key events, and accelerators.
+ Some one-time customizations are performed here.
+ Others are dynamically tested throughout idlelib by calls to the
+ isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
+ are initialized here as well.
"""
- if not runningAsOSXApp(): return
-
- hideTkConsole(root)
- overrideRootMenu(root, flist)
- addOpenEventSupport(root, flist)
+ _initializeTkVariantTests(root)
+ if isAquaTk():
+ hideTkConsole(root)
+ overrideRootMenu(root, flist)
+ addOpenEventSupport(root, flist)
diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py
index ddce6e9..9c51b8f 100644
--- a/Lib/idlelib/rpc.py
+++ b/Lib/idlelib/rpc.py
@@ -199,7 +199,7 @@ class SocketIO(object):
raise
except KeyboardInterrupt:
raise
- except socket.error:
+ except OSError:
raise
except Exception as ex:
return ("CALLEXC", ex)
@@ -340,7 +340,7 @@ class SocketIO(object):
n = self.sock.send(s[:BUFSIZE])
except (AttributeError, TypeError):
raise OSError("socket no longer exists")
- except socket.error:
+ except OSError:
raise
else:
s = s[n:]
@@ -357,7 +357,7 @@ class SocketIO(object):
return None
try:
s = self.sock.recv(BUFSIZE)
- except socket.error:
+ except OSError:
raise EOFError
if len(s) == 0:
raise EOFError
@@ -537,7 +537,7 @@ class RPCClient(SocketIO):
SocketIO.__init__(self, working_sock)
else:
print("** Invalid host: ", address, file=sys.__stderr__)
- raise socket.error
+ raise OSError
def get_remote_proxy(self, oid):
return RPCProxy(self, oid)
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index c1859b6..13cec62 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -150,8 +150,8 @@ def manage_socket(address):
try:
server = MyRPCServer(address, MyHandler)
break
- except socket.error as err:
- print("IDLE Subprocess: socket error: " + err.args[1] +
+ except OSError as err:
+ print("IDLE Subprocess: OSError: " + err.args[1] +
", retrying....", file=sys.__stderr__)
socket_error = err
else:
diff --git a/Lib/idlelib/tabbedpages.py b/Lib/idlelib/tabbedpages.py
index 2557732..965f9f8 100644
--- a/Lib/idlelib/tabbedpages.py
+++ b/Lib/idlelib/tabbedpages.py
@@ -467,9 +467,12 @@ class TabbedPageSet(Frame):
self._tab_set.set_selected_tab(page_name)
-if __name__ == '__main__':
+def _tabbed_pages(parent):
# test dialog
root=Tk()
+ width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
+ root.geometry("+%d+%d"%(x, y + 175))
+ root.title("Test tabbed pages")
tabPage=TabbedPageSet(root, page_names=['Foobar','Baz'], n_rows=0,
expand_tabs=False,
)
@@ -488,3 +491,8 @@ if __name__ == '__main__':
labelPgName.pack(padx=5)
entryPgName.pack(padx=5)
root.mainloop()
+
+
+if __name__ == '__main__':
+ from idlelib.idle_test.htest import run
+ run(_tabbed_pages)
diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py
index dd50544..4257eea 100644
--- a/Lib/idlelib/textView.py
+++ b/Lib/idlelib/textView.py
@@ -9,15 +9,21 @@ class TextViewer(Toplevel):
"""A simple text viewer dialog for IDLE
"""
- def __init__(self, parent, title, text, modal=True):
+ def __init__(self, parent, title, text, modal=True, _htest=False):
"""Show the given text in a scrollable window with a 'close' button
+ If modal option set to False, user can interact with other windows,
+ otherwise they will be unable to interact with other windows until
+ the textview window is closed.
+
+ _htest - bool; change box location when running htest.
"""
Toplevel.__init__(self, parent)
self.configure(borderwidth=5)
+ # place dialog below parent if running htest
self.geometry("=%dx%d+%d+%d" % (625, 500,
- parent.winfo_rootx() + 10,
- parent.winfo_rooty() + 10))
+ parent.winfo_rootx() + 10,
+ parent.winfo_rooty() + (10 if not _htest else 100)))
#elguavas - config placeholders til config stuff completed
self.bg = '#ffffff'
self.fg = '#000000'
@@ -66,32 +72,15 @@ def view_file(parent, title, filename, encoding=None, modal=True):
try:
with open(filename, 'r', encoding=encoding) as file:
contents = file.read()
- except OSError:
- import tkinter.messagebox as tkMessageBox
+ except IOError:
tkMessageBox.showerror(title='File Load Error',
message='Unable to load file %r .' % filename,
parent=parent)
else:
return view_text(parent, title, contents, modal)
-
if __name__ == '__main__':
- #test the dialog
- root=Tk()
- root.title('textView test')
- filename = './textView.py'
- with open(filename, 'r') as f:
- text = f.read()
- btn1 = Button(root, text='view_text',
- command=lambda:view_text(root, 'view_text', text))
- btn1.pack(side=LEFT)
- btn2 = Button(root, text='view_file',
- command=lambda:view_file(root, 'view_file', filename))
- btn2.pack(side=LEFT)
- btn3 = Button(root, text='nonmodal view_text',
- command=lambda:view_text(root, 'nonmodal view_text', text,
- modal=False))
- btn3.pack(side=LEFT)
- close = Button(root, text='Close', command=root.destroy)
- close.pack(side=RIGHT)
- root.mainloop()
+ import unittest
+ unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False)
+ from idlelib.idle_test.htest import run
+ run(TextViewer)