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