blob: 15dea2eba3a561e7e3c4bdbc93995f4d28dfe233 [file] [log] [blame]
Fredrik Lundhd4893982005-11-12 15:28:52 +00001# Tkinter font wrapper
Guido van Rossum3d16d3e1998-08-11 19:07:58 +00002#
Fredrik Lundhd4893982005-11-12 15:28:52 +00003# written by Fredrik Lundh, February 1998
Guido van Rossum3d16d3e1998-08-11 19:07:58 +00004#
5# FIXME: should add 'displayof' option where relevant (actual, families,
6# measure, and metrics)
Fred Draked038ca82000-10-23 18:31:14 +00007#
Guido van Rossum3d16d3e1998-08-11 19:07:58 +00008
9__version__ = "0.9"
10
11import Tkinter
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000012
13# weight/slant
14NORMAL = "normal"
Martin v. Löwis1ef23652003-06-14 21:40:04 +000015ROMAN = "roman"
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000016BOLD = "bold"
17ITALIC = "italic"
18
Martin v. Löwisfe84d172004-08-18 11:06:45 +000019def nametofont(name):
20 """Given the name of a tk named font, returns a Font representation.
21 """
22 return Font(name=name, exists=True)
Tim Petersa45cacf2004-08-20 03:47:14 +000023
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000024class 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)
Martin v. Löwisfe84d172004-08-18 11:06:45 +000031 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.
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000035
Martin v. Löwisfe84d172004-08-18 11:06:45 +000036 the following are ignored if font is specified:
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000037
38 family -- font 'family', e.g. Courier, Times, Helvetica
39 size -- font size in points
40 weight -- font thickness: NORMAL, BOLD
Martin v. Löwis1ef23652003-06-14 21:40:04 +000041 slant -- font slant: ROMAN, ITALIC
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000042 underline -- font underlining: false (0), true (1)
43 overstrike -- font strikeout: false (0), true (1)
Tim Petersa45cacf2004-08-20 03:47:14 +000044
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000045 """
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):
Fred Draked038ca82000-10-23 18:31:14 +000055 options = []
56 for k in args:
57 options.append("-"+k)
58 return tuple(options)
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000059
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
Martin v. Löwisfe84d172004-08-18 11:06:45 +000066 def __init__(self, root=None, font=None, name=None, exists=False, **options):
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000067 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
Martin v. Löwisfe84d172004-08-18 11:06:45 +000077
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:
Martin v. Löwisfe84d172004-08-18 11:06:45 +000085 root.tk.call("font", "configure", self.name, *font)
86 else:
87 # create new font (raises TclError if the font exists)
Tim Petersa45cacf2004-08-20 03:47:14 +000088 root.tk.call("font", "create", self.name, *font)
Martin v. Löwisfe84d172004-08-18 11:06:45 +000089 self.delete_font = True
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000090 # 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
Martin v. Löwisfe84d172004-08-18 11:06:45 +000098 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
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000107 def __del__(self):
108 try:
Martin v. Löwisfe84d172004-08-18 11:06:45 +0000109 if self.delete_font:
110 self._call("font", "delete", self.name)
Georg Brandl314fce92006-04-12 15:28:49 +0000111 except (KeyboardInterrupt, SystemExit):
112 raise
113 except Exception:
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000114 pass
Tim Petersa45cacf2004-08-20 03:47:14 +0000115
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000116 def copy(self):
117 "Return a distinct copy of the current font"
Raymond Hettingerff41c482003-04-06 09:01:11 +0000118 return Font(self._root, **self.actual())
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000119
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:
Raymond Hettingerff41c482003-04-06 09:01:11 +0000136 self._call("font", "config", self.name,
137 *self._set(options))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000138 else:
139 return self._mkdict(
140 self._split(self._call("font", "config", self.name))
141 )
142
143 configure = config
Fred Draked038ca82000-10-23 18:31:14 +0000144
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000145 def measure(self, text):
146 "Return text width"
Eric S. Raymondfc170b12001-02-09 11:51:27 +0000147 return int(self._call("font", "measure", self.name, text))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000148
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:
Eric S. Raymondfc170b12001-02-09 11:51:27 +0000156 return int(
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000157 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):
Eric S. Raymondfc170b12001-02-09 11:51:27 +0000163 options[res[i][1:]] = int(res[i+1])
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000164 return options
165
166def families(root=None):
167 "Get font families (as a tuple)"
168 if not root:
169 root = Tkinter._default_root
Fred Draked038ca82000-10-23 18:31:14 +0000170 return root.tk.splitlist(root.tk.call("font", "families"))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000171
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
Fred Draked038ca82000-10-23 18:31:14 +0000180
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000181if __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)
Fred Draked038ca82000-10-23 18:31:14 +0000213
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000214 w.config(font=fb)
215
216 Tkinter.mainloop()