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