Passed
Pull Request — master (#291)
by Marek
02:00
created

ProblemsDlg._addProblemsFleets()   F

Complexity

Conditions 20

Size

Total Lines 52
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 41
nop 3
dl 0
loc 52
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like osci.dialog.ProblemsDlg.ProblemsDlg._addProblemsFleets() 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
21
import pygameui as ui
22
import re
23
from osci import gdata, client, res
24
import ige.ospace.Const as Const
25
from ige.ospace import Utils, Rules
26
import string, math, copy
27
28
class ProblemsDlg:
29
    """Displays 'Problem locator' dialog.
30
31
    """
32
    def __init__(self, app):
33
        self.app = app
34
        self.createUI()
35
36
    def display(self):
37
        self.show()
38
        self.win.show()
39
        # register for updates
40
        if self not in gdata.updateDlgs:
41
            gdata.updateDlgs.append(self)
42
43
    def hide(self):
44
        self.win.setStatus(_("Ready."))
45
        self.win.hide()
46
        # unregister updates
47
        if self in gdata.updateDlgs:
48
            gdata.updateDlgs.remove(self)
49
50
    def update(self):
51
        self.show()
52
53
    class Problems:
54
        def __init__(self, win):
55
            self.items = []
56
            self.checkboxes = {gdata.CRI : win.vCritical.checked,
57
                               gdata.MAJ : win.vMajor.checked,
58
                               gdata.MIN : win.vMinor.checked,
59
                               gdata.INFO: win.vInfo.checked}
60
61
        def append(self, severity, item):
62
            if self.checkboxes[severity]:
63
                item.foreground = gdata.sevColors[severity]
64
                self.items.append(item)
65
66
    def _addProblemsStructStatus(self, problems, struct, planet):
67
        player = client.getPlayer()
68
        status = struct[Const.STRUCT_IDX_STATUS]
69
        tech = client.getFullTechInfo(struct[Const.STRUCT_IDX_TECHID])
70
71
        if hasattr(player, 'techs'):
72
            techEff = Rules.techImprEff[player.techs.get(struct[Const.STRUCT_IDX_TECHID], Rules.techBaseImprovement)]
73
        else:
74
            techEff = Rules.techImprEff[Rules.techBaseImprovement]
75
76
        HPturn = max(1, int(0.02 * tech.maxHP * techEff))
77
        turnsToDestroy = math.ceil(struct[Const.STRUCT_IDX_HP] / HPturn)
78
79
        if turnsToDestroy < Rules.turnsPerDay * 2:
80
            severity = gdata.MAJ
81
            if turnsToDestroy < Rules.turnsPerDay:
82
                severity = gdata.CRI
83
        else:
84
            severity = gdata.MIN
85
86
        if not status & Const.STRUCT_STATUS_ON:
87
            # structure is off
88
            problems.append(severity, ui.Item(planet.name, tOID = planet.oid, tType = Const.T_PLANET,
89
                    vDescription = _('Structure (%s) is off and will be destroyed in %s turns.') % (tech.name, res.formatTime(turnsToDestroy))))
90
91
        if status & Const.STRUCT_STATUS_DETER:
92
            problems.append(gdata.MAJ, ui.Item(planet.name, tOID = planet.oid, tType = Const.T_PLANET,
93
                vDescription = _('Structure (%s) is deteriorating.') % (tech.name,)))
94
        if status & Const.STRUCT_STATUS_NOBIO:
95
            problems.append(gdata.INFO, ui.Item(planet.name, tOID = planet.oid, tType = Const.T_PLANET,
96
                vDescription = _('Structure (%s) has insufficient supply of biomatter.') % (tech.name,)))
97
        if status & Const.STRUCT_STATUS_NOEN:
98
            problems.append(gdata.INFO, ui.Item(planet.name, tOID = planet.oid, tType = Const.T_PLANET,
99
                vDescription = _('Structure (%s) has insufficient supply of energy.') % (tech.name,)))
100
        if status & Const.STRUCT_STATUS_NOPOP:
101
            problems.append(gdata.INFO, ui.Item(planet.name, tOID = planet.oid, tType = Const.T_PLANET,
102
                vDescription = _('Structure (%s) has insufficient supply of workers.') % (tech.name,)))
103
        if status & Const.STRUCT_STATUS_REPAIRING:
104
            problems.append(gdata.INFO, ui.Item(planet.name, tOID = planet.oid, tType = Const.T_PLANET,
105
                vDescription = _('Structure (%s) is repairing.') % (tech.name,)))
106
107
    def _addProblemsFleets(self, problems, fleet):
108
        player = client.getPlayer()
109
        energyReserve = fleet.storEn  * 100 / fleet.maxEn
110
        system = None
111
112
        maxRefuelMax = 0
113
        if hasattr(fleet, 'orbiting') and fleet.orbiting:
114
            system = client.get(fleet.orbiting, noUpdate = 1)
115
            if hasattr(system, 'planets'):
116
                for planetID in system.planets:
117
                    planet = client.get(planetID, noUpdate = 1)
118
                    if hasattr(planet, 'owner') and hasattr(planet, 'refuelMax'):
119
                        if player.diplomacyRels.has_key(planet.owner):
120
                            dipl = client.getDiplomacyWith(planet.owner)
121
                            if dipl.pacts.has_key(Const.PACT_ALLOW_TANKING) and dipl.pacts[Const.PACT_ALLOW_TANKING][0] == Const.PACT_ACTIVE:
122
                                maxRefuelMax = max(maxRefuelMax, planet.refuelMax)
123
                        else:
124
                            if planet.owner == player.oid:
125
                                maxRefuelMax = max(maxRefuelMax, planet.refuelMax)
126
        else:
127
            # we do not report fleets in flight
128
            return
129
130
        systemName = res.getUnknownName()
131
        if system and hasattr(system, "name"):
132
            systemName = system.name
133
134
        # is fleet named?
135
        if hasattr(fleet,'customname') and fleet.customname:
136
            name = fleet.customname
137
        else:
138
            name = getattr(fleet, "name", None)
139
140
        note = _(' and is NOT refueling')
141
        if maxRefuelMax:
142
            severity = gdata.INFO
143
            if maxRefuelMax <= energyReserve:
144
                note = _(' and CAN refuel, but reached planet maximum refuel amount')
145
            else:
146
                note = _(' and IS refueling')
147
        elif energyReserve < 25:
148
            severity = gdata.CRI
149
        elif energyReserve < 50:
150
            severity = gdata.MAJ
151
        elif energyReserve == 100:
152
            problems.append(gdata.INFO, ui.Item(systemName, tOID = fleet.oid, tType = Const.T_FLEET,
153
                        vDescription = _('Fleet "%s" has full fuel tanks.') % (name)))
154
            return
155
        else:
156
            severity = gdata.INFO
157
        problems.append(severity, ui.Item(systemName, tOID = fleet.oid, tType = Const.T_FLEET,
158
                    vDescription = _('Fleet "%s" is low on fuel [%d %%]%s.') % (name, energyReserve, note)))
159
160
    def _addProblemsMaterial(self, problems, system, mat, totalMat, material):
161
        if mat >= 0:
162
            return
163
        surplusTurns = totalMat / (-mat)
164
        if surplusTurns < Rules.turnsPerDay * 7:
165
            severity = gdata.MAJ
166
        elif surplusTurns < Rules.turnsPerDay * 2:
167
            severity = gdata.CRI
168
        else:
169
            severity = gdata.MIN
170
171
        if totalMat > 0:
172
            note = _(' surplus %d (%s)' % (totalMat, res.formatTime(surplusTurns)))
173
        else:
174
            note = _(' with no surplus left!')
175
        problems.append(severity, ui.Item(system.name, tOID = system.oid, tType = Const.T_SYSTEM,
176
                     vDescription = _('%s decreasing - last turn change %d, %s.') % (material, mat, note)))
177
178
    def _addProblemsResearch(self, problems):
179
        player = client.getPlayer()
180
        totalEtc = 0
181
        # compute length of research queue
182
        for task in player.rsrchQueue:
183
            tech = client.getTechInfo(task.techID)
184
            fulltech = client.getFullTechInfo(task.techID)
185
            researchSci = Utils.getTechRCost(player, task.techID, task.improvement)
186
            maxImprovement = min(Rules.techMaxImprovement,fulltech.maxImprovement)
187
            maxImpTotalSci = 0
188
            if task.improveToMax and task.improvement < maxImprovement:
189
                for impr in range(task.improvement+1,maxImprovement+1):
190
                    maxImpTotalSci += Utils.getTechRCost(player, task.techID, impr)
191
            if task.changeSci > 0:
192
                value = float(researchSci - task.currSci) / max(task.changeSci, player.effSciPoints)
193
                totalEtc += int(value + 1)
194
                if player.effSciPoints != 0:
195
                    totalEtc += float(maxImpTotalSci) / player.effSciPoints
196
            elif task.changeSci < 0:
197
                totalEtc -= float(task.currSci) / min(task.changeSci, player.effSciPoints)
198
            elif player.effSciPoints > 0:
199
                value = float(researchSci) / player.effSciPoints
200
                totalEtc += int(value + 1)
201
                totalEtc += float(maxImpTotalSci) / player.effSciPoints
202
            else:
203
                totalEtc = 99999
204
                break
205
206
        # check empty research queue
207
        if totalEtc == 0 and len(player.rsrchQueue) == 0 and player.effSciPoints > 0:
208
            problems.append(gdata.CRI, ui.Item(_('Research'), tType = Const.T_TECHNOLOGY,
209
                vDescription = _('Research queue is empty.')))
210
        # check short reseach queue
211
        elif totalEtc < Rules.turnsPerDay * 2:
212
            severity = gdata.MIN
213
            if totalEtc < Rules.turnsPerDay:
214
                severity = gdata.MAJ
215
            problems.append(severity, ui.Item(_('Research'), tType = Const.T_TECHNOLOGY,
216
                        vDescription = _('Research queue ends in %s turns, %d item(s) on list.') % (res.formatTime(totalEtc), len(player.rsrchQueue))))
217
218
    def _addProblemsGlobalQueues(self, problems):
219
        player = client.getPlayer()
220
        ## go through all planets to understand the state of global queues
221
        # holder for (number , eff production) of planets set to each queue
222
        globalQueueStats=[(0,0), (0,0), (0,0), (0,0), (0,0)]
223
        queConstValues = [0, 0, 0, 0, 0]
224
        queEtc = [0, 0, 0, 0, 0]
225
226
        for planetID in player.planets:
227
            planet = client.get(planetID, noUpdate = 1)
228
            globalQueueStats[planet.globalQueue] = tuple([sum(x) for x in zip(globalQueueStats[planet.globalQueue], (1, planet.effProdProd))])
229
230
        # evaluate depletion rate of the global queue
231
        for queue in xrange(5):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
232
            quePlanets, queEffProd = globalQueueStats[queue]
233
            for task in player.prodQueues[queue]:
234
                if task.isShip:
235
                    tech = player.shipDesigns[task.techID]
236
                else:
237
                    tech = client.getFullTechInfo(task.techID)
238
                queConstValues[queue] += task.quantity * tech.buildProd
239
            if queEffProd > 0:
240
                queEtc[queue] = math.ceil(float(queConstValues[queue])/queEffProd)
241
            else:
242
                queEtc[queue] = 99999
243
244
        # creation of items with global queue problems
245
        for queue in xrange(1, 5):
246
            queName = res.globalQueueName(queue)
247
            quePlanets = globalQueueStats[queue][0]
248
            # check empty global production queue with at least one planet [so its relevant]
249
            if queConstValues[queue] == 0 and  quePlanets > 0:
250
                problems.append(gdata.CRI, ui.Item(_('Global queue ' + queName), tType = Const.T_QUEUE,
251
                    vDescription = _('Global production queue {0} used by {1} planet(s) is empty.'.format(queName, quePlanets))))
252
            # check end of global production queue
253
            elif queEtc[queue] < Rules.turnsPerDay * 2:
254
                severity = gdata.MIN
255
                if queEtc[queue] < Rules.turnsPerDay:
256
                    severity = gdata.MAJ
257
                problems.append(severity, ui.Item(_('Global queue ' + queName), tType = Const.T_QUEUE,
258
                    vDescription = _('Global production queue {0} used by {1} planet(s) runs out in {2} turns.'.format(queName, quePlanets, res.formatTime(queEtc[queue])))))
259
        return queEtc[0] # time of depletion of the default queue will be reused later
260
261
    def _addProblemsSlots(self, problems, system):
262
        player = client.getPlayer()
263
        playerPlanets = set(system.planets) & set(player.planets)
264
        freeSlots = {}
265
        for planetID in system.planets:
266
            planet = client.get(planetID, noUpdate = 1)
267
            freeSlots[planetID] = planet.plSlots - len(planet.slots)
268
        buildingInfo = {}
269
270
        for planetID in playerPlanets:
271
            planet = client.get(planetID, noUpdate = 1)
272
            if planet.prodQueue and planet.effProdProd > 0:
273
                for task in planet.prodQueue:
274
                    if task.isShip:
275
                        continue
276
                    tech = client.getFullTechInfo(task.techID)
277
                    if tech.isStructure and hasattr(task, "demolishStruct") and task.demolishStruct == 0:
278
                        freeSlots[planetID] -= task.quantity
279
                        # information about source and target of constructing
280
                        if buildingInfo.has_key((planetID, task.targetID)):
281
                            buildingInfo[(planetID, task.targetID)] += task.quantity
282
                        else:
283
                            buildingInfo[(planetID, task.targetID)] = task.quantity
284
                    elif tech.isProject and tech.id == 3802:
285
                        # we are constructing Habitable Surface Expansion
286
                        # so after construction we will have new slot on the planet
287
                        fleeSlots[task.targetID] += 1
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable fleeSlots does not seem to be defined.
Loading history...
288
289
        for planetID, free in freeSlots.items():
290
            if free < 0:
291
                # not enough space, report for every planet that builds on this one
292
                planet = client.get(planetID, noUpdate = 1)
293
                for (sourceID, targetID), quantity in buildingInfo.items():
294
                    if planetID == targetID:
295
                        source = client.get(sourceID, noUpdate = 1)
296
                        problems.append(gdata.MAJ, ui.Item(source.name, tOID = sourceID, tType = Const.T_PLANET,
297
                            vDescription = _('There is no space for all constructed buildings on %s.') % (planet.name)))
298
299
    def _addProblemsDefaultQueue(self, problems, planet, defaultQueueEtc):
300
        player = client.getPlayer()
301
        if not planet.effProdProd > 0:
302
            return
303
        planetEtc = 0
304
        # compute length of production queue
305
        for task in planet.prodQueue:
306
            if task.isShip:
307
                tech = player.shipDesigns[task.techID]
308
            else:
309
                tech = client.getFullTechInfo(task.techID)
310
            modifier = Rules.buildOnAnotherPlanetMod if task.targetID != planet.oid else 1
311
            planetEtc += math.ceil(float(task.quantity * tech.buildProd * modifier - task.currProd) / planet.effProdProd)
312
313
        etc = planetEtc + defaultQueueEtc
314
        # check empty production queue
315
        if not etc:
316
            problems.append(gdata.CRI, ui.Item(planet.name, tOID = planet.oid, tType = Const.T_PLANET,
317
                vDescription = _('Production queue is empty.')))
318
            return
319
        if etc < Rules.turnsPerDay:
320
            severity = gdata.MAJ
321
        elif etc < Rules.turnsPerDay * 2:
322
            severity = gdata.MIN
323
        else:
324
            severity = gdata.INFO
325
326
        problems.append(severity, ui.Item(planet.name, tOID = planet.oid, tType = Const.T_PLANET,
327
            vDescription = _('Production queue may end in {0} turns ({1} directly in planet queue).'.format(res.formatTime(etc), res.formatTime(planetEtc)))))
328
329
    def show(self):
330
        critical = self.win.vCritical.checked
331
        major = self.win.vMajor.checked
332
        minor = self.win.vMinor.checked
333
        info = self.win.vInfo.checked
334
335
        disp = 1
336
337
        player = client.getPlayer()
338
        problems = self.Problems(self.win)
339
        systems = set([])
340
        for planetID in player.planets:
341
            planet = client.get(planetID, noUpdate = 1)
342
            systems.add(planet.compOf)
343
344
        defaultQueueEtc = self._addProblemsGlobalQueues(problems)
345
346
347
        for systemID in systems:
348
            system = client.get(systemID, noUpdate = 1)
349
            bio = 0
350
            totalBio = 0
351
            en = 0
352
            totalEn = 0
353
            # holds modified planets
354
            planetCopies = {}
355
356
            for planetID in system.planets:
357
                planet = client.get(planetID, noUpdate = 1)
358
                # copy of planet to change plSlots count
359
                if hasattr(planet, 'owner') and planet.owner == player.oid:
360
                    # add planet to the global queue stats
361
                    # compute bio and en for system
362
                    bio += planet.changeBio
363
                    totalBio += max(0, planet.storBio - planet.minBio)
364
                    en  += planet.changeEn
365
                    totalEn += max(0, planet.storEn - planet.minEn)
366
                    # the planet needs to have global queue 0 - the default one - to have its queue reported
367
                    if self.win.vPlanets.checked:
368
                        if not planet.globalQueue:
369
                            self._addProblemsDefaultQueue(problems, planet, defaultQueueEtc)
370
                        for struct in planet.slots:
371
                            self._addProblemsStructStatus(problems, struct, planet)
372
373
            # free slots within the system
374
            self._addProblemsSlots(problems, system)
375
            # check bio for system
376
            if self.win.vSystems.checked:
377
                self._addProblemsMaterial(problems, system, bio, totalBio, 'Bio')
378
                self._addProblemsMaterial(problems, system, en, totalEn, 'En')
379
380
        # check fleets
381
        if self.win.vFleets.checked:
382
            for fleetID in player.fleets:
383
                fleet = client.get(fleetID, noUpdate = 1)
384
                self._addProblemsFleets(problems, fleet)
385
386
        # check research queue
387
        if self.win.vResearch.checked:
388
            self._addProblemsResearch(problems)
389
            
390
391
        self.win.vProblems.items = problems.items
392
        self.win.vProblems.itemsChanged()
393
394
    def onClose(self, widget, action, data):
395
        self.hide()
396
397
    def onShowSource(self, widget, action, data):
398
        item = self.win.vProblems.selection[0]
399
        if item.tType == Const.T_FLEET:
400
            object = client.get(item.tOID, noUpdate = 1)
401
            # center on map
402
            if hasattr(object, "x"):
403
                gdata.mainGameDlg.win.vStarMap.highlightPos = (object.x, object.y)
404
                gdata.mainGameDlg.win.vStarMap.setPos(object.x, object.y)
405
                self.hide()
406
                return
407
        elif item.tType in (Const.T_SYSTEM, Const.T_PLANET):
408
            if item.tOID != Const.OID_NONE:
409
                gdata.mainGameDlg.onSelectMapObj(None, None, item.tOID)
410
                return
411
        elif item.tType == Const.T_TECHNOLOGY:
412
            gdata.mainGameDlg.researchDlg.display()
413
            return
414
        elif item.tType == Const.T_QUEUE:
415
            gdata.mainGameDlg.globalQueuesDlg.display()
416
        self.win.setStatus(_("Cannot show location."))
417
418
    def onShowLocation(self, widget, action, data):
419
        item = self.win.vProblems.selection[0]
420
        if item.tType in (Const.T_SYSTEM, Const.T_PLANET, Const.T_FLEET):
421
            object = client.get(item.tOID, noUpdate = 1)
422
            # center on map
423
            if hasattr(object, "x"):
424
                gdata.mainGameDlg.win.vStarMap.highlightPos = (object.x, object.y)
425
                gdata.mainGameDlg.win.vStarMap.setPos(object.x, object.y)
426
                self.hide()
427
                return
428
        self.win.setStatus(_("Cannot show location."))
429
430
    def onToggleCondition(self, widget, action, data):
431
        self.update()
432
433
    def createUI(self):
434
        screenWidth, screenHeight = gdata.scrnSize
435
        # size of dialog in layout metrics (for SimpleGridLM)
436
        cols = 40
437
        rows = 29
438
        # dialog width and height in pixels
439
        isSmallWin = screenHeight == 600 and screenWidth == 800
440
        width = cols * 20 + 4 * (not isSmallWin)
441
        height = rows * 20 + 4 * (not isSmallWin)
442
        #creating dialog window
443
        self.win = ui.Window(self.app,
444
            modal = 1,
445
            escKeyClose = 1,
446
            movable = 0,
447
            title = _("Problems Locator"),
448
            titleOnly = isSmallWin,
449
            #rect = ui.Rect((screenWidth - width) / 2, ((screenHeight - height) / 2) * (not isSmallWin), width, height),
450
            rect = ui.Rect((screenWidth - 800 - 4 * (not isSmallWin)) / 2, (screenHeight - 600 - 4 * (not isSmallWin)) / 2, width, height),
451
            layoutManager = ui.SimpleGridLM(),
452
        )
453
        self.win.subscribeAction('*', self)
454
        # first row is window title
455
        rows -= 1
456
457
        ui.Listbox(self.win, layout = (0, 0, cols, rows - 2), id = 'vProblems',
458
            columns = [(_('Location'), 'text', 10, ui.ALIGN_W),
459
            (_('Problem description'), 'vDescription', 30, ui.ALIGN_W)],
460
            columnLabels = 1, action='onShowSource', rmbAction='onShowLocation')
461
462
        btnWidth = 4
463
        ui.Check(self.win, layout = (btnWidth * 0, rows - 2, btnWidth, 1), id = 'vSystems',
464
            text = _('Systems'), action = 'onToggleCondition', checked = 1)
465
        ui.Check(self.win, layout = (btnWidth * 1, rows - 2, btnWidth, 1), id = 'vPlanets',
466
            text = _('Planets'), action = 'onToggleCondition', checked = 1)
467
        ui.Check(self.win, layout = (btnWidth * 2, rows - 2, btnWidth, 1), id = 'vFleets',
468
            text = _('Fleets'), action = 'onToggleCondition', checked = 1)
469
        ui.Check(self.win, layout = (btnWidth * 3, rows - 2, btnWidth, 1), id = 'vResearch',
470
            text = _('Research'), action = 'onToggleCondition', checked = 1)
471
472
        ui.Check(self.win, layout = (btnWidth * 6, rows - 2, btnWidth, 1), id = 'vCritical',
473
            text = _('Critical'), action = 'onToggleCondition', checked = 1)
474
        ui.Check(self.win, layout = (btnWidth * 7, rows - 2, btnWidth, 1), id = 'vMajor',
475
            text = _('Major'), action = 'onToggleCondition', checked = 1)
476
        ui.Check(self.win, layout = (btnWidth * 8, rows - 2, btnWidth, 1), id = 'vMinor',
477
            text = _('Minor'), action = 'onToggleCondition', checked = 1)
478
        ui.Check(self.win, layout = (btnWidth * 9, rows - 2, btnWidth, 1), id = 'vInfo',
479
            text = _('Info'), action = 'onToggleCondition', checked = 0)
480
481
        # dialog bottom line
482
        ui.Title(self.win, layout = (0, rows - 1, cols - 5, 1))
483
        ui.TitleButton(self.win, layout = (cols - 5, rows - 1, 5, 1), text = _("Close"), action = 'onClose')
484