define searchdialogbase used by search replace and grep dialogs from tkinter import toplevel from tkinter ttk import frame entry label button checkbutton radiobutton from tkinter simpledialog import setupdialog class searchdialogbase title search dialog replace in subclasses icon search needwrapbutton 1 not in find in files def initself root engine self root root self bell root bell self engine engine self top none def openself text searchphrasenone make dialog visible on top of others and ready to use self text text if not self top self createwidgets else self top deiconify self top tkraise self top transienttext winfotoplevel if searchphrase self ent delete0 end self ent insertend searchphrase self ent focusset self ent selectionrange0 end self ent icursor0 self top grabset def closeself eventnone put dialog away for later use if self top self top grabrelease self top transient self top withdraw def createwidgetsself top toplevelself root top bindreturn self defaultcommand top bindescape self close top protocolwmdeletewindow self close top wmtitleself title top wmiconnameself icon setupdialogtop self top top self frame frametop padding5px self frame gridstickynwes top gridcolumnconfigure0 weight100 top gridrowconfigure0 weight100 self row 0 self frame gridcolumnconfigure0 pad2 weight0 self frame gridcolumnconfigure1 pad2 minsize100 weight100 self createentries row 0 and maybe 1 cols 0 1 self createoptionbuttons next row cols 0 1 self createotherbuttons next row cols 0 1 self createcommandbuttons col 2 all rows def makeentryself labeltext var label labelself frame textlabeltext label gridrowself row column0 stickynw entry entryself frame textvariablevar exportselection0 entry gridrowself row column1 stickynwe self row self row 1 return entry label def createentriesself create one or more entry lines with makeentry self ent self makeentryfind self engine patvar0 def makeframeself labeltextnone if labeltext label labelself frame textlabeltext label gridrowself row column0 stickynw else label frame frameself frame frame gridrowself row column1 columnspan1 stickynwe self row self row 1 return frame label def createoptionbuttonsself frame self makeframeoptions0 engine self engine options engine revar regular expression engine casevar match case engine wordvar whole word if self needwrapbutton options appendengine wrapvar wrap around for var label in options btn checkbuttonframe variablevar textlabel btn packsideleft fillboth return frame options def createotherbuttonsself frame self makeframedirection0 var self engine backvar others 1 up 0 down for val label in others btn radiobuttonframe variablevar valueval textlabel btn packsideleft fillboth return frame others def makebuttonself label command isdef0 return command button gridded in command frame b buttonself buttonframe textlabel commandcommand defaultisdef and active or normal cols rowsself buttonframe gridsize b gridpady1 rowrows column0 stickyew self buttonframe gridrowspanrows1 return b def createcommandbuttonsself place buttons in vertical command frame gridded on right f self buttonframe frameself frame f gridrow0 column2 padx2 pady2 ipadx2 ipady2 b self makebuttonclose self close b lower class searchbasesearchdialogbase htest create autoopening dialog with no text connection def initself parent import re from idlelib import searchengine self root parent self engine searchengine getparent self createwidgets printparent geometry width height x y listmapint re split x parent geometry self top geometrydd x 40 y 175 def defaultcommandself dummy pass if name main from unittest import main main idlelib idletest testsearchbase verbosity2 exitfalse from idlelib idletest htest import run runsearchbase create most of a 3 or 4 row 3 column search dialog 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 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 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 replace in subclasses not in find in files initialize root engine and top attributes top level widget set in create_widgets called from open frame container for all widgets in dialog 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_widgets title of dialog class attribute override in subclasses icon of dialog ditto use unclear if cannot minimize dialog 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 row 0 and maybe 1 cols 0 1 next row cols 0 1 next row cols 0 1 col 2 all rows return entry label entry gridded labeled entry for text entry label label widget returned for testing return frame label frame gridded labeled frame for option or other buttons label label widget returned for testing 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 return frame others for testing others is a list of value label pairs a gridded frame from make_frame is filled with radio buttons htest
from tkinter import Toplevel
from tkinter.ttk import Frame, Entry, Label, Button, Checkbutton, Radiobutton
from tkinter.simpledialog import _setup_dialog


class SearchDialogBase:
    

    title = "Search Dialog"  
    icon = "Search"
    needwrapbutton = 1  

    def __init__(self, root, engine):
        
        self.root = root
        self.bell = root.bell
        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()
        else:
            self.top.deiconify()
            self.top.tkraise()
        self.top.transient(text.winfo_toplevel())
        if searchphrase:
            self.ent.delete(0,"end")
            self.ent.insert("end",searchphrase)
        self.ent.focus_set()
        self.ent.selection_range(0, "end")
        self.ent.icursor(0)
        self.top.grab_set()

    def close(self, event=None):
        "Put dialog away for later use."
        if self.top:
            self.top.grab_release()
            self.top.transient('')
            self.top.withdraw()

    def create_widgets(self):
        
        top = Toplevel(self.root)
        top.bind("<Return>", self.default_command)
        top.bind("<Escape>", self.close)
        top.protocol("WM_DELETE_WINDOW", self.close)
        top.wm_title(self.title)
        top.wm_iconname(self.icon)
        _setup_dialog(top)
        self.top = top
        self.frame = Frame(top, padding="5px")
        self.frame.grid(sticky="nwes")
        top.grid_columnconfigure(0, weight=100)
        top.grid_rowconfigure(0, weight=100)

        self.row = 0
        self.frame.grid_columnconfigure(0, pad=2, weight=0)
        self.frame.grid_columnconfigure(1, pad=2, minsize=100, weight=100)

        self.create_entries()  
        self.create_option_buttons()  
        self.create_other_buttons()  
        self.create_command_buttons()  

    def make_entry(self, label_text, var):
        
        label = Label(self.frame, text=label_text)
        label.grid(row=self.row, column=0, sticky="nw")
        entry = Entry(self.frame, textvariable=var, exportselection=0)
        entry.grid(row=self.row, column=1, sticky="nwe")
        self.row = self.row + 1
        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):
        
        if labeltext:
            label = Label(self.frame, text=labeltext)
            label.grid(row=self.row, column=0, sticky="nw")
        else:
            label = ''
        frame = Frame(self.frame)
        frame.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
        self.row = self.row + 1
        return frame, label

    def create_option_buttons(self):
        
        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, variable=var, text=label)
            btn.pack(side="left", fill="both")
        return frame, options

    def create_other_buttons(self):
        
        frame = self.make_frame("Direction")[0]
        var = self.engine.backvar
        others = [(1, 'Up'), (0, 'Down')]
        for val, label in others:
            btn = Radiobutton(frame, variable=var, value=val, text=label)
            btn.pack(side="left", fill="both")
        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")
        cols,rows=self.buttonframe.grid_size()
        b.grid(pady=1,row=rows,column=0,sticky="ew")
        self.buttonframe.grid(rowspan=rows+1)
        return b

    def create_command_buttons(self):
        "Place buttons in vertical command frame gridded on right."
        f = self.buttonframe = Frame(self.frame)
        f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)

        b = self.make_button("Close", self.close)
        b.lower()


class _searchbase(SearchDialogBase):  
    "Create auto-opening dialog with no text connection."

    def __init__(self, parent):
        import re
        from idlelib import searchengine

        self.root = parent
        self.engine = searchengine.get(parent)
        self.create_widgets()
        print(parent.geometry())
        width,height, x,y = list(map(int, re.split('[x+]', parent.geometry())))
        self.top.geometry("+%d+%d" % (x + 40, y + 175))

    def default_command(self, dummy): pass


if __name__ == '__main__':
    from unittest import main
    main('idlelib.idle_test.test_searchbase', verbosity=2, exit=False)

    from idlelib.idle_test.htest import run
    run(_searchbase)