0001 """ 0002 Dialog for building Tkinter accelerator key bindings 0003 """ 0004 from Tkinter import * 0005 import tkMessageBox 0006 import string, os 0007 0008 class GetKeysDialog(Toplevel): 0009 def __init__(self,parent,title,action,currentKeySequences): 0010 """ 0011 action - string, the name of the virtual event these keys will be 0012 mapped to 0013 currentKeys - list, a list of all key sequence lists currently mapped 0014 to virtual events, for overlap checking 0015 """ 0016 Toplevel.__init__(self, parent) 0017 self.configure(borderwidth=5) 0018 self.resizable(height=FALSE,width=FALSE) 0019 self.title(title) 0020 self.transient(parent) 0021 self.grab_set() 0022 self.protocol("WM_DELETE_WINDOW", self.Cancel) 0023 self.parent = parent 0024 self.action=action 0025 self.currentKeySequences=currentKeySequences 0026 self.result='' 0027 self.keyString=StringVar(self) 0028 self.keyString.set('') 0029 self.SetModifiersForPlatform() 0030 self.modifier_vars = [] 0031 for modifier in self.modifiers: 0032 variable = StringVar(self) 0033 variable.set('') 0034 self.modifier_vars.append(variable) 0035 self.CreateWidgets() 0036 self.LoadFinalKeyList() 0037 self.withdraw() #hide while setting geometry 0038 self.update_idletasks() 0039 self.geometry("+%d+%d" % 0040 ((parent.winfo_rootx()+((parent.winfo_width()/2) 0041 -(self.winfo_reqwidth()/2)), 0042 parent.winfo_rooty()+((parent.winfo_height()/2) 0043 -(self.winfo_reqheight()/2)) )) ) #centre dialog over parent 0044 self.deiconify() #geometry set, unhide 0045 self.wait_window() 0046 0047 def CreateWidgets(self): 0048 frameMain = Frame(self,borderwidth=2,relief=SUNKEN) 0049 frameMain.pack(side=TOP,expand=TRUE,fill=BOTH) 0050 frameButtons=Frame(self) 0051 frameButtons.pack(side=BOTTOM,fill=X) 0052 self.buttonOK = Button(frameButtons,text='OK', 0053 width=8,command=self.OK) 0054 self.buttonOK.grid(row=0,column=0,padx=5,pady=5) 0055 self.buttonCancel = Button(frameButtons,text='Cancel', 0056 width=8,command=self.Cancel) 0057 self.buttonCancel.grid(row=0,column=1,padx=5,pady=5) 0058 self.frameKeySeqBasic = Frame(frameMain) 0059 self.frameKeySeqAdvanced = Frame(frameMain) 0060 self.frameControlsBasic = Frame(frameMain) 0061 self.frameHelpAdvanced = Frame(frameMain) 0062 self.frameKeySeqAdvanced.grid(row=0,column=0,sticky=NSEW,padx=5,pady=5) 0063 self.frameKeySeqBasic.grid(row=0,column=0,sticky=NSEW,padx=5,pady=5) 0064 self.frameKeySeqBasic.lift() 0065 self.frameHelpAdvanced.grid(row=1,column=0,sticky=NSEW,padx=5) 0066 self.frameControlsBasic.grid(row=1,column=0,sticky=NSEW,padx=5) 0067 self.frameControlsBasic.lift() 0068 self.buttonLevel = Button(frameMain,command=self.ToggleLevel, 0069 text='Advanced Key Binding Entry >>') 0070 self.buttonLevel.grid(row=2,column=0,stick=EW,padx=5,pady=5) 0071 labelTitleBasic = Label(self.frameKeySeqBasic, 0072 text="New keys for '"+self.action+"' :") 0073 labelTitleBasic.pack(anchor=W) 0074 labelKeysBasic = Label(self.frameKeySeqBasic,justify=LEFT, 0075 textvariable=self.keyString,relief=GROOVE,borderwidth=2) 0076 labelKeysBasic.pack(ipadx=5,ipady=5,fill=X) 0077 self.modifier_checkbuttons = {} 0078 column = 0 0079 for modifier, variable in zip(self.modifiers, self.modifier_vars): 0080 label = self.modifier_label.get(modifier, modifier) 0081 check=Checkbutton(self.frameControlsBasic, 0082 command=self.BuildKeyString, 0083 text=label,variable=variable,onvalue=modifier,offvalue='') 0084 check.grid(row=0,column=column,padx=2,sticky=W) 0085 self.modifier_checkbuttons[modifier] = check 0086 column += 1 0087 labelFnAdvice=Label(self.frameControlsBasic,justify=LEFT, 0088 text=\ 0089 "Select the desired modifier keys\n"+ 0090 "above, and the final key from the\n"+ 0091 "list on the right.\n\n" + 0092 "Use upper case Symbols when using\n" + 0093 "the Shift modifier. (Letters will be\n" + 0094 "converted automatically.)") 0095 labelFnAdvice.grid(row=1,column=0,columnspan=4,padx=2,sticky=W) 0096 self.listKeysFinal=Listbox(self.frameControlsBasic,width=15,height=10, 0097 selectmode=SINGLE) 0098 self.listKeysFinal.bind('<ButtonRelease-1>',self.FinalKeySelected) 0099 self.listKeysFinal.grid(row=0,column=4,rowspan=4,sticky=NS) 0100 scrollKeysFinal=Scrollbar(self.frameControlsBasic,orient=VERTICAL, 0101 command=self.listKeysFinal.yview) 0102 self.listKeysFinal.config(yscrollcommand=scrollKeysFinal.set) 0103 scrollKeysFinal.grid(row=0,column=5,rowspan=4,sticky=NS) 0104 self.buttonClear=Button(self.frameControlsBasic, 0105 text='Clear Keys',command=self.ClearKeySeq) 0106 self.buttonClear.grid(row=2,column=0,columnspan=4) 0107 labelTitleAdvanced = Label(self.frameKeySeqAdvanced,justify=LEFT, 0108 text="Enter new binding(s) for '"+self.action+"' :\n"+ 0109 "(These bindings will not be checked for validity!)") 0110 labelTitleAdvanced.pack(anchor=W) 0111 self.entryKeysAdvanced=Entry(self.frameKeySeqAdvanced, 0112 textvariable=self.keyString) 0113 self.entryKeysAdvanced.pack(fill=X) 0114 labelHelpAdvanced=Label(self.frameHelpAdvanced,justify=LEFT, 0115 text="Key bindings are specified using Tkinter keysyms as\n"+ 0116 "in these samples: <Control-f>, <Shift-F2>, <F12>,\n" 0117 "<Control-space>, <Meta-less>, <Control-Alt-Shift-X>.\n" 0118 "Upper case is used when the Shift modifier is present!\n\n" + 0119 "'Emacs style' multi-keystroke bindings are specified as\n" + 0120 "follows: <Control-x><Control-y>, where the first key\n" + 0121 "is the 'do-nothing' keybinding.\n\n" + 0122 "Multiple separate bindings for one action should be\n"+ 0123 "separated by a space, eg., <Alt-v> <Meta-v>." ) 0124 labelHelpAdvanced.grid(row=0,column=0,sticky=NSEW) 0125 0126 def SetModifiersForPlatform(self): 0127 """Determine list of names of key modifiers for this platform. 0128 0129 The names are used to build Tk bindings -- it doesn't matter if the 0130 keyboard has these keys, it matters if Tk understands them. The 0131 order is also important: key binding equality depends on it, so 0132 config-keys.def must use the same ordering. 0133 """ 0134 import sys 0135 if sys.platform == 'darwin' and sys.executable.count('.app'): 0136 self.modifiers = ['Shift', 'Control', 'Option', 'Command'] 0137 else: 0138 self.modifiers = ['Control', 'Alt', 'Shift'] 0139 self.modifier_label = {'Control': 'Ctrl'} 0140 0141 def ToggleLevel(self): 0142 if self.buttonLevel.cget('text')[:8]=='Advanced': 0143 self.ClearKeySeq() 0144 self.buttonLevel.config(text='<< Basic Key Binding Entry') 0145 self.frameKeySeqAdvanced.lift() 0146 self.frameHelpAdvanced.lift() 0147 self.entryKeysAdvanced.focus_set() 0148 else: 0149 self.ClearKeySeq() 0150 self.buttonLevel.config(text='Advanced Key Binding Entry >>') 0151 self.frameKeySeqBasic.lift() 0152 self.frameControlsBasic.lift() 0153 0154 def FinalKeySelected(self,event): 0155 self.BuildKeyString() 0156 0157 def BuildKeyString(self): 0158 keyList = modifiers = self.GetModifiers() 0159 finalKey = self.listKeysFinal.get(ANCHOR) 0160 if finalKey: 0161 finalKey = self.TranslateKey(finalKey, modifiers) 0162 keyList.append(finalKey) 0163 self.keyString.set('<' + string.join(keyList,'-') + '>') 0164 0165 def GetModifiers(self): 0166 modList = [variable.get() for variable in self.modifier_vars] 0167 return filter(None, modList) 0168 0169 def ClearKeySeq(self): 0170 self.listKeysFinal.select_clear(0,END) 0171 self.listKeysFinal.yview(MOVETO, '0.0') 0172 for variable in self.modifier_vars: 0173 variable.set('') 0174 self.keyString.set('') 0175 0176 def LoadFinalKeyList(self): 0177 #these tuples are also available for use in validity checks 0178 self.functionKeys=('F1','F2','F2','F4','F5','F6','F7','F8','F9', 0179 'F10','F11','F12') 0180 self.alphanumKeys=tuple(string.ascii_lowercase+string.digits) 0181 self.punctuationKeys=tuple('~!@#%^&*()_-+={}[]|;:,.<>/?') 0182 self.whitespaceKeys=('Tab','Space','Return') 0183 self.editKeys=('BackSpace','Delete','Insert') 0184 self.moveKeys=('Home','End','Page Up','Page Down','Left Arrow', 0185 'Right Arrow','Up Arrow','Down Arrow') 0186 #make a tuple of most of the useful common 'final' keys 0187 keys=(self.alphanumKeys+self.punctuationKeys+self.functionKeys+ 0188 self.whitespaceKeys+self.editKeys+self.moveKeys) 0189 self.listKeysFinal.insert(END, *keys) 0190 0191 def TranslateKey(self, key, modifiers): 0192 "Translate from keycap symbol to the Tkinter keysym" 0193 translateDict = {'Space':'space', 0194 '~':'asciitilde','!':'exclam','@':'at','#':'numbersign', 0195 '%':'percent','^':'asciicircum','&':'ampersand','*':'asterisk', 0196 '(':'parenleft',')':'parenright','_':'underscore','-':'minus', 0197 '+':'plus','=':'equal','{':'braceleft','}':'braceright', 0198 '[':'bracketleft',']':'bracketright','|':'bar',';':'semicolon', 0199 ':':'colon',',':'comma','.':'period','<':'less','>':'greater', 0200 '/':'slash','?':'question','Page Up':'Prior','Page Down':'Next', 0201 'Left Arrow':'Left','Right Arrow':'Right','Up Arrow':'Up', 0202 'Down Arrow': 'Down', 'Tab':'tab'} 0203 if key in translateDict.keys(): 0204 key = translateDict[key] 0205 if 'Shift' in modifiers and key in string.ascii_lowercase: 0206 key = key.upper() 0207 key = 'Key-' + key 0208 return key 0209 0210 def OK(self, event=None): 0211 if self.KeysOK(): 0212 self.result=self.keyString.get() 0213 self.destroy() 0214 0215 def Cancel(self, event=None): 0216 self.result='' 0217 self.destroy() 0218 0219 def KeysOK(self): 0220 "Validity check on user's keybinding selection" 0221 keys = self.keyString.get() 0222 keys.strip() 0223 finalKey = self.listKeysFinal.get(ANCHOR) 0224 modifiers = self.GetModifiers() 0225 # create a key sequence list for overlap check: 0226 keySequence = keys.split() 0227 keysOK = False 0228 title = 'Key Sequence Error' 0229 if not keys: 0230 tkMessageBox.showerror(title=title, parent=self, 0231 message='No keys specified.') 0232 elif not keys.endswith('>'): 0233 tkMessageBox.showerror(title=title, parent=self, 0234 message='Missing the final Key') 0235 elif not modifiers and finalKey not in self.functionKeys: 0236 tkMessageBox.showerror(title=title, parent=self, 0237 message='No modifier key(s) specified.') 0238 elif (modifiers == ['Shift']) \ 0239 and (finalKey not in 0240 self.functionKeys + ('Tab', 'Space')): 0241 msg = 'The shift modifier by itself may not be used with' \ 0242 ' this key symbol; only with F1-F12, Tab, or Space' 0243 tkMessageBox.showerror(title=title, parent=self, 0244 message=msg) 0245 elif keySequence in self.currentKeySequences: 0246 msg = 'This key combination is already in use.' 0247 tkMessageBox.showerror(title=title, parent=self, 0248 message=msg) 0249 else: 0250 keysOK = True 0251 return keysOK 0252 0253 if __name__ == '__main__': 0254 #test the dialog 0255 root=Tk() 0256 def run(): 0257 keySeq='' 0258 dlg=GetKeysDialog(root,'Get Keys','find-again',[]) 0259 print dlg.result 0260 Button(root,text='Dialog',command=run).pack() 0261 root.mainloop() 0262
Generated by PyXR 0.9.4