File: helper.py

package info (click to toggle)
python-traitsui 8.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 18,232 kB
  • sloc: python: 58,982; makefile: 113
file content (177 lines) | stat: -rw-r--r-- 6,139 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# (C) Copyright 2004-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" Defines various helper functions that are useful for creating Traits-based
    user interfaces.
"""

from operator import itemgetter

from traits.api import BaseTraitHandler, CTrait, Enum, TraitError


# -------------------------------------------------------------------------
#  Trait definitions:
# -------------------------------------------------------------------------

# Layout orientation for a control and its associated editor
Orientation = Enum("horizontal", "vertical")

# Docking drag bar style:
DockStyle = Enum("horizontal", "vertical", "tab", "fixed")


def user_name_for(name):
    """Returns a "user-friendly" name for a specified trait."""
    name = name.replace("_", " ")
    name = name[:1].upper() + name[1:]
    result = ""
    last_lower = 0
    for c in name:
        if c.isupper() and last_lower:
            result += " "
        last_lower = c.islower()
        result += c
    return result


def commatize(value):
    """Formats a specified value as an integer string with embedded commas.
    For example: commatize( 12345 ) returns "12,345".
    """
    s = str(abs(value))
    s = s.rjust(((len(s) + 2) / 3) * 3)
    result = ",".join([s[i : i + 3] for i in range(0, len(s), 3)]).lstrip()
    if value >= 0:
        return result

    return "-" + result


def enum_values_changed(values, strfunc=str):
    """Recomputes the mappings for a new set of enumeration values."""

    if isinstance(values, dict):
        data = [(strfunc(v), n) for n, v in values.items()]
        if len(data) > 0:
            data.sort(key=itemgetter(0))
            col = data[0][0].find(":") + 1
            if col > 0:
                data = [(n[col:], v) for n, v in data]
    elif not isinstance(values, SequenceTypes):
        handler = values
        if isinstance(handler, CTrait):
            handler = handler.handler
        if not isinstance(handler, BaseTraitHandler):
            raise TraitError("Invalid value for 'values' specified")
        if handler.is_mapped:
            data = [(strfunc(n), n) for n in handler.map.keys()]
            data.sort(key=itemgetter(0))
        else:
            data = [(strfunc(v), v) for v in handler.values]
    else:
        data = [(strfunc(v), v) for v in values]

    names = [x[0] for x in data]
    mapping = {}
    inverse_mapping = {}
    for name, value in data:
        mapping[name] = value
        inverse_mapping[value] = name

    return (names, mapping, inverse_mapping)


def compute_column_widths(available_space, requested, min_widths, user_widths):
    """Distribute column space amongst columns based on requested space.

    Widths requests can be specified as one of the following:

    - a value greater than 1.0 is treated as a fixed width with no flexibility
      (ie. a minimum width as specified and a weight of 0.0)

    - a value between 0.0 and 1.0 is treaded as a flexible width column with
      the specified width as a weight and a minimum width provided by the
      min_widths entry.

    - a value less than or equal to 0.0 is treated as a flexible width column
      with a weight of 0.1 and a minimum width provided by the min_widths
      parameter.

    If user widths are supplied then any non-None values override the
    requested widths, and are treated as having a flexibility of 0.

    Space is distributed by evaluating each column from smallest weight to
    largest and seeing if the weighted proportion of the remaining space is
    more than the minimum, and if so replacing the width with the weighted
    width.  The column is then removed from the available width and the
    total weight and the analysis continues.

    Parameters
    ----------
    available_space : int
        The available horizontal space.
    requested : list of numbers
        The requested width or weight for each column.
    min_widths : None or list of ints
        The minimum width for each flexible column
    user_widths : None or list of int or None
        Any widths specified by the user resizing the columns manually.

    Returns
    -------
    widths : list of ints
        The assigned width for each column
    """
    widths = []
    weights = []
    if min_widths is None:
        min_widths = [30] * len(requested)

    # determine flexibility and default width of each column
    for request, min_width in zip(requested, min_widths):
        if request >= 1.0:
            weights.append(0.0)
            widths.append(int(request))
        else:
            if request <= 0:
                weights.append(0.1)
            else:
                weights.append(request)
            widths.append(min_width)

    # if the user has changed the width of a column manually respect that
    if user_widths is not None:
        for i, user_width in enumerate(user_widths):
            if user_width is not None:
                widths[i] = user_width
                weights[i] = 0.0

    total_weight = sum(weights)
    if sum(widths) < available_space and total_weight > 0:
        # do inflexible first, then work up from smallest to largest
        for i, weight in sorted(enumerate(weights), key=itemgetter(1, 0)):
            total_weight = sum(weights)
            stretched = int(weight / total_weight * available_space)
            widths[i] = max(stretched, widths[i])

            # once we have dealt with a column, it no longer counts as flexible
            # and its space is no longer available
            weights[i] = 0.0
            available_space -= widths[i]

    return widths


# -------------------------------------------------------------------------
#  Other definitions:
# -------------------------------------------------------------------------

SequenceTypes = (tuple, list)