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 if not isinstance(v, basestring): 51 v = str(v) 52 options.append("-"+k) 53 options.append(v) 54 return tuple(options) 55 56 def _get(self, args): 57 options = [] 58 for k in args: 59 options.append("-"+k) 60 return tuple(options) 61 62 def _mkdict(self, args): 63 options = {} 64 for i in range(0, len(args), 2): 65 options[args[i][1:]] = args[i+1] 66 return options 67 68 def __init__(self, root=None, font=None, name=None, exists=False, **options): 69 if not root: 70 root = Tkinter._default_root 71 tk = getattr(root, 'tk', root) 72 if font: 73 # get actual settings corresponding to the given font 74 font = tk.splitlist(tk.call("font", "actual", font)) 75 else: 76 font = self._set(options) 77 if not name: 78 name = "font" + str(id(self)) 79 self.name = name 80 81 if exists: 82 self.delete_font = False 83 # confirm font exists 84 if self.name not in tk.splitlist(tk.call("font", "names")): 85 raise Tkinter._tkinter.TclError, "named font %s does not already exist" % (self.name,) 86 # if font config info supplied, apply it 87 if font: 88 tk.call("font", "configure", self.name, *font) 89 else: 90 # create new font (raises TclError if the font exists) 91 tk.call("font", "create", self.name, *font) 92 self.delete_font = True 93 self._tk = tk 94 self._split = tk.splitlist 95 self._call = tk.call 96 97 def __str__(self): 98 return self.name 99 100 def __eq__(self, other): 101 return isinstance(other, Font) and self.name == other.name 102 103 def __getitem__(self, key): 104 return self.cget(key) 105 106 def __setitem__(self, key, value): 107 self.configure(**{key: value}) 108 109 def __del__(self): 110 try: 111 if self.delete_font: 112 self._call("font", "delete", self.name) 113 except (KeyboardInterrupt, SystemExit): 114 raise 115 except Exception: 116 pass 117 118 def copy(self): 119 "Return a distinct copy of the current font" 120 return Font(self._tk, **self.actual()) 121 122 def actual(self, option=None): 123 "Return actual font attributes" 124 if option: 125 return self._call("font", "actual", self.name, "-"+option) 126 else: 127 return self._mkdict( 128 self._split(self._call("font", "actual", self.name)) 129 ) 130 131 def cget(self, option): 132 "Get font attribute" 133 return self._call("font", "config", self.name, "-"+option) 134 135 def config(self, **options): 136 "Modify font attributes" 137 if options: 138 self._call("font", "config", self.name, 139 *self._set(options)) 140 else: 141 return self._mkdict( 142 self._split(self._call("font", "config", self.name)) 143 ) 144 145 configure = config 146 147 def measure(self, text): 148 "Return text width" 149 return int(self._call("font", "measure", self.name, text)) 150 151 def metrics(self, *options): 152 """Return font metrics. 153 154 For best performance, create a dummy widget 155 using this font before calling this method.""" 156 157 if options: 158 return int( 159 self._call("font", "metrics", self.name, self._get(options)) 160 ) 161 else: 162 res = self._split(self._call("font", "metrics", self.name)) 163 options = {} 164 for i in range(0, len(res), 2): 165 options[res[i][1:]] = int(res[i+1]) 166 return options 167 168def families(root=None): 169 "Get font families (as a tuple)" 170 if not root: 171 root = Tkinter._default_root 172 return root.tk.splitlist(root.tk.call("font", "families")) 173 174def names(root=None): 175 "Get names of defined fonts (as a tuple)" 176 if not root: 177 root = Tkinter._default_root 178 return root.tk.splitlist(root.tk.call("font", "names")) 179 180# -------------------------------------------------------------------- 181# test stuff 182 183if __name__ == "__main__": 184 185 root = Tkinter.Tk() 186 187 # create a font 188 f = Font(family="times", size=30, weight=NORMAL) 189 190 print f.actual() 191 print f.actual("family") 192 print f.actual("weight") 193 194 print f.config() 195 print f.cget("family") 196 print f.cget("weight") 197 198 print names() 199 200 print f.measure("hello"), f.metrics("linespace") 201 202 print f.metrics() 203 204 f = Font(font=("Courier", 20, "bold")) 205 print f.measure("hello"), f.metrics("linespace") 206 207 w = Tkinter.Label(root, text="Hello, world", font=f) 208 w.pack() 209 210 w = Tkinter.Button(root, text="Quit!", command=root.destroy) 211 w.pack() 212 213 fb = Font(font=w["font"]).copy() 214 fb.config(weight=BOLD) 215 216 w.config(font=fb) 217 218 Tkinter.mainloop() 219