10a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# Tkinter font wrapper
20a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#
30a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# written by Fredrik Lundh, February 1998
40a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#
50a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# FIXME: should add 'displayof' option where relevant (actual, families,
60a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#        measure, and metrics)
70a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#
80a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
90a8c90248264a8b26970b4473770bcc3df8515fJosh Gao__version__ = "0.9"
100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
110a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport Tkinter
120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# weight/slant
140a8c90248264a8b26970b4473770bcc3df8515fJosh GaoNORMAL = "normal"
150a8c90248264a8b26970b4473770bcc3df8515fJosh GaoROMAN = "roman"
160a8c90248264a8b26970b4473770bcc3df8515fJosh GaoBOLD   = "bold"
170a8c90248264a8b26970b4473770bcc3df8515fJosh GaoITALIC = "italic"
180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
190a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef nametofont(name):
200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    """Given the name of a tk named font, returns a Font representation.
210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    """
220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    return Font(name=name, exists=True)
230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
240a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass Font:
250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    """Represents a named font.
270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    Constructor options are:
290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    font -- font specifier (name, system font, or (family, size, style)-tuple)
310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    name -- name to use for this font configuration (defaults to a unique name)
320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    exists -- does a named font by this name already exist?
330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao       Creates a new named font if False, points to the existing font if True.
340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao       Raises _Tkinter.TclError if the assertion is false.
350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao       the following are ignored if font is specified:
370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    family -- font 'family', e.g. Courier, Times, Helvetica
390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    size -- font size in points
400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    weight -- font thickness: NORMAL, BOLD
410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    slant -- font slant: ROMAN, ITALIC
420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    underline -- font underlining: false (0), true (1)
430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    overstrike -- font strikeout: false (0), true (1)
440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    """
460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _set(self, kw):
480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        options = []
490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for k, v in kw.items():
500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            options.append("-"+k)
510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            options.append(str(v))
520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return tuple(options)
530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _get(self, args):
550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        options = []
560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for k in args:
570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            options.append("-"+k)
580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return tuple(options)
590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def _mkdict(self, args):
610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        options = {}
620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for i in range(0, len(args), 2):
630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            options[args[i][1:]] = args[i+1]
640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return options
650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, root=None, font=None, name=None, exists=False, **options):
670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not root:
680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            root = Tkinter._default_root
690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if font:
700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # get actual settings corresponding to the given font
710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            font = root.tk.splitlist(root.tk.call("font", "actual", font))
720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            font = self._set(options)
740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if not name:
750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            name = "font" + str(id(self))
760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.name = name
770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if exists:
790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.delete_font = False
800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # confirm font exists
810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if self.name not in root.tk.call("font", "names"):
820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                raise Tkinter._tkinter.TclError, "named font %s does not already exist" % (self.name,)
830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # if font config info supplied, apply it
840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if font:
850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                root.tk.call("font", "configure", self.name, *font)
860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # create new font (raises TclError if the font exists)
880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            root.tk.call("font", "create", self.name, *font)
890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self.delete_font = True
900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # backlinks!
910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self._root  = root
920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self._split = root.tk.splitlist
930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self._call  = root.tk.call
940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __str__(self):
960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.name
970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __eq__(self, other):
990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.name == other.name and isinstance(other, Font)
1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __getitem__(self, key):
1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self.cget(key)
1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __setitem__(self, key, value):
1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.configure(**{key: value})
1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __del__(self):
1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        try:
1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if self.delete_font:
1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self._call("font", "delete", self.name)
1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        except (KeyboardInterrupt, SystemExit):
1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            raise
1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        except Exception:
1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            pass
1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def copy(self):
1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        "Return a distinct copy of the current font"
1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return Font(self._root, **self.actual())
1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def actual(self, option=None):
1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        "Return actual font attributes"
1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if option:
1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return self._call("font", "actual", self.name, "-"+option)
1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return self._mkdict(
1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self._split(self._call("font", "actual", self.name))
1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                )
1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def cget(self, option):
1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        "Get font attribute"
1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return self._call("font", "config", self.name, "-"+option)
1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def config(self, **options):
1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        "Modify font attributes"
1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if options:
1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self._call("font", "config", self.name,
1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                  *self._set(options))
1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return self._mkdict(
1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self._split(self._call("font", "config", self.name))
1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                )
1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    configure = config
1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def measure(self, text):
1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        "Return text width"
1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return int(self._call("font", "measure", self.name, text))
1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def metrics(self, *options):
1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        """Return font metrics.
1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        For best performance, create a dummy widget
1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        using this font before calling this method."""
1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if options:
1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return int(
1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                self._call("font", "metrics", self.name, self._get(options))
1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                )
1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            res = self._split(self._call("font", "metrics", self.name))
1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            options = {}
1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            for i in range(0, len(res), 2):
1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                options[res[i][1:]] = int(res[i+1])
1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            return options
1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef families(root=None):
1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    "Get font families (as a tuple)"
1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if not root:
1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        root = Tkinter._default_root
1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    return root.tk.splitlist(root.tk.call("font", "families"))
1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef names(root=None):
1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    "Get names of defined fonts (as a tuple)"
1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    if not root:
1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        root = Tkinter._default_root
1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    return root.tk.splitlist(root.tk.call("font", "names"))
1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# --------------------------------------------------------------------
1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao# test stuff
1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif __name__ == "__main__":
1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    root = Tkinter.Tk()
1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    # create a font
1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    f = Font(family="times", size=30, weight=NORMAL)
1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print f.actual()
1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print f.actual("family")
1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print f.actual("weight")
1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print f.config()
1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print f.cget("family")
1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print f.cget("weight")
1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print names()
1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print f.measure("hello"), f.metrics("linespace")
1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print f.metrics()
2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    f = Font(font=("Courier", 20, "bold"))
2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    print f.measure("hello"), f.metrics("linespace")
2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    w = Tkinter.Label(root, text="Hello, world", font=f)
2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    w.pack()
2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    w = Tkinter.Button(root, text="Quit!", command=root.destroy)
2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    w.pack()
2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    fb = Font(font=w["font"]).copy()
2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    fb.config(weight=BOLD)
2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    w.config(font=fb)
2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    Tkinter.mainloop()
217