1# ------------------------------------------------------------------------------
2# Copyright (c) 2007, Riverbank Computing Limited
3# All rights reserved.
4#
5# This software is provided without warranty under the terms of the BSD license.
6# However, when used with the GPL version of PyQt the additional terms
7# described in the PyQt GPL exception also apply
8
9#
10# Author: Riverbank Computing Limited
11# ------------------------------------------------------------------------------
12
13""" Trait definition for a PyQt-based font.
14"""
15
16
17from pyface.qt import QtGui
18
19from traits.api import Trait, TraitHandler, TraitError
20
21
22# -------------------------------------------------------------------------
23#  Convert a string into a valid QFont object (if possible):
24# -------------------------------------------------------------------------
25
26# Mapping of strings to valid QFont style hints.
27font_families = {
28    "default": QtGui.QFont.AnyStyle,
29    "decorative": QtGui.QFont.Decorative,
30    "roman": QtGui.QFont.Serif,
31    "script": QtGui.QFont.SansSerif,
32    "swiss": QtGui.QFont.SansSerif,
33    "modern": QtGui.QFont.TypeWriter,
34}
35
36# Mapping of strings to QFont styles.
37font_styles = {
38    "slant": QtGui.QFont.StyleOblique,
39    "italic": QtGui.QFont.StyleItalic,
40}
41
42# Mapping of strings to QFont weights.
43font_weights = {"light": QtGui.QFont.Light, "bold": QtGui.QFont.Bold}
44
45# Strings to ignore in text representations of fonts
46font_noise = ["pt", "point", "family"]
47
48
49def font_to_str(font):
50    """ Converts a QFont into a string description of itself.
51    """
52    weight = {QtGui.QFont.Light: " Light", QtGui.QFont.Bold: " Bold"}.get(
53        font.weight(), ""
54    )
55    style = {
56        QtGui.QFont.StyleOblique: " Slant",
57        QtGui.QFont.StyleItalic: " Italic",
58    }.get(font.style(), "")
59    underline = ""
60    if font.underline():
61        underline = " underline"
62    return "%s point %s%s%s%s" % (
63        font.pointSize(),
64        str(font.family()),
65        style,
66        weight,
67        underline,
68    )
69
70
71def create_traitsfont(value):
72    """ Create a TraitFont object from a string description.
73    """
74    if isinstance(value, QtGui.QFont):
75        return TraitsFont(value)
76
77    point_size = None
78    family = ""
79    style = QtGui.QFont.StyleNormal
80    weight = QtGui.QFont.Normal
81    underline = False
82    facename = []
83
84    for word in value.split():
85        lword = word.lower()
86        if lword in font_families:
87            f = QtGui.QFont()
88            f.setStyleHint(font_families[lword])
89            family = f.defaultFamily()
90        elif lword in font_styles:
91            style = font_styles[lword]
92        elif lword in font_weights:
93            weight = font_weights[lword]
94        elif lword == "underline":
95            underline = True
96        elif lword not in font_noise:
97            if point_size is None:
98                try:
99                    point_size = int(lword)
100                    continue
101                except:
102                    pass
103            facename.append(word)
104
105    if facename:
106        family = " ".join(facename)
107
108    if family:
109        fnt = TraitsFont(family)
110    else:
111        fnt = TraitsFont()
112
113    fnt.setStyle(style)
114    fnt.setWeight(weight)
115    fnt.setUnderline(underline)
116
117    if point_size is None:
118        fnt.setPointSize(QtGui.QApplication.font().pointSize())
119    else:
120        fnt.setPointSize(point_size)
121
122    return fnt
123
124
125class TraitsFont(QtGui.QFont):
126    """ A Traits-specific QFont.
127    """
128
129    def __reduce_ex__(self, protocol):
130        """ Returns the pickleable form of a TraitsFont object.
131        """
132        return (create_traitsfont, (font_to_str(self),))
133
134    def __str__(self):
135        """ Returns a printable form of the font.
136        """
137        return font_to_str(self)
138
139
140# -------------------------------------------------------------------------
141#  'TraitPyQtFont' class'
142# -------------------------------------------------------------------------
143
144
145class TraitPyQtFont(TraitHandler):
146    """ Ensures that values assigned to a trait attribute are valid font
147    descriptor strings; the value actually assigned is the corresponding
148    TraitsFont.
149    """
150
151    def validate(self, object, name, value):
152        """ Validates that the value is a valid font descriptor string. If so,
153        it returns the corresponding TraitsFont; otherwise, it raises a
154        TraitError.
155        """
156        if value is None:
157            return None
158
159        try:
160            return create_traitsfont(value)
161        except:
162            pass
163
164        raise TraitError(object, name, "a font descriptor string", repr(value))
165
166    def info(self):
167        return (
168            "a string describing a font (e.g. '12 pt bold italic "
169            "swiss family Arial' or 'default 12')"
170        )
171
172
173# -------------------------------------------------------------------------
174#  Callable that returns an instance of the PyQtToolkitEditorFactory for font
175#  editors.
176# -------------------------------------------------------------------------
177
178### FIXME: We have declared the 'editor' to be a function instead of  the
179# traitsui.qt4.font_editor.ToolkitEditorFactory class, since the
180# latter is leading to too many circular imports. In the future, try to see if
181# there is a better way to do this.
182
183
184def get_font_editor(*args, **traits):
185    from traitsui.qt4.font_editor import ToolkitEditorFactory
186
187    return ToolkitEditorFactory(*args, **traits)
188
189
190# -------------------------------------------------------------------------
191#  Define a PyQt specific font trait:
192# -------------------------------------------------------------------------
193
194PyQtFont = Trait(TraitsFont(), TraitPyQtFont(), editor=get_font_editor)
195