1# 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()
217