blob: 0103195da3dd543d6835e436febd177d944cdd86 [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
Georg Brandl14fc4272008-05-17 18:39:55 +000011import tkinter
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000012
Andrew Svetlov5af3e1a2012-04-03 09:39:47 +030013
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000014# weight/slant
15NORMAL = "normal"
Martin v. Löwis1ef23652003-06-14 21:40:04 +000016ROMAN = "roman"
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000017BOLD = "bold"
18ITALIC = "italic"
19
Andrew Svetlov5af3e1a2012-04-03 09:39:47 +030020
Martin v. Löwisfe84d172004-08-18 11:06:45 +000021def nametofont(name):
22 """Given the name of a tk named font, returns a Font representation.
23 """
24 return Font(name=name, exists=True)
Tim Petersa45cacf2004-08-20 03:47:14 +000025
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000026
Andrew Svetlov5af3e1a2012-04-03 09:39:47 +030027class Font:
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000028 """Represents a named font.
29
30 Constructor options are:
31
32 font -- font specifier (name, system font, or (family, size, style)-tuple)
Martin v. Löwisfe84d172004-08-18 11:06:45 +000033 name -- name to use for this font configuration (defaults to a unique name)
34 exists -- does a named font by this name already exist?
35 Creates a new named font if False, points to the existing font if True.
36 Raises _tkinter.TclError if the assertion is false.
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000037
Martin v. Löwisfe84d172004-08-18 11:06:45 +000038 the following are ignored if font is specified:
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000039
40 family -- font 'family', e.g. Courier, Times, Helvetica
41 size -- font size in points
42 weight -- font thickness: NORMAL, BOLD
Martin v. Löwis1ef23652003-06-14 21:40:04 +000043 slant -- font slant: ROMAN, ITALIC
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000044 underline -- font underlining: false (0), true (1)
45 overstrike -- font strikeout: false (0), true (1)
Tim Petersa45cacf2004-08-20 03:47:14 +000046
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000047 """
48
49 def _set(self, kw):
50 options = []
51 for k, v in kw.items():
52 options.append("-"+k)
53 options.append(str(v))
54 return tuple(options)
55
56 def _get(self, args):
Fred Draked038ca82000-10-23 18:31:14 +000057 options = []
58 for k in args:
59 options.append("-"+k)
60 return tuple(options)
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000061
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
Andrew Svetlov5af3e1a2012-04-03 09:39:47 +030068 def __init__(self, root=None, font=None, name=None, exists=False,
69 **options):
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000070 if not root:
Georg Brandl14fc4272008-05-17 18:39:55 +000071 root = tkinter._default_root
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000072 if font:
73 # get actual settings corresponding to the given font
74 font = root.tk.splitlist(root.tk.call("font", "actual", font))
75 else:
76 font = self._set(options)
77 if not name:
78 name = "font" + str(id(self))
79 self.name = name
Martin v. Löwisfe84d172004-08-18 11:06:45 +000080
81 if exists:
82 self.delete_font = False
83 # confirm font exists
84 if self.name not in root.tk.call("font", "names"):
Georg Brandl14fc4272008-05-17 18:39:55 +000085 raise tkinter._tkinter.TclError(
86 "named font %s does not already exist" % (self.name,))
Martin v. Löwisfe84d172004-08-18 11:06:45 +000087 # if font config info supplied, apply it
88 if font:
Martin v. Löwisfe84d172004-08-18 11:06:45 +000089 root.tk.call("font", "configure", self.name, *font)
90 else:
91 # create new font (raises TclError if the font exists)
Tim Petersa45cacf2004-08-20 03:47:14 +000092 root.tk.call("font", "create", self.name, *font)
Martin v. Löwisfe84d172004-08-18 11:06:45 +000093 self.delete_font = True
Guido van Rossum3d16d3e1998-08-11 19:07:58 +000094 # backlinks!
95 self._root = root
96 self._split = root.tk.splitlist
97 self._call = root.tk.call
98
99 def __str__(self):
100 return self.name
101
Martin v. Löwisfe84d172004-08-18 11:06:45 +0000102 def __eq__(self, other):
Amaury Forgeot d'Arcd61d0772010-09-17 23:27:09 +0000103 return isinstance(other, Font) and self.name == other.name
Martin v. Löwisfe84d172004-08-18 11:06:45 +0000104
105 def __getitem__(self, key):
106 return self.cget(key)
107
108 def __setitem__(self, key, value):
109 self.configure(**{key: value})
110
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000111 def __del__(self):
112 try:
Martin v. Löwisfe84d172004-08-18 11:06:45 +0000113 if self.delete_font:
114 self._call("font", "delete", self.name)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000115 except (KeyboardInterrupt, SystemExit):
116 raise
117 except Exception:
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000118 pass
Tim Petersa45cacf2004-08-20 03:47:14 +0000119
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000120 def copy(self):
121 "Return a distinct copy of the current font"
Raymond Hettingerff41c482003-04-06 09:01:11 +0000122 return Font(self._root, **self.actual())
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000123
124 def actual(self, option=None):
125 "Return actual font attributes"
126 if option:
127 return self._call("font", "actual", self.name, "-"+option)
128 else:
129 return self._mkdict(
130 self._split(self._call("font", "actual", self.name))
131 )
132
133 def cget(self, option):
134 "Get font attribute"
135 return self._call("font", "config", self.name, "-"+option)
136
137 def config(self, **options):
138 "Modify font attributes"
139 if options:
Raymond Hettingerff41c482003-04-06 09:01:11 +0000140 self._call("font", "config", self.name,
141 *self._set(options))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000142 else:
143 return self._mkdict(
Andrew Svetlov5af3e1a2012-04-03 09:39:47 +0300144 self._split(self._call("font", "config", self.name)))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000145
146 configure = config
Fred Draked038ca82000-10-23 18:31:14 +0000147
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000148 def measure(self, text):
149 "Return text width"
Eric S. Raymondfc170b12001-02-09 11:51:27 +0000150 return int(self._call("font", "measure", self.name, text))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000151
152 def metrics(self, *options):
153 """Return font metrics.
154
155 For best performance, create a dummy widget
156 using this font before calling this method."""
157
158 if options:
Eric S. Raymondfc170b12001-02-09 11:51:27 +0000159 return int(
Andrew Svetlov5af3e1a2012-04-03 09:39:47 +0300160 self._call("font", "metrics", self.name, self._get(options)))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000161 else:
162 res = self._split(self._call("font", "metrics", self.name))
163 options = {}
164 for i in range(0, len(res), 2):
Eric S. Raymondfc170b12001-02-09 11:51:27 +0000165 options[res[i][1:]] = int(res[i+1])
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000166 return options
167
Andrew Svetlov5af3e1a2012-04-03 09:39:47 +0300168
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000169def families(root=None):
170 "Get font families (as a tuple)"
171 if not root:
Georg Brandl14fc4272008-05-17 18:39:55 +0000172 root = tkinter._default_root
Fred Draked038ca82000-10-23 18:31:14 +0000173 return root.tk.splitlist(root.tk.call("font", "families"))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000174
Andrew Svetlov5af3e1a2012-04-03 09:39:47 +0300175
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000176def names(root=None):
177 "Get names of defined fonts (as a tuple)"
178 if not root:
Georg Brandl14fc4272008-05-17 18:39:55 +0000179 root = tkinter._default_root
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000180 return root.tk.splitlist(root.tk.call("font", "names"))
181
Andrew Svetlov5af3e1a2012-04-03 09:39:47 +0300182
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000183# --------------------------------------------------------------------
184# test stuff
Fred Draked038ca82000-10-23 18:31:14 +0000185
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000186if __name__ == "__main__":
187
Georg Brandl14fc4272008-05-17 18:39:55 +0000188 root = tkinter.Tk()
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000189
190 # create a font
191 f = Font(family="times", size=30, weight=NORMAL)
192
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000193 print(f.actual())
194 print(f.actual("family"))
195 print(f.actual("weight"))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000196
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000197 print(f.config())
198 print(f.cget("family"))
199 print(f.cget("weight"))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000200
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000201 print(names())
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000202
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000203 print(f.measure("hello"), f.metrics("linespace"))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000204
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000205 print(f.metrics())
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000206
207 f = Font(font=("Courier", 20, "bold"))
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000208 print(f.measure("hello"), f.metrics("linespace"))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000209
Georg Brandl14fc4272008-05-17 18:39:55 +0000210 w = tkinter.Label(root, text="Hello, world", font=f)
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000211 w.pack()
212
Georg Brandl14fc4272008-05-17 18:39:55 +0000213 w = tkinter.Button(root, text="Quit!", command=root.destroy)
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000214 w.pack()
215
216 fb = Font(font=w["font"]).copy()
217 fb.config(weight=BOLD)
Fred Draked038ca82000-10-23 18:31:14 +0000218
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000219 w.config(font=fb)
220
Georg Brandl14fc4272008-05-17 18:39:55 +0000221 tkinter.mainloop()