font.py revision 645058d11a0ce6616d93cce692bb1378e61efb80
1# Tkinter font wrapper 2# 3# written by Fredrik Lundh, February 1998 4# 5 6__version__ = "0.9" 7 8import itertools 9import tkinter 10 11 12# weight/slant 13NORMAL = "normal" 14ROMAN = "roman" 15BOLD = "bold" 16ITALIC = "italic" 17 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 24 25class Font: 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 counter = itertools.count(1) 48 49 def _set(self, kw): 50 options = [] 51 for k, v in kw.items(): 52 options.append("-"+k) 53 options.append(str(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, 69 **options): 70 if not root: 71 root = tkinter._default_root 72 tk = getattr(root, 'tk', root) 73 if font: 74 # get actual settings corresponding to the given font 75 font = tk.splitlist(tk.call("font", "actual", font)) 76 else: 77 font = self._set(options) 78 if not name: 79 name = "font" + str(next(self.counter)) 80 self.name = name 81 82 if exists: 83 self.delete_font = False 84 # confirm font exists 85 if self.name not in tk.splitlist(tk.call("font", "names")): 86 raise tkinter._tkinter.TclError( 87 "named font %s does not already exist" % (self.name,)) 88 # if font config info supplied, apply it 89 if font: 90 tk.call("font", "configure", self.name, *font) 91 else: 92 # create new font (raises TclError if the font exists) 93 tk.call("font", "create", self.name, *font) 94 self.delete_font = True 95 self._tk = tk 96 self._split = tk.splitlist 97 self._call = tk.call 98 99 def __str__(self): 100 return self.name 101 102 def __eq__(self, other): 103 return isinstance(other, Font) and self.name == other.name 104 105 def __getitem__(self, key): 106 return self.cget(key) 107 108 def __setitem__(self, key, value): 109 self.configure(**{key: value}) 110 111 def __del__(self): 112 try: 113 if self.delete_font: 114 self._call("font", "delete", self.name) 115 except (KeyboardInterrupt, SystemExit): 116 raise 117 except Exception: 118 pass 119 120 def copy(self): 121 "Return a distinct copy of the current font" 122 return Font(self._tk, **self.actual()) 123 124 def actual(self, option=None, displayof=None): 125 "Return actual font attributes" 126 args = () 127 if displayof: 128 args = ('-displayof', displayof) 129 if option: 130 args = args + ('-' + option, ) 131 return self._call("font", "actual", self.name, *args) 132 else: 133 return self._mkdict( 134 self._split(self._call("font", "actual", self.name, *args))) 135 136 def cget(self, option): 137 "Get font attribute" 138 return self._call("font", "config", self.name, "-"+option) 139 140 def config(self, **options): 141 "Modify font attributes" 142 if options: 143 self._call("font", "config", self.name, 144 *self._set(options)) 145 else: 146 return self._mkdict( 147 self._split(self._call("font", "config", self.name))) 148 149 configure = config 150 151 def measure(self, text, displayof=None): 152 "Return text width" 153 args = (text,) 154 if displayof: 155 args = ('-displayof', displayof, text) 156 return self._root.tk.getint(self._call("font", "measure", self.name, *args)) 157 158 def metrics(self, *options, **kw): 159 """Return font metrics. 160 161 For best performance, create a dummy widget 162 using this font before calling this method.""" 163 args = () 164 displayof = kw.pop('displayof', None) 165 if displayof: 166 args = ('-displayof', displayof) 167 if options: 168 args = args + self._get(options) 169 return self._root.tk.getint( 170 self._call("font", "metrics", self.name, *args)) 171 else: 172 res = self._split(self._call("font", "metrics", self.name, *args)) 173 options = {} 174 for i in range(0, len(res), 2): 175 options[res[i][1:]] = self._root.tk.getint(res[i+1]) 176 return options 177 178 179def families(root=None, displayof=None): 180 "Get font families (as a tuple)" 181 if not root: 182 root = tkinter._default_root 183 args = () 184 if displayof: 185 args = ('-displayof', displayof) 186 return root.tk.splitlist(root.tk.call("font", "families", *args)) 187 188 189def names(root=None): 190 "Get names of defined fonts (as a tuple)" 191 if not root: 192 root = tkinter._default_root 193 return root.tk.splitlist(root.tk.call("font", "names")) 194 195 196# -------------------------------------------------------------------- 197# test stuff 198 199if __name__ == "__main__": 200 201 root = tkinter.Tk() 202 203 # create a font 204 f = Font(family="times", size=30, weight=NORMAL) 205 206 print(f.actual()) 207 print(f.actual("family")) 208 print(f.actual("weight")) 209 210 print(f.config()) 211 print(f.cget("family")) 212 print(f.cget("weight")) 213 214 print(names()) 215 216 print(f.measure("hello"), f.metrics("linespace")) 217 218 print(f.metrics(displayof=root)) 219 220 f = Font(font=("Courier", 20, "bold")) 221 print(f.measure("hello"), f.metrics("linespace", displayof=root)) 222 223 w = tkinter.Label(root, text="Hello, world", font=f) 224 w.pack() 225 226 w = tkinter.Button(root, text="Quit!", command=root.destroy) 227 w.pack() 228 229 fb = Font(font=w["font"]).copy() 230 fb.config(weight=BOLD) 231 232 w.config(font=fb) 233 234 tkinter.mainloop() 235