Completed
Push — master ( 14e154...734e4f )
by Marek
16s queued 15s
created

osci.dialog.TechInfoDlg   F

Complexity

Total Complexity 88

Size/Duplication

Total Lines 461
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 352
dl 0
loc 461
rs 2
c 0
b 0
f 0
wmc 88

10 Functions

Rating   Name   Duplication   Size   Complexity  
A getTechShortname() 0 6 3
A perc100_2Text() 0 2 1
A getRateName() 0 2 1
A bool2Text() 0 2 2
A perc2Text() 0 2 1
A structWeapons2Text() 0 15 5
A getTechName() 0 5 2
A addAttr() 0 3 1
A cclass2Text() 0 2 1
A percBase1_2Text() 0 2 1

20 Methods

Rating   Name   Duplication   Size   Complexity  
A TechInfoDlg._getStratResText() 0 16 5
A TechInfoDlg.__init__() 0 3 1
A TechInfoDlg._getTechType() 0 11 5
A TechInfoDlg._prepareTypeButtons() 0 11 1
B TechInfoDlg._processAttributes() 0 18 7
A TechInfoDlg._preResearch() 0 6 1
A TechInfoDlg.display() 0 7 2
A TechInfoDlg.update() 0 2 1
C TechInfoDlg._researchContext() 0 26 11
A TechInfoDlg._getGranularDependency() 0 11 5
A TechInfoDlg.hide() 0 6 2
A TechInfoDlg._productionDependency() 0 12 3
B TechInfoDlg.createUI() 0 42 1
A TechInfoDlg._stratResources() 0 14 5
A TechInfoDlg.onCancel() 0 2 1
A TechInfoDlg.show() 0 25 5
A TechInfoDlg._descriptionFlavor() 0 15 2
A TechInfoDlg._getPlayerRace() 0 9 2
A TechInfoDlg.onClose() 0 2 1
C TechInfoDlg._researchEnablement() 0 18 9

How to fix   Complexity   

Complexity

Complex classes like osci.dialog.TechInfoDlg often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#
2
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
3
#
4
#  This file is part of Outer Space.
5
#
6
#  Outer Space is free software; you can redistribute it and/or modify
7
#  it under the terms of the GNU General Public License as published by
8
#  the Free Software Foundation; either version 2 of the License, or
9
#  (at your option) any later version.
10
#
11
#  Outer Space is distributed in the hope that it will be useful,
12
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
#  GNU General Public License for more details.
15
#
16
#  You should have received a copy of the GNU General Public License
17
#  along with Outer Space; if not, write to the Free Software
18
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
#
20
import string
21
22
import pygameui as ui
23
24
from ige.ospace import Rules, Utils
25
import ige.ospace.Const as Const
26
27
from osci import gdata, res, client
28
29
30
def cclass2Text(cclass):
31
    return [_("small"), _("medium"), _("large"), _("planet")][cclass]
32
33
34
def structWeapons2Text(array):
35
    string = ''
36
    i = 0
37
    j = 0
38
    for weaponNum in array:
39
        if i>3:
40
            break
41
        if weaponNum > 0:
42
            if j > 0:
43
                string += _(', ')
44
            string += _('%d') % ( weaponNum )
45
            string += [_("S"), _("M"), _("L"), _("P"), _("?"), _("?"), _("?"), _("?"), _("?"), _("?")][i]
46
            j+=1
47
        i+=1
48
    return string
49
50
51
def bool2Text(value):
52
    return _("Yes") if value else _("No")
53
54
55
def perc2Text(value):
56
    return _('%d%%') % (value * 100)
57
58
59
def percBase1_2Text(value):
60
    return _('%+d%%') % ((value - 1) * 100)
61
62
63
def perc100_2Text(value):
64
    return _('%d%%') % (value)
65
66
67
def getTechName(techID):
68
    try:
69
        return client.getFullTechInfo(techID).name
70
    except:
71
        return _('Unknown')
72
73
74
def getTechShortname(techID):
75
    tech = client.getFullTechInfo(techID)
76
    try:
77
        return tech.shortname if tech.shortname else tech.name
78
    except:
79
        return _('Unknown')
80
81
82
def getRateName(rate):
83
    return _('%d Turns') % (int(rate))
84
85
86
V_NONE = 0x00
87
V_STRUCT = 0x01
88
V_HULL = 0x02
89
V_SEQUIP = 0x04
90
V_PROJECT = 0x08
91
V_EFF = 0x10 # attr * techEff
92
V_EFF_REV = 0x20 # attr / techEff
93
V_ALL = V_STRUCT|V_HULL|V_SEQUIP|V_PROJECT
94
95
techAttrs = {}
96
97
defaultAttr = (_('Not specified'), V_NONE, True, None, int)
98
99
100
def addAttr(attr, descr, props, showIfDefault, default=0, convertor=int):
101
    global techAttrs
102
    techAttrs[attr] = (descr, props, showIfDefault, default, convertor)
103
104
105
addAttr('buildProd', _('Constr. reqs - construction points'), V_ALL, 0)
106
107
addAttr('operBio', _('Operational reqs - biomatter'), V_ALL, 0)
108
addAttr('operMin', _('Operational reqs - minerals'), V_ALL, 0)
109
addAttr('operEn', _('Operational reqs - energy'), V_ALL, 0)
110
addAttr('operWorkers', _('Operational reqs - workers'), V_ALL, 0)
111
112
addAttr('prodBio', _('Production - biomatter'), V_STRUCT | V_EFF, 0)
113
addAttr('prodMin', _('Production - minerals'), V_STRUCT | V_EFF, 0)
114
addAttr('prodEn', _('Production - energy'), V_STRUCT | V_EFF, 0)
115
addAttr('prodPop', _('Production - population'), V_STRUCT | V_EFF, 0)
116
addAttr('prodProd', _('Production - constr. points'), V_STRUCT | V_PROJECT | V_EFF, 0)
117
addAttr('prodSci', _('Production - research points'), V_STRUCT | V_PROJECT | V_EFF, 0)
118
addAttr('prodEnv', _('Production - env. effect'), V_STRUCT | V_EFF, 0)
119
120
addAttr('solarMod', _('Orbital Shift Effect'), V_STRUCT | V_EFF, 0)
121
122
addAttr('storBio', _('Storage - biomatter'), V_STRUCT | V_EFF, 0)
123
addAttr('storMin', _('Storage - minerals'), V_STRUCT | V_EFF, 0)
124
addAttr('storEn', _('Storage - energy'), V_ALL | V_EFF, 0)
125
addAttr('storPop', _('Accommodate population'), V_STRUCT | V_EFF, 0)
126
127
addAttr('revoltThr', _('Lowers revolt threshold by'), V_STRUCT | V_PROJECT | V_EFF, 0)
128
addAttr('moraleTrgt', _('Increases max morale by'), V_STRUCT | V_PROJECT | V_EFF, 0)
129
addAttr('govPwr', _('Government power'), V_STRUCT | V_EFF, 0)
130
addAttr('maxHP', _('Hit points'), V_STRUCT | V_HULL | V_SEQUIP | V_EFF, 0)
131
132
addAttr('scannerPwr', _('Scanner power'), V_STRUCT | V_SEQUIP | V_EFF, 0)
133
addAttr('planetShield', _('Planetary shield'), V_STRUCT | V_EFF, 0)
134
addAttr('systemAtt', _('Fleet attack (bonus)'), V_STRUCT | V_EFF, 0)
135
addAttr('systemDef', _('Fleet defense (bonus)'), V_STRUCT | V_EFF, 0)
136
addAttr('refuelMax', _('Maximum refuel percent'), V_STRUCT | V_EFF, 0, convertor=perc100_2Text)
137
addAttr('refuelInc', _('Refuel increase percent'), V_STRUCT | V_EFF, 0, convertor=perc100_2Text)
138
addAttr('repairShip', _('Ship repair percent'), V_STRUCT | V_EFF, 0, convertor=perc100_2Text)
139
addAttr('upgradeShip', _('Ship upgrade capacity'), V_STRUCT | V_EFF, 0)
140
addAttr('trainShipInc', _('Exp. points per turn'), V_STRUCT | V_EFF, 0, convertor=float)
141
addAttr('trainShipMax', _('Exp. cap (base exp multiple)'), V_STRUCT | V_EFF, 0)
142
addAttr('fleetSpeedBoost', _('Boost speed of fleets'), V_STRUCT | V_EFF, 0, convertor=float)
143
addAttr('structWeapons', _('Weapons'), V_STRUCT, 0, convertor=structWeapons2Text)
144
145
addAttr('weaponClass', _('Target class'), V_SEQUIP, True, convertor=cclass2Text)
146
addAttr('weaponDmgMin', _('Weapon minimum damage'), V_SEQUIP | V_EFF, 0)
147
addAttr('weaponDmgMax', _('Weapon maximum damage'), V_SEQUIP | V_EFF, 0)
148
addAttr('weaponIsMissile', _('Missile weapon (ECM counts)'), V_SEQUIP | V_HULL, 0, convertor=bool2Text)
149
addAttr('weaponIgnoreShield', _('Weapon ignore shield'), V_SEQUIP | V_HULL, 0, convertor=bool2Text)
150
addAttr('weaponAtt', _('Weapon attack'), V_SEQUIP | V_EFF, 0)
151
addAttr('weaponROF', _('Weapon Rate Of Fire'), V_SEQUIP, 0, convertor=float)
152
153
addAttr('mineclass', _('Mine Class Deployed'), V_STRUCT, 0, convertor=getTechShortname)
154
addAttr('minenum', _('Maximum Supported Mines'), V_STRUCT | V_EFF, 0, convertor=int)
155
addAttr('minerate', _('Mine Rate of Deploy'), V_STRUCT | V_EFF_REV, 0, convertor=getRateName)
156
157
addAttr('minHull', _('Minimum required hull'), V_SEQUIP | V_HULL, 0, convertor=cclass2Text)
158
addAttr('weight', _('Weight'), V_SEQUIP | V_HULL, 0)
159
addAttr('slots', _('Slots'), V_SEQUIP | V_HULL, 0)
160
addAttr('maxWeight', _('Maximum payload'), V_HULL, 0)
161
addAttr('engPwr', _('Engine power'), V_SEQUIP | V_EFF, 0)
162
addAttr('engStlPwr', _('Engine sub-light speed power'), V_SEQUIP | V_EFF, 0)
163
164
addAttr('signature', _('Scan signature'), V_SEQUIP | V_HULL, 0)
165
addAttr('signatureCloak', _('Signature visibility'), V_SEQUIP | V_HULL, 0)
166
addAttr('signatureDecloak', _('Signature visibility'), V_SEQUIP | V_HULL, 0)
167
addAttr('minSignature', _('Min. signature'), V_SEQUIP | V_HULL, 0)
168
169
addAttr('combatDef', _('Combat defence'), V_SEQUIP | V_HULL | V_EFF, 0)
170
addAttr('combatAtt', _('Combat attack'), V_SEQUIP | V_HULL | V_EFF, 0)
171
addAttr('missileDef', _('Missile defence'), V_SEQUIP | V_EFF, 0)
172
addAttr('combatAttPerc', _('Combat defense (extra)'), V_SEQUIP | V_HULL | V_EFF, 0, default=1, convertor=percBase1_2Text)
173
addAttr('combatDefPerc', _('Combat attack (extra)'), V_SEQUIP | V_HULL | V_EFF, 0, default=1, convertor=percBase1_2Text)
174
addAttr('missileDefPerc', _('Missile defence (extra)'), V_SEQUIP | V_EFF, 0, default=1, convertor=percBase1_2Text)
175
176
addAttr('shieldPerc', _('Shield strength'), V_SEQUIP | V_HULL | V_EFF, 0, convertor=perc2Text)
177
addAttr('shieldRechargeFix', _('Shield recharge fixed'), V_SEQUIP | V_HULL | V_EFF, 0)
178
addAttr('shieldRechargePerc', _('Shield recharge percent'), V_SEQUIP | V_HULL | V_EFF, 0, convertor=perc2Text)
179
addAttr('damageAbsorb', _('Armor damage absorbstion'), V_SEQUIP | V_HULL, 0)
180
181
addAttr('addMP', _('Device MP'), V_SEQUIP | V_HULL, 0)
182
183
184
class TechInfoDlg:
185
186
    def __init__(self, app):
187
        self.app = app
188
        self.createUI()
189
190
    def display(self, techID):
191
        self.techID = techID
192
        self.show()
193
        self.win.show()
194
        # register for updates
195
        if self not in gdata.updateDlgs:
196
            gdata.updateDlgs.append(self)
197
198
    def hide(self):
199
        self.win.setStatus(_("Ready."))
200
        self.win.hide()
201
        # unregister updates
202
        if self in gdata.updateDlgs:
203
            gdata.updateDlgs.remove(self)
204
205
    def update(self):
206
        self.show()
207
208
    def _getGranularDependency(self, techMod):
209
        descr = []
210
        if techMod[0]:
211
            descr.append(_(" - %d %% on planet's environment") % (techMod[0] * 100))
212
        if techMod[1]:
213
            descr.append(_(" - %d %% on planet's min. abundance") % (techMod[1] * 100))
214
        if techMod[2]:
215
            descr.append(_(" - %d %% on planet's en. abundance") % (techMod[2] * 100))
216
        if techMod[3]:
217
            descr.append(_(" - %d %% is not dependent on any planet's attribute") % (techMod[3] * 100))
218
        return descr
219
220
    def _productionDependency(self, tech):
221
        descr = []
222
        for techModName, text in [('prodBioMod', _('Bio')),
223
                                  ('prodEnMod', _('Energy')),
224
                                  ('prodProdMod', _('Constr. point')),
225
                                  ('prodSciMod', _('Research point'))]:
226
            techMod = getattr(tech, techModName)
227
            if techMod not in ([0, 0, 0, 0], [0, 0, 0, 1]):
228
                descr.append(_("%s production depends:" % text))
229
                descr.extend(self._getGranularDependency(techMod))
230
                descr.append("")
231
        return descr
232
233
    def _getStratResText(self, tech, attr):
234
        try:
235
            resourceDict = getattr(tech, attr)
236
        except AttributeError:
237
            return ""
238
        requiredStratRes = []
239
        for res in resourceDict:
240
            try:
241
                amount = resourceDict[res] / float(Rules.stratResAmountBig)
242
            except IndexError:
243
                # this is case of research resources - it's pure list, not a dictionary
244
                requiredStratRes += [gdata.stratRes[res]]
245
            else:
246
                # continuation of build resources
247
                requiredStratRes += ['{0} ({1})'.format(gdata.stratRes[res], amount)]
248
        return ', '.join(requiredStratRes)
249
250
    def _researchContext(self, tech):
251
        player = client.getPlayer()
252
253
        descr = []
254
        improvement = player.techs.get(self.techID, 0)
255
        if hasattr(tech, 'researchMod'):
256
            prefix = _('Improvement') if improvement else _('Research')
257
            descr.append(_('%s costs: %d') % (prefix, Utils.getTechRCost(player, self.techID)))
258
            descr.append('')
259
        # requires
260
        if tech.researchRequires and improvement == 0:
261
            descr.append(_('Research requires:'))
262
            for tmpTechID, improvement in tech.researchRequires:
263
                tmpTech = client.getTechInfo(tmpTechID)
264
                descr.append(_(' - %s improvement %d (TL%d)') % (tmpTech.name, improvement, tmpTech.level))
265
            if hasattr(tech, "researchReqSRes"):
266
                for stratRes in tech.researchReqSRes:
267
                    descr.append(_(" - %s (strategic resource)") % (gdata.stratRes[stratRes]))
268
            descr.append('')
269
        if hasattr(tech, "researchDisables") and tech.researchDisables:
270
            descr.append(_("Research disables:"))
271
            for tmpTechID in tech.researchDisables:
272
                tmpTech = client.getTechInfo(tmpTechID)
273
                descr.append(_(" - %s (TL%d)") % (tmpTech.name, tmpTech.level))
274
            descr.append('')
275
        return descr
276
277
    def _preResearch(self, tech):
278
        descr = []
279
        descr.append(_('Estimated use:'))
280
        descr.extend(tech.textPreRsrch.split('\n'))
281
        descr.append('')
282
        return descr
283
284
    def _getTechType(self, tech):
285
        techType = V_NONE
286
        if getattr(tech, 'isStructure', 0):
287
            techType = V_STRUCT
288
        elif getattr(tech, 'isShipHull', 0):
289
            techType = V_HULL
290
        elif getattr(tech, 'isShipEquip', 0):
291
            techType = V_SEQUIP
292
        elif getattr(tech, 'isProject', 0):
293
            techType = V_PROJECT
294
        return techType
295
296
    def _prepareTypeButtons(self, tech):
297
        techType = self._getTechType(tech)
298
299
        self.win.vStruct.pressed = techType == V_STRUCT
300
        self.win.vStruct.enabled = getattr(tech, 'isStructure', 0)
301
        self.win.vHull.pressed = techType == V_HULL
302
        self.win.vHull.enabled = getattr(tech, 'isShipHull', 0)
303
        self.win.vSEquip.pressed = techType == V_SEQUIP
304
        self.win.vSEquip.enabled = getattr(tech, 'isShipEquip', 0)
305
        self.win.vProject.pressed = techType == V_PROJECT
306
        self.win.vProject.enabled = getattr(tech, 'isProject', 0)
307
308
    def _processAttributes(self, tech):
309
        techEff = Rules.techImprEff[client.getPlayer().techs.get(self.techID, Rules.techBaseImprovement)]
310
        items = []
311
        techType = self._getTechType(tech)
312
313
        for attr in dir(tech):
314
            value = getattr(tech, attr)
315
            descr, props, showIfDef, default, convertor = techAttrs.get(attr, defaultAttr)
316
            if techType & props and (value != default or showIfDef):
317
                item = ui.Item(descr, tValue=convertor(value))
318
                if V_EFF & props:
319
                    item.font = 'normal-bold'
320
                    item.tValue = convertor(value * techEff)
321
                elif V_EFF_REV & props:
322
                    item.font = 'normal-bold'
323
                    item.tValue = convertor(value / techEff)
324
                items.append(item)
325
        return items
326
327
    def _getPlayerRace(self):
328
        player = client.getPlayer()
329
        raceChoosen = None
330
        if player.techLevel == 1:
331
            techID = set(player.techs).intersection(set([1990, 1991, 1992])).pop()  # these are the race-selecting techs
332
            raceChoosen = client.getTechInfo(techID).data
333
        else:
334
            raceChoosen = player.race
335
        return raceChoosen
336
337
    def _researchEnablement(self, tech):
338
        descr = []
339
        tmp = []
340
        raceChoosen = self._getPlayerRace()
341
342
        for improvement in range(1, 6):
343
            for tmpTechID in tech.researchEnables[improvement]:
344
                tmpTech = client.getTechInfo(tmpTechID)
345
                racesDispl = ""
346
                if 0 < len(tmpTech.researchRaces) < 3 and not raceChoosen:
347
                    racesDispl = _(", only for %s") % tmpTech.researchRaces
348
                if not raceChoosen or not tmpTech.researchRaces or raceChoosen in tmpTech.researchRaces:
349
                    tmp.append(_(' - %s (with improvement %d, on TL%d%s)') % (tmpTech.name, improvement, tmpTech.level, racesDispl))
350
        if tmp:
351
            descr.append(_('Research/Improvement enables:'))
352
            descr.extend(tmp)
353
            descr.append('')
354
        return descr
355
356
    def _stratResources(self, tech):
357
        descr = []
358
359
        requiredStratResForBuilding = self._getStratResText(tech, "buildSRes")
360
        requiredStratResForResearch = self._getStratResText(tech, "researchReqSRes")
361
        if len(requiredStratResForBuilding) > 0 or len(requiredStratResForResearch) > 0:
362
            descr.append(_("Required strategic resources:"))
363
            if len(requiredStratResForBuilding) > 0:
364
                descr.append(_(" - for building: %s") % requiredStratResForBuilding)
365
            if len(requiredStratResForResearch) > 0:
366
                descr.append(_(" - for research: %s") % requiredStratResForResearch)
367
368
            descr.append("")
369
        return descr
370
371
    def _descriptionFlavor(self, tech):
372
        descr = []
373
374
        # description
375
        descr.append(_('Description:'))
376
        if tech.textDescr != u'Not specified':
377
            descr.extend(tech.textDescr.split('\n'))
378
        else:
379
            descr.extend(tech.textPreRsrch.split('\n'))
380
        descr.append('')
381
        # flavor
382
        descr.append(_('Rumours:'))
383
        descr.extend(tech.textFlavor.split('\n'))
384
        descr.append('')
385
        return descr
386
387
    def show(self):
388
        tech = client.getTechInfo(self.techID)
389
        self.win.title = _('Technology: %s (TL%d)') % (tech.name, tech.level)
390
        # fill data
391
        # fill description
392
        descr = []
393
        descr.extend(self._researchContext(tech))
394
        if hasattr(tech, 'partialData') and hasattr(tech, 'textPreRsrch'):
395
            # preresearch info
396
            descr.extend(self._preResearch(tech))
397
        elif not hasattr(tech, 'partialData'):
398
            descr.extend(self._researchEnablement(tech))
399
            descr.extend(self._productionDependency(tech))
400
            descr.extend(self._stratResources(tech))
401
            descr.extend(self._descriptionFlavor(tech))
402
403
        if not len(descr):
404
            descr.append(_('No information available'))
405
        #
406
        self.win.vDescr.text = descr
407
408
        self._prepareTypeButtons(tech)
409
410
        self.win.vData.items = self._processAttributes(tech)
411
        self.win.vData.itemsChanged()
412
413
    def onCancel(self, widget, action, data):
414
        self.hide()
415
416
    def onClose(self, widget, action, data):
417
        self.hide()
418
419
    def createUI(self):
420
        w, h = gdata.scrnSize
421
        self.win = ui.Window(self.app,
422
                             modal=1,
423
                             escKeyClose=1,
424
                             titleOnly=w == 800 and h == 600,
425
                             movable=0,
426
                             title=_('Split Fleet'),
427
                             rect=ui.Rect((w - 800 - 4 * (w != 800)) / 2,
428
                                          (h - 600 - 4 * (h != 600)) / 2,
429
                                          800 + 4 * (w != 800),
430
                                          580 + 4 * (h != 600)),
431
                             layoutManager=ui.SimpleGridLM())
432
        self.win.subscribeAction('*', self)
433
        # tech data
434
        ui.Title(self.win, layout=(0, 0, 18, 1), text=_('Data'),
435
                 align=ui.ALIGN_W, font='normal-bold')
436
        ui.Listbox(self.win, layout=(0, 1, 18, 25), id='vData',
437
                   columns=((_('Property'), 'text', 11, ui.ALIGN_W),
438
                   (_('Value'), 'tValue', 7, ui.ALIGN_E)),
439
                   columnLabels=0)
440
        ui.Button(self.win, layout=(1, 26, 4, 1), text=_('Structure'),
441
                  id='vStruct', toggle=0, data=V_STRUCT)
442
        ui.Button(self.win, layout=(5, 26, 4, 1), text=_('Ship Hull'),
443
                  id='vHull', toggle=0, data=V_HULL)
444
        ui.Button(self.win, layout=(9, 26, 4, 1), text=_('Ship Eq.'),
445
                  id='vSEquip', toggle=0, data=V_SEQUIP)
446
        ui.Button(self.win, layout=(13, 26, 4, 1), text=_('Project'),
447
                  id='vProject', toggle=0, data=V_PROJECT)
448
        ui.Button(self.win, layout=(17, 26, 1, 1), text='',
449
                  id='vEmpty1', toggle=0)
450
        ui.Button(self.win, layout=(0, 26, 1, 1), text='',
451
                  id='vEmpty2', toggle=0)
452
        # text field
453
        ui.Title(self.win, layout=(18, 0, 22, 1), text=_('Description'),
454
                 align=ui.ALIGN_W, font='normal-bold')
455
        s = ui.Scrollbar(self.win, layout=(39, 1, 1, 26))
456
        t = ui.Text(self.win, layout=(18, 1, 21, 26), id='vDescr', editable=0)
457
        t.attachVScrollbar(s)
458
        # status bar + submit/cancel
459
        ui.TitleButton(self.win, layout=(35, 27, 5, 1), text=_('Close'), action='onClose')
460
        ui.Title(self.win, id='vStatusBar', layout=(0, 27, 35, 1), align=ui.ALIGN_W)
461