1from __future__ import division, print_function
2"""Tools for gridding widgets
3
4History:
52003-05-06 ROwen    Adapted from ChangedGridder.
62004-05-18 ROwen    Modified _StatusConfigGridSet._makeChangedWdg
7                    to eliminate two unused args (colSpan and sticky).
82004-08-11 ROwen    Renamed StatusConfigGridSet->_StatusConfigGridSet.
9                    Define __all__ to restrict import.
102004-09-14 ROwen    Stopped importing RO.Wdg to avoid circular imports.
112004-11-29 ROwen    Modified to include ConfigCat as a class constant.
122005-01-05 ROwen    Got rid of the changed widget; use autoIsCurrent mode
13                    in RO.Wdg widgets to indicate "changed", instead.
142005-05-26 ROwen    Bug fix: gridWdg mis-set nextCol if cfgWdg False or None.
15                    Improved error message for units and cfgUnits being the same widget.
162006-04-27 ROwen    Removed ignored clearMenu and defMenu arguments (thanks pychecker!).
172006-10-31 ROwen    Added support adding help text and URL to created widgets.
18                    Modified for changed Gridder._BaseGridSet.
192007-12-19 ROwen    Added numStatusCols argument. This makes it easier to start all configuration widgets
20                    in the same column.
212008-03-14 ROwen    Bug fix: removed unused statusCols argument.
222011-05-04 ROwen    Bug fix: was not setting _maxNextCol
232015-09-24 ROwen    Replace "== None" with "is None" to modernize the code.
242015-11-03 ROwen    Replace "!= None" with "is not None" to modernize the code.
252015-11-05 ROwen    Changed ==/!= True/False to is/is not True/False to modernize the code.
26"""
27__all__ = ['StatusConfigGridder']
28
29import Gridder
30
31ConfigCat = "config"
32
33class StatusConfigGridder(Gridder.Gridder):
34    ConfigCat = ConfigCat
35    def __init__(self,
36        master,
37        row = 0,
38        col = 0,
39        sticky = "e",
40        numStatusCols = None,
41    ):
42        """Create an object that grids a set of status and configuration widgets.
43
44        Inputs:
45        - master        Master widget into which to grid
46        - row           Starting row
47        - col           Starting column
48        - sticky        Default sticky setting for the status and config widgets
49        - numStatusCols default number of columns for status widgets (including units but not label);
50                        if None then the first configuration widget is gridded in the next column after
51                        the last status widget.
52                        You may wish to specify more columns than required; it is almost always harmless
53                        and your code will still work if you add status widgets that use more columns.
54
55        """
56        Gridder.Gridder.__init__(self,
57            master = master,
58            row = row,
59            col = col,
60            sticky = sticky,
61        )
62        if numStatusCols is not None:
63            numStatusCols = int(numStatusCols)
64        self._numStatusCols = numStatusCols
65
66    def gridWdg(self,
67        label = None,
68        dataWdg = None,
69        units = None,
70        cfgWdg = None,
71        cat = None,
72    **kargs):
73        """Grids (in order)
74        - labelWdg: a label widget
75        - dataWdg: one or more status widgets
76        - unitsWdg: units
77        (the following are all None if cfgWdg not specified):
78        - cfgWdg: one or more config widgets
79        - cfgUnitsWdg: a config units label
80
81        Configuration widgets are automatically added
82        to the show/hide set ConfigCat and so are hidded by default.
83        To display them you must call showHideWdg(config=True)
84
85        Warning: a widget cannot be gridded twice, so:
86        - Units cannot be an actual widget; it must be a string
87          or variable (or None)
88        - There should be no common widgets in dataWdg or cfgWdg
89
90        Returns a _StatusConfigGridSet object that allows easy access
91        to the various widgets and related information.
92        Increments row.next.
93
94        Notes:
95        - If a widget is None or False then nothing is gridded or added to gs.wdgSet for that widget,
96          but space is handled differently in the two cases:
97          - If a widget is None then the appropriate number of empty columns are used for it
98          - If a widget is False then no columns are used for it
99        - If a label or units widget is "" then an empty RO.Wdg.StrLabel is gridded (which you can then
100          set as you desire).
101        """
102        basicArgs = self._basicKArgs(**kargs)
103        basicArgs.setdefault("numStatusCols", self._numStatusCols)
104        gs = _StatusConfigGridSet(
105            master = self._master,
106            label = label,
107            dataWdg = dataWdg,
108            cfgWdg = cfgWdg,
109            units = units,
110        **basicArgs)
111        self._nextRow = gs.row + 1
112        self._nextCol = max(gs.nextCol, self._nextCol)
113        self._maxNextCol = max(self._maxNextCol, self._nextCol)
114
115        if cat is not None:
116            self.addShowHideWdg(cat, gs.wdgSet)
117
118        # set show/hide category ConfigCat for configuration widgets
119        if cfgWdg:
120            self.addShowHideWdg(ConfigCat, gs.cfgWdg)
121            self.addShowHideWdg(ConfigCat, gs.cfgUnitsWdg)
122
123        return gs
124
125
126class _StatusConfigGridSet(Gridder._BaseGridSet):
127    def __init__ (self,
128        master,
129        label = None,
130        dataWdg = None,
131        units = None,
132        cfgWdg = None,
133        cfgUnits = None,
134        row = 0,
135        col = 0,
136        colSpan = 1,
137        cfgColSpan = None,
138        sticky = "e",
139        cfgSticky = None,
140        numStatusCols = None,
141        helpText = None,
142        helpURL = None,
143    ):
144        """Creates and grids (in order) the following attributes:
145        - labelWdg: a label widget
146        - dataWdg: one or more status widgets
147        - unitsWdg: units
148        (the following are all None if cfgWdg not specified):
149        - cfgWdg: one or more config widgets
150        - cfgUnitsWdg: a config units label
151
152        Inputs:
153        - label         label text, variable, widget, None, False or "" (see Notes)
154        - dataWdg       the status widgets: a widget or sequence of widgets,
155                        each of which can be None or False (see Notes)
156        - units         status units text, variable, widget, None, False or "" (see Notes)
157        - cfgWdg        one or more configuration widgets (same rules as dataWdg)
158        - cfgUnits      units for the config widget; defaults to units (but see Error Conditions below);
159                        ignored if cfgWdg is None or True
160        - cat           one or more show/hide categories; if specified then all widgets are added
161                        to the show/hide list using these categories
162        - row           row in which to grid; -1 means the same row as last time; default is the next row
163        - col           column at which to start gridding; default is the default column
164        - colSpan       column span for each of the data (status) widgets
165        - cfgColSpan    column span for each of the config widgets; defaults to colSpan
166        - sticky        sticky option for the status widgets
167        - cfgSticky     sticky option for the config widgets; defaults to sticky
168        - numStatusCols number of columns for status widgets (including units but not label);
169                        if None then the first configuration widget is gridded in the next column after
170                        the last status widget.
171        - helpText      help text for any created widgets; if True then copied from the first dataWdg
172        - helpURL       help URL for any created widgets; if True then copied from the first dataWdg
173
174        Error Conditions:
175        - Raise ValueError if units and cfgUnits are the same widget (but only if cfgWdg is
176          not None or False, because otherwise cfgUnits is ignored).
177          This is because a widget cannot be gridded in two places.
178        - Raise RuntimeError if numStatusCols is not None and you use more than numStatusCols columns
179          for status widgets
180        """
181        if cfgColSpan is None:
182            cfgColSpan = colSpan
183        if cfgUnits is None:
184            cfgUnits = units
185        if cfgSticky is None:
186            cfgSticky = sticky
187        if numStatusCols is not None:
188            numStatusCols = int(numStatusCols)
189
190        Gridder._BaseGridSet.__init__(self,
191            master,
192            row,
193            col,
194            helpText = helpText,
195            helpURL = helpURL,
196        )
197
198        self._numStatusCols = numStatusCols
199
200        self._setHelpFromDataWdg(dataWdg)
201
202        self.labelWdg = self._makeWdg(label)
203        self._gridWdg(self.labelWdg, sticky="e", colSpan=1)
204
205        self.dataWdg = dataWdg
206        self._gridWdg(self.dataWdg, sticky=sticky, colSpan=colSpan)
207
208        self.unitsWdg = self._makeWdg(units)
209        self._gridWdg(self.unitsWdg, sticky="w", colSpan=1)
210
211        if self._numStatusCols is not None:
212            cfgStartCol = self.begCol + 1 + self._numStatusCols # 1 for label
213            overflowCols = self.nextCol - cfgStartCol
214            if overflowCols > 0:
215                raise RuntimeError("Too many status widgets; numStatusCols=%s; num used=%s" %
216                    (self._numStatusCols, self._numStatusCols + overflowCols))
217            self.nextCol = cfgStartCol
218
219        if cfgWdg:
220            self.cfgWdg = cfgWdg
221            self._gridWdg(self.cfgWdg, sticky=cfgSticky, colSpan=cfgColSpan)
222
223            self.cfgUnitsWdg = self._makeWdg(cfgUnits)
224            if self.cfgUnitsWdg and self.cfgUnitsWdg == self.unitsWdg:
225                raise ValueError("units is a widget, so cfgUnits must be specified and must be a different widget")
226            self._gridWdg(self.cfgUnitsWdg, sticky="w", colSpan=1)
227        else:
228            self.cfgWdg = None
229            self.cfgUnitsWdg = None
230            if cfgWdg is not False:
231                self.nextCol += cfgColSpan
232            if cfgUnits is not False:
233                self.nextCol += 1
234