├── CTkPopupKeyboard ├── __init__.py ├── numpad.py └── keyboard.py ├── LICENSE └── README.md /CTkPopupKeyboard/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | CustomTkinter PopupKeyboard 3 | Author: Akash Bora 4 | License: MIT 5 | This is an on-screen keyboard widget made for customtkinter. 6 | Homepage: https://github.com/Akascape/CTkPopupKeyboard 7 | """ 8 | 9 | __version__ = '0.2' 10 | 11 | from .keyboard import PopupKeyboard 12 | from .numpad import PopupNumpad 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Akash Bora 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CTkPopupKeyboard 2 | **On-Screen Keyboard/Numpad widget for customtkinter entries and textbox.** 3 | 4 | ## Features 5 | - Customize keyboard and numpad styles 6 | - All keyboard characters available 7 | - transparency effect 8 | - double click popup window 9 | 10 | ![screenshot](https://user-images.githubusercontent.com/89206401/236622957-5e140b42-eeaa-41de-aeb3-a6d95d3023b1.png) 11 | 12 | ## Installation 13 | ### [GitHub repo size](https://github.com/Akascape/CTkPopupKeyboard/archive/refs/heads/main.zip) 14 | 15 | **Download the source code, paste the `CTkPopupKeyboard` folder in the directory where your program is present.** 16 | 17 | ## Example Program: 18 | ```python 19 | from CTkPopupKeyboard import PopupKeyboard, PopupNumpad 20 | import customtkinter 21 | 22 | def show_popup(): 23 | # Disable/Enable popup 24 | if switch.get()==1: 25 | keyboard.disable = False 26 | numpad.disable = False 27 | else: 28 | keyboard.disable = True 29 | numpad.disable = True 30 | 31 | root = customtkinter.CTk() 32 | 33 | text_box = customtkinter.CTkTextbox(root) 34 | text_box.pack(fill="both", padx=10, pady=10) 35 | 36 | # attach popup keyboard to text_box 37 | keyboard = PopupKeyboard(text_box) 38 | 39 | entry = customtkinter.CTkEntry(root, placeholder_text="Write Something...") 40 | entry.pack(fill="both", padx=10, pady=10) 41 | 42 | # attach popup keyboard to entry 43 | numpad = PopupNumpad(entry) 44 | 45 | switch = customtkinter.CTkSwitch(root, text="On-Screen Keyboard", command=show_popup) 46 | switch.pack(pady=10) 47 | switch.toggle() 48 | 49 | root.mainloop() 50 | ``` 51 | 52 | ## Arguments for PopupKeyboard/PopupNumpad 53 | | Parameter | Description | 54 | |-----------| ------------| 55 | | **attach** | parent widget to which the keyboard will be attached | 56 | | x | **optional**, change the horizontal offset of the widget manually | 57 | | y | **optional**, change the vertical offset of the widget manually | 58 | | keywidth | change the default width of the keys | 59 | | keyheight | change the default height of the keys | 60 | | **key_color** | change the fg_color of the buttons | 61 | | text_color | change the text_color of the buttons | 62 | | hover_color | change the hover_color of the buttons | 63 | | **alpha** | change the transparency of the whole keyboard widget (range: 0-1) | 64 | | **fg_color** | change the background frame color of the widget | 65 | | corner | adjust roundness of the frame corners | 66 | | relief | change the button style for keyboard, **only for PopupKeyboard:** (flat, raised, sunken, groove, ridge) | 67 | | point | define if you want the point in numpad, **only for PopupNumpad:** boolean | 68 | | _*Other Button Parameters_ | _All other parameters for ctkbutton/tkbutton can be passed in popupkeyboard_ | 69 | -------------------------------------------------------------------------------- /CTkPopupKeyboard/numpad.py: -------------------------------------------------------------------------------- 1 | ''' 2 | On-screen Popup Numpad for customtkinter 3 | Author: Akash Bora 4 | ''' 5 | 6 | from tkinter import * 7 | from customtkinter import * 8 | import sys 9 | 10 | class PopupNumpad(CTkToplevel): 11 | 12 | def __init__(self, attach, x=None, y=None, keycolor=None, 13 | fg_color=None, keyheight: int = 50, keywidth: int = 50, 14 | alpha: float = 0.85, corner=20, point=True, **kwargs): 15 | 16 | super().__init__(takefocus=1) 17 | 18 | self.focus() 19 | self.corner = corner 20 | self.disable = True 21 | 22 | if sys.platform.startswith("win"): 23 | self.overrideredirect(True) 24 | self.transparent_color = self._apply_appearance_mode(self._fg_color) 25 | self.attributes("-transparentcolor", self.transparent_color) 26 | elif sys.platform.startswith("darwin"): 27 | self.overrideredirect(True) 28 | self.bind('', lambda e: self.withdraw() if not self.disable else None, add="+") 29 | self.transparent_color = 'systemTransparent' 30 | self.attributes("-transparent", True) 31 | else: 32 | self.attributes("-type", "splash") 33 | self.transparent_color = '#000001' 34 | self.corner = 0 35 | self.withdraw() 36 | 37 | self.disable = False 38 | self.fg_color = ThemeManager.theme["CTkFrame"]["fg_color"] if fg_color is None else fg_color 39 | self.frame = CTkFrame(self, bg_color=self.transparent_color, fg_color=self.fg_color, corner_radius=self.corner, border_width=2) 40 | self.frame.pack(expand=True, fill="both") 41 | self.attach = attach 42 | self.keycolor = ThemeManager.theme["CTkFrame"]["fg_color"] if keycolor is None else keycolor 43 | self.keywidth = keywidth 44 | self.keyheight = keyheight 45 | self.point = point 46 | 47 | self.resizable(width=False, height=False) 48 | self.transient(self.master) 49 | 50 | self.frame_color = ThemeManager.theme["CTkFrame"]["fg_color"] 51 | self.row1 = CTkFrame(self.frame, fg_color=self.frame_color) 52 | self.row2 = CTkFrame(self.frame, fg_color=self.frame_color) 53 | self.row3 = CTkFrame(self.frame, fg_color=self.frame_color) 54 | self.row4 = CTkFrame(self.frame, fg_color=self.frame_color) 55 | 56 | self.row1.grid(row=1, column=0, pady=(10,0)) 57 | self.row2.grid(row=2, column=0, padx=10) 58 | self.row3.grid(row=3, column=0, padx=10) 59 | self.row4.grid(row=4, column=0, pady=(0,10)) 60 | 61 | self._init_keys(**kwargs) 62 | 63 | # hide/show PopupNumpad 64 | self.attach.bind('', lambda e: self.withdraw() if not self.disable else None, add="+") 65 | self.attach.bind('', lambda e: self._iconify(), add="+") 66 | self.bind('', lambda e: self.withdraw() if not self.disable else None, add="+") 67 | 68 | self.update_idletasks() 69 | self.x = x 70 | self.y = y 71 | self._iconify() 72 | self.attributes('-alpha', alpha) 73 | 74 | def _init_keys(self, **kwargs): 75 | self.keys = { 76 | 'row1' : ['7','8','9'], 77 | 'row2' : ['4','5','6'], 78 | 'row3' : ['1','2','3'], 79 | 'row4' : ['.','0','◀'] 80 | } 81 | 82 | for row in self.keys.keys(): 83 | if row == 'row1': 84 | i = 1 85 | for k in self.keys[row]: 86 | CTkButton(self.row1, 87 | text=k, 88 | width=self.keywidth, 89 | height=self.keyheight, 90 | fg_color=self.keycolor, 91 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 92 | i += 1 93 | elif row == 'row2': 94 | i = 2 95 | for k in self.keys[row]: 96 | CTkButton(self.row2, 97 | text=k, 98 | width=self.keywidth, 99 | height=self.keyheight, 100 | fg_color=self.keycolor, 101 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 102 | i += 1 103 | i = 2 104 | elif row == 'row3': 105 | i = 2 106 | for k in self.keys[row]: 107 | CTkButton(self.row3, 108 | text=k, 109 | width=self.keywidth, 110 | height=self.keyheight, 111 | fg_color=self.keycolor, 112 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 113 | i += 1 114 | 115 | elif row == 'row4': 116 | i = 2 117 | for k in self.keys[row]: 118 | CTkButton(self.row4, 119 | text=k, 120 | width=self.keywidth, 121 | height=self.keyheight, 122 | fg_color=self.keycolor, 123 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 124 | i += 1 125 | 126 | self.up = False 127 | self.hide = False 128 | 129 | def destroy_popup(self): 130 | self.destroy() 131 | self.disable = True 132 | 133 | def _iconify(self): 134 | if self.disable: return 135 | if self.hide: 136 | self.deiconify() 137 | self.focus() 138 | self.hide = False 139 | self.x_pos = self.attach.winfo_rootx() if self.x is None else self.x 140 | self.y_pos = self.attach.winfo_rooty() + self.attach.winfo_reqheight() + 5 if self.y is None else self.y 141 | self.geometry('{}x{}+{}+{}'.format(self.frame.winfo_reqwidth(), 142 | self.frame.winfo_reqheight(), 143 | self.x_pos,self.y_pos)) 144 | else: 145 | self.withdraw() 146 | self.hide = True 147 | 148 | def _attach_key_press(self, k): 149 | if k == '◀': 150 | try: 151 | text = self.attach.get(0.0, END) 152 | self.attach.delete(0.0, END) 153 | self.attach.insert(0.0, text[:-2]) 154 | except TypeError: 155 | text = self.attach.get() 156 | self.attach.delete(0, END) 157 | self.attach.insert(0, text[:-1]) 158 | return 159 | if k == ".": 160 | if self.point: 161 | self.attach.insert(INSERT, k) 162 | else: 163 | self.attach.insert(INSERT, k) 164 | 165 | -------------------------------------------------------------------------------- /CTkPopupKeyboard/keyboard.py: -------------------------------------------------------------------------------- 1 | ''' 2 | On-screen Popup Keyboard for customtkinter 3 | Author: Akash Bora 4 | ''' 5 | 6 | from tkinter import * 7 | from customtkinter import * 8 | import sys 9 | 10 | class PopupKeyboard(CTkToplevel): 11 | 12 | def __init__(self, attach, x=None, y=None, key_color=None, 13 | text_color=None, hover_color=None, fg_color=None, 14 | keywidth: int = 5, keyheight: int = 2, command=None, 15 | alpha: float = 0.85, corner=20, **kwargs): 16 | 17 | super().__init__(takefocus=0) 18 | 19 | self.focus() 20 | self.corner = corner 21 | self.disable = True 22 | 23 | if sys.platform.startswith("win"): 24 | self.overrideredirect(True) 25 | self.transparent_color = self._apply_appearance_mode(self._fg_color) 26 | self.attributes("-transparentcolor", self.transparent_color) 27 | elif sys.platform.startswith("darwin"): 28 | self.overrideredirect(True) 29 | self.bind('', lambda e: self.withdraw() if not self.disable else None, add="+") 30 | self.transparent_color = 'systemTransparent' 31 | self.attributes("-transparent", True) 32 | if not text_color: 33 | text_color = "black" 34 | else: 35 | self.attributes("-type", "splash") 36 | self.transparent_color = '#000001' 37 | self.corner = 0 38 | self.withdraw() 39 | 40 | self.disable = False 41 | self.fg_color = ThemeManager.theme["CTkFrame"]["fg_color"] if fg_color is None else fg_color 42 | self.frame = CTkFrame(self, bg_color=self.transparent_color, fg_color=self.fg_color, corner_radius=self.corner, border_width=2) 43 | self.frame.pack(expand=True, fill="both") 44 | 45 | self.attach = attach 46 | self.keywidth = keywidth 47 | self.keyheight = keyheight 48 | self.keycolor = self._apply_appearance_mode(ThemeManager.theme["CTkFrame"]["top_fg_color"]) if key_color is None else key_color 49 | self.textcolor = self._apply_appearance_mode(ThemeManager.theme["CTkLabel"]["text_color"]) if text_color is None else text_color 50 | self.hovercolor = self._apply_appearance_mode(ThemeManager.theme["CTkButton"]["hover_color"]) if hover_color is None else hover_color 51 | self.command = command 52 | self.resizable(width=False, height=False) 53 | self.transient(self.master) 54 | 55 | self.row1_1 = CTkFrame(self.frame) 56 | self.row2_1 = CTkFrame(self.frame) 57 | self.row3_1 = CTkFrame(self.frame) 58 | self.row4_1 = CTkFrame(self.frame) 59 | self.row5_1 = CTkFrame(self.frame) 60 | 61 | self.row1_2 = CTkFrame(self.frame) 62 | self.row2_2 = CTkFrame(self.frame) 63 | self.row3_2 = CTkFrame(self.frame) 64 | self.row4_2 = CTkFrame(self.frame) 65 | self.row5_2 = CTkFrame(self.frame) 66 | 67 | self.row1_1.grid(row=1, column=0, pady=(5,0)) 68 | self.row2_1.grid(row=2, column=0, padx=5) 69 | self.row3_1.grid(row=3, column=0, padx=5) 70 | self.row4_1.grid(row=4, column=0) 71 | self.row5_1.grid(row=5, column=0, pady=(0,5)) 72 | 73 | self._init_keys(**kwargs) 74 | 75 | # hide/show PopupKeyboard 76 | self.attach.bind('', lambda e: self.withdraw() if not self.disable else None, add="+") 77 | self.attach.bind('', lambda e: self._iconify(), add="+") 78 | self.bind('', lambda e: self.withdraw() if not self.disable else None, add="+") 79 | 80 | self.update_idletasks() 81 | self.x = x 82 | self.y = y 83 | self._iconify() 84 | self.attributes('-alpha', alpha) 85 | 86 | def _init_keys(self, **kwargs): 87 | self.keys = { 88 | 'row1' : ['1','2','3','4','5','6','7','8','9','0','◀'], 89 | 'row2' : ['Tab','q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p','\ '], 90 | 'row3' : ['⋀','a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',';','Enter'], 91 | 'row4' : ['z', 'x', 'c', 'v', 'b', 'n', 'm',',','.','?'], 92 | 'row5' : ['[',']','+',' space ','-','~',"'"] 93 | } 94 | 95 | self.keys2 = { 96 | 'row1' : ['!','@','#','$','%','^','&','*','(',')','◀'], 97 | 'row2' : ['Tab','Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P','|'], 98 | 'row3' : ['⋁','A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',':','Enter'], 99 | 'row4' : ['Z', 'X', 'C', 'V', 'B', 'N', 'M','<','>','/'], 100 | 'row5' : ['{','}','=',' space ','_','`','"'] 101 | } 102 | 103 | for row in self.keys.keys(): 104 | if row == 'row1': 105 | i = 1 106 | for k in self.keys[row]: 107 | Button(self.row1_1, 108 | text=k, 109 | width=self.keywidth, 110 | height=self.keyheight, 111 | bg=self.keycolor, 112 | fg=self.textcolor, 113 | highlightthickness=0, 114 | borderwidth=1, 115 | activebackground=self.hovercolor, 116 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 117 | i += 1 118 | i = 1 119 | for k in self.keys2[row]: 120 | Button(self.row1_2, 121 | text=k, 122 | width=self.keywidth, 123 | height=self.keyheight, 124 | bg=self.keycolor, 125 | fg=self.textcolor, 126 | highlightthickness=0, 127 | borderwidth=1, 128 | activebackground=self.hovercolor, 129 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 130 | i += 1 131 | elif row == 'row2': 132 | i = 2 133 | for k in self.keys[row]: 134 | Button(self.row2_1, 135 | text=k, 136 | width=self.keywidth, 137 | height=self.keyheight, 138 | bg=self.keycolor, 139 | fg=self.textcolor, 140 | highlightthickness=0, 141 | borderwidth=1, 142 | activebackground=self.hovercolor, 143 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 144 | i += 1 145 | i = 2 146 | for k in self.keys2[row]: 147 | Button(self.row2_2, 148 | text=k, 149 | width=self.keywidth, 150 | height=self.keyheight, 151 | bg=self.keycolor, 152 | fg=self.textcolor, 153 | highlightthickness=0, 154 | borderwidth=1, 155 | activebackground=self.hovercolor, 156 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 157 | i += 1 158 | elif row == 'row3': 159 | i = 2 160 | for k in self.keys[row]: 161 | Button(self.row3_1, 162 | text=k, 163 | width=self.keywidth, 164 | height=self.keyheight, 165 | bg=self.keycolor, 166 | fg=self.textcolor, 167 | highlightthickness=0, 168 | borderwidth=1, 169 | activebackground=self.hovercolor, 170 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 171 | i += 1 172 | i = 2 173 | for k in self.keys2[row]: 174 | Button(self.row3_2, 175 | text=k, 176 | width=self.keywidth, 177 | height=self.keyheight, 178 | bg=self.keycolor, 179 | fg=self.textcolor, 180 | highlightthickness=0, 181 | borderwidth=1, 182 | activebackground=self.hovercolor, 183 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 184 | i += 1 185 | elif row == 'row4': 186 | i = 2 187 | for k in self.keys[row]: 188 | Button(self.row4_1, 189 | text=k, 190 | width=self.keywidth, 191 | height=self.keyheight, 192 | bg=self.keycolor, 193 | fg=self.textcolor, 194 | highlightthickness=0, 195 | borderwidth=1, 196 | activebackground=self.hovercolor, 197 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 198 | i += 1 199 | i = 2 200 | for k in self.keys2[row]: 201 | Button(self.row4_2, 202 | text=k, 203 | width=self.keywidth, 204 | height=self.keyheight, 205 | bg=self.keycolor, 206 | fg=self.textcolor, 207 | highlightthickness=0, 208 | borderwidth=1, 209 | activebackground=self.hovercolor, 210 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 211 | i += 1 212 | else: 213 | i = 3 214 | for k in self.keys[row]: 215 | if k == ' space ': 216 | Button(self.row5_1, 217 | text=k, 218 | width=self.keywidth * 3, 219 | height=self.keyheight, 220 | bg=self.keycolor, 221 | fg=self.textcolor, 222 | highlightthickness=0, 223 | borderwidth=1, 224 | activebackground=self.hovercolor, 225 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 226 | else: 227 | Button(self.row5_1, 228 | text=k, 229 | width=self.keywidth, 230 | height=self.keyheight, 231 | bg=self.keycolor, 232 | fg=self.textcolor, 233 | highlightthickness=0, 234 | borderwidth=1, 235 | activebackground=self.hovercolor, 236 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 237 | i += 1 238 | i = 3 239 | for k in self.keys2[row]: 240 | if k == ' space ': 241 | Button(self.row5_2, 242 | text=k, 243 | width=self.keywidth * 3, 244 | height=self.keyheight, 245 | bg=self.keycolor, 246 | fg=self.textcolor, 247 | highlightthickness=0, 248 | borderwidth=1, 249 | activebackground=self.hovercolor, 250 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 251 | else: 252 | Button(self.row5_2, 253 | text=k, 254 | width=self.keywidth, 255 | height=self.keyheight, 256 | bg=self.keycolor, 257 | fg=self.textcolor, 258 | highlightthickness=0, 259 | borderwidth=1, 260 | activebackground=self.hovercolor, 261 | command=lambda k=k: self._attach_key_press(k), **kwargs).grid(row=0,column=i) 262 | i += 1 263 | 264 | self.up = False 265 | self.hide = False 266 | 267 | def destroy_popup(self): 268 | self.destroy() 269 | self.disable = True 270 | 271 | def _iconify(self): 272 | if self.disable: return 273 | if self.hide: 274 | self.deiconify() 275 | self.focus() 276 | self.hide = False 277 | self.x_pos = self.attach.winfo_rootx() if self.x is None else self.x 278 | self.y_pos = self.attach.winfo_rooty() + self.attach.winfo_reqheight() + 5 if self.y is None else self.y 279 | self.geometry('{}x{}+{}+{}'.format(self.frame.winfo_reqwidth(), 280 | self.frame.winfo_reqheight(), 281 | self.x_pos,self.y_pos)) 282 | else: 283 | self.withdraw() 284 | self.hide = True 285 | 286 | def _attach_key_press(self, k): 287 | if k == 'Enter': 288 | if self.command: self.command() 289 | self.withdraw() 290 | 291 | elif k == '⋁' or k =='⋀': 292 | if self.up: 293 | self.up = False 294 | self.row1_2.grid_forget() 295 | self.row2_2.grid_forget() 296 | self.row3_2.grid_forget() 297 | self.row4_2.grid_forget() 298 | self.row5_2.grid_forget() 299 | 300 | self.row1_1.grid(row=1, column=0, pady=(5,0)) 301 | self.row2_1.grid(row=2, column=0, padx=5) 302 | self.row3_1.grid(row=3, column=0, padx=5) 303 | self.row4_1.grid(row=4, column=0) 304 | self.row5_1.grid(row=5, column=0, pady=(0,5)) 305 | else: 306 | self.up = True 307 | self.row1_1.grid_forget() 308 | self.row2_1.grid_forget() 309 | self.row3_1.grid_forget() 310 | self.row4_1.grid_forget() 311 | self.row5_1.grid_forget() 312 | 313 | self.row1_2.grid(row=1, column=0, pady=(5,0)) 314 | self.row2_2.grid(row=2, column=0, padx=5) 315 | self.row3_2.grid(row=3, column=0, padx=5) 316 | self.row4_2.grid(row=4, column=0) 317 | self.row5_2.grid(row=5, column=0, pady=(0,5)) 318 | self.focus_set() 319 | self.update() 320 | elif k == ' space ': 321 | self.attach.insert(END, ' ') 322 | elif k == '◀': 323 | try: 324 | text = self.attach.get(0.0, END) 325 | self.attach.delete(0.0, END) 326 | self.attach.insert(0.0, text[:-2]) 327 | except TypeError: 328 | text = self.attach.get() 329 | self.attach.delete(0, END) 330 | self.attach.insert(0, text[:-1]) 331 | 332 | elif k == 'Tab': 333 | self.attach.insert(END, ' ') 334 | else: 335 | self.attach.insert(INSERT, k) 336 | 337 | --------------------------------------------------------------------------------