Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
query.py391 linesDownload Raw Back to idlelib
1"""2Dialogs that query users and verify the answer before accepting.3 4Query is the generic base class for a popup dialog.5The user must either enter a valid answer or close the dialog.6Entries are validated when <Return> is entered or [Ok] is clicked.7Entries are ignored when [Cancel] or [X] are clicked.8The 'return value' is .result set to either a valid answer or None.9 10Subclass SectionName gets a name for a new config file section.11Configdialog uses it for new highlight theme and keybinding set names.12Subclass ModuleName gets a name for File => Open Module.13Subclass HelpSource gets menu item and path for additions to Help menu.14"""15# Query and Section name result from splitting GetCfgSectionNameDialog16# of configSectionNameDialog.py (temporarily config_sec.py) into17# generic and specific parts.  3.6 only, July 2016.18# ModuleName.entry_ok came from editor.EditorWindow.load_module.19# HelpSource was extracted from configHelpSourceEdit.py (temporarily20# config_help.py), with darwin code moved from ok to path_ok.21 22import importlib.util, importlib.abc23import os24import shlex25from sys import executable, platform  # Platform is set for one test.26 27from tkinter import Toplevel, StringVar, BooleanVar, W, E, S28from tkinter.ttk import Frame, Button, Entry, Label, Checkbutton29from tkinter import filedialog30from tkinter.font import Font31from tkinter.simpledialog import _setup_dialog32 33class Query(Toplevel):34    """Base class for getting verified answer from a user.35 36    For this base class, accept any non-blank string.37    """38    def __init__(self, parent, title, message, *, text0='', used_names={},39                 _htest=False, _utest=False):40        """Create modal popup, return when destroyed.41 42        Additional subclass init must be done before this unless43        _utest=True is passed to suppress wait_window().44 45        title - string, title of popup dialog46        message - string, informational message to display47        text0 - initial value for entry48        used_names - names already in use49        _htest - bool, change box location when running htest50        _utest - bool, leave window hidden and not modal51        """52        self.parent = parent  # Needed for Font call.53        self.message = message54        self.text0 = text055        self.used_names = used_names56 57        Toplevel.__init__(self, parent)58        self.withdraw()  # Hide while configuring, especially geometry.59        self.title(title)60        self.transient(parent)61        if not _utest:  # Otherwise fail when directly run unittest.62            self.grab_set()63 64        _setup_dialog(self)65        if self._windowingsystem == 'aqua':66            self.bind("<Command-.>", self.cancel)67        self.bind('<Key-Escape>', self.cancel)68        self.protocol("WM_DELETE_WINDOW", self.cancel)69        self.bind('<Key-Return>', self.ok)70        self.bind("<KP_Enter>", self.ok)71 72        self.create_widgets()73        self.update_idletasks()  # Need here for winfo_reqwidth below.74        self.geometry(  # Center dialog over parent (or below htest box).75                "+%d+%d" % (76                    parent.winfo_rootx() +77                    (parent.winfo_width()/2 - self.winfo_reqwidth()/2),78                    parent.winfo_rooty() +79                    ((parent.winfo_height()/2 - self.winfo_reqheight()/2)80                    if not _htest else 150)81                ) )82        self.resizable(height=False, width=False)83 84        if not _utest:85            self.deiconify()  # Unhide now that geometry set.86            self.entry.focus_set()87            self.wait_window()88 89    def create_widgets(self, ok_text='OK'):  # Do not replace.90        """Create entry (rows, extras, buttons.91 92        Entry stuff on rows 0-2, spanning cols 0-2.93        Buttons on row 99, cols 1, 2.94        """95        # Bind to self the widgets needed for entry_ok or unittest.96        self.frame = frame = Frame(self, padding=10)97        frame.grid(column=0, row=0, sticky='news')98        frame.grid_columnconfigure(0, weight=1)99 100        entrylabel = Label(frame, anchor='w', justify='left',101                           text=self.message)102        self.entryvar = StringVar(self, self.text0)103        self.entry = Entry(frame, width=30, textvariable=self.entryvar)104        self.error_font = Font(name='TkCaptionFont',105                               exists=True, root=self.parent)106        self.entry_error = Label(frame, text=' ', foreground='red',107                                 font=self.error_font)108        # Display or blank error by setting ['text'] =.109        entrylabel.grid(column=0, row=0, columnspan=3, padx=5, sticky=W)110        self.entry.grid(column=0, row=1, columnspan=3, padx=5, sticky=W+E,111                        pady=[10,0])112        self.entry_error.grid(column=0, row=2, columnspan=3, padx=5,113                              sticky=W+E)114 115        self.create_extra()116 117        self.button_ok = Button(118                frame, text=ok_text, default='active', command=self.ok)119        self.button_cancel = Button(120                frame, text='Cancel', command=self.cancel)121 122        self.button_ok.grid(column=1, row=99, padx=5)123        self.button_cancel.grid(column=2, row=99, padx=5)124 125    def create_extra(self): pass  # Override to add widgets.126 127    def showerror(self, message, widget=None):128        #self.bell(displayof=self)129        (widget or self.entry_error)['text'] = 'ERROR: ' + message130 131    def entry_ok(self):  # Example: usually replace.132        "Return non-blank entry or None."133        entry = self.entry.get().strip()134        if not entry:135            self.showerror('blank line.')136            return None137        return entry138 139    def ok(self, event=None):  # Do not replace.140        '''If entry is valid, bind it to 'result' and destroy tk widget.141 142        Otherwise leave dialog open for user to correct entry or cancel.143        '''144        self.entry_error['text'] = ''145        entry = self.entry_ok()146        if entry is not None:147            self.result = entry148            self.destroy()149        else:150            # [Ok] moves focus.  (<Return> does not.)  Move it back.151            self.entry.focus_set()152 153    def cancel(self, event=None):  # Do not replace.154        "Set dialog result to None and destroy tk widget."155        self.result = None156        self.destroy()157 158    def destroy(self):159        self.grab_release()160        super().destroy()161 162 163class SectionName(Query):164    "Get a name for a config file section name."165    # Used in ConfigDialog.GetNewKeysName, .GetNewThemeName (837)166 167    def __init__(self, parent, title, message, used_names,168                 *, _htest=False, _utest=False):169        super().__init__(parent, title, message, used_names=used_names,170                         _htest=_htest, _utest=_utest)171 172    def entry_ok(self):173        "Return sensible ConfigParser section name or None."174        name = self.entry.get().strip()175        if not name:176            self.showerror('no name specified.')177            return None178        elif len(name)>30:179            self.showerror('name is longer than 30 characters.')180            return None181        elif name in self.used_names:182            self.showerror('name is already in use.')183            return None184        return name185 186 187class ModuleName(Query):188    "Get a module name for Open Module menu entry."189    # Used in open_module (editor.EditorWindow until move to iobinding).190 191    def __init__(self, parent, title, message, text0,192                 *, _htest=False, _utest=False):193        super().__init__(parent, title, message, text0=text0,194                       _htest=_htest, _utest=_utest)195 196    def entry_ok(self):197        "Return entered module name as file path or None."198        name = self.entry.get().strip()199        if not name:200            self.showerror('no name specified.')201            return None202        # XXX Ought to insert current file's directory in front of path.203        try:204            spec = importlib.util.find_spec(name)205        except (ValueError, ImportError) as msg:206            self.showerror(str(msg))207            return None208        if spec is None:209            self.showerror("module not found.")210            return None211        if not isinstance(spec.loader, importlib.abc.SourceLoader):212            self.showerror("not a source-based module.")213            return None214        try:215            file_path = spec.loader.get_filename(name)216        except AttributeError:217            self.showerror("loader does not support get_filename.")218            return None219        except ImportError:220            # Some special modules require this (e.g. os.path)221            try:222                file_path = spec.loader.get_filename()223            except TypeError:224                self.showerror("loader failed to get filename.")225                return None226        return file_path227 228 229class Goto(Query):230    "Get a positive line number for editor Go To Line."231    # Used in editor.EditorWindow.goto_line_event.232 233    def entry_ok(self):234        try:235            lineno = int(self.entry.get())236        except ValueError:237            self.showerror('not a base 10 integer.')238            return None239        if lineno <= 0:240            self.showerror('not a positive integer.')241            return None242        return lineno243 244 245class HelpSource(Query):246    "Get menu name and help source for Help menu."247    # Used in ConfigDialog.HelpListItemAdd/Edit, (941/9)248 249    def __init__(self, parent, title, *, menuitem='', filepath='',250                 used_names={}, _htest=False, _utest=False):251        """Get menu entry and url/local file for Additional Help.252 253        User enters a name for the Help resource and a web url or file254        name. The user can browse for the file.255        """256        self.filepath = filepath257        message = 'Name for item on Help menu:'258        super().__init__(259                parent, title, message, text0=menuitem,260                used_names=used_names, _htest=_htest, _utest=_utest)261 262    def create_extra(self):263        "Add path widjets to rows 10-12."264        frame = self.frame265        pathlabel = Label(frame, anchor='w', justify='left',266                          text='Help File Path: Enter URL or browse for file')267        self.pathvar = StringVar(self, self.filepath)268        self.path = Entry(frame, textvariable=self.pathvar, width=40)269        browse = Button(frame, text='Browse', width=8,270                        command=self.browse_file)271        self.path_error = Label(frame, text=' ', foreground='red',272                                font=self.error_font)273 274        pathlabel.grid(column=0, row=10, columnspan=3, padx=5, pady=[10,0],275                       sticky=W)276        self.path.grid(column=0, row=11, columnspan=2, padx=5, sticky=W+E,277                       pady=[10,0])278        browse.grid(column=2, row=11, padx=5, sticky=W+S)279        self.path_error.grid(column=0, row=12, columnspan=3, padx=5,280                             sticky=W+E)281 282    def askfilename(self, filetypes, initdir, initfile):  # htest #283        # Extracted from browse_file so can mock for unittests.284        # Cannot unittest as cannot simulate button clicks.285        # Test by running htest, such as by running this file.286        return filedialog.Open(parent=self, filetypes=filetypes)\287               .show(initialdir=initdir, initialfile=initfile)288 289    def browse_file(self):290        filetypes = [291            ("HTML Files", "*.htm *.html", "TEXT"),292            ("Text Files", "*.txt", "TEXT"),293            ("All Files", "*")]294        path = self.pathvar.get()295        if path:296            dir, base = os.path.split(path)297        else:298            base = None299            if platform[:3] == 'win':300                dir = os.path.join(os.path.dirname(executable), 'Doc')301                if not os.path.isdir(dir):302                    dir = os.getcwd()303            else:304                dir = os.getcwd()305        file = self.askfilename(filetypes, dir, base)306        if file:307            self.pathvar.set(file)308 309    item_ok = SectionName.entry_ok  # localize for test override310 311    def path_ok(self):312        "Simple validity check for menu file path"313        path = self.path.get().strip()314        if not path: #no path specified315            self.showerror('no help file path specified.', self.path_error)316            return None317        elif not path.startswith(('www.', 'http')):318            if path[:5] == 'file:':319                path = path[5:]320            if not os.path.exists(path):321                self.showerror('help file path does not exist.',322                               self.path_error)323                return None324            if platform == 'darwin':  # for Mac Safari325                path =  "file://" + path326        return path327 328    def entry_ok(self):329        "Return apparently valid (name, path) or None"330        self.path_error['text'] = ''331        name = self.item_ok()332        path = self.path_ok()333        return None if name is None or path is None else (name, path)334 335class CustomRun(Query):336    """Get settings for custom run of module.337 338    1. Command line arguments to extend sys.argv.339    2. Whether to restart Shell or not.340    """341    # Used in runscript.run_custom_event342 343    def __init__(self, parent, title, *, cli_args=[],344                 _htest=False, _utest=False):345        """cli_args is a list of strings.346 347        The list is assigned to the default Entry StringVar.348        The strings are displayed joined by ' ' for display.349        """350        message = 'Command Line Arguments for sys.argv:'351        super().__init__(352                parent, title, message, text0=cli_args,353                _htest=_htest, _utest=_utest)354 355    def create_extra(self):356        "Add run mode on rows 10-12."357        frame = self.frame358        self.restartvar = BooleanVar(self, value=True)359        restart = Checkbutton(frame, variable=self.restartvar, onvalue=True,360                              offvalue=False, text='Restart shell')361        self.args_error = Label(frame, text=' ', foreground='red',362                                font=self.error_font)363 364        restart.grid(column=0, row=10, columnspan=3, padx=5, sticky='w')365        self.args_error.grid(column=0, row=12, columnspan=3, padx=5,366                             sticky='we')367 368    def cli_args_ok(self):369        "Return command line arg list or None if error."370        cli_string = self.entry.get().strip()371        try:372            cli_args = shlex.split(cli_string, posix=True)373        except ValueError as err:374            self.showerror(str(err))375            return None376        return cli_args377 378    def entry_ok(self):379        "Return apparently valid (cli_args, restart) or None."380        cli_args = self.cli_args_ok()381        restart = self.restartvar.get()382        return None if cli_args is None else (cli_args, restart)383 384 385if __name__ == '__main__':386    from unittest import main387    main('idlelib.idle_test.test_query', verbosity=2, exit=False)388 389    from idlelib.idle_test.htest import run390    run(Query, HelpSource, CustomRun)391 
codekingpro/portable-devtools · Team Ai