summaryrefslogtreecommitdiffstats
path: root/Lib/idlelib/query.py
blob: e3937a1340b9a9696dedd5207c14c9460b84ecfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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)