(root)/
Python-3.11.7/
Lib/
tkinter/
simpledialog.py
       1  #
       2  # An Introduction to Tkinter
       3  #
       4  # Copyright (c) 1997 by Fredrik Lundh
       5  #
       6  # This copyright applies to Dialog, askinteger, askfloat and asktring
       7  #
       8  # fredrik@pythonware.com
       9  # http://www.pythonware.com
      10  #
      11  """This modules handles dialog boxes.
      12  
      13  It contains the following public symbols:
      14  
      15  SimpleDialog -- A simple but flexible modal dialog box
      16  
      17  Dialog -- a base class for dialogs
      18  
      19  askinteger -- get an integer from the user
      20  
      21  askfloat -- get a float from the user
      22  
      23  askstring -- get a string from the user
      24  """
      25  
      26  from tkinter import *
      27  from tkinter import _get_temp_root, _destroy_temp_root
      28  from tkinter import messagebox
      29  
      30  
      31  class ESC[4;38;5;81mSimpleDialog:
      32  
      33      def __init__(self, master,
      34                   text='', buttons=[], default=None, cancel=None,
      35                   title=None, class_=None):
      36          if class_:
      37              self.root = Toplevel(master, class_=class_)
      38          else:
      39              self.root = Toplevel(master)
      40          if title:
      41              self.root.title(title)
      42              self.root.iconname(title)
      43  
      44          _setup_dialog(self.root)
      45  
      46          self.message = Message(self.root, text=text, aspect=400)
      47          self.message.pack(expand=1, fill=BOTH)
      48          self.frame = Frame(self.root)
      49          self.frame.pack()
      50          self.num = default
      51          self.cancel = cancel
      52          self.default = default
      53          self.root.bind('<Return>', self.return_event)
      54          for num in range(len(buttons)):
      55              s = buttons[num]
      56              b = Button(self.frame, text=s,
      57                         command=(lambda self=self, num=num: self.done(num)))
      58              if num == default:
      59                  b.config(relief=RIDGE, borderwidth=8)
      60              b.pack(side=LEFT, fill=BOTH, expand=1)
      61          self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window)
      62          self.root.transient(master)
      63          _place_window(self.root, master)
      64  
      65      def go(self):
      66          self.root.wait_visibility()
      67          self.root.grab_set()
      68          self.root.mainloop()
      69          self.root.destroy()
      70          return self.num
      71  
      72      def return_event(self, event):
      73          if self.default is None:
      74              self.root.bell()
      75          else:
      76              self.done(self.default)
      77  
      78      def wm_delete_window(self):
      79          if self.cancel is None:
      80              self.root.bell()
      81          else:
      82              self.done(self.cancel)
      83  
      84      def done(self, num):
      85          self.num = num
      86          self.root.quit()
      87  
      88  
      89  class ESC[4;38;5;81mDialog(ESC[4;38;5;149mToplevel):
      90  
      91      '''Class to open dialogs.
      92  
      93      This class is intended as a base class for custom dialogs
      94      '''
      95  
      96      def __init__(self, parent, title = None):
      97          '''Initialize a dialog.
      98  
      99          Arguments:
     100  
     101              parent -- a parent window (the application window)
     102  
     103              title -- the dialog title
     104          '''
     105          master = parent
     106          if master is None:
     107              master = _get_temp_root()
     108  
     109          Toplevel.__init__(self, master)
     110  
     111          self.withdraw() # remain invisible for now
     112          # If the parent is not viewable, don't
     113          # make the child transient, or else it
     114          # would be opened withdrawn
     115          if parent is not None and parent.winfo_viewable():
     116              self.transient(parent)
     117  
     118          if title:
     119              self.title(title)
     120  
     121          _setup_dialog(self)
     122  
     123          self.parent = parent
     124  
     125          self.result = None
     126  
     127          body = Frame(self)
     128          self.initial_focus = self.body(body)
     129          body.pack(padx=5, pady=5)
     130  
     131          self.buttonbox()
     132  
     133          if self.initial_focus is None:
     134              self.initial_focus = self
     135  
     136          self.protocol("WM_DELETE_WINDOW", self.cancel)
     137  
     138          _place_window(self, parent)
     139  
     140          self.initial_focus.focus_set()
     141  
     142          # wait for window to appear on screen before calling grab_set
     143          self.wait_visibility()
     144          self.grab_set()
     145          self.wait_window(self)
     146  
     147      def destroy(self):
     148          '''Destroy the window'''
     149          self.initial_focus = None
     150          Toplevel.destroy(self)
     151          _destroy_temp_root(self.master)
     152  
     153      #
     154      # construction hooks
     155  
     156      def body(self, master):
     157          '''create dialog body.
     158  
     159          return widget that should have initial focus.
     160          This method should be overridden, and is called
     161          by the __init__ method.
     162          '''
     163          pass
     164  
     165      def buttonbox(self):
     166          '''add standard button box.
     167  
     168          override if you do not want the standard buttons
     169          '''
     170  
     171          box = Frame(self)
     172  
     173          w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
     174          w.pack(side=LEFT, padx=5, pady=5)
     175          w = Button(box, text="Cancel", width=10, command=self.cancel)
     176          w.pack(side=LEFT, padx=5, pady=5)
     177  
     178          self.bind("<Return>", self.ok)
     179          self.bind("<Escape>", self.cancel)
     180  
     181          box.pack()
     182  
     183      #
     184      # standard button semantics
     185  
     186      def ok(self, event=None):
     187  
     188          if not self.validate():
     189              self.initial_focus.focus_set() # put focus back
     190              return
     191  
     192          self.withdraw()
     193          self.update_idletasks()
     194  
     195          try:
     196              self.apply()
     197          finally:
     198              self.cancel()
     199  
     200      def cancel(self, event=None):
     201  
     202          # put focus back to the parent window
     203          if self.parent is not None:
     204              self.parent.focus_set()
     205          self.destroy()
     206  
     207      #
     208      # command hooks
     209  
     210      def validate(self):
     211          '''validate the data
     212  
     213          This method is called automatically to validate the data before the
     214          dialog is destroyed. By default, it always validates OK.
     215          '''
     216  
     217          return 1 # override
     218  
     219      def apply(self):
     220          '''process the data
     221  
     222          This method is called automatically to process the data, *after*
     223          the dialog is destroyed. By default, it does nothing.
     224          '''
     225  
     226          pass # override
     227  
     228  
     229  # Place a toplevel window at the center of parent or screen
     230  # It is a Python implementation of ::tk::PlaceWindow.
     231  def _place_window(w, parent=None):
     232      w.wm_withdraw() # Remain invisible while we figure out the geometry
     233      w.update_idletasks() # Actualize geometry information
     234  
     235      minwidth = w.winfo_reqwidth()
     236      minheight = w.winfo_reqheight()
     237      maxwidth = w.winfo_vrootwidth()
     238      maxheight = w.winfo_vrootheight()
     239      if parent is not None and parent.winfo_ismapped():
     240          x = parent.winfo_rootx() + (parent.winfo_width() - minwidth) // 2
     241          y = parent.winfo_rooty() + (parent.winfo_height() - minheight) // 2
     242          vrootx = w.winfo_vrootx()
     243          vrooty = w.winfo_vrooty()
     244          x = min(x, vrootx + maxwidth - minwidth)
     245          x = max(x, vrootx)
     246          y = min(y, vrooty + maxheight - minheight)
     247          y = max(y, vrooty)
     248          if w._windowingsystem == 'aqua':
     249              # Avoid the native menu bar which sits on top of everything.
     250              y = max(y, 22)
     251      else:
     252          x = (w.winfo_screenwidth() - minwidth) // 2
     253          y = (w.winfo_screenheight() - minheight) // 2
     254  
     255      w.wm_maxsize(maxwidth, maxheight)
     256      w.wm_geometry('+%d+%d' % (x, y))
     257      w.wm_deiconify() # Become visible at the desired location
     258  
     259  
     260  def _setup_dialog(w):
     261      if w._windowingsystem == "aqua":
     262          w.tk.call("::tk::unsupported::MacWindowStyle", "style",
     263                    w, "moveableModal", "")
     264      elif w._windowingsystem == "x11":
     265          w.wm_attributes("-type", "dialog")
     266  
     267  # --------------------------------------------------------------------
     268  # convenience dialogues
     269  
     270  class ESC[4;38;5;81m_QueryDialog(ESC[4;38;5;149mDialog):
     271  
     272      def __init__(self, title, prompt,
     273                   initialvalue=None,
     274                   minvalue = None, maxvalue = None,
     275                   parent = None):
     276  
     277          self.prompt   = prompt
     278          self.minvalue = minvalue
     279          self.maxvalue = maxvalue
     280  
     281          self.initialvalue = initialvalue
     282  
     283          Dialog.__init__(self, parent, title)
     284  
     285      def destroy(self):
     286          self.entry = None
     287          Dialog.destroy(self)
     288  
     289      def body(self, master):
     290  
     291          w = Label(master, text=self.prompt, justify=LEFT)
     292          w.grid(row=0, padx=5, sticky=W)
     293  
     294          self.entry = Entry(master, name="entry")
     295          self.entry.grid(row=1, padx=5, sticky=W+E)
     296  
     297          if self.initialvalue is not None:
     298              self.entry.insert(0, self.initialvalue)
     299              self.entry.select_range(0, END)
     300  
     301          return self.entry
     302  
     303      def validate(self):
     304          try:
     305              result = self.getresult()
     306          except ValueError:
     307              messagebox.showwarning(
     308                  "Illegal value",
     309                  self.errormessage + "\nPlease try again",
     310                  parent = self
     311              )
     312              return 0
     313  
     314          if self.minvalue is not None and result < self.minvalue:
     315              messagebox.showwarning(
     316                  "Too small",
     317                  "The allowed minimum value is %s. "
     318                  "Please try again." % self.minvalue,
     319                  parent = self
     320              )
     321              return 0
     322  
     323          if self.maxvalue is not None and result > self.maxvalue:
     324              messagebox.showwarning(
     325                  "Too large",
     326                  "The allowed maximum value is %s. "
     327                  "Please try again." % self.maxvalue,
     328                  parent = self
     329              )
     330              return 0
     331  
     332          self.result = result
     333  
     334          return 1
     335  
     336  
     337  class ESC[4;38;5;81m_QueryInteger(ESC[4;38;5;149m_QueryDialog):
     338      errormessage = "Not an integer."
     339  
     340      def getresult(self):
     341          return self.getint(self.entry.get())
     342  
     343  
     344  def askinteger(title, prompt, **kw):
     345      '''get an integer from the user
     346  
     347      Arguments:
     348  
     349          title -- the dialog title
     350          prompt -- the label text
     351          **kw -- see SimpleDialog class
     352  
     353      Return value is an integer
     354      '''
     355      d = _QueryInteger(title, prompt, **kw)
     356      return d.result
     357  
     358  
     359  class ESC[4;38;5;81m_QueryFloat(ESC[4;38;5;149m_QueryDialog):
     360      errormessage = "Not a floating point value."
     361  
     362      def getresult(self):
     363          return self.getdouble(self.entry.get())
     364  
     365  
     366  def askfloat(title, prompt, **kw):
     367      '''get a float from the user
     368  
     369      Arguments:
     370  
     371          title -- the dialog title
     372          prompt -- the label text
     373          **kw -- see SimpleDialog class
     374  
     375      Return value is a float
     376      '''
     377      d = _QueryFloat(title, prompt, **kw)
     378      return d.result
     379  
     380  
     381  class ESC[4;38;5;81m_QueryString(ESC[4;38;5;149m_QueryDialog):
     382      def __init__(self, *args, **kw):
     383          if "show" in kw:
     384              self.__show = kw["show"]
     385              del kw["show"]
     386          else:
     387              self.__show = None
     388          _QueryDialog.__init__(self, *args, **kw)
     389  
     390      def body(self, master):
     391          entry = _QueryDialog.body(self, master)
     392          if self.__show is not None:
     393              entry.configure(show=self.__show)
     394          return entry
     395  
     396      def getresult(self):
     397          return self.entry.get()
     398  
     399  
     400  def askstring(title, prompt, **kw):
     401      '''get a string from the user
     402  
     403      Arguments:
     404  
     405          title -- the dialog title
     406          prompt -- the label text
     407          **kw -- see SimpleDialog class
     408  
     409      Return value is a string
     410      '''
     411      d = _QueryString(title, prompt, **kw)
     412      return d.result
     413  
     414  
     415  if __name__ == '__main__':
     416  
     417      def test():
     418          root = Tk()
     419          def doit(root=root):
     420              d = SimpleDialog(root,
     421                           text="This is a test dialog.  "
     422                                "Would this have been an actual dialog, "
     423                                "the buttons below would have been glowing "
     424                                "in soft pink light.\n"
     425                                "Do you believe this?",
     426                           buttons=["Yes", "No", "Cancel"],
     427                           default=0,
     428                           cancel=2,
     429                           title="Test Dialog")
     430              print(d.go())
     431              print(askinteger("Spam", "Egg count", initialvalue=12*12))
     432              print(askfloat("Spam", "Egg weight\n(in tons)", minvalue=1,
     433                             maxvalue=100))
     434              print(askstring("Spam", "Egg label"))
     435          t = Button(root, text='Test', command=doit)
     436          t.pack()
     437          q = Button(root, text='Quit', command=t.quit)
     438          q.pack()
     439          t.mainloop()
     440  
     441      test()