blob: 5b5a6ba36b7827e933038ac6c73b3c1ddb2fc431 [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)
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000111 except (AttributeError, Tkinter.TclError):
112 pass
Tim Petersa45cacf2004-08-20 03:47:14 +0000113
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000114 def copy(self):
115 "Return a distinct copy of the current font"
Raymond Hettingerff41c482003-04-06 09:01:11 +0000116 return Font(self._root, **self.actual())
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000117
118 def actual(self, option=None):
119 "Return actual font attributes"
120 if option:
121 return self._call("font", "actual", self.name, "-"+option)
122 else:
123 return self._mkdict(
124 self._split(self._call("font", "actual", self.name))
125 )
126
127 def cget(self, option):
128 "Get font attribute"
129 return self._call("font", "config", self.name, "-"+option)
130
131 def config(self, **options):
132 "Modify font attributes"
133 if options:
Raymond Hettingerff41c482003-04-06 09:01:11 +0000134 self._call("font", "config", self.name,
135 *self._set(options))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000136 else:
137 return self._mkdict(
138 self._split(self._call("font", "config", self.name))
139 )
140
141 configure = config
Fred Draked038ca82000-10-23 18:31:14 +0000142
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000143 def measure(self, text):
144 "Return text width"
Eric S. Raymondfc170b12001-02-09 11:51:27 +0000145 return int(self._call("font", "measure", self.name, text))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000146
147 def metrics(self, *options):
148 """Return font metrics.
149
150 For best performance, create a dummy widget
151 using this font before calling this method."""
152
153 if options:
Eric S. Raymondfc170b12001-02-09 11:51:27 +0000154 return int(
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000155 self._call("font", "metrics", self.name, self._get(options))
156 )
157 else:
158 res = self._split(self._call("font", "metrics", self.name))
159 options = {}
160 for i in range(0, len(res), 2):
Eric S. Raymondfc170b12001-02-09 11:51:27 +0000161 options[res[i][1:]] = int(res[i+1])
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000162 return options
163
164def families(root=None):
165 "Get font families (as a tuple)"
166 if not root:
167 root = Tkinter._default_root
Fred Draked038ca82000-10-23 18:31:14 +0000168 return root.tk.splitlist(root.tk.call("font", "families"))
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000169
170def names(root=None):
171 "Get names of defined fonts (as a tuple)"
172 if not root:
173 root = Tkinter._default_root
174 return root.tk.splitlist(root.tk.call("font", "names"))
175
176# --------------------------------------------------------------------
177# test stuff
Fred Draked038ca82000-10-23 18:31:14 +0000178
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000179if __name__ == "__main__":
180
181 root = Tkinter.Tk()
182
183 # create a font
184 f = Font(family="times", size=30, weight=NORMAL)
185
186 print f.actual()
187 print f.actual("family")
188 print f.actual("weight")
189
190 print f.config()
191 print f.cget("family")
192 print f.cget("weight")
193
194 print names()
195
196 print f.measure("hello"), f.metrics("linespace")
197
198 print f.metrics()
199
200 f = Font(font=("Courier", 20, "bold"))
201 print f.measure("hello"), f.metrics("linespace")
202
203 w = Tkinter.Label(root, text="Hello, world", font=f)
204 w.pack()
205
206 w = Tkinter.Button(root, text="Quit!", command=root.destroy)
207 w.pack()
208
209 fb = Font(font=w["font"]).copy()
210 fb.config(weight=BOLD)
Fred Draked038ca82000-10-23 18:31:14 +0000211
Guido van Rossum3d16d3e1998-08-11 19:07:58 +0000212 w.config(font=fb)
213
214 Tkinter.mainloop()