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, root=None): 21 """Given the name of a tk named font, returns a Font representation. 22 """ 23 return Font(name=name, exists=True, root=root) 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 root is None: 72 root = tkinter._get_default_root('use font') 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 __repr__(self): 104 return f"<{self.__class__.__module__}.{self.__class__.__qualname__}" \ 105 f" object {self.name!r}>" 106 107 def __eq__(self, other): 108 if not isinstance(other, Font): 109 return NotImplemented 110 return self.name == other.name and self._tk == other._tk 111 112 def __getitem__(self, key): 113 return self.cget(key) 114 115 def __setitem__(self, key, value): 116 self.configure(**{key: value}) 117 118 def __del__(self): 119 try: 120 if self.delete_font: 121 self._call("font", "delete", self.name) 122 except Exception: 123 pass 124 125 def copy(self): 126 "Return a distinct copy of the current font" 127 return Font(self._tk, **self.actual()) 128 129 def actual(self, option=None, displayof=None): 130 "Return actual font attributes" 131 args = () 132 if displayof: 133 args = ('-displayof', displayof) 134 if option: 135 args = args + ('-' + option, ) 136 return self._call("font", "actual", self.name, *args) 137 else: 138 return self._mkdict( 139 self._split(self._call("font", "actual", self.name, *args))) 140 141 def cget(self, option): 142 "Get font attribute" 143 return self._call("font", "config", self.name, "-"+option) 144 145 def config(self, **options): 146 "Modify font attributes" 147 if options: 148 self._call("font", "config", self.name, 149 *self._set(options)) 150 else: 151 return self._mkdict( 152 self._split(self._call("font", "config", self.name))) 153 154 configure = config 155 156 def measure(self, text, displayof=None): 157 "Return text width" 158 args = (text,) 159 if displayof: 160 args = ('-displayof', displayof, text) 161 return self._tk.getint(self._call("font", "measure", self.name, *args)) 162 163 def metrics(self, *options, **kw): 164 """Return font metrics. 165 166 For best performance, create a dummy widget 167 using this font before calling this method.""" 168 args = () 169 displayof = kw.pop('displayof', None) 170 if displayof: 171 args = ('-displayof', displayof) 172 if options: 173 args = args + self._get(options) 174 return self._tk.getint( 175 self._call("font", "metrics", self.name, *args)) 176 else: 177 res = self._split(self._call("font", "metrics", self.name, *args)) 178 options = {} 179 for i in range(0, len(res), 2): 180 options[res[i][1:]] = self._tk.getint(res[i+1]) 181 return options 182 183 184def families(root=None, displayof=None): 185 "Get font families (as a tuple)" 186 if root is None: 187 root = tkinter._get_default_root('use font.families()') 188 args = () 189 if displayof: 190 args = ('-displayof', displayof) 191 return root.tk.splitlist(root.tk.call("font", "families", *args)) 192 193 194def names(root=None): 195 "Get names of defined fonts (as a tuple)" 196 if root is None: 197 root = tkinter._get_default_root('use font.names()') 198 return root.tk.splitlist(root.tk.call("font", "names")) 199 200 201# -------------------------------------------------------------------- 202# test stuff 203 204if __name__ == "__main__": 205 206 root = tkinter.Tk() 207 208 # create a font 209 f = Font(family="times", size=30, weight=NORMAL) 210 211 print(f.actual()) 212 print(f.actual("family")) 213 print(f.actual("weight")) 214 215 print(f.config()) 216 print(f.cget("family")) 217 print(f.cget("weight")) 218 219 print(names()) 220 221 print(f.measure("hello"), f.metrics("linespace")) 222 223 print(f.metrics(displayof=root)) 224 225 f = Font(font=("Courier", 20, "bold")) 226 print(f.measure("hello"), f.metrics("linespace", displayof=root)) 227 228 w = tkinter.Label(root, text="Hello, world", font=f) 229 w.pack() 230 231 w = tkinter.Button(root, text="Quit!", command=root.destroy) 232 w.pack() 233 234 fb = Font(font=w["font"]).copy() 235 fb.config(weight=BOLD) 236 237 w.config(font=fb) 238 239 tkinter.mainloop() 240