1# Tkinter font wrapper 2# 3# written by Fredrik Lundh, February 1998 4# 5# FIXME: should add 'displayof' option where relevant (actual, families, 6# measure, and metrics) 7# 8 9__version__ = "0.9" 10 11import Tkinter 12 13# weight/slant 14NORMAL = "normal" 15ROMAN = "roman" 16BOLD = "bold" 17ITALIC = "italic" 18 19def nametofont(name): 20 """Given the name of a tk named font, returns a Font representation. 21 """ 22 return Font(name=name, exists=True) 23 24class Font: 25 26 """Represents a named font. 27 28 Constructor options are: 29 30 font -- font specifier (name, system font, or (family, size, style)-tuple) 31 name -- name to use for this font configuration (defaults to a unique name) 32 exists -- does a named font by this name already exist? 33 Creates a new named font if False, points to the existing font if True. 34 Raises _Tkinter.TclError if the assertion is false. 35 36 the following are ignored if font is specified: 37 38 family -- font 'family', e.g. Courier, Times, Helvetica 39 size -- font size in points 40 weight -- font thickness: NORMAL, BOLD 41 slant -- font slant: ROMAN, ITALIC 42 underline -- font underlining: false (0), true (1) 43 overstrike -- font strikeout: false (0), true (1) 44 45 """ 46 47 def _set(self, kw): 48 options = [] 49 for k, v in kw.items(): 50 options.append("-"+k) 51 options.append(str(v)) 52 return tuple(options) 53 54 def _get(self, args): 55 options = [] 56 for k in args: 57 options.append("-"+k) 58 return tuple(options) 59 60 def _mkdict(self, args): 61 options = {} 62 for i in range(0, len(args), 2): 63 options[args[i][1:]] = args[i+1] 64 return options 65 66 def __init__(self, root=None, font=None, name=None, exists=False, **options): 67 if not root: 68 root = Tkinter._default_root 69 tk = getattr(root, 'tk', root) 70 if font: 71 # get actual settings corresponding to the given font 72 font = tk.splitlist(tk.call("font", "actual", font)) 73 else: 74 font = self._set(options) 75 if not name: 76 name = "font" + str(id(self)) 77 self.name = name 78 79 if exists: 80 self.delete_font = False 81 # confirm font exists 82 if self.name not in tk.splitlist(tk.call("font", "names")): 83 raise Tkinter._tkinter.TclError, "named font %s does not already exist" % (self.name,) 84 # if font config info supplied, apply it 85 if font: 86 tk.call("font", "configure", self.name, *font) 87 else: 88 # create new font (raises TclError if the font exists) 89 tk.call("font", "create", self.name, *font) 90 self.delete_font = True 91 self._tk = tk 92 self._split = tk.splitlist 93 self._call = tk.call 94 95 def __str__(self): 96 return self.name 97 98 def __eq__(self, other): 99 return isinstance(other, Font) and self.name == other.name 100 101 def __getitem__(self, key): 102 return self.cget(key) 103 104 def __setitem__(self, key, value): 105 self.configure(**{key: value}) 106 107 def __del__(self): 108 try: 109 if self.delete_font: 110 self._call("font", "delete", self.name) 111 except (KeyboardInterrupt, SystemExit): 112 raise 113 except Exception: 114 pass 115 116 def copy(self): 117 "Return a distinct copy of the current font" 118 return Font(self._tk, **self.actual()) 119 120 def actual(self, option=None): 121 "Return actual font attributes" 122 if option: 123 return self._call("font", "actual", self.name, "-"+option) 124 else: 125 return self._mkdict( 126 self._split(self._call("font", "actual", self.name)) 127 ) 128 129 def cget(self, option): 130 "Get font attribute" 131 return self._call("font", "config", self.name, "-"+option) 132 133 def config(self, **options): 134 "Modify font attributes" 135 if options: 136 self._call("font", "config", self.name, 137 *self._set(options)) 138 else: 139 return self._mkdict( 140 self._split(self._call("font", "config", self.name)) 141 ) 142 143 configure = config 144 145 def measure(self, text): 146 "Return text width" 147 return int(self._call("font", "measure", self.name, text)) 148 149 def metrics(self, *options): 150 """Return font metrics. 151 152 For best performance, create a dummy widget 153 using this font before calling this method.""" 154 155 if options: 156 return int( 157 self._call("font", "metrics", self.name, self._get(options)) 158 ) 159 else: 160 res = self._split(self._call("font", "metrics", self.name)) 161 options = {} 162 for i in range(0, len(res), 2): 163 options[res[i][1:]] = int(res[i+1]) 164 return options 165 166def families(root=None): 167 "Get font families (as a tuple)" 168 if not root: 169 root = Tkinter._default_root 170 return root.tk.splitlist(root.tk.call("font", "families")) 171 172def names(root=None): 173 "Get names of defined fonts (as a tuple)" 174 if not root: 175 root = Tkinter._default_root 176 return root.tk.splitlist(root.tk.call("font", "names")) 177 178# -------------------------------------------------------------------- 179# test stuff 180 181if __name__ == "__main__": 182 183 root = Tkinter.Tk() 184 185 # create a font 186 f = Font(family="times", size=30, weight=NORMAL) 187 188 print f.actual() 189 print f.actual("family") 190 print f.actual("weight") 191 192 print f.config() 193 print f.cget("family") 194 print f.cget("weight") 195 196 print names() 197 198 print f.measure("hello"), f.metrics("linespace") 199 200 print f.metrics() 201 202 f = Font(font=("Courier", 20, "bold")) 203 print f.measure("hello"), f.metrics("linespace") 204 205 w = Tkinter.Label(root, text="Hello, world", font=f) 206 w.pack() 207 208 w = Tkinter.Button(root, text="Quit!", command=root.destroy) 209 w.pack() 210 211 fb = Font(font=w["font"]).copy() 212 fb.config(weight=BOLD) 213 214 w.config(font=fb) 215 216 Tkinter.mainloop() 217