font.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. # Tkinter font wrapper
  2. #
  3. # written by Fredrik Lundh, February 1998
  4. #
  5. import itertools
  6. import tkinter
  7. __version__ = "0.9"
  8. __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC",
  9. "nametofont", "Font", "families", "names"]
  10. # weight/slant
  11. NORMAL = "normal"
  12. ROMAN = "roman"
  13. BOLD = "bold"
  14. ITALIC = "italic"
  15. def nametofont(name):
  16. """Given the name of a tk named font, returns a Font representation.
  17. """
  18. return Font(name=name, exists=True)
  19. class Font:
  20. """Represents a named font.
  21. Constructor options are:
  22. font -- font specifier (name, system font, or (family, size, style)-tuple)
  23. name -- name to use for this font configuration (defaults to a unique name)
  24. exists -- does a named font by this name already exist?
  25. Creates a new named font if False, points to the existing font if True.
  26. Raises _tkinter.TclError if the assertion is false.
  27. the following are ignored if font is specified:
  28. family -- font 'family', e.g. Courier, Times, Helvetica
  29. size -- font size in points
  30. weight -- font thickness: NORMAL, BOLD
  31. slant -- font slant: ROMAN, ITALIC
  32. underline -- font underlining: false (0), true (1)
  33. overstrike -- font strikeout: false (0), true (1)
  34. """
  35. counter = itertools.count(1)
  36. def _set(self, kw):
  37. options = []
  38. for k, v in kw.items():
  39. options.append("-"+k)
  40. options.append(str(v))
  41. return tuple(options)
  42. def _get(self, args):
  43. options = []
  44. for k in args:
  45. options.append("-"+k)
  46. return tuple(options)
  47. def _mkdict(self, args):
  48. options = {}
  49. for i in range(0, len(args), 2):
  50. options[args[i][1:]] = args[i+1]
  51. return options
  52. def __init__(self, root=None, font=None, name=None, exists=False,
  53. **options):
  54. if not root:
  55. root = tkinter._get_default_root('use font')
  56. tk = getattr(root, 'tk', root)
  57. if font:
  58. # get actual settings corresponding to the given font
  59. font = tk.splitlist(tk.call("font", "actual", font))
  60. else:
  61. font = self._set(options)
  62. if not name:
  63. name = "font" + str(next(self.counter))
  64. self.name = name
  65. if exists:
  66. self.delete_font = False
  67. # confirm font exists
  68. if self.name not in tk.splitlist(tk.call("font", "names")):
  69. raise tkinter._tkinter.TclError(
  70. "named font %s does not already exist" % (self.name,))
  71. # if font config info supplied, apply it
  72. if font:
  73. tk.call("font", "configure", self.name, *font)
  74. else:
  75. # create new font (raises TclError if the font exists)
  76. tk.call("font", "create", self.name, *font)
  77. self.delete_font = True
  78. self._tk = tk
  79. self._split = tk.splitlist
  80. self._call = tk.call
  81. def __str__(self):
  82. return self.name
  83. def __eq__(self, other):
  84. if not isinstance(other, Font):
  85. return NotImplemented
  86. return self.name == other.name and self._tk == other._tk
  87. def __getitem__(self, key):
  88. return self.cget(key)
  89. def __setitem__(self, key, value):
  90. self.configure(**{key: value})
  91. def __del__(self):
  92. try:
  93. if self.delete_font:
  94. self._call("font", "delete", self.name)
  95. except Exception:
  96. pass
  97. def copy(self):
  98. "Return a distinct copy of the current font"
  99. return Font(self._tk, **self.actual())
  100. def actual(self, option=None, displayof=None):
  101. "Return actual font attributes"
  102. args = ()
  103. if displayof:
  104. args = ('-displayof', displayof)
  105. if option:
  106. args = args + ('-' + option, )
  107. return self._call("font", "actual", self.name, *args)
  108. else:
  109. return self._mkdict(
  110. self._split(self._call("font", "actual", self.name, *args)))
  111. def cget(self, option):
  112. "Get font attribute"
  113. return self._call("font", "config", self.name, "-"+option)
  114. def config(self, **options):
  115. "Modify font attributes"
  116. if options:
  117. self._call("font", "config", self.name,
  118. *self._set(options))
  119. else:
  120. return self._mkdict(
  121. self._split(self._call("font", "config", self.name)))
  122. configure = config
  123. def measure(self, text, displayof=None):
  124. "Return text width"
  125. args = (text,)
  126. if displayof:
  127. args = ('-displayof', displayof, text)
  128. return self._tk.getint(self._call("font", "measure", self.name, *args))
  129. def metrics(self, *options, **kw):
  130. """Return font metrics.
  131. For best performance, create a dummy widget
  132. using this font before calling this method."""
  133. args = ()
  134. displayof = kw.pop('displayof', None)
  135. if displayof:
  136. args = ('-displayof', displayof)
  137. if options:
  138. args = args + self._get(options)
  139. return self._tk.getint(
  140. self._call("font", "metrics", self.name, *args))
  141. else:
  142. res = self._split(self._call("font", "metrics", self.name, *args))
  143. options = {}
  144. for i in range(0, len(res), 2):
  145. options[res[i][1:]] = self._tk.getint(res[i+1])
  146. return options
  147. def families(root=None, displayof=None):
  148. "Get font families (as a tuple)"
  149. if not root:
  150. root = tkinter._get_default_root('use font.families()')
  151. args = ()
  152. if displayof:
  153. args = ('-displayof', displayof)
  154. return root.tk.splitlist(root.tk.call("font", "families", *args))
  155. def names(root=None):
  156. "Get names of defined fonts (as a tuple)"
  157. if not root:
  158. root = tkinter._get_default_root('use font.names()')
  159. return root.tk.splitlist(root.tk.call("font", "names"))
  160. # --------------------------------------------------------------------
  161. # test stuff
  162. if __name__ == "__main__":
  163. root = tkinter.Tk()
  164. # create a font
  165. f = Font(family="times", size=30, weight=NORMAL)
  166. print(f.actual())
  167. print(f.actual("family"))
  168. print(f.actual("weight"))
  169. print(f.config())
  170. print(f.cget("family"))
  171. print(f.cget("weight"))
  172. print(names())
  173. print(f.measure("hello"), f.metrics("linespace"))
  174. print(f.metrics(displayof=root))
  175. f = Font(font=("Courier", 20, "bold"))
  176. print(f.measure("hello"), f.metrics("linespace", displayof=root))
  177. w = tkinter.Label(root, text="Hello, world", font=f)
  178. w.pack()
  179. w = tkinter.Button(root, text="Quit!", command=root.destroy)
  180. w.pack()
  181. fb = Font(font=w["font"]).copy()
  182. fb.config(weight=BOLD)
  183. w.config(font=fb)
  184. tkinter.mainloop()