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 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, displayof=None): 123 "Return actual font attributes" 124 args = () 125 if displayof: 126 args = ('-displayof', displayof) 127 if option: 128 args = args + ('-' + option, ) 129 return self._call("font", "actual", self.name, *args) 130 else: 131 return self._mkdict( 132 self._split(self._call("font", "actual", self.name, *args))) 133 134 def cget(self, option): 135 "Get font attribute" 136 return self._call("font", "config", self.name, "-"+option) 137 138 def config(self, **options): 139 "Modify font attributes" 140 if options: 141 self._call("font", "config", self.name, 142 *self._set(options)) 143 else: 144 return self._mkdict( 145 self._split(self._call("font", "config", self.name))) 146 147 configure = config 148 149 def measure(self, text, displayof=None): 150 "Return text width" 151 args = (text,) 152 if displayof: 153 args = ('-displayof', displayof, text) 154 return self._tk.getint(self._call("font", "measure", self.name, *args)) 155 156 def metrics(self, *options, **kw): 157 """Return font metrics. 158 159 For best performance, create a dummy widget 160 using this font before calling this method.""" 161 args = () 162 displayof = kw.pop('displayof', None) 163 if displayof: 164 args = ('-displayof', displayof) 165 if options: 166 args = args + self._get(options) 167 return self._tk.getint( 168 self._call("font", "metrics", self.name, *args)) 169 else: 170 res = self._split(self._call("font", "metrics", self.name, *args)) 171 options = {} 172 for i in range(0, len(res), 2): 173 options[res[i][1:]] = self._tk.getint(res[i+1]) 174 return options 175 176 177def families(root=None, displayof=None): 178 "Get font families (as a tuple)" 179 if not root: 180 root = tkinter._default_root 181 args = () 182 if displayof: 183 args = ('-displayof', displayof) 184 return root.tk.splitlist(root.tk.call("font", "families", *args)) 185 186 187def names(root=None): 188 "Get names of defined fonts (as a tuple)" 189 if not root: 190 root = tkinter._default_root 191 return root.tk.splitlist(root.tk.call("font", "names")) 192 193 194# -------------------------------------------------------------------- 195# test stuff 196 197if __name__ == "__main__": 198 199 root = tkinter.Tk() 200 201 # create a font 202 f = Font(family="times", size=30, weight=NORMAL) 203 204 print(f.actual()) 205 print(f.actual("family")) 206 print(f.actual("weight")) 207 208 print(f.config()) 209 print(f.cget("family")) 210 print(f.cget("weight")) 211 212 print(names()) 213 214 print(f.measure("hello"), f.metrics("linespace")) 215 216 print(f.metrics(displayof=root)) 217 218 f = Font(font=("Courier", 20, "bold")) 219 print(f.measure("hello"), f.metrics("linespace", displayof=root)) 220 221 w = tkinter.Label(root, text="Hello, world", font=f) 222 w.pack() 223 224 w = tkinter.Button(root, text="Quit!", command=root.destroy) 225 w.pack() 226 227 fb = Font(font=w["font"]).copy() 228 fb.config(weight=BOLD) 229 230 w.config(font=fb) 231 232 tkinter.mainloop() 233