font.py 6.4 KB

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