blob: b245623e30eed1d83f0d875111b13c18744556fa [file] [log] [blame]
Georg Brandl33cece02008-05-20 06:58:21 +00001# 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
Georg Brandl6634bf22008-05-20 07:13:37 +000011import Tkinter
Georg Brandl33cece02008-05-20 06:58:21 +000012
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.
Georg Brandl6634bf22008-05-20 07:13:37 +000034 Raises _Tkinter.TclError if the assertion is false.
Georg Brandl33cece02008-05-20 06:58:21 +000035
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():
Serhiy Storchaka96f50202017-05-26 08:15:51 +030050 if not isinstance(v, basestring):
51 v = str(v)
Georg Brandl33cece02008-05-20 06:58:21 +000052 options.append("-"+k)
Serhiy Storchaka96f50202017-05-26 08:15:51 +030053 options.append(v)
Georg Brandl33cece02008-05-20 06:58:21 +000054 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, **options):
69 if not root:
Georg Brandl6634bf22008-05-20 07:13:37 +000070 root = Tkinter._default_root
Serhiy Storchaka44ae5132014-08-17 15:31:41 +030071 tk = getattr(root, 'tk', root)
Georg Brandl33cece02008-05-20 06:58:21 +000072 if font:
73 # get actual settings corresponding to the given font
Serhiy Storchaka44ae5132014-08-17 15:31:41 +030074 font = tk.splitlist(tk.call("font", "actual", font))
Georg Brandl33cece02008-05-20 06:58:21 +000075 else:
76 font = self._set(options)
77 if not name:
78 name = "font" + str(id(self))
79 self.name = name
80
81 if exists:
82 self.delete_font = False
83 # confirm font exists
Serhiy Storchaka44ae5132014-08-17 15:31:41 +030084 if self.name not in tk.splitlist(tk.call("font", "names")):
Georg Brandl6634bf22008-05-20 07:13:37 +000085 raise Tkinter._tkinter.TclError, "named font %s does not already exist" % (self.name,)
Georg Brandl33cece02008-05-20 06:58:21 +000086 # if font config info supplied, apply it
87 if font:
Serhiy Storchaka44ae5132014-08-17 15:31:41 +030088 tk.call("font", "configure", self.name, *font)
Georg Brandl33cece02008-05-20 06:58:21 +000089 else:
90 # create new font (raises TclError if the font exists)
Serhiy Storchaka44ae5132014-08-17 15:31:41 +030091 tk.call("font", "create", self.name, *font)
Georg Brandl33cece02008-05-20 06:58:21 +000092 self.delete_font = True
Serhiy Storchaka44ae5132014-08-17 15:31:41 +030093 self._tk = tk
94 self._split = tk.splitlist
95 self._call = tk.call
Georg Brandl33cece02008-05-20 06:58:21 +000096
97 def __str__(self):
98 return self.name
99
100 def __eq__(self, other):
Serhiy Storchaka186f6652014-07-24 17:48:28 +0300101 return isinstance(other, Font) and self.name == other.name
Georg Brandl33cece02008-05-20 06:58:21 +0000102
103 def __getitem__(self, key):
104 return self.cget(key)
105
106 def __setitem__(self, key, value):
107 self.configure(**{key: value})
108
109 def __del__(self):
110 try:
111 if self.delete_font:
112 self._call("font", "delete", self.name)
113 except (KeyboardInterrupt, SystemExit):
114 raise
115 except Exception:
116 pass
117
118 def copy(self):
119 "Return a distinct copy of the current font"
Serhiy Storchaka44ae5132014-08-17 15:31:41 +0300120 return Font(self._tk, **self.actual())
Georg Brandl33cece02008-05-20 06:58:21 +0000121
122 def actual(self, option=None):
123 "Return actual font attributes"
124 if option:
125 return self._call("font", "actual", self.name, "-"+option)
126 else:
127 return self._mkdict(
128 self._split(self._call("font", "actual", self.name))
129 )
130
131 def cget(self, option):
132 "Get font attribute"
133 return self._call("font", "config", self.name, "-"+option)
134
135 def config(self, **options):
136 "Modify font attributes"
137 if options:
138 self._call("font", "config", self.name,
139 *self._set(options))
140 else:
141 return self._mkdict(
142 self._split(self._call("font", "config", self.name))
143 )
144
145 configure = config
146
147 def measure(self, text):
148 "Return text width"
149 return int(self._call("font", "measure", self.name, text))
150
151 def metrics(self, *options):
152 """Return font metrics.
153
154 For best performance, create a dummy widget
155 using this font before calling this method."""
156
157 if options:
158 return int(
159 self._call("font", "metrics", self.name, self._get(options))
160 )
161 else:
162 res = self._split(self._call("font", "metrics", self.name))
163 options = {}
164 for i in range(0, len(res), 2):
165 options[res[i][1:]] = int(res[i+1])
166 return options
167
168def families(root=None):
169 "Get font families (as a tuple)"
170 if not root:
Georg Brandl6634bf22008-05-20 07:13:37 +0000171 root = Tkinter._default_root
Georg Brandl33cece02008-05-20 06:58:21 +0000172 return root.tk.splitlist(root.tk.call("font", "families"))
173
174def names(root=None):
175 "Get names of defined fonts (as a tuple)"
176 if not root:
Georg Brandl6634bf22008-05-20 07:13:37 +0000177 root = Tkinter._default_root
Georg Brandl33cece02008-05-20 06:58:21 +0000178 return root.tk.splitlist(root.tk.call("font", "names"))
179
180# --------------------------------------------------------------------
181# test stuff
182
183if __name__ == "__main__":
184
Georg Brandl6634bf22008-05-20 07:13:37 +0000185 root = Tkinter.Tk()
Georg Brandl33cece02008-05-20 06:58:21 +0000186
187 # create a font
188 f = Font(family="times", size=30, weight=NORMAL)
189
190 print f.actual()
191 print f.actual("family")
192 print f.actual("weight")
193
194 print f.config()
195 print f.cget("family")
196 print f.cget("weight")
197
198 print names()
199
200 print f.measure("hello"), f.metrics("linespace")
201
202 print f.metrics()
203
204 f = Font(font=("Courier", 20, "bold"))
205 print f.measure("hello"), f.metrics("linespace")
206
Georg Brandl6634bf22008-05-20 07:13:37 +0000207 w = Tkinter.Label(root, text="Hello, world", font=f)
Georg Brandl33cece02008-05-20 06:58:21 +0000208 w.pack()
209
Georg Brandl6634bf22008-05-20 07:13:37 +0000210 w = Tkinter.Button(root, text="Quit!", command=root.destroy)
Georg Brandl33cece02008-05-20 06:58:21 +0000211 w.pack()
212
213 fb = Font(font=w["font"]).copy()
214 fb.config(weight=BOLD)
215
216 w.config(font=fb)
217
Georg Brandl6634bf22008-05-20 07:13:37 +0000218 Tkinter.mainloop()