python-2.5.2/win32/Lib/idlelib/keybindingDialog.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 """
       
     2 Dialog for building Tkinter accelerator key bindings
       
     3 """
       
     4 from Tkinter import *
       
     5 import tkMessageBox
       
     6 import string, os
       
     7 
       
     8 class GetKeysDialog(Toplevel):
       
     9     def __init__(self,parent,title,action,currentKeySequences):
       
    10         """
       
    11         action - string, the name of the virtual event these keys will be
       
    12                  mapped to
       
    13         currentKeys - list, a list of all key sequence lists currently mapped
       
    14                  to virtual events, for overlap checking
       
    15         """
       
    16         Toplevel.__init__(self, parent)
       
    17         self.configure(borderwidth=5)
       
    18         self.resizable(height=FALSE,width=FALSE)
       
    19         self.title(title)
       
    20         self.transient(parent)
       
    21         self.grab_set()
       
    22         self.protocol("WM_DELETE_WINDOW", self.Cancel)
       
    23         self.parent = parent
       
    24         self.action=action
       
    25         self.currentKeySequences=currentKeySequences
       
    26         self.result=''
       
    27         self.keyString=StringVar(self)
       
    28         self.keyString.set('')
       
    29         self.SetModifiersForPlatform() # set self.modifiers, self.modifier_label
       
    30         self.modifier_vars = []
       
    31         for modifier in self.modifiers:
       
    32             variable = StringVar(self)
       
    33             variable.set('')
       
    34             self.modifier_vars.append(variable)
       
    35         self.advanced = False
       
    36         self.CreateWidgets()
       
    37         self.LoadFinalKeyList()
       
    38         self.withdraw() #hide while setting geometry
       
    39         self.update_idletasks()
       
    40         self.geometry("+%d+%d" %
       
    41             ((parent.winfo_rootx()+((parent.winfo_width()/2)
       
    42                 -(self.winfo_reqwidth()/2)),
       
    43               parent.winfo_rooty()+((parent.winfo_height()/2)
       
    44                 -(self.winfo_reqheight()/2)) )) ) #centre dialog over parent
       
    45         self.deiconify() #geometry set, unhide
       
    46         self.wait_window()
       
    47 
       
    48     def CreateWidgets(self):
       
    49         frameMain = Frame(self,borderwidth=2,relief=SUNKEN)
       
    50         frameMain.pack(side=TOP,expand=TRUE,fill=BOTH)
       
    51         frameButtons=Frame(self)
       
    52         frameButtons.pack(side=BOTTOM,fill=X)
       
    53         self.buttonOK = Button(frameButtons,text='OK',
       
    54                 width=8,command=self.OK)
       
    55         self.buttonOK.grid(row=0,column=0,padx=5,pady=5)
       
    56         self.buttonCancel = Button(frameButtons,text='Cancel',
       
    57                 width=8,command=self.Cancel)
       
    58         self.buttonCancel.grid(row=0,column=1,padx=5,pady=5)
       
    59         self.frameKeySeqBasic = Frame(frameMain)
       
    60         self.frameKeySeqAdvanced = Frame(frameMain)
       
    61         self.frameControlsBasic = Frame(frameMain)
       
    62         self.frameHelpAdvanced = Frame(frameMain)
       
    63         self.frameKeySeqAdvanced.grid(row=0,column=0,sticky=NSEW,padx=5,pady=5)
       
    64         self.frameKeySeqBasic.grid(row=0,column=0,sticky=NSEW,padx=5,pady=5)
       
    65         self.frameKeySeqBasic.lift()
       
    66         self.frameHelpAdvanced.grid(row=1,column=0,sticky=NSEW,padx=5)
       
    67         self.frameControlsBasic.grid(row=1,column=0,sticky=NSEW,padx=5)
       
    68         self.frameControlsBasic.lift()
       
    69         self.buttonLevel = Button(frameMain,command=self.ToggleLevel,
       
    70                 text='Advanced Key Binding Entry >>')
       
    71         self.buttonLevel.grid(row=2,column=0,stick=EW,padx=5,pady=5)
       
    72         labelTitleBasic = Label(self.frameKeySeqBasic,
       
    73                 text="New keys for  '"+self.action+"' :")
       
    74         labelTitleBasic.pack(anchor=W)
       
    75         labelKeysBasic = Label(self.frameKeySeqBasic,justify=LEFT,
       
    76                 textvariable=self.keyString,relief=GROOVE,borderwidth=2)
       
    77         labelKeysBasic.pack(ipadx=5,ipady=5,fill=X)
       
    78         self.modifier_checkbuttons = {}
       
    79         column = 0
       
    80         for modifier, variable in zip(self.modifiers, self.modifier_vars):
       
    81             label = self.modifier_label.get(modifier, modifier)
       
    82             check=Checkbutton(self.frameControlsBasic,
       
    83                 command=self.BuildKeyString,
       
    84                 text=label,variable=variable,onvalue=modifier,offvalue='')
       
    85             check.grid(row=0,column=column,padx=2,sticky=W)
       
    86             self.modifier_checkbuttons[modifier] = check
       
    87             column += 1
       
    88         labelFnAdvice=Label(self.frameControlsBasic,justify=LEFT,
       
    89                             text=\
       
    90                             "Select the desired modifier keys\n"+
       
    91                             "above, and the final key from the\n"+
       
    92                             "list on the right.\n\n" +
       
    93                             "Use upper case Symbols when using\n" +
       
    94                             "the Shift modifier.  (Letters will be\n" +
       
    95                             "converted automatically.)")
       
    96         labelFnAdvice.grid(row=1,column=0,columnspan=4,padx=2,sticky=W)
       
    97         self.listKeysFinal=Listbox(self.frameControlsBasic,width=15,height=10,
       
    98                 selectmode=SINGLE)
       
    99         self.listKeysFinal.bind('<ButtonRelease-1>',self.FinalKeySelected)
       
   100         self.listKeysFinal.grid(row=0,column=4,rowspan=4,sticky=NS)
       
   101         scrollKeysFinal=Scrollbar(self.frameControlsBasic,orient=VERTICAL,
       
   102                 command=self.listKeysFinal.yview)
       
   103         self.listKeysFinal.config(yscrollcommand=scrollKeysFinal.set)
       
   104         scrollKeysFinal.grid(row=0,column=5,rowspan=4,sticky=NS)
       
   105         self.buttonClear=Button(self.frameControlsBasic,
       
   106                 text='Clear Keys',command=self.ClearKeySeq)
       
   107         self.buttonClear.grid(row=2,column=0,columnspan=4)
       
   108         labelTitleAdvanced = Label(self.frameKeySeqAdvanced,justify=LEFT,
       
   109                 text="Enter new binding(s) for  '"+self.action+"' :\n"+
       
   110                 "(These bindings will not be checked for validity!)")
       
   111         labelTitleAdvanced.pack(anchor=W)
       
   112         self.entryKeysAdvanced=Entry(self.frameKeySeqAdvanced,
       
   113                 textvariable=self.keyString)
       
   114         self.entryKeysAdvanced.pack(fill=X)
       
   115         labelHelpAdvanced=Label(self.frameHelpAdvanced,justify=LEFT,
       
   116             text="Key bindings are specified using Tkinter keysyms as\n"+
       
   117                  "in these samples: <Control-f>, <Shift-F2>, <F12>,\n"
       
   118                  "<Control-space>, <Meta-less>, <Control-Alt-Shift-X>.\n"
       
   119                  "Upper case is used when the Shift modifier is present!\n\n" +
       
   120                  "'Emacs style' multi-keystroke bindings are specified as\n" +
       
   121                  "follows: <Control-x><Control-y>, where the first key\n" +
       
   122                  "is the 'do-nothing' keybinding.\n\n" +
       
   123                  "Multiple separate bindings for one action should be\n"+
       
   124                  "separated by a space, eg., <Alt-v> <Meta-v>." )
       
   125         labelHelpAdvanced.grid(row=0,column=0,sticky=NSEW)
       
   126 
       
   127     def SetModifiersForPlatform(self):
       
   128         """Determine list of names of key modifiers for this platform.
       
   129 
       
   130         The names are used to build Tk bindings -- it doesn't matter if the
       
   131         keyboard has these keys, it matters if Tk understands them. The
       
   132         order is also important: key binding equality depends on it, so
       
   133         config-keys.def must use the same ordering.
       
   134         """
       
   135         import sys
       
   136         if sys.platform == 'darwin' and sys.argv[0].count('.app'):
       
   137             self.modifiers = ['Shift', 'Control', 'Option', 'Command']
       
   138         else:
       
   139             self.modifiers = ['Control', 'Alt', 'Shift']
       
   140         self.modifier_label = {'Control': 'Ctrl'} # short name
       
   141 
       
   142     def ToggleLevel(self):
       
   143         if  self.buttonLevel.cget('text')[:8]=='Advanced':
       
   144             self.ClearKeySeq()
       
   145             self.buttonLevel.config(text='<< Basic Key Binding Entry')
       
   146             self.frameKeySeqAdvanced.lift()
       
   147             self.frameHelpAdvanced.lift()
       
   148             self.entryKeysAdvanced.focus_set()
       
   149             self.advanced = True
       
   150         else:
       
   151             self.ClearKeySeq()
       
   152             self.buttonLevel.config(text='Advanced Key Binding Entry >>')
       
   153             self.frameKeySeqBasic.lift()
       
   154             self.frameControlsBasic.lift()
       
   155             self.advanced = False
       
   156 
       
   157     def FinalKeySelected(self,event):
       
   158         self.BuildKeyString()
       
   159 
       
   160     def BuildKeyString(self):
       
   161         keyList = modifiers = self.GetModifiers()
       
   162         finalKey = self.listKeysFinal.get(ANCHOR)
       
   163         if finalKey:
       
   164             finalKey = self.TranslateKey(finalKey, modifiers)
       
   165             keyList.append(finalKey)
       
   166         self.keyString.set('<' + string.join(keyList,'-') + '>')
       
   167 
       
   168     def GetModifiers(self):
       
   169         modList = [variable.get() for variable in self.modifier_vars]
       
   170         return filter(None, modList)
       
   171 
       
   172     def ClearKeySeq(self):
       
   173         self.listKeysFinal.select_clear(0,END)
       
   174         self.listKeysFinal.yview(MOVETO, '0.0')
       
   175         for variable in self.modifier_vars:
       
   176             variable.set('')
       
   177         self.keyString.set('')
       
   178 
       
   179     def LoadFinalKeyList(self):
       
   180         #these tuples are also available for use in validity checks
       
   181         self.functionKeys=('F1','F2','F2','F4','F5','F6','F7','F8','F9',
       
   182                 'F10','F11','F12')
       
   183         self.alphanumKeys=tuple(string.ascii_lowercase+string.digits)
       
   184         self.punctuationKeys=tuple('~!@#%^&*()_-+={}[]|;:,.<>/?')
       
   185         self.whitespaceKeys=('Tab','Space','Return')
       
   186         self.editKeys=('BackSpace','Delete','Insert')
       
   187         self.moveKeys=('Home','End','Page Up','Page Down','Left Arrow',
       
   188                 'Right Arrow','Up Arrow','Down Arrow')
       
   189         #make a tuple of most of the useful common 'final' keys
       
   190         keys=(self.alphanumKeys+self.punctuationKeys+self.functionKeys+
       
   191                 self.whitespaceKeys+self.editKeys+self.moveKeys)
       
   192         self.listKeysFinal.insert(END, *keys)
       
   193 
       
   194     def TranslateKey(self, key, modifiers):
       
   195         "Translate from keycap symbol to the Tkinter keysym"
       
   196         translateDict = {'Space':'space',
       
   197                 '~':'asciitilde','!':'exclam','@':'at','#':'numbersign',
       
   198                 '%':'percent','^':'asciicircum','&':'ampersand','*':'asterisk',
       
   199                 '(':'parenleft',')':'parenright','_':'underscore','-':'minus',
       
   200                 '+':'plus','=':'equal','{':'braceleft','}':'braceright',
       
   201                 '[':'bracketleft',']':'bracketright','|':'bar',';':'semicolon',
       
   202                 ':':'colon',',':'comma','.':'period','<':'less','>':'greater',
       
   203                 '/':'slash','?':'question','Page Up':'Prior','Page Down':'Next',
       
   204                 'Left Arrow':'Left','Right Arrow':'Right','Up Arrow':'Up',
       
   205                 'Down Arrow': 'Down', 'Tab':'Tab'}
       
   206         if key in translateDict.keys():
       
   207             key = translateDict[key]
       
   208         if 'Shift' in modifiers and key in string.ascii_lowercase:
       
   209             key = key.upper()
       
   210         key = 'Key-' + key
       
   211         return key
       
   212 
       
   213     def OK(self, event=None):
       
   214         if self.advanced or self.KeysOK():  # doesn't check advanced string yet
       
   215             self.result=self.keyString.get()
       
   216             self.destroy()
       
   217 
       
   218     def Cancel(self, event=None):
       
   219         self.result=''
       
   220         self.destroy()
       
   221 
       
   222     def KeysOK(self):
       
   223         '''Validity check on user's 'basic' keybinding selection.
       
   224 
       
   225         Doesn't check the string produced by the advanced dialog because
       
   226         'modifiers' isn't set.
       
   227 
       
   228         '''
       
   229         keys = self.keyString.get()
       
   230         keys.strip()
       
   231         finalKey = self.listKeysFinal.get(ANCHOR)
       
   232         modifiers = self.GetModifiers()
       
   233         # create a key sequence list for overlap check:
       
   234         keySequence = keys.split()
       
   235         keysOK = False
       
   236         title = 'Key Sequence Error'
       
   237         if not keys:
       
   238             tkMessageBox.showerror(title=title, parent=self,
       
   239                                    message='No keys specified.')
       
   240         elif not keys.endswith('>'):
       
   241             tkMessageBox.showerror(title=title, parent=self,
       
   242                                    message='Missing the final Key')
       
   243         elif (not modifiers
       
   244               and finalKey not in self.functionKeys + self.moveKeys):
       
   245             tkMessageBox.showerror(title=title, parent=self,
       
   246                                    message='No modifier key(s) specified.')
       
   247         elif (modifiers == ['Shift']) \
       
   248                  and (finalKey not in
       
   249                       self.functionKeys + self.moveKeys + ('Tab', 'Space')):
       
   250             msg = 'The shift modifier by itself may not be used with'\
       
   251                   ' this key symbol.'
       
   252             tkMessageBox.showerror(title=title, parent=self, message=msg)
       
   253         elif keySequence in self.currentKeySequences:
       
   254             msg = 'This key combination is already in use.'
       
   255             tkMessageBox.showerror(title=title, parent=self, message=msg)
       
   256         else:
       
   257             keysOK = True
       
   258         return keysOK
       
   259 
       
   260 if __name__ == '__main__':
       
   261     #test the dialog
       
   262     root=Tk()
       
   263     def run():
       
   264         keySeq=''
       
   265         dlg=GetKeysDialog(root,'Get Keys','find-again',[])
       
   266         print dlg.result
       
   267     Button(root,text='Dialog',command=run).pack()
       
   268     root.mainloop()