mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #27380: IDLE: add base Query dialog, with ttk widgets and subclass
SectionName. These split class GetCfgSectionNameDialog from configSectionNameDialog.py, temporarily renamed config_sec.py in 3.7.9a2. More Query subclasses are planned.
This commit is contained in:
parent
754a5c1a1d
commit
68a53c5d39
6 changed files with 326 additions and 188 deletions
|
@ -1,98 +0,0 @@
|
||||||
"""
|
|
||||||
Dialog that allows user to specify a new config file section name.
|
|
||||||
Used to get new highlight theme and keybinding set names.
|
|
||||||
The 'return value' for the dialog, used two placed in configdialog.py,
|
|
||||||
is the .result attribute set in the Ok and Cancel methods.
|
|
||||||
"""
|
|
||||||
from tkinter import *
|
|
||||||
import tkinter.messagebox as tkMessageBox
|
|
||||||
|
|
||||||
class GetCfgSectionNameDialog(Toplevel):
|
|
||||||
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)
|
|
||||||
self.resizable(height=FALSE, width=FALSE)
|
|
||||||
self.title(title)
|
|
||||||
self.transient(parent)
|
|
||||||
self.grab_set()
|
|
||||||
self.protocol("WM_DELETE_WINDOW", self.Cancel)
|
|
||||||
self.parent = parent
|
|
||||||
self.message = message
|
|
||||||
self.used_names = used_names
|
|
||||||
self.create_widgets()
|
|
||||||
self.withdraw() #hide while setting geometry
|
|
||||||
self.update_idletasks()
|
|
||||||
#needs to be done here so that the winfo_reqwidth is valid
|
|
||||||
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)
|
|
||||||
if not _htest else 100)
|
|
||||||
) ) #centre dialog over parent (or below htest box)
|
|
||||||
self.deiconify() #geometry set, unhide
|
|
||||||
self.wait_window()
|
|
||||||
|
|
||||||
def create_widgets(self):
|
|
||||||
self.name = StringVar(self.parent)
|
|
||||||
self.fontSize = StringVar(self.parent)
|
|
||||||
self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
|
|
||||||
self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
|
|
||||||
self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT,
|
|
||||||
padx=5, pady=5, text=self.message) #,aspect=200)
|
|
||||||
entryName = Entry(self.frameMain, textvariable=self.name, width=30)
|
|
||||||
entryName.focus_set()
|
|
||||||
self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH)
|
|
||||||
entryName.pack(padx=5, pady=5)
|
|
||||||
|
|
||||||
frameButtons = Frame(self, pady=2)
|
|
||||||
frameButtons.pack(side=BOTTOM)
|
|
||||||
self.buttonOk = Button(frameButtons, text='Ok',
|
|
||||||
width=8, command=self.Ok)
|
|
||||||
self.buttonOk.pack(side=LEFT, padx=5)
|
|
||||||
self.buttonCancel = Button(frameButtons, text='Cancel',
|
|
||||||
width=8, command=self.Cancel)
|
|
||||||
self.buttonCancel.pack(side=RIGHT, padx=5)
|
|
||||||
|
|
||||||
def name_ok(self):
|
|
||||||
''' After stripping entered name, check that it is a sensible
|
|
||||||
ConfigParser file section name. Return it if it is, '' if not.
|
|
||||||
'''
|
|
||||||
name = self.name.get().strip()
|
|
||||||
if not name: #no name specified
|
|
||||||
tkMessageBox.showerror(title='Name Error',
|
|
||||||
message='No name specified.', parent=self)
|
|
||||||
elif len(name)>30: #name too long
|
|
||||||
tkMessageBox.showerror(title='Name Error',
|
|
||||||
message='Name too long. It should be no more than '+
|
|
||||||
'30 characters.', parent=self)
|
|
||||||
name = ''
|
|
||||||
elif name in self.used_names:
|
|
||||||
tkMessageBox.showerror(title='Name Error',
|
|
||||||
message='This name is already in use.', parent=self)
|
|
||||||
name = ''
|
|
||||||
return name
|
|
||||||
|
|
||||||
def Ok(self, event=None):
|
|
||||||
name = self.name_ok()
|
|
||||||
if name:
|
|
||||||
self.result = name
|
|
||||||
self.destroy()
|
|
||||||
|
|
||||||
def Cancel(self, event=None):
|
|
||||||
self.result = ''
|
|
||||||
self.destroy()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
import unittest
|
|
||||||
unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False)
|
|
||||||
|
|
||||||
from idlelib.idle_test.htest import run
|
|
||||||
run(GetCfgSectionNameDialog)
|
|
|
@ -18,7 +18,7 @@ import tkinter.font as tkFont
|
||||||
from idlelib.config import idleConf
|
from idlelib.config import idleConf
|
||||||
from idlelib.dynoption import DynOptionMenu
|
from idlelib.dynoption import DynOptionMenu
|
||||||
from idlelib.config_key import GetKeysDialog
|
from idlelib.config_key import GetKeysDialog
|
||||||
from idlelib.config_sec import GetCfgSectionNameDialog
|
from idlelib.query import SectionName
|
||||||
from idlelib.config_help import GetHelpSourceDialog
|
from idlelib.config_help import GetHelpSourceDialog
|
||||||
from idlelib.tabbedpages import TabbedPageSet
|
from idlelib.tabbedpages import TabbedPageSet
|
||||||
from idlelib.textview import view_text
|
from idlelib.textview import view_text
|
||||||
|
@ -684,7 +684,7 @@ class ConfigDialog(Toplevel):
|
||||||
def GetNewKeysName(self, message):
|
def GetNewKeysName(self, message):
|
||||||
usedNames = (idleConf.GetSectionList('user', 'keys') +
|
usedNames = (idleConf.GetSectionList('user', 'keys') +
|
||||||
idleConf.GetSectionList('default', 'keys'))
|
idleConf.GetSectionList('default', 'keys'))
|
||||||
newKeySet = GetCfgSectionNameDialog(
|
newKeySet = SectionName(
|
||||||
self, 'New Custom Key Set', message, usedNames).result
|
self, 'New Custom Key Set', message, usedNames).result
|
||||||
return newKeySet
|
return newKeySet
|
||||||
|
|
||||||
|
@ -837,7 +837,7 @@ class ConfigDialog(Toplevel):
|
||||||
def GetNewThemeName(self, message):
|
def GetNewThemeName(self, message):
|
||||||
usedNames = (idleConf.GetSectionList('user', 'highlight') +
|
usedNames = (idleConf.GetSectionList('user', 'highlight') +
|
||||||
idleConf.GetSectionList('default', 'highlight'))
|
idleConf.GetSectionList('default', 'highlight'))
|
||||||
newTheme = GetCfgSectionNameDialog(
|
newTheme = SectionName(
|
||||||
self, 'New Custom Theme', message, usedNames).result
|
self, 'New Custom Theme', message, usedNames).result
|
||||||
return newTheme
|
return newTheme
|
||||||
|
|
||||||
|
|
|
@ -137,18 +137,6 @@ _editor_window_spec = {
|
||||||
"Best to close editor first."
|
"Best to close editor first."
|
||||||
}
|
}
|
||||||
|
|
||||||
GetCfgSectionNameDialog_spec = {
|
|
||||||
'file': 'config_sec',
|
|
||||||
'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 = {
|
GetHelpSourceDialog_spec = {
|
||||||
'file': 'config_help',
|
'file': 'config_help',
|
||||||
'kwds': {'title': 'Get helpsource',
|
'kwds': {'title': 'Get helpsource',
|
||||||
|
@ -245,6 +233,17 @@ _percolator_spec = {
|
||||||
"Test for actions like text entry, and removal."
|
"Test for actions like text entry, and removal."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Query_spec = {
|
||||||
|
'file': 'query',
|
||||||
|
'kwds': {'title':'Query',
|
||||||
|
'message':'Enter something',
|
||||||
|
'_htest': True},
|
||||||
|
'msg': "Enter with <Return> or [Ok]. Print valid entry to Shell\n"
|
||||||
|
"Blank line, after stripping, is ignored\n"
|
||||||
|
"Close dialog with valid entry, [Cancel] or [X]",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
_replace_dialog_spec = {
|
_replace_dialog_spec = {
|
||||||
'file': 'replace',
|
'file': 'replace',
|
||||||
'kwds': {},
|
'kwds': {},
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
"""Unit tests for idlelib.config_sec"""
|
|
||||||
import unittest
|
|
||||||
from idlelib.idle_test.mock_tk import Var, Mbox
|
|
||||||
from idlelib import config_sec as name_dialog_module
|
|
||||||
|
|
||||||
name_dialog = name_dialog_module.GetCfgSectionNameDialog
|
|
||||||
|
|
||||||
class Dummy_name_dialog:
|
|
||||||
# Mock for testing the following methods of name_dialog
|
|
||||||
name_ok = name_dialog.name_ok
|
|
||||||
Ok = name_dialog.Ok
|
|
||||||
Cancel = name_dialog.Cancel
|
|
||||||
# Attributes, constant or variable, needed for tests
|
|
||||||
used_names = ['used']
|
|
||||||
name = Var()
|
|
||||||
result = None
|
|
||||||
destroyed = False
|
|
||||||
def destroy(self):
|
|
||||||
self.destroyed = True
|
|
||||||
|
|
||||||
# name_ok calls Mbox.showerror if name is not ok
|
|
||||||
orig_mbox = name_dialog_module.tkMessageBox
|
|
||||||
showerror = Mbox.showerror
|
|
||||||
|
|
||||||
class ConfigNameTest(unittest.TestCase):
|
|
||||||
dialog = Dummy_name_dialog()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
name_dialog_module.tkMessageBox = Mbox
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
name_dialog_module.tkMessageBox = orig_mbox
|
|
||||||
|
|
||||||
def test_blank_name(self):
|
|
||||||
self.dialog.name.set(' ')
|
|
||||||
self.assertEqual(self.dialog.name_ok(), '')
|
|
||||||
self.assertEqual(showerror.title, 'Name Error')
|
|
||||||
self.assertIn('No', showerror.message)
|
|
||||||
|
|
||||||
def test_used_name(self):
|
|
||||||
self.dialog.name.set('used')
|
|
||||||
self.assertEqual(self.dialog.name_ok(), '')
|
|
||||||
self.assertEqual(showerror.title, 'Name Error')
|
|
||||||
self.assertIn('use', showerror.message)
|
|
||||||
|
|
||||||
def test_long_name(self):
|
|
||||||
self.dialog.name.set('good'*8)
|
|
||||||
self.assertEqual(self.dialog.name_ok(), '')
|
|
||||||
self.assertEqual(showerror.title, 'Name Error')
|
|
||||||
self.assertIn('too long', showerror.message)
|
|
||||||
|
|
||||||
def test_good_name(self):
|
|
||||||
self.dialog.name.set(' good ')
|
|
||||||
showerror.title = 'No Error' # should not be called
|
|
||||||
self.assertEqual(self.dialog.name_ok(), 'good')
|
|
||||||
self.assertEqual(showerror.title, 'No Error')
|
|
||||||
|
|
||||||
def test_ok(self):
|
|
||||||
self.dialog.destroyed = False
|
|
||||||
self.dialog.name.set('good')
|
|
||||||
self.dialog.Ok()
|
|
||||||
self.assertEqual(self.dialog.result, 'good')
|
|
||||||
self.assertTrue(self.dialog.destroyed)
|
|
||||||
|
|
||||||
def test_cancel(self):
|
|
||||||
self.dialog.destroyed = False
|
|
||||||
self.dialog.Cancel()
|
|
||||||
self.assertEqual(self.dialog.result, '')
|
|
||||||
self.assertTrue(self.dialog.destroyed)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main(verbosity=2, exit=False)
|
|
164
Lib/idlelib/idle_test/test_query.py
Normal file
164
Lib/idlelib/idle_test/test_query.py
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
"""Test idlelib.query.
|
||||||
|
|
||||||
|
Coverage: 100%.
|
||||||
|
"""
|
||||||
|
from test.support import requires
|
||||||
|
from tkinter import Tk
|
||||||
|
import unittest
|
||||||
|
from unittest import mock
|
||||||
|
from idlelib.idle_test.mock_tk import Var, Mbox_func
|
||||||
|
from idlelib import query
|
||||||
|
Query, SectionName = query.Query, query.SectionName
|
||||||
|
|
||||||
|
class Dummy_Query:
|
||||||
|
# Mock for testing the following methods Query
|
||||||
|
entry_ok = Query.entry_ok
|
||||||
|
ok = Query.ok
|
||||||
|
cancel = Query.cancel
|
||||||
|
# Attributes, constant or variable, needed for tests
|
||||||
|
entry = Var()
|
||||||
|
result = None
|
||||||
|
destroyed = False
|
||||||
|
def destroy(self):
|
||||||
|
self.destroyed = True
|
||||||
|
|
||||||
|
# entry_ok calls modal messagebox.showerror if entry is not ok.
|
||||||
|
# Mock showerrer returns, so don't need to click to continue.
|
||||||
|
orig_showerror = query.showerror
|
||||||
|
showerror = Mbox_func() # Instance has __call__ method.
|
||||||
|
|
||||||
|
def setUpModule():
|
||||||
|
query.showerror = showerror
|
||||||
|
|
||||||
|
def tearDownModule():
|
||||||
|
query.showerror = orig_showerror
|
||||||
|
|
||||||
|
|
||||||
|
class QueryTest(unittest.TestCase):
|
||||||
|
dialog = Dummy_Query()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
showerror.title = None
|
||||||
|
self.dialog.result = None
|
||||||
|
self.dialog.destroyed = False
|
||||||
|
|
||||||
|
def test_blank_entry(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
Equal = self.assertEqual
|
||||||
|
dialog.entry.set(' ')
|
||||||
|
Equal(dialog.entry_ok(), '')
|
||||||
|
Equal((dialog.result, dialog.destroyed), (None, False))
|
||||||
|
Equal(showerror.title, 'Entry Error')
|
||||||
|
self.assertIn('Blank', showerror.message)
|
||||||
|
|
||||||
|
def test_good_entry(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
Equal = self.assertEqual
|
||||||
|
dialog.entry.set(' good ')
|
||||||
|
Equal(dialog.entry_ok(), 'good')
|
||||||
|
Equal((dialog.result, dialog.destroyed), (None, False))
|
||||||
|
Equal(showerror.title, None)
|
||||||
|
|
||||||
|
def test_ok(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
Equal = self.assertEqual
|
||||||
|
dialog.entry.set('good')
|
||||||
|
Equal(dialog.ok(), None)
|
||||||
|
Equal((dialog.result, dialog.destroyed), ('good', True))
|
||||||
|
|
||||||
|
def test_cancel(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
Equal = self.assertEqual
|
||||||
|
Equal(self.dialog.cancel(), None)
|
||||||
|
Equal((dialog.result, dialog.destroyed), (None, True))
|
||||||
|
|
||||||
|
|
||||||
|
class Dummy_SectionName:
|
||||||
|
# Mock for testing the following method of Section_Name
|
||||||
|
entry_ok = SectionName.entry_ok
|
||||||
|
# Attributes, constant or variable, needed for tests
|
||||||
|
used_names = ['used']
|
||||||
|
entry = Var()
|
||||||
|
|
||||||
|
class SectionNameTest(unittest.TestCase):
|
||||||
|
dialog = Dummy_SectionName()
|
||||||
|
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
showerror.title = None
|
||||||
|
|
||||||
|
def test_blank_name(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
Equal = self.assertEqual
|
||||||
|
dialog.entry.set(' ')
|
||||||
|
Equal(dialog.entry_ok(), '')
|
||||||
|
Equal(showerror.title, 'Name Error')
|
||||||
|
self.assertIn('No', showerror.message)
|
||||||
|
|
||||||
|
def test_used_name(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
Equal = self.assertEqual
|
||||||
|
dialog.entry.set('used')
|
||||||
|
Equal(self.dialog.entry_ok(), '')
|
||||||
|
Equal(showerror.title, 'Name Error')
|
||||||
|
self.assertIn('use', showerror.message)
|
||||||
|
|
||||||
|
def test_long_name(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
Equal = self.assertEqual
|
||||||
|
dialog.entry.set('good'*8)
|
||||||
|
Equal(self.dialog.entry_ok(), '')
|
||||||
|
Equal(showerror.title, 'Name Error')
|
||||||
|
self.assertIn('too long', showerror.message)
|
||||||
|
|
||||||
|
def test_good_entry(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
Equal = self.assertEqual
|
||||||
|
dialog.entry.set(' good ')
|
||||||
|
Equal(dialog.entry_ok(), 'good')
|
||||||
|
Equal(showerror.title, None)
|
||||||
|
|
||||||
|
|
||||||
|
class QueryGuiTest(unittest.TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
requires('gui')
|
||||||
|
cls.root = Tk()
|
||||||
|
cls.dialog = Query(cls.root, 'TEST', 'test', _utest=True)
|
||||||
|
cls.dialog.destroy = mock.Mock()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
del cls.dialog
|
||||||
|
cls.root.destroy()
|
||||||
|
del cls.root
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.dialog.entry.delete(0, 'end')
|
||||||
|
self.dialog.result = None
|
||||||
|
self.dialog.destroy.reset_mock()
|
||||||
|
|
||||||
|
def test_click_ok(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
dialog.entry.insert(0, 'abc')
|
||||||
|
dialog.button_ok.invoke()
|
||||||
|
self.assertEqual(dialog.result, 'abc')
|
||||||
|
self.assertTrue(dialog.destroy.called)
|
||||||
|
|
||||||
|
def test_click_blank(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
dialog.button_ok.invoke()
|
||||||
|
self.assertEqual(dialog.result, None)
|
||||||
|
self.assertFalse(dialog.destroy.called)
|
||||||
|
|
||||||
|
def test_click_cancel(self):
|
||||||
|
dialog = self.dialog
|
||||||
|
dialog.entry.insert(0, 'abc')
|
||||||
|
dialog.button_cancel.invoke()
|
||||||
|
self.assertEqual(dialog.result, None)
|
||||||
|
self.assertTrue(dialog.destroy.called)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=2, exit=False)
|
148
Lib/idlelib/query.py
Normal file
148
Lib/idlelib/query.py
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
"""
|
||||||
|
Dialogs that query users and verify the answer before accepting.
|
||||||
|
Use ttk widgets, limiting use to tcl/tk 8.5+, as in IDLE 3.6+.
|
||||||
|
|
||||||
|
Query is the generic base class for a popup dialog.
|
||||||
|
The user must either enter a valid answer or close the dialog.
|
||||||
|
Entries are validated when <Return> is entered or [Ok] is clicked.
|
||||||
|
Entries are ignored when [Cancel] or [X] are clicked.
|
||||||
|
The 'return value' is .result set to either a valid answer or None.
|
||||||
|
|
||||||
|
Subclass SectionName gets a name for a new config file section.
|
||||||
|
Configdialog uses it for new highlight theme and keybinding set names.
|
||||||
|
"""
|
||||||
|
# Query and Section name result from splitting GetCfgSectionNameDialog
|
||||||
|
# of configSectionNameDialog.py (temporarily config_sec.py) into
|
||||||
|
# generic and specific parts.
|
||||||
|
|
||||||
|
from tkinter import FALSE, TRUE, Toplevel
|
||||||
|
from tkinter.messagebox import showerror
|
||||||
|
from tkinter.ttk import Frame, Button, Entry, Label
|
||||||
|
|
||||||
|
class Query(Toplevel):
|
||||||
|
"""Base class for getting verified answer from a user.
|
||||||
|
|
||||||
|
For this base class, accept any non-blank string.
|
||||||
|
"""
|
||||||
|
def __init__(self, parent, title, message,
|
||||||
|
*, _htest=False, _utest=False): # Call from override.
|
||||||
|
"""Create popup, do not return until tk widget destroyed.
|
||||||
|
|
||||||
|
Additional subclass init must be done before calling this.
|
||||||
|
|
||||||
|
title - string, title of popup dialog
|
||||||
|
message - string, informational message to display
|
||||||
|
_htest - bool, change box location when running htest
|
||||||
|
_utest - bool, leave window hidden and not modal
|
||||||
|
"""
|
||||||
|
Toplevel.__init__(self, parent)
|
||||||
|
self.configure(borderwidth=5)
|
||||||
|
self.resizable(height=FALSE, width=FALSE)
|
||||||
|
self.title(title)
|
||||||
|
self.transient(parent)
|
||||||
|
self.grab_set()
|
||||||
|
self.bind('<Key-Return>', self.ok)
|
||||||
|
self.protocol("WM_DELETE_WINDOW", self.cancel)
|
||||||
|
self.parent = parent
|
||||||
|
self.message = message
|
||||||
|
self.create_widgets()
|
||||||
|
self.update_idletasks()
|
||||||
|
#needs to be done here so that the winfo_reqwidth is valid
|
||||||
|
self.withdraw() # Hide while configuring, especially geometry.
|
||||||
|
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)
|
||||||
|
if not _utest:
|
||||||
|
self.deiconify() #geometry set, unhide
|
||||||
|
self.wait_window()
|
||||||
|
|
||||||
|
def create_widgets(self): # Call from override, if any.
|
||||||
|
frame = Frame(self, borderwidth=2, relief='sunken', )
|
||||||
|
label = Label(frame, anchor='w', justify='left',
|
||||||
|
text=self.message)
|
||||||
|
self.entry = Entry(frame, width=30) # Bind name for entry_ok.
|
||||||
|
self.entry.focus_set()
|
||||||
|
|
||||||
|
buttons = Frame(self) # Bind buttons for invoke in unittest.
|
||||||
|
self.button_ok = Button(buttons, text='Ok',
|
||||||
|
width=8, command=self.ok)
|
||||||
|
self.button_cancel = Button(buttons, text='Cancel',
|
||||||
|
width=8, command=self.cancel)
|
||||||
|
|
||||||
|
frame.pack(side='top', expand=TRUE, fill='both')
|
||||||
|
label.pack(padx=5, pady=5)
|
||||||
|
self.entry.pack(padx=5, pady=5)
|
||||||
|
buttons.pack(side='bottom')
|
||||||
|
self.button_ok.pack(side='left', padx=5)
|
||||||
|
self.button_cancel.pack(side='right', padx=5)
|
||||||
|
|
||||||
|
def entry_ok(self): # Usually replace.
|
||||||
|
"Check that entry not blank."
|
||||||
|
entry = self.entry.get().strip()
|
||||||
|
if not entry:
|
||||||
|
showerror(title='Entry Error',
|
||||||
|
message='Blank line.', parent=self)
|
||||||
|
return entry
|
||||||
|
|
||||||
|
def ok(self, event=None): # Do not replace.
|
||||||
|
'''If entry is valid, bind it to 'result' and destroy tk widget.
|
||||||
|
|
||||||
|
Otherwise leave dialog open for user to correct entry or cancel.
|
||||||
|
'''
|
||||||
|
entry = self.entry_ok()
|
||||||
|
if entry:
|
||||||
|
self.result = entry
|
||||||
|
self.destroy()
|
||||||
|
else:
|
||||||
|
# [Ok] (but not <Return>) moves focus. Move it back.
|
||||||
|
self.entry.focus_set()
|
||||||
|
|
||||||
|
def cancel(self, event=None): # Do not replace.
|
||||||
|
"Set dialog result to None and destroy tk widget."
|
||||||
|
self.result = None
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
class SectionName(Query):
|
||||||
|
"Get a name for a config file section name."
|
||||||
|
|
||||||
|
def __init__(self, parent, title, message, used_names,
|
||||||
|
*, _htest=False, _utest=False):
|
||||||
|
"used_names - collection of strings already in use"
|
||||||
|
|
||||||
|
self.used_names = used_names
|
||||||
|
Query.__init__(self, parent, title, message,
|
||||||
|
_htest=_htest, _utest=_utest)
|
||||||
|
# This call does ot return until tk widget is destroyed.
|
||||||
|
|
||||||
|
def entry_ok(self):
|
||||||
|
'''Stripping entered name, check that it is a sensible
|
||||||
|
ConfigParser file section name. Return it if it is, '' if not.
|
||||||
|
'''
|
||||||
|
name = self.entry.get().strip()
|
||||||
|
if not name:
|
||||||
|
showerror(title='Name Error',
|
||||||
|
message='No name specified.', parent=self)
|
||||||
|
elif len(name)>30:
|
||||||
|
showerror(title='Name Error',
|
||||||
|
message='Name too long. It should be no more than '+
|
||||||
|
'30 characters.', parent=self)
|
||||||
|
name = ''
|
||||||
|
elif name in self.used_names:
|
||||||
|
showerror(title='Name Error',
|
||||||
|
message='This name is already in use.', parent=self)
|
||||||
|
name = ''
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import unittest
|
||||||
|
unittest.main('idlelib.idle_test.test_query', verbosity=2, exit=False)
|
||||||
|
|
||||||
|
from idlelib.idle_test.htest import run
|
||||||
|
run(Query)
|
Loading…
Add table
Add a link
Reference in a new issue