diff options
Diffstat (limited to 'Lib/idlelib/query.py')
-rw-r--r-- | Lib/idlelib/query.py | 124 |
1 files changed, 72 insertions, 52 deletions
diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index c806c6b..a4584df 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -10,6 +10,8 @@ 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. +Subclass ModuleName gets a name for File => Open Module. +Subclass HelpSource gets menu item and path for additions to Help menu. """ # Query and Section name result from splitting GetCfgSectionNameDialog # of configSectionNameDialog.py (temporarily config_sec.py) into @@ -21,10 +23,10 @@ Configdialog uses it for new highlight theme and keybinding set names. import importlib import os from sys import executable, platform # Platform is set for one test. -from tkinter import Toplevel, StringVar +from tkinter import Toplevel, StringVar, W, E, N, S from tkinter import filedialog -from tkinter.messagebox import showerror from tkinter.ttk import Frame, Button, Entry, Label +from tkinter.font import Font class Query(Toplevel): """Base class for getting verified answer from a user. @@ -47,18 +49,26 @@ class Query(Toplevel): """ Toplevel.__init__(self, parent) self.withdraw() # Hide while configuring, especially geometry. - self.configure(borderwidth=5) - self.resizable(height=False, width=False) + self.parent = parent self.title(title) + self.message = message + self.text0 = text0 + self.used_names = used_names self.transient(parent) self.grab_set() - self.bind('<Key-Return>', self.ok) + windowingsystem = self.tk.call('tk', 'windowingsystem') + if windowingsystem == 'aqua': + try: + self.tk.call('::tk::unsupported::MacWindowStyle', 'style', + self._w, 'moveableModal', '') + except: + pass + self.bind("<Command-.>", self.cancel) self.bind('<Key-Escape>', self.cancel) self.protocol("WM_DELETE_WINDOW", self.cancel) - self.parent = parent - self.message = message - self.text0 = text0 - self.used_names = used_names + self.bind('<Key-Return>', self.ok) + self.bind("<KP_Enter>", self.ok) + self.resizable(height=False, width=False) self.create_widgets() self.update_idletasks() # Needed here for winfo_reqwidth below. self.geometry( # Center dialog over parent (or below htest box). @@ -75,32 +85,42 @@ class Query(Toplevel): def create_widgets(self): # Call from override, if any. # Bind to self widgets needed for entry_ok or unittest. - self.frame = frame = Frame(self, borderwidth=2, relief='sunken', ) + self.frame = frame = Frame(self, padding=10) + frame.grid(column=0, row=0, sticky='news') + frame.grid_columnconfigure(0, weight=1) + entrylabel = Label(frame, anchor='w', justify='left', text=self.message) self.entryvar = StringVar(self, self.text0) self.entry = Entry(frame, width=30, textvariable=self.entryvar) self.entry.focus_set() - - buttons = Frame(self) - self.button_ok = Button(buttons, text='Ok', default='active', - 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') - entrylabel.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) + self.error_font = Font(name='TkCaptionFont', + exists=True, root=self.parent) + self.entry_error = Label(frame, text=' ', foreground='red', + font=self.error_font) + self.button_ok = Button( + frame, text='OK', default='active', command=self.ok) + self.button_cancel = Button( + frame, text='Cancel', command=self.cancel) + + entrylabel.grid(column=0, row=0, columnspan=3, padx=5, sticky=W) + self.entry.grid(column=0, row=1, columnspan=3, padx=5, sticky=W+E, + pady=[10,0]) + self.entry_error.grid(column=0, row=2, columnspan=3, padx=5, + sticky=W+E) + self.button_ok.grid(column=1, row=99, padx=5) + self.button_cancel.grid(column=2, row=99, padx=5) + + def showerror(self, message, widget=None): + #self.bell(displayof=self) + (widget or self.entry_error)['text'] = 'ERROR: ' + message def entry_ok(self): # Example: usually replace. "Return non-blank entry or None." + self.entry_error['text'] = '' entry = self.entry.get().strip() if not entry: - showerror(title='Entry Error', - message='Blank line.', parent=self) + self.showerror('blank line.') return None return entry @@ -134,19 +154,16 @@ class SectionName(Query): def entry_ok(self): "Return sensible ConfigParser section name or None." + self.entry_error['text'] = '' name = self.entry.get().strip() if not name: - showerror(title='Name Error', - message='No name specified.', parent=self) + self.showerror('no name specified.') return None elif len(name)>30: - showerror(title='Name Error', - message='Name too long. It should be no more than '+ - '30 characters.', parent=self) + self.showerror('name is longer than 30 characters.') return None elif name in self.used_names: - showerror(title='Name Error', - message='This name is already in use.', parent=self) + self.showerror('name is already in use.') return None return name @@ -162,30 +179,27 @@ class ModuleName(Query): def entry_ok(self): "Return entered module name as file path or None." + self.entry_error['text'] = '' name = self.entry.get().strip() if not name: - showerror(title='Name Error', - message='No name specified.', parent=self) + self.showerror('no name specified.') return None # XXX Ought to insert current file's directory in front of path. try: spec = importlib.util.find_spec(name) except (ValueError, ImportError) as msg: - showerror("Import Error", str(msg), parent=self) + self.showerror(str(msg)) return None if spec is None: - showerror("Import Error", "module not found", - parent=self) + self.showerror("module not found") return None if not isinstance(spec.loader, importlib.abc.SourceLoader): - showerror("Import Error", "not a source-based module", - parent=self) + self.showerror("not a source-based module") return None try: file_path = spec.loader.get_filename(name) except AttributeError: - showerror("Import Error", - "loader does not support get_filename", + self.showerror("loader does not support get_filename", parent=self) return None return file_path @@ -204,8 +218,9 @@ class HelpSource(Query): """ self.filepath = filepath message = 'Name for item on Help menu:' - super().__init__(parent, title, message, text0=menuitem, - used_names=used_names, _htest=_htest, _utest=_utest) + super().__init__( + parent, title, message, text0=menuitem, + used_names=used_names, _htest=_htest, _utest=_utest) def create_widgets(self): super().create_widgets() @@ -216,10 +231,16 @@ class HelpSource(Query): self.path = Entry(frame, textvariable=self.pathvar, width=40) browse = Button(frame, text='Browse', width=8, command=self.browse_file) + self.path_error = Label(frame, text=' ', foreground='red', + font=self.error_font) - pathlabel.pack(anchor='w', padx=5, pady=3) - self.path.pack(anchor='w', padx=5, pady=3) - browse.pack(pady=3) + pathlabel.grid(column=0, row=10, columnspan=3, padx=5, pady=[10,0], + sticky=W) + self.path.grid(column=0, row=11, columnspan=2, padx=5, sticky=W+E, + pady=[10,0]) + browse.grid(column=2, row=11, padx=5, sticky=W+S) + self.path_error.grid(column=0, row=12, columnspan=3, padx=5, + sticky=W+E) def askfilename(self, filetypes, initdir, initfile): # htest # # Extracted from browse_file so can mock for unittests. @@ -256,17 +277,14 @@ class HelpSource(Query): "Simple validity check for menu file path" path = self.path.get().strip() if not path: #no path specified - showerror(title='File Path Error', - message='No help file path specified.', - parent=self) + self.showerror('no help file path specified.', self.path_error) return None elif not path.startswith(('www.', 'http')): if path[:5] == 'file:': path = path[5:] if not os.path.exists(path): - showerror(title='File Path Error', - message='Help file path does not exist.', - parent=self) + self.showerror('help file path does not exist.', + self.path_error) return None if platform == 'darwin': # for Mac Safari path = "file://" + path @@ -274,6 +292,8 @@ class HelpSource(Query): def entry_ok(self): "Return apparently valid (name, path) or None" + self.entry_error['text'] = '' + self.path_error['text'] = '' name = self.item_ok() path = self.path_ok() return None if name is None or path is None else (name, path) |